From e595d248c73207181f1edd8ae2607a723299803f Mon Sep 17 00:00:00 2001 From: qindel Date: Fri, 3 May 2024 10:21:34 +0000 Subject: [PATCH] refs #311 removes a lot of innecesaries directories and files --- composer.lock | 9979 ----------------- config/bundles.php | 15 - config/packages/cache.yaml | 19 - config/packages/debug.yaml | 5 - config/packages/doctrine.yaml | 43 - config/packages/doctrine_migrations.yaml | 6 - config/packages/framework.yaml | 24 - config/packages/mailer.yaml | 3 - config/packages/messenger.yaml | 24 - config/packages/monolog.yaml | 61 - config/packages/notifier.yaml | 12 - config/packages/routing.yaml | 12 - config/packages/security.yaml | 40 - config/packages/translation.yaml | 13 - config/packages/twig.yaml | 6 - config/packages/validator.yaml | 13 - config/packages/web_profiler.yaml | 15 - config/preload.php | 5 - config/routes.yaml | 3 - config/routes/annotations.yaml | 7 - config/routes/framework.yaml | 4 - config/routes/web_profiler.yaml | 8 - config/services.yaml | 27 - docs/ogdhcp_api_documentation.md | 121 - migrations/.gitignore | 0 native/Sources/Clients/README.es.txt | 9 - native/Sources/Clients/ogAdmClient/Makefile | 31 - .../Clients/ogAdmClient/ogAdmClient.cfg | 5 - .../Clients/ogAdmClient/sources/ogAdmClient.c | 2331 ---- .../Clients/ogAdmClient/sources/ogAdmClient.h | 183 - .../Clients/ogAdmClient/sources/ogAdmClient.o | Bin 108604 -> 0 bytes .../ogagent-oglive_3.0.0-20190520_all.deb | Bin 33624 -> 0 bytes ...gent-oglive_3.0.0-20190520_amd64.buildinfo | 182 - ...gagent-oglive_3.0.0-20190520_amd64.changes | 26 - .../Clients/ogagent/oglive/build-stamp | 0 .../Clients/ogagent/oglive/configure-stamp | 0 .../installed-by-dh_installdocs | 1 - .../Clients/ogagent/oglive/debian/files | 2 - .../debian/ogagent-oglive.debhelper.log | 14 - .../debian/ogagent-oglive/DEBIAN/control | 11 - .../debian/ogagent-oglive/DEBIAN/md5sums | 44 - .../debian/ogagent-oglive/DEBIAN/postinst | 21 - .../debian/ogagent-oglive/DEBIAN/postrm | 10 - .../ogagent-oglive/etc/ogagent/ogagent.cfg | 1 - .../ogagent-oglive/etc/ogagent/ogclient.cfg | 1 - .../debian/ogagent-oglive/usr/bin/ogagent | 6 - .../usr/share/OGAgent/cfg/ogagent.cfg | 27 - .../usr/share/OGAgent/cfg/ogclient.cfg | 11 - .../usr/share/OGAgent/opengnsys/RESTApi.py | 196 - .../usr/share/OGAgent/opengnsys/__init__.py | 60 - .../usr/share/OGAgent/opengnsys/certs.py | 101 - .../usr/share/OGAgent/opengnsys/config.py | 58 - .../usr/share/OGAgent/opengnsys/httpserver.py | 150 - .../usr/share/OGAgent/opengnsys/ipc.py | 423 - .../OGAgent/opengnsys/linux/OGAgentService.py | 147 - .../share/OGAgent/opengnsys/linux/__init__.py | 32 - .../share/OGAgent/opengnsys/linux/daemon.py | 182 - .../usr/share/OGAgent/opengnsys/linux/log.py | 80 - .../OGAgent/opengnsys/linux/operations.py | 286 - .../opengnsys/linux/renamer/__init__.py | 61 - .../OGAgent/opengnsys/linux/renamer/debian.py | 68 - .../opengnsys/linux/renamer/opensuse.py | 66 - .../OGAgent/opengnsys/linux/renamer/redhat.py | 74 - .../usr/share/OGAgent/opengnsys/loader.py | 111 - .../usr/share/OGAgent/opengnsys/log.py | 103 - .../share/OGAgent/opengnsys/macos/__init__.py | 32 - .../OGAgent/opengnsys/macos/operations.py | 263 - .../OGAgent/opengnsys/modules/__init__.py | 0 .../modules/client/OpenGnSys/__init__.py | 63 - .../opengnsys/modules/client/__init__.py | 0 .../modules/server/OpenGnSys/__init__.py | 558 - .../opengnsys/modules/server/__init__.py | 0 .../OGAgent/opengnsys/oglive/__init__.py | 32 - .../share/OGAgent/opengnsys/oglive/daemon.py | 182 - .../OGAgent/opengnsys/oglive/operations.py | 258 - .../usr/share/OGAgent/opengnsys/operations.py | 57 - .../share/OGAgent/opengnsys/scriptThread.py | 51 - .../usr/share/OGAgent/opengnsys/service.py | 249 - .../usr/share/OGAgent/opengnsys/utils.py | 72 - .../opengnsys/windows/OGAgentService.py | 124 - .../OGAgent/opengnsys/windows/__init__.py | 39 - .../share/OGAgent/opengnsys/windows/log.py | 77 - .../OGAgent/opengnsys/windows/operations.py | 269 - .../OGAgent/opengnsys/workers/__init__.py | 2 - .../opengnsys/workers/client_worker.py | 114 - .../opengnsys/workers/server_worker.py | 186 - .../doc/ogagent-oglive/changelog.Debian.gz | Bin 214 -> 0 bytes .../usr/share/doc/ogagent-oglive/copyright | 26 - .../usr/share/doc/ogagent-oglive/readme.txt | 3 - native/Sources/Includes/Database.cpp | 203 - native/Sources/Includes/Database.h | 61 - native/Sources/Includes/Database.o | Bin 33312 -> 0 bytes native/Sources/Includes/ogAdmLib.c | 1074 -- native/Sources/Includes/ogAdmLib.h | 319 - native/Sources/Services/ogAdmAgent/Makefile | 42 - .../Services/ogAdmAgent/ogAdmAgent.cfg | 7 - .../ogAdmAgent/sources/ogAdmAgent.cpp | 914 -- .../Services/ogAdmAgent/sources/ogAdmAgent.h | 81 - .../Services/ogAdmAgent/sources/ogAdmAgent.o | Bin 127632 -> 0 bytes native/Sources/Services/ogAdmRepoAux | 285 - native/Sources/Services/ogAdmServer/Makefile | 38 - .../ogAdmServer/Sources-Services-.txt | 1 - .../Services/ogAdmServer/ogAdmServer.cfg | 8 - .../Services/ogAdmServer/sources/dbi.c | 37 - .../Services/ogAdmServer/sources/dbi.h | 22 - .../Services/ogAdmServer/sources/dbi.o | Bin 6728 -> 0 bytes .../ogAdmServer/sources/ogAdmServer.c | 4961 -------- .../ogAdmServer/sources/ogAdmServer.h | 60 - .../ogAdmServer/sources/ogAdmServer.o | Bin 249504 -> 0 bytes .../Services/ogAdmServer/tests/clients.json | 1 - .../ogAdmServer/tests/config/ogAdmServer.cfg | 8 - .../ogAdmServer/tests/create_basic_image.json | 1 - .../ogAdmServer/tests/create_image.json | 1 - .../tests/create_incremental_image.json | 1 - .../ogAdmServer/tests/post_clients.json | 1 - .../ogAdmServer/tests/post_shell_output.json | 1 - .../ogAdmServer/tests/post_shell_run.json | 1 - .../Services/ogAdmServer/tests/poweroff.json | 1 - .../Services/ogAdmServer/tests/reboot.json | 1 - .../tests/restore_basic_image.json | 1 - .../ogAdmServer/tests/restore_image.json | 1 - .../tests/restore_incremental_image.json | 1 - .../Services/ogAdmServer/tests/run-tests.py | 41 - .../Services/ogAdmServer/tests/run-tests.sh | 22 - .../ogAdmServer/tests/run_schedule.json | 1 - .../Services/ogAdmServer/tests/session.json | 1 - .../ogAdmServer/tests/setup_image.json | 1 - .../Services/ogAdmServer/tests/stop.json | 1 - .../tests/units/test_0001_get_clients.py | 19 - .../tests/units/test_0002_post_clients.py | 24 - .../tests/units/test_0003_post_wol.py | 34 - .../tests/units/test_0004_post_shell_run.py | 35 - .../units/test_0005_post_shell_output.py | 28 - .../tests/units/test_0006_post_session.py | 34 - .../tests/units/test_0007_post_poweroff.py | 28 - .../tests/units/test_0008_post_reboot.py | 28 - .../tests/units/test_0009_post_stop.py | 28 - .../tests/units/test_0010_post_refresh.py | 28 - .../tests/units/test_0011_post_hardware.py | 28 - .../tests/units/test_0012_post_software.py | 35 - .../tests/units/test_0013_nonexistent.py | 30 - .../tests/units/test_0014_big_request.py | 19 - .../tests/units/test_0015_wrong_headers.py | 29 - .../units/test_0016_post_image_create.py | 39 - .../units/test_0017_post_image_restore.py | 40 - .../tests/units/test_0018_post_setup.py | 56 - .../test_0019_post_image_create_basic.py | 47 - ...test_0020_post_image_create_incremental.py | 49 - .../test_0021_post_image_restore_basic.py | 50 - ...est_0022_post_image_restore_incremental.py | 52 - .../units/test_0023_post_run_schedule.py | 28 - .../Services/ogAdmServer/tests/wol.json | 1 - native/Sources/Services/ogAdmServerAux | 47 - native/Sources/Services/opengnsys.default | 15 - native/Sources/Services/opengnsys.init | 224 - phpunit.xml.dist | 37 - sbin/ogBootAgent | Bin 107040 -> 0 bytes sbin/ogBootRepoAux | 285 - sbin/ogBootServer | Bin 169464 -> 0 bytes sbin/ogBootServerAux | 47 - server/tftpboot/NetbootPXE.es.txt | 77 - server/tftpboot/grldr | Bin 273039 -> 0 bytes server/tftpboot/grldr-0.4.4 | Bin 220049 -> 0 bytes server/tftpboot/grub/default | 74 - server/tftpboot/grub/examples/refind | 4 - server/tftpboot/grub/grub.cfg | 9 - server/tftpboot/grub/templates/00unknown | 77 - server/tftpboot/grub/templates/10 | 4 - server/tftpboot/grub/templates/11 | 4 - server/tftpboot/grub/templates/12 | 4 - server/tftpboot/grub/templates/13 | 4 - server/tftpboot/grub/templates/19pxeADMIN | 22 - server/tftpboot/grub/templates/pxe | 37 - server/tftpboot/menu.lst/default | 8 - server/tftpboot/menu.lst/examples/default | 25 - .../examples/defaultHOTREBOOT_WindowsXP | 36 - server/tftpboot/menu.lst/templates/00unknown | 6 - server/tftpboot/menu.lst/templates/10 | 8 - server/tftpboot/menu.lst/templates/11 | 8 - server/tftpboot/menu.lst/templates/12 | 6 - server/tftpboot/menu.lst/templates/13 | 8 - server/tftpboot/menu.lst/templates/19pxeADMIN | 20 - server/tftpboot/menu.lst/templates/pxe | 38 - symfony.lock | 261 - templates/base.html.twig | 19 - templates/dhcp/index.html.twig | 20 - tests/bootstrap.php | 11 - translations/.gitignore | 0 188 files changed, 28670 deletions(-) delete mode 100644 composer.lock delete mode 100644 config/bundles.php delete mode 100644 config/packages/cache.yaml delete mode 100644 config/packages/debug.yaml delete mode 100644 config/packages/doctrine.yaml delete mode 100644 config/packages/doctrine_migrations.yaml delete mode 100644 config/packages/framework.yaml delete mode 100644 config/packages/mailer.yaml delete mode 100644 config/packages/messenger.yaml delete mode 100644 config/packages/monolog.yaml delete mode 100644 config/packages/notifier.yaml delete mode 100644 config/packages/routing.yaml delete mode 100644 config/packages/security.yaml delete mode 100644 config/packages/translation.yaml delete mode 100644 config/packages/twig.yaml delete mode 100644 config/packages/validator.yaml delete mode 100644 config/packages/web_profiler.yaml delete mode 100644 config/preload.php delete mode 100644 config/routes.yaml delete mode 100644 config/routes/annotations.yaml delete mode 100644 config/routes/framework.yaml delete mode 100644 config/routes/web_profiler.yaml delete mode 100644 config/services.yaml delete mode 100644 docs/ogdhcp_api_documentation.md delete mode 100644 migrations/.gitignore delete mode 100644 native/Sources/Clients/README.es.txt delete mode 100644 native/Sources/Clients/ogAdmClient/Makefile delete mode 100644 native/Sources/Clients/ogAdmClient/ogAdmClient.cfg delete mode 100644 native/Sources/Clients/ogAdmClient/sources/ogAdmClient.c delete mode 100644 native/Sources/Clients/ogAdmClient/sources/ogAdmClient.h delete mode 100644 native/Sources/Clients/ogAdmClient/sources/ogAdmClient.o delete mode 100644 native/Sources/Clients/ogagent/ogagent-oglive_3.0.0-20190520_all.deb delete mode 100644 native/Sources/Clients/ogagent/ogagent-oglive_3.0.0-20190520_amd64.buildinfo delete mode 100644 native/Sources/Clients/ogagent/ogagent-oglive_3.0.0-20190520_amd64.changes delete mode 100644 native/Sources/Clients/ogagent/oglive/build-stamp delete mode 100644 native/Sources/Clients/ogagent/oglive/configure-stamp delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/.debhelper/generated/ogagent-oglive/installed-by-dh_installdocs delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/files delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.debhelper.log delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/control delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/md5sums delete mode 100755 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/postinst delete mode 100755 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/postrm delete mode 120000 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/etc/ogagent/ogagent.cfg delete mode 120000 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/etc/ogagent/ogclient.cfg delete mode 100755 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/bin/ogagent delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/cfg/ogagent.cfg delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/cfg/ogclient.cfg delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/RESTApi.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/certs.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/config.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/httpserver.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/ipc.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/OGAgentService.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/daemon.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/log.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/operations.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/debian.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/opensuse.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/redhat.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/loader.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/log.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/macos/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/macos/operations.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/client/OpenGnSys/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/client/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/server/OpenGnSys/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/server/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/daemon.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/operations.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/operations.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/scriptThread.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/service.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/utils.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/OGAgentService.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/log.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/operations.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/__init__.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/client_worker.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/server_worker.py delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/changelog.Debian.gz delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/copyright delete mode 100644 native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/readme.txt delete mode 100644 native/Sources/Includes/Database.cpp delete mode 100644 native/Sources/Includes/Database.h delete mode 100644 native/Sources/Includes/Database.o delete mode 100644 native/Sources/Includes/ogAdmLib.c delete mode 100644 native/Sources/Includes/ogAdmLib.h delete mode 100644 native/Sources/Services/ogAdmAgent/Makefile delete mode 100644 native/Sources/Services/ogAdmAgent/ogAdmAgent.cfg delete mode 100644 native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.cpp delete mode 100644 native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.h delete mode 100644 native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.o delete mode 100755 native/Sources/Services/ogAdmRepoAux delete mode 100644 native/Sources/Services/ogAdmServer/Makefile delete mode 100644 native/Sources/Services/ogAdmServer/Sources-Services-.txt delete mode 100644 native/Sources/Services/ogAdmServer/ogAdmServer.cfg delete mode 100644 native/Sources/Services/ogAdmServer/sources/dbi.c delete mode 100644 native/Sources/Services/ogAdmServer/sources/dbi.h delete mode 100644 native/Sources/Services/ogAdmServer/sources/dbi.o delete mode 100644 native/Sources/Services/ogAdmServer/sources/ogAdmServer.c delete mode 100644 native/Sources/Services/ogAdmServer/sources/ogAdmServer.h delete mode 100644 native/Sources/Services/ogAdmServer/sources/ogAdmServer.o delete mode 100644 native/Sources/Services/ogAdmServer/tests/clients.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/config/ogAdmServer.cfg delete mode 100644 native/Sources/Services/ogAdmServer/tests/create_basic_image.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/create_image.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/create_incremental_image.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/post_clients.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/post_shell_output.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/post_shell_run.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/poweroff.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/reboot.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/restore_basic_image.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/restore_image.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/restore_incremental_image.json delete mode 100755 native/Sources/Services/ogAdmServer/tests/run-tests.py delete mode 100755 native/Sources/Services/ogAdmServer/tests/run-tests.sh delete mode 100644 native/Sources/Services/ogAdmServer/tests/run_schedule.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/session.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/setup_image.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/stop.json delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0001_get_clients.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0002_post_clients.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0003_post_wol.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0004_post_shell_run.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0005_post_shell_output.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0006_post_session.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0007_post_poweroff.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0008_post_reboot.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0009_post_stop.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0010_post_refresh.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0011_post_hardware.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0012_post_software.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0013_nonexistent.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0014_big_request.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0015_wrong_headers.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0016_post_image_create.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0017_post_image_restore.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0018_post_setup.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0019_post_image_create_basic.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0020_post_image_create_incremental.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0021_post_image_restore_basic.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0022_post_image_restore_incremental.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/units/test_0023_post_run_schedule.py delete mode 100644 native/Sources/Services/ogAdmServer/tests/wol.json delete mode 100755 native/Sources/Services/ogAdmServerAux delete mode 100644 native/Sources/Services/opengnsys.default delete mode 100755 native/Sources/Services/opengnsys.init delete mode 100644 phpunit.xml.dist delete mode 100755 sbin/ogBootAgent delete mode 100755 sbin/ogBootRepoAux delete mode 100755 sbin/ogBootServer delete mode 100755 sbin/ogBootServerAux delete mode 100644 server/tftpboot/NetbootPXE.es.txt delete mode 100644 server/tftpboot/grldr delete mode 100644 server/tftpboot/grldr-0.4.4 delete mode 100644 server/tftpboot/grub/default delete mode 100644 server/tftpboot/grub/examples/refind delete mode 100644 server/tftpboot/grub/grub.cfg delete mode 100644 server/tftpboot/grub/templates/00unknown delete mode 100644 server/tftpboot/grub/templates/10 delete mode 100644 server/tftpboot/grub/templates/11 delete mode 100644 server/tftpboot/grub/templates/12 delete mode 100644 server/tftpboot/grub/templates/13 delete mode 100644 server/tftpboot/grub/templates/19pxeADMIN delete mode 100644 server/tftpboot/grub/templates/pxe delete mode 100644 server/tftpboot/menu.lst/default delete mode 100644 server/tftpboot/menu.lst/examples/default delete mode 100644 server/tftpboot/menu.lst/examples/defaultHOTREBOOT_WindowsXP delete mode 100644 server/tftpboot/menu.lst/templates/00unknown delete mode 100644 server/tftpboot/menu.lst/templates/10 delete mode 100644 server/tftpboot/menu.lst/templates/11 delete mode 100644 server/tftpboot/menu.lst/templates/12 delete mode 100644 server/tftpboot/menu.lst/templates/13 delete mode 100644 server/tftpboot/menu.lst/templates/19pxeADMIN delete mode 100644 server/tftpboot/menu.lst/templates/pxe delete mode 100644 symfony.lock delete mode 100644 templates/base.html.twig delete mode 100644 templates/dhcp/index.html.twig delete mode 100644 tests/bootstrap.php delete mode 100644 translations/.gitignore diff --git a/composer.lock b/composer.lock deleted file mode 100644 index dfae44f..0000000 --- a/composer.lock +++ /dev/null @@ -1,9979 +0,0 @@ -{ - "_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": "43222d4e7d812629851d6a72102dd880", - "packages": [ - { - "name": "doctrine/annotations", - "version": "1.14.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af", - "reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af", - "shasum": "" - }, - "require": { - "doctrine/lexer": "^1 || ^2", - "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" - }, - "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^4.4 || ^5.4 || ^6", - "vimeo/psalm": "^4.10" - }, - "suggest": { - "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.14.3" - }, - "time": "2023-02-01T09:20:38+00:00" - }, - { - "name": "doctrine/cache", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/cache.git", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", - "shasum": "" - }, - "require": { - "php": "~7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": ">2.2,<2.4" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psr/cache": "^1.0 || ^2.0 || ^3.0", - "symfony/cache": "^4.4 || ^5.4 || ^6", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", - "homepage": "https://www.doctrine-project.org/projects/cache.html", - "keywords": [ - "abstraction", - "apcu", - "cache", - "caching", - "couchdb", - "memcached", - "php", - "redis", - "xcache" - ], - "support": { - "issues": "https://github.com/doctrine/cache/issues", - "source": "https://github.com/doctrine/cache/tree/2.2.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", - "type": "tidelift" - } - ], - "time": "2022-05-20T20:07:39+00:00" - }, - { - "name": "doctrine/collections", - "version": "1.8.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/collections.git", - "reference": "2b44dd4cbca8b5744327de78bafef5945c7e7b5e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/2b44dd4cbca8b5744327de78bafef5945c7e7b5e", - "reference": "2b44dd4cbca8b5744327de78bafef5945c7e7b5e", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^0.5.3 || ^1", - "php": "^7.1.3 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9.0 || ^10.0", - "phpstan/phpstan": "^1.4.8", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.1.5", - "vimeo/psalm": "^4.22" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Collections\\": "lib/Doctrine/Common/Collections" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.", - "homepage": "https://www.doctrine-project.org/projects/collections.html", - "keywords": [ - "array", - "collections", - "iterators", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/collections/issues", - "source": "https://github.com/doctrine/collections/tree/1.8.0" - }, - "time": "2022-09-01T20:12:10+00:00" - }, - { - "name": "doctrine/common", - "version": "3.4.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/common.git", - "reference": "8b5e5650391f851ed58910b3e3d48a71062eeced" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/8b5e5650391f851ed58910b3e3d48a71062eeced", - "reference": "8b5e5650391f851ed58910b3e3d48a71062eeced", - "shasum": "" - }, - "require": { - "doctrine/persistence": "^2.0 || ^3.0", - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9.0 || ^10.0", - "doctrine/collections": "^1", - "phpstan/phpstan": "^1.4.1", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0", - "squizlabs/php_codesniffer": "^3.0", - "symfony/phpunit-bridge": "^6.1", - "vimeo/psalm": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and much more.", - "homepage": "https://www.doctrine-project.org/projects/common.html", - "keywords": [ - "common", - "doctrine", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/common/issues", - "source": "https://github.com/doctrine/common/tree/3.4.3" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcommon", - "type": "tidelift" - } - ], - "time": "2022-10-09T11:47:59+00:00" - }, - { - "name": "doctrine/dbal", - "version": "2.13.9", - "source": { - "type": "git", - "url": "https://github.com/doctrine/dbal.git", - "reference": "c480849ca3ad6706a39c970cdfe6888fa8a058b8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/c480849ca3ad6706a39c970cdfe6888fa8a058b8", - "reference": "c480849ca3ad6706a39c970cdfe6888fa8a058b8", - "shasum": "" - }, - "require": { - "doctrine/cache": "^1.0|^2.0", - "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1.0", - "ext-pdo": "*", - "php": "^7.1 || ^8" - }, - "require-dev": { - "doctrine/coding-standard": "9.0.0", - "jetbrains/phpstorm-stubs": "2021.1", - "phpstan/phpstan": "1.4.6", - "phpunit/phpunit": "^7.5.20|^8.5|9.5.16", - "psalm/plugin-phpunit": "0.16.1", - "squizlabs/php_codesniffer": "3.6.2", - "symfony/cache": "^4.4", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", - "vimeo/psalm": "4.22.0" - }, - "suggest": { - "symfony/console": "For helpful console commands such as SQL execution and import of files." - }, - "bin": [ - "bin/doctrine-dbal" - ], - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\DBAL\\": "lib/Doctrine/DBAL" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - } - ], - "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", - "homepage": "https://www.doctrine-project.org/projects/dbal.html", - "keywords": [ - "abstraction", - "database", - "db2", - "dbal", - "mariadb", - "mssql", - "mysql", - "oci8", - "oracle", - "pdo", - "pgsql", - "postgresql", - "queryobject", - "sasql", - "sql", - "sqlanywhere", - "sqlite", - "sqlserver", - "sqlsrv" - ], - "support": { - "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/2.13.9" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", - "type": "tidelift" - } - ], - "time": "2022-05-02T20:28:55+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.3" - }, - "time": "2024-01-30T19:34:25+00:00" - }, - { - "name": "doctrine/doctrine-bundle", - "version": "2.7.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/DoctrineBundle.git", - "reference": "22d53b2c5ad03929628fb4a928b01135585b7179" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/22d53b2c5ad03929628fb4a928b01135585b7179", - "reference": "22d53b2c5ad03929628fb4a928b01135585b7179", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1", - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/dbal": "^2.13.1 || ^3.3.2", - "doctrine/persistence": "^2.2 || ^3", - "doctrine/sql-formatter": "^1.0.1", - "php": "^7.1 || ^8.0", - "symfony/cache": "^4.4 || ^5.4 || ^6.0", - "symfony/config": "^4.4.3 || ^5.4 || ^6.0", - "symfony/console": "^4.4 || ^5.4 || ^6.0", - "symfony/dependency-injection": "^4.4.18 || ^5.4 || ^6.0", - "symfony/deprecation-contracts": "^2.1 || ^3", - "symfony/doctrine-bridge": "^4.4.22 || ^5.4 || ^6.0", - "symfony/framework-bundle": "^4.4 || ^5.4 || ^6.0", - "symfony/service-contracts": "^1.1.1 || ^2.0 || ^3" - }, - "conflict": { - "doctrine/orm": "<2.11 || >=3.0", - "twig/twig": "<1.34 || >=2.0,<2.4" - }, - "require-dev": { - "doctrine/coding-standard": "^9.0", - "doctrine/orm": "^2.11 || ^3.0", - "friendsofphp/proxy-manager-lts": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.0 || ^9.3 || ^10.0", - "psalm/plugin-phpunit": "^0.16.1", - "psalm/plugin-symfony": "^3", - "psr/log": "^1.1.4 || ^2.0 || ^3.0", - "symfony/phpunit-bridge": "^6.1", - "symfony/property-info": "^4.4 || ^5.4 || ^6.0", - "symfony/proxy-manager-bridge": "^4.4 || ^5.4 || ^6.0", - "symfony/security-bundle": "^4.4 || ^5.4 || ^6.0", - "symfony/twig-bridge": "^4.4 || ^5.4 || ^6.0", - "symfony/validator": "^4.4 || ^5.4 || ^6.0", - "symfony/web-profiler-bundle": "^4.4 || ^5.4 || ^6.0", - "symfony/yaml": "^4.4 || ^5.4 || ^6.0", - "twig/twig": "^1.34 || ^2.12 || ^3.0", - "vimeo/psalm": "^4.7" - }, - "suggest": { - "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", - "ext-pdo": "*", - "symfony/web-profiler-bundle": "To use the data collector." - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Doctrine\\Bundle\\DoctrineBundle\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, - { - "name": "Doctrine Project", - "homepage": "https://www.doctrine-project.org/" - } - ], - "description": "Symfony DoctrineBundle", - "homepage": "https://www.doctrine-project.org", - "keywords": [ - "database", - "dbal", - "orm", - "persistence" - ], - "support": { - "issues": "https://github.com/doctrine/DoctrineBundle/issues", - "source": "https://github.com/doctrine/DoctrineBundle/tree/2.7.2" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdoctrine-bundle", - "type": "tidelift" - } - ], - "time": "2022-12-07T12:07:11+00:00" - }, - { - "name": "doctrine/doctrine-migrations-bundle", - "version": "3.3.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", - "reference": "1dd42906a5fb9c5960723e2ebb45c68006493835" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/1dd42906a5fb9c5960723e2ebb45c68006493835", - "reference": "1dd42906a5fb9c5960723e2ebb45c68006493835", - "shasum": "" - }, - "require": { - "doctrine/doctrine-bundle": "^2.4", - "doctrine/migrations": "^3.2", - "php": "^7.2|^8.0", - "symfony/deprecation-contracts": "^2.1 || ^3", - "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" - }, - "require-dev": { - "doctrine/coding-standard": "^12", - "doctrine/orm": "^2.6 || ^3", - "doctrine/persistence": "^2.0 || ^3 ", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-deprecation-rules": "^1", - "phpstan/phpstan-phpunit": "^1", - "phpstan/phpstan-strict-rules": "^1.1", - "phpstan/phpstan-symfony": "^1.3", - "phpunit/phpunit": "^8.5|^9.5", - "psalm/plugin-phpunit": "^0.18.4", - "psalm/plugin-symfony": "^3 || ^5", - "symfony/phpunit-bridge": "^6.3 || ^7", - "symfony/var-exporter": "^5.4 || ^6 || ^7", - "vimeo/psalm": "^4.30 || ^5.15" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Doctrine\\Bundle\\MigrationsBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Doctrine Project", - "homepage": "https://www.doctrine-project.org" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony DoctrineMigrationsBundle", - "homepage": "https://www.doctrine-project.org", - "keywords": [ - "dbal", - "migrations", - "schema" - ], - "support": { - "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", - "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.3.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdoctrine-migrations-bundle", - "type": "tidelift" - } - ], - "time": "2023-11-13T19:44:41+00:00" - }, - { - "name": "doctrine/event-manager", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/event-manager.git", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^0.5.3 || ^1", - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": "<2.9" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.8", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.24" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", - "homepage": "https://www.doctrine-project.org/projects/event-manager.html", - "keywords": [ - "event", - "event dispatcher", - "event manager", - "event system", - "events" - ], - "support": { - "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/1.2.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", - "type": "tidelift" - } - ], - "time": "2022-10-12T20:51:15+00:00" - }, - { - "name": "doctrine/inflector", - "version": "2.0.9", - "source": { - "type": "git", - "url": "https://github.com/doctrine/inflector.git", - "reference": "2930cd5ef353871c821d5c43ed030d39ac8cfe65" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/2930cd5ef353871c821d5c43ed030d39ac8cfe65", - "reference": "2930cd5ef353871c821d5c43ed030d39ac8cfe65", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^11.0", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", - "homepage": "https://www.doctrine-project.org/projects/inflector.html", - "keywords": [ - "inflection", - "inflector", - "lowercase", - "manipulation", - "php", - "plural", - "singular", - "strings", - "uppercase", - "words" - ], - "support": { - "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.9" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", - "type": "tidelift" - } - ], - "time": "2024-01-15T18:05:13+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, - { - "name": "doctrine/lexer", - "version": "2.1.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", - "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^12", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^4.11 || ^5.21" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/2.1.1" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" - } - ], - "time": "2024-02-05T11:35:39+00:00" - }, - { - "name": "doctrine/migrations", - "version": "3.4.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/migrations.git", - "reference": "362f07ff732a2b4498be919561536800cec29500" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/362f07ff732a2b4498be919561536800cec29500", - "reference": "362f07ff732a2b4498be919561536800cec29500", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2", - "doctrine/dbal": "^2.11 || ^3.0", - "doctrine/deprecations": "^0.5.3 || ^1", - "doctrine/event-manager": "^1.0", - "friendsofphp/proxy-manager-lts": "^1.0", - "php": "^7.2 || ^8.0", - "psr/log": "^1.1.3 || ^2 || ^3", - "symfony/console": "^3.4 || ^4.4.16 || ^5.0 || ^6.0", - "symfony/stopwatch": "^3.4 || ^4.0 || ^5.0 || ^6.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "doctrine/orm": "^2.6", - "doctrine/persistence": "^1.3 || ^2.0", - "doctrine/sql-formatter": "^1.0", - "ergebnis/composer-normalize": "^2.9", - "ext-pdo_sqlite": "*", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-deprecation-rules": "^1", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.1", - "phpstan/phpstan-symfony": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.4", - "symfony/cache": "^3.4.26 || ^4.2.12 || ^5.0 || ^6.0", - "symfony/process": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0" - }, - "suggest": { - "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.", - "symfony/yaml": "Allows the use of yaml for migration configuration files." - }, - "bin": [ - "bin/doctrine-migrations" - ], - "type": "library", - "extra": { - "composer-normalize": { - "indent-size": 4, - "indent-style": "space" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Migrations\\": "lib/Doctrine/Migrations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Michael Simonson", - "email": "contact@mikesimonson.com" - } - ], - "description": "PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to use and a powerful tool.", - "homepage": "https://www.doctrine-project.org/projects/migrations.html", - "keywords": [ - "database", - "dbal", - "migrations" - ], - "support": { - "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.4.3" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fmigrations", - "type": "tidelift" - } - ], - "time": "2023-09-07T12:23:11+00:00" - }, - { - "name": "doctrine/orm", - "version": "2.18.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/orm.git", - "reference": "f2176a9ce56cafdfd1624d54bfdb076819083d5b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/f2176a9ce56cafdfd1624d54bfdb076819083d5b", - "reference": "f2176a9ce56cafdfd1624d54bfdb076819083d5b", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2", - "doctrine/cache": "^1.12.1 || ^2.1.1", - "doctrine/collections": "^1.5 || ^2.1", - "doctrine/common": "^3.0.3", - "doctrine/dbal": "^2.13.1 || ^3.2", - "doctrine/deprecations": "^0.5.3 || ^1", - "doctrine/event-manager": "^1.2 || ^2", - "doctrine/inflector": "^1.4 || ^2.0", - "doctrine/instantiator": "^1.3 || ^2", - "doctrine/lexer": "^2 || ^3", - "doctrine/persistence": "^2.4 || ^3", - "ext-ctype": "*", - "php": "^7.1 || ^8.0", - "psr/cache": "^1 || ^2 || ^3", - "symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0", - "symfony/polyfill-php72": "^1.23", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/annotations": "<1.13 || >= 3.0" - }, - "require-dev": { - "doctrine/annotations": "^1.13 || ^2", - "doctrine/coding-standard": "^9.0.2 || ^12.0", - "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/phpstan": "~1.4.10 || 1.10.35", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", - "psr/log": "^1 || ^2 || ^3", - "squizlabs/php_codesniffer": "3.7.2", - "symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0", - "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "vimeo/psalm": "4.30.0 || 5.16.0" - }, - "suggest": { - "ext-dom": "Provides support for XSD validation for XML mapping files", - "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0", - "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" - }, - "bin": [ - "bin/doctrine" - ], - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\ORM\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "Object-Relational-Mapper for PHP", - "homepage": "https://www.doctrine-project.org/projects/orm.html", - "keywords": [ - "database", - "orm" - ], - "support": { - "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.18.0" - }, - "time": "2024-01-31T15:53:12+00:00" - }, - { - "name": "doctrine/persistence", - "version": "3.2.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/persistence.git", - "reference": "63fee8c33bef740db6730eb2a750cd3da6495603" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/persistence/zipball/63fee8c33bef740db6730eb2a750cd3da6495603", - "reference": "63fee8c33bef740db6730eb2a750cd3da6495603", - "shasum": "" - }, - "require": { - "doctrine/event-manager": "^1 || ^2", - "php": "^7.2 || ^8.0", - "psr/cache": "^1.0 || ^2.0 || ^3.0" - }, - "conflict": { - "doctrine/common": "<2.10" - }, - "require-dev": { - "composer/package-versions-deprecated": "^1.11", - "doctrine/coding-standard": "^11", - "doctrine/common": "^3.0", - "phpstan/phpstan": "1.9.4", - "phpstan/phpstan-phpunit": "^1", - "phpstan/phpstan-strict-rules": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.5", - "symfony/cache": "^4.4 || ^5.4 || ^6.0", - "vimeo/psalm": "4.30.0 || 5.3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Persistence\\": "src/Persistence" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share.", - "homepage": "https://www.doctrine-project.org/projects/persistence.html", - "keywords": [ - "mapper", - "object", - "odm", - "orm", - "persistence" - ], - "support": { - "issues": "https://github.com/doctrine/persistence/issues", - "source": "https://github.com/doctrine/persistence/tree/3.2.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fpersistence", - "type": "tidelift" - } - ], - "time": "2023-05-17T18:32:04+00:00" - }, - { - "name": "doctrine/sql-formatter", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/25a06c7bf4c6b8218f47928654252863ffc890a5", - "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4" - }, - "bin": [ - "bin/sql-formatter" - ], - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\SqlFormatter\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jeremy Dorn", - "email": "jeremy@jeremydorn.com", - "homepage": "https://jeremydorn.com/" - } - ], - "description": "a PHP SQL highlighting library", - "homepage": "https://github.com/doctrine/sql-formatter/", - "keywords": [ - "highlight", - "sql" - ], - "support": { - "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.1.3" - }, - "time": "2022-05-23T21:33:49+00:00" - }, - { - "name": "egulias/email-validator", - "version": "3.2.6", - "source": { - "type": "git", - "url": "https://github.com/egulias/EmailValidator.git", - "reference": "e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7", - "reference": "e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7", - "shasum": "" - }, - "require": { - "doctrine/lexer": "^1.2|^2", - "php": ">=7.2", - "symfony/polyfill-intl-idn": "^1.15" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.8|^9.3.3", - "vimeo/psalm": "^4" - }, - "suggest": { - "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Egulias\\EmailValidator\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eduardo Gulias Davis" - } - ], - "description": "A library for validating emails against several RFCs", - "homepage": "https://github.com/egulias/EmailValidator", - "keywords": [ - "email", - "emailvalidation", - "emailvalidator", - "validation", - "validator" - ], - "support": { - "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/3.2.6" - }, - "funding": [ - { - "url": "https://github.com/egulias", - "type": "github" - } - ], - "time": "2023-06-01T07:04:22+00:00" - }, - { - "name": "friendsofphp/proxy-manager-lts", - "version": "v1.0.16", - "source": { - "type": "git", - "url": "https://github.com/FriendsOfPHP/proxy-manager-lts.git", - "reference": "ecadbdc9052e4ad08c60c8a02268712e50427f7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/proxy-manager-lts/zipball/ecadbdc9052e4ad08c60c8a02268712e50427f7c", - "reference": "ecadbdc9052e4ad08c60c8a02268712e50427f7c", - "shasum": "" - }, - "require": { - "laminas/laminas-code": "~3.4.1|^4.0", - "php": ">=7.1", - "symfony/filesystem": "^4.4.17|^5.0|^6.0|^7.0" - }, - "conflict": { - "laminas/laminas-stdlib": "<3.2.1", - "zendframework/zend-stdlib": "<3.2.1" - }, - "replace": { - "ocramius/proxy-manager": "^2.1" - }, - "require-dev": { - "ext-phar": "*", - "symfony/phpunit-bridge": "^5.4|^6.0|^7.0" - }, - "type": "library", - "extra": { - "thanks": { - "name": "ocramius/proxy-manager", - "url": "https://github.com/Ocramius/ProxyManager" - } - }, - "autoload": { - "psr-4": { - "ProxyManager\\": "src/ProxyManager" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - } - ], - "description": "Adding support for a wider range of PHP versions to ocramius/proxy-manager", - "homepage": "https://github.com/FriendsOfPHP/proxy-manager-lts", - "keywords": [ - "aop", - "lazy loading", - "proxy", - "proxy pattern", - "service proxies" - ], - "support": { - "issues": "https://github.com/FriendsOfPHP/proxy-manager-lts/issues", - "source": "https://github.com/FriendsOfPHP/proxy-manager-lts/tree/v1.0.16" - }, - "funding": [ - { - "url": "https://github.com/Ocramius", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ocramius/proxy-manager", - "type": "tidelift" - } - ], - "time": "2023-05-24T07:17:17+00:00" - }, - { - "name": "laminas/laminas-code", - "version": "3.4.1", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-code.git", - "reference": "1cb8f203389ab1482bf89c0e70a04849bacd7766" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-code/zipball/1cb8f203389ab1482bf89c0e70a04849bacd7766", - "reference": "1cb8f203389ab1482bf89c0e70a04849bacd7766", - "shasum": "" - }, - "require": { - "laminas/laminas-eventmanager": "^2.6 || ^3.0", - "laminas/laminas-zendframework-bridge": "^1.0", - "php": "^7.1" - }, - "conflict": { - "phpspec/prophecy": "<1.9.0" - }, - "replace": { - "zendframework/zend-code": "self.version" - }, - "require-dev": { - "doctrine/annotations": "^1.7", - "ext-phar": "*", - "laminas/laminas-coding-standard": "^1.0", - "laminas/laminas-stdlib": "^2.7 || ^3.0", - "phpunit/phpunit": "^7.5.16 || ^8.4" - }, - "suggest": { - "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "laminas/laminas-stdlib": "Laminas\\Stdlib component" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4.x-dev", - "dev-develop": "3.5.x-dev", - "dev-dev-4.0": "4.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Laminas\\Code\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", - "homepage": "https://laminas.dev", - "keywords": [ - "code", - "laminas" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-code/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-code/issues", - "rss": "https://github.com/laminas/laminas-code/releases.atom", - "source": "https://github.com/laminas/laminas-code" - }, - "time": "2019-12-31T16:28:24+00:00" - }, - { - "name": "laminas/laminas-eventmanager", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-eventmanager.git", - "reference": "ce4dc0bdf3b14b7f9815775af9dfee80a63b4748" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/ce4dc0bdf3b14b7f9815775af9dfee80a63b4748", - "reference": "ce4dc0bdf3b14b7f9815775af9dfee80a63b4748", - "shasum": "" - }, - "require": { - "laminas/laminas-zendframework-bridge": "^1.0", - "php": "^5.6 || ^7.0" - }, - "replace": { - "zendframework/zend-eventmanager": "self.version" - }, - "require-dev": { - "athletic/athletic": "^0.1", - "container-interop/container-interop": "^1.1.0", - "laminas/laminas-coding-standard": "~1.0.0", - "laminas/laminas-stdlib": "^2.7.3 || ^3.0", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" - }, - "suggest": { - "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", - "laminas/laminas-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev", - "dev-develop": "3.3-dev" - } - }, - "autoload": { - "psr-4": { - "Laminas\\EventManager\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Trigger and listen to events within a PHP application", - "homepage": "https://laminas.dev", - "keywords": [ - "event", - "eventmanager", - "events", - "laminas" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-eventmanager/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-eventmanager/issues", - "rss": "https://github.com/laminas/laminas-eventmanager/releases.atom", - "source": "https://github.com/laminas/laminas-eventmanager" - }, - "time": "2019-12-31T16:44:52+00:00" - }, - { - "name": "laminas/laminas-zendframework-bridge", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-zendframework-bridge.git", - "reference": "6ede70583e101030bcace4dcddd648f760ddf642" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/6ede70583e101030bcace4dcddd648f760ddf642", - "reference": "6ede70583e101030bcace4dcddd648f760ddf642", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1 || ^9.3", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "extra": { - "laminas": { - "module": "Laminas\\ZendFrameworkBridge" - } - }, - "autoload": { - "files": [ - "src/autoload.php" - ], - "psr-4": { - "Laminas\\ZendFrameworkBridge\\": "src//" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Alias legacy ZF class names to Laminas Project equivalents.", - "keywords": [ - "ZendFramework", - "autoloading", - "laminas", - "zf" - ], - "support": { - "forum": "https://discourse.laminas.dev/", - "issues": "https://github.com/laminas/laminas-zendframework-bridge/issues", - "rss": "https://github.com/laminas/laminas-zendframework-bridge/releases.atom", - "source": "https://github.com/laminas/laminas-zendframework-bridge" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "abandoned": true, - "time": "2020-09-14T14:23:00+00:00" - }, - { - "name": "monolog/monolog", - "version": "2.9.2", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "437cb3628f4cf6042cc10ae97fc2b8472e48ca1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/437cb3628f4cf6042cc10ae97fc2b8472e48ca1f", - "reference": "437cb3628f4cf6042cc10ae97fc2b8472e48ca1f", - "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 || ^2@dev", - "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.9.2" - }, - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } - ], - "time": "2023-10-27T15:25:26+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.6.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" - }, - "time": "2022-03-15T21:29:03+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "0.4.14", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "cf4fc7d2aeca6910fba061901ffd7d107ccfdbcc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cf4fc7d2aeca6910fba061901ffd7d107ccfdbcc", - "reference": "cf4fc7d2aeca6910fba061901ffd7d107ccfdbcc", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "phing/phing": "^2.16.3", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12.60", - "phpstan/phpstan-strict-rules": "^0.12.5", - "phpunit/phpunit": "^7.5.20", - "symfony/process": "^5.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.4-dev" - } - }, - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/0.4.14" - }, - "time": "2021-03-19T10:54:14+00:00" - }, - { - "name": "psr/cache", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/master" - }, - "time": "2016-08-06T20:24:11+00:00" - }, - { - "name": "psr/container", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", - "shasum": "" - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.1" - }, - "time": "2021-03-05T17:36:06+00:00" - }, - { - "name": "psr/event-dispatcher", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", - "shasum": "" - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\EventDispatcher\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Standard interfaces for event handling.", - "keywords": [ - "events", - "psr", - "psr-14" - ], - "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" - }, - "time": "2019-01-08T18:20:26+00:00" - }, - { - "name": "psr/link", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/link.git", - "reference": "eea8e8662d5cd3ae4517c9b864493f59fca95562" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/link/zipball/eea8e8662d5cd3ae4517c9b864493f59fca95562", - "reference": "eea8e8662d5cd3ae4517c9b864493f59fca95562", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Link\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for HTTP links", - "keywords": [ - "http", - "http-link", - "link", - "psr", - "psr-13", - "rest" - ], - "support": { - "source": "https://github.com/php-fig/link/tree/master" - }, - "time": "2016-10-28T16:06:13+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": "symfony/amqp-messenger", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/amqp-messenger.git", - "reference": "41d9dadbfd9edd0906c9a8d6b614f2fab274408f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/amqp-messenger/zipball/41d9dadbfd9edd0906c9a8d6b614f2fab274408f", - "reference": "41d9dadbfd9edd0906c9a8d6b614f2fab274408f", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/messenger": "^5.3|^6.0" - }, - "require-dev": { - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/property-access": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" - }, - "type": "symfony-messenger-bridge", - "autoload": { - "psr-4": { - "Symfony\\Component\\Messenger\\Bridge\\Amqp\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony AMQP extension Messenger Bridge", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/amqp-messenger/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/asset", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/asset.git", - "reference": "0e98032106d18e57f57176d616e3fae80db7a845" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/asset/zipball/0e98032106d18e57f57176d616e3fae80db7a845", - "reference": "0e98032106d18e57f57176d616e3fae80db7a845", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/http-foundation": "<5.3" - }, - "require-dev": { - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/http-foundation": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Asset\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/asset/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/cache", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/cache.git", - "reference": "db1adb004e2da984085d0178964eb6f319d3cba1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/db1adb004e2da984085d0178964eb6f319d3cba1", - "reference": "db1adb004e2da984085d0178964eb6f319d3cba1", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/cache": "^1.0|^2.0", - "psr/log": "^1.1|^2|^3", - "symfony/cache-contracts": "^1.1.7|^2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/var-exporter": "^4.4|^5.0|^6.0" - }, - "conflict": { - "doctrine/dbal": "<2.13.1", - "symfony/dependency-injection": "<4.4", - "symfony/http-kernel": "<4.4", - "symfony/var-dumper": "<4.4" - }, - "provide": { - "psr/cache-implementation": "1.0|2.0", - "psr/simple-cache-implementation": "1.0|2.0", - "symfony/cache-implementation": "1.0|2.0" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/cache": "^1.6|^2.0", - "doctrine/dbal": "^2.13.1|^3|^4", - "predis/predis": "^1.1", - "psr/simple-cache": "^1.0|^2.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Cache\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", - "homepage": "https://symfony.com", - "keywords": [ - "caching", - "psr6" - ], - "support": { - "source": "https://github.com/symfony/cache/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/cache-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/cache-contracts.git", - "reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/64be4a7acb83b6f2bf6de9a02cee6dad41277ebc", - "reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/cache": "^1.0|^2.0|^3.0" - }, - "suggest": { - "symfony/cache-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Cache\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to caching", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-01-02T09:53:40+00:00" - }, - { - "name": "symfony/config", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "6b763438a22a4f20885e994ad6702f6a3f25430e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/6b763438a22a4f20885e994ad6702f6a3f25430e", - "reference": "6b763438a22a4f20885e994ad6702f6a3f25430e", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22" - }, - "conflict": { - "symfony/finder": "<4.4" - }, - "require-dev": { - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/config/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/console", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "dbdf6adcb88d5f83790e1efb57ef4074309d3931" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/dbdf6adcb88d5f83790e1efb57ef4074309d3931", - "reference": "dbdf6adcb88d5f83790e1efb57ef4074309d3931", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" - }, - "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" - }, - "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T14:28:09+00:00" - }, - { - "name": "symfony/dependency-injection", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/dependency-injection.git", - "reference": "45474d527212ca67cdb93f6c5e6da68f4bc67118" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/45474d527212ca67cdb93f6c5e6da68f4bc67118", - "reference": "45474d527212ca67cdb93f6c5e6da68f4bc67118", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.1.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22", - "symfony/service-contracts": "^1.1.6|^2" - }, - "conflict": { - "ext-psr": "<1.1|>=2", - "symfony/config": "<5.3", - "symfony/finder": "<4.4", - "symfony/proxy-manager-bridge": "<4.4", - "symfony/yaml": "<4.4.26" - }, - "provide": { - "psr/container-implementation": "1.0", - "symfony/service-implementation": "1.0|2.0" - }, - "require-dev": { - "symfony/config": "^5.3|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4.26|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DependencyInjection\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows you to standardize and centralize the way objects are constructed in your application", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:37:36+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-01-02T09:53:40+00:00" - }, - { - "name": "symfony/doctrine-bridge", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "f1c8d1f75ede8ba6810498666159c50ca05beec0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/f1c8d1f75ede8ba6810498666159c50ca05beec0", - "reference": "f1c8d1f75ede8ba6810498666159c50ca05beec0", - "shasum": "" - }, - "require": { - "doctrine/event-manager": "~1.0", - "doctrine/persistence": "^2|^3", - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3" - }, - "conflict": { - "doctrine/dbal": "<2.13.1", - "doctrine/lexer": "<1.1", - "doctrine/orm": "<2.7.4", - "symfony/cache": "<5.4", - "symfony/dependency-injection": "<4.4", - "symfony/form": "<5.4.21|>=6,<6.2.7", - "symfony/http-kernel": "<5", - "symfony/messenger": "<4.4", - "symfony/property-info": "<5", - "symfony/proxy-manager-bridge": "<4.4.19", - "symfony/security-bundle": "<5", - "symfony/security-core": "<5.3", - "symfony/validator": "<5.4.25|>=6,<6.2.12|>=6.3,<6.3.1" - }, - "require-dev": { - "doctrine/annotations": "^1.10.4|^2", - "doctrine/collections": "^1.0|^2.0", - "doctrine/data-fixtures": "^1.1", - "doctrine/dbal": "^2.13.1|^3|^4", - "doctrine/orm": "^2.7.4|^3", - "psr/log": "^1|^2|^3", - "symfony/cache": "^5.4|^6.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/doctrine-messenger": "^5.1|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/form": "^5.4.21|^6.2.7", - "symfony/http-kernel": "^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/property-access": "^4.4|^5.0|^6.0", - "symfony/property-info": "^5.0|^6.0", - "symfony/proxy-manager-bridge": "^4.4|^5.0|^6.0", - "symfony/security-core": "^5.3|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", - "symfony/validator": "^5.4.25|~6.2.12|^6.3.1", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "doctrine/data-fixtures": "", - "doctrine/dbal": "", - "doctrine/orm": "", - "symfony/form": "", - "symfony/property-info": "", - "symfony/validator": "" - }, - "type": "symfony-bridge", - "autoload": { - "psr-4": { - "Symfony\\Bridge\\Doctrine\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides integration for Doctrine with various Symfony components", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/doctrine-messenger", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/doctrine-messenger.git", - "reference": "21351af73e7dbe9db64f4b6bb15af6f55c78bf6e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/21351af73e7dbe9db64f4b6bb15af6f55c78bf6e", - "reference": "21351af73e7dbe9db64f4b6bb15af6f55c78bf6e", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/messenger": "^5.1|^6.0", - "symfony/service-contracts": "^1.1|^2|^3" - }, - "conflict": { - "doctrine/dbal": "<2.13", - "doctrine/persistence": "<1.3" - }, - "require-dev": { - "doctrine/dbal": "^2.13|^3|^4", - "doctrine/persistence": "^1.3|^2|^3", - "symfony/property-access": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" - }, - "type": "symfony-messenger-bridge", - "autoload": { - "psr-4": { - "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Doctrine Messenger Bridge", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/doctrine-messenger/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/dotenv", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/dotenv.git", - "reference": "4de488440104b99d2e7c0717ee432e760b061e32" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/4de488440104b99d2e7c0717ee432e760b061e32", - "reference": "4de488440104b99d2e7c0717ee432e760b061e32", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3" - }, - "require-dev": { - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Dotenv\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Registers environment variables from a .env file", - "homepage": "https://symfony.com", - "keywords": [ - "dotenv", - "env", - "environment" - ], - "support": { - "source": "https://github.com/symfony/dotenv/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/error-handler", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/error-handler.git", - "reference": "39225b1e47fdd91a6924b1e7d7a4523da2e1894b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/39225b1e47fdd91a6924b1e7d7a4523da2e1894b", - "reference": "39225b1e47fdd91a6924b1e7d7a4523da2e1894b", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "require-dev": { - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" - }, - "bin": [ - "Resources/bin/patch-type-declarations" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\ErrorHandler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to manage errors and ease debugging PHP code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/error-handler/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "7a69a85c7ea5bdd1e875806a99c51a87d3a74b38" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7a69a85c7ea5bdd1e875806a99c51a87d3a74b38", - "reference": "7a69a85c7ea5bdd1e875806a99c51a87d3a74b38", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^2|^3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/dependency-injection": "<4.4" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/event-dispatcher-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/f98b54df6ad059855739db6fcbc2d36995283fe1", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/event-dispatcher": "^1" - }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-01-02T09:53:40+00:00" - }, - { - "name": "symfony/expression-language", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/expression-language.git", - "reference": "d59441c10a5a73cd9d4d778b8253595a16f6716d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/d59441c10a5a73cd9d4d778b8253595a16f6716d", - "reference": "d59441c10a5a73cd9d4d778b8253595a16f6716d", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\ExpressionLanguage\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an engine that can compile and evaluate expressions", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/expression-language/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "5a553607d4ffbfa9c0ab62facadea296c9db7086" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/5a553607d4ffbfa9c0ab62facadea296c9db7086", - "reference": "5a553607d4ffbfa9c0ab62facadea296c9db7086", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/finder", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "abe6d6f77d9465fed3cd2d029b29d03b56b56435" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/abe6d6f77d9465fed3cd2d029b29d03b56b56435", - "reference": "abe6d6f77d9465fed3cd2d029b29d03b56b56435", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/flex", - "version": "v1.21.5", - "source": { - "type": "git", - "url": "https://github.com/symfony/flex.git", - "reference": "6b46a001639f810d01f4f1b39be1291192a711d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/6b46a001639f810d01f4f1b39be1291192a711d4", - "reference": "6b46a001639f810d01f4f1b39be1291192a711d4", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0|^2.0", - "php": ">=7.1" - }, - "require-dev": { - "composer/composer": "^1.0.2|^2.0", - "symfony/dotenv": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/phpunit-bridge": "^4.4.12|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Symfony\\Flex\\Flex" - }, - "autoload": { - "psr-4": { - "Symfony\\Flex\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien.potencier@gmail.com" - } - ], - "description": "Composer plugin for Symfony", - "support": { - "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v1.21.5" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-05T18:04:39+00:00" - }, - { - "name": "symfony/form", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/form.git", - "reference": "c827d421caccf467ef1a969e7412a8755fa88093" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/c827d421caccf467ef1a969e7412a8755fa88093", - "reference": "c827d421caccf467ef1a969e7412a8755fa88093", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/options-resolver": "^5.1|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-icu": "^1.21", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.23", - "symfony/property-access": "^5.0.8|^6.0", - "symfony/service-contracts": "^1.1|^2|^3" - }, - "conflict": { - "symfony/console": "<4.4", - "symfony/dependency-injection": "<4.4", - "symfony/doctrine-bridge": "<5.4.21|>=6,<6.2.7", - "symfony/error-handler": "<4.4.5", - "symfony/framework-bundle": "<4.4", - "symfony/http-kernel": "<4.4", - "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3", - "symfony/translation-contracts": "<1.1.7", - "symfony/twig-bridge": "<5.4.21|>=6,<6.2.7" - }, - "require-dev": { - "doctrine/collections": "^1.0|^2.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", - "symfony/security-csrf": "^4.4|^5.0|^6.0", - "symfony/translation": "^5.4.35|~6.3.12|^6.4.3", - "symfony/uid": "^5.1|^6.0", - "symfony/validator": "^4.4.17|^5.1.9|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/security-csrf": "For protecting forms against CSRF attacks.", - "symfony/twig-bridge": "For templating with Twig.", - "symfony/validator": "For form validation." - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Form\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows to easily create, process and reuse HTML forms", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/form/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T14:28:09+00:00" - }, - { - "name": "symfony/framework-bundle", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/framework-bundle.git", - "reference": "89805687f360133f18bdedfb32138ce0ddd5383c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/89805687f360133f18bdedfb32138ce0ddd5383c", - "reference": "89805687f360133f18bdedfb32138ce0ddd5383c", - "shasum": "" - }, - "require": { - "ext-xml": "*", - "php": ">=7.2.5", - "symfony/cache": "^5.2|^6.0", - "symfony/config": "^5.3|^6.0", - "symfony/dependency-injection": "^5.4.5|^6.0.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/error-handler": "^4.4.1|^5.0.1|^6.0", - "symfony/event-dispatcher": "^5.1|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^5.4.24|^6.2.11", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22", - "symfony/routing": "^5.3|^6.0" - }, - "conflict": { - "doctrine/annotations": "<1.13.1", - "doctrine/cache": "<1.11", - "doctrine/persistence": "<1.3", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/asset": "<5.3", - "symfony/console": "<5.2.5|>=7.0", - "symfony/dom-crawler": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/form": "<5.2", - "symfony/http-client": "<4.4", - "symfony/lock": "<4.4", - "symfony/mailer": "<5.2", - "symfony/messenger": "<5.4", - "symfony/mime": "<4.4", - "symfony/property-access": "<5.3", - "symfony/property-info": "<4.4", - "symfony/security-csrf": "<5.3", - "symfony/serializer": "<5.2", - "symfony/service-contracts": ">=3.0", - "symfony/stopwatch": "<4.4", - "symfony/translation": "<5.3", - "symfony/twig-bridge": "<4.4", - "symfony/twig-bundle": "<4.4", - "symfony/validator": "<5.3.11", - "symfony/web-profiler-bundle": "<4.4", - "symfony/workflow": "<5.2" - }, - "require-dev": { - "doctrine/annotations": "^1.13.1|^2", - "doctrine/cache": "^1.11|^2.0", - "doctrine/persistence": "^1.3|^2|^3", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/asset": "^5.3|^6.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/console": "^5.4.9|^6.0.9", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/dom-crawler": "^4.4.30|^5.3.7|^6.0", - "symfony/dotenv": "^5.1|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/form": "^5.2|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/mailer": "^5.2|^6.0", - "symfony/messenger": "^5.4|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/notifier": "^5.4|^6.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/property-info": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0", - "symfony/security-bundle": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/string": "^5.0|^6.0", - "symfony/translation": "^5.3|^6.0", - "symfony/twig-bundle": "^4.4|^5.0|^6.0", - "symfony/validator": "^5.3.11|^6.0", - "symfony/web-link": "^4.4|^5.0|^6.0", - "symfony/workflow": "^5.2|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0", - "twig/twig": "^2.10|^3.0" - }, - "suggest": { - "ext-apcu": "For best performance of the system caches", - "symfony/console": "For using the console commands", - "symfony/form": "For using forms", - "symfony/property-info": "For using the property_info service", - "symfony/serializer": "For using the serializer service", - "symfony/validator": "For using validation", - "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", - "symfony/yaml": "For using the debug:config and lint:yaml commands" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Symfony\\Bundle\\FrameworkBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T14:28:09+00:00" - }, - { - "name": "symfony/http-client", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "53e4cc088a5f3466dc77c9f121f17e8e02ecc9c3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/53e4cc088a5f3466dc77c9f121f17e8e02ecc9c3", - "reference": "53e4cc088a5f3466dc77c9f121f17e8e02ecc9c3", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-client-contracts": "^2.4", - "symfony/polyfill-php73": "^1.11", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.0|^2|^3" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "2.4" - }, - "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", - "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "php-http/message-factory": "^1.0", - "psr/http-client": "^1.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", - "homepage": "https://symfony.com", - "keywords": [ - "http" - ], - "support": { - "source": "https://github.com/symfony/http-client/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T14:02:34+00:00" - }, - { - "name": "symfony/http-client-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70", - "reference": "ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/http-client-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-04-12T15:48:08+00:00" - }, - { - "name": "symfony/http-foundation", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "f2ab692a22aef1cd54beb893aa0068bdfb093928" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f2ab692a22aef1cd54beb893aa0068bdfb093928", - "reference": "f2ab692a22aef1cd54beb893aa0068bdfb093928", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "predis/predis": "~1.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0" - }, - "suggest": { - "symfony/mime": "To use the file extension guesser" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Defines an object-oriented layer for the HTTP specification", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/http-kernel", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "949bc7721c83fa9f81fc6c9697db0aa340c64f4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/949bc7721c83fa9f81fc6c9697db0aa340c64f4d", - "reference": "949bc7721c83fa9f81fc6c9697db0aa340c64f4d", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^5.0|^6.0", - "symfony/http-foundation": "^5.4.21|^6.2.7", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/browser-kit": "<5.4", - "symfony/cache": "<5.0", - "symfony/config": "<5.0", - "symfony/console": "<4.4", - "symfony/dependency-injection": "<5.3", - "symfony/doctrine-bridge": "<5.0", - "symfony/form": "<5.0", - "symfony/http-client": "<5.0", - "symfony/mailer": "<5.0", - "symfony/messenger": "<5.0", - "symfony/translation": "<5.0", - "symfony/twig-bridge": "<5.0", - "symfony/validator": "<5.0", - "twig/twig": "<2.13" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" - }, - "require-dev": { - "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/config": "^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client-contracts": "^1.1|^2|^3", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2|^3", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpKernel\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a structured process for converting a Request into a Response", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-30T20:00:46+00:00" - }, - { - "name": "symfony/intl", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/intl.git", - "reference": "2fb503f81cd8032a624c55764d1fbaf893aa3cf4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/2fb503f81cd8032a624c55764d1fbaf893aa3cf4", - "reference": "2fb503f81cd8032a624c55764d1fbaf893aa3cf4", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/var-exporter": "^5.4|^6.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\Intl\\": "" - }, - "classmap": [ - "Resources/stubs" - ], - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - }, - { - "name": "Eriksen Costa", - "email": "eriksen.costa@infranology.com.br" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a PHP replacement layer for the C intl extension that includes additional data from the ICU library", - "homepage": "https://symfony.com", - "keywords": [ - "i18n", - "icu", - "internationalization", - "intl", - "l10n", - "localization" - ], - "support": { - "source": "https://github.com/symfony/intl/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/mailer", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/mailer.git", - "reference": "664724b0fb4646dee30859d0ed9131a2d7633320" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/664724b0fb4646dee30859d0ed9131a2d7633320", - "reference": "664724b0fb4646dee30859d0ed9131a2d7633320", - "shasum": "" - }, - "require": { - "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=7.2.5", - "psr/event-dispatcher": "^1", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/mime": "^5.2.6|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3" - }, - "conflict": { - "symfony/http-kernel": "<4.4" - }, - "require-dev": { - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Mailer\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Helps sending emails", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/mailer/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T07:33:37+00:00" - }, - { - "name": "symfony/messenger", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/messenger.git", - "reference": "3c1eda00d7e9879558397fe8c8cfaea24b0de4e0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/3c1eda00d7e9879558397fe8c8cfaea24b0de4e0", - "reference": "3c1eda00d7e9879558397fe8c8cfaea24b0de4e0", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2|^3", - "symfony/amqp-messenger": "^5.1|^6.0", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/doctrine-messenger": "^5.1|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/redis-messenger": "^5.1|^6.0" - }, - "conflict": { - "symfony/event-dispatcher": "<4.4", - "symfony/framework-bundle": "<4.4", - "symfony/http-kernel": "<4.4", - "symfony/serializer": "<5.0" - }, - "require-dev": { - "psr/cache": "^1.0|^2.0|^3.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/property-access": "^4.4|^5.0|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/serializer": "^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/validator": "^4.4|^5.0|^6.0" - }, - "suggest": { - "enqueue/messenger-adapter": "For using the php-enqueue library as a transport." - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Messenger\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Samuel Roze", - "email": "samuel.roze@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Helps applications send and receive messages to/from other applications or via message queues", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/messenger/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-30T11:13:29+00:00" - }, - { - "name": "symfony/mime", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/mime.git", - "reference": "ee94d9b538f93abbbc1ee4ccff374593117b04a9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/ee94d9b538f93abbbc1ee4ccff374593117b04a9", - "reference": "ee94d9b538f93abbbc1ee4ccff374593117b04a9", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "egulias/email-validator": "~3.0.0", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/mailer": "<4.4", - "symfony/serializer": "<5.4.35|>=6,<6.3.12|>=6.4,<6.4.3" - }, - "require-dev": { - "egulias/email-validator": "^2.1.10|^3.1|^4", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/property-access": "^4.4|^5.1|^6.0", - "symfony/property-info": "^4.4|^5.1|^6.0", - "symfony/serializer": "^5.4.35|~6.3.12|^6.4.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Mime\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows manipulating MIME messages", - "homepage": "https://symfony.com", - "keywords": [ - "mime", - "mime-type" - ], - "support": { - "source": "https://github.com/symfony/mime/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-30T08:00:51+00:00" - }, - { - "name": "symfony/monolog-bridge", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/monolog-bridge.git", - "reference": "83e7438fd2ead9af4fd2fac7bb9b6fc0e8823387" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/83e7438fd2ead9af4fd2fac7bb9b6fc0e8823387", - "reference": "83e7438fd2ead9af4fd2fac7bb9b6fc0e8823387", - "shasum": "" - }, - "require": { - "monolog/monolog": "^1.25.1|^2", - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-kernel": "^5.3|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3" - }, - "conflict": { - "symfony/console": "<4.4", - "symfony/http-foundation": "<5.3" - }, - "require-dev": { - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/mailer": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/security-core": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings.", - "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", - "symfony/var-dumper": "For using the debugging handlers like the console handler or the log server handler." - }, - "type": "symfony-bridge", - "autoload": { - "psr-4": { - "Symfony\\Bridge\\Monolog\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides integration for Monolog with various Symfony components", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/monolog-bridge/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/monolog-bundle", - "version": "v3.10.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/monolog-bundle.git", - "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", - "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", - "shasum": "" - }, - "require": { - "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", - "php": ">=7.2.5", - "symfony/config": "^5.4 || ^6.0 || ^7.0", - "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", - "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", - "symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0" - }, - "require-dev": { - "symfony/console": "^5.4 || ^6.0 || ^7.0", - "symfony/phpunit-bridge": "^6.3 || ^7.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Bundle\\MonologBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony MonologBundle", - "homepage": "https://symfony.com", - "keywords": [ - "log", - "logging" - ], - "support": { - "issues": "https://github.com/symfony/monolog-bundle/issues", - "source": "https://github.com/symfony/monolog-bundle/tree/v3.10.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-11-06T17:08:13+00:00" - }, - { - "name": "symfony/notifier", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/notifier.git", - "reference": "96c5ff38efab8031d3396d4cd4743a1776f31c69" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/notifier/zipball/96c5ff38efab8031d3396d4cd4743a1776f31c69", - "reference": "96c5ff38efab8031d3396d4cd4743a1776f31c69", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2|^3", - "symfony/polyfill-php80": "^1.15" - }, - "conflict": { - "symfony/discord-notifier": "<5.3", - "symfony/esendex-notifier": "<5.3", - "symfony/firebase-notifier": "<5.3", - "symfony/free-mobile-notifier": "<5.3", - "symfony/google-chat-notifier": "<5.3", - "symfony/http-kernel": "<4.4", - "symfony/infobip-notifier": "<5.3", - "symfony/linked-in-notifier": "<5.3", - "symfony/mattermost-notifier": "<5.3", - "symfony/mobyt-notifier": "<5.3", - "symfony/nexmo-notifier": "<5.3", - "symfony/ovh-cloud-notifier": "<5.3", - "symfony/rocket-chat-notifier": "<5.3", - "symfony/sendinblue-notifier": "<5.3", - "symfony/sinch-notifier": "<5.3", - "symfony/slack-notifier": "<5.3", - "symfony/sms77-notifier": "<5.3", - "symfony/smsapi-notifier": "<5.3", - "symfony/telegram-notifier": "<5.3", - "symfony/twilio-notifier": "<5.3", - "symfony/zulip-notifier": "<5.3" - }, - "require-dev": { - "symfony/event-dispatcher-contracts": "^2|^3", - "symfony/http-client-contracts": "^2|^3", - "symfony/messenger": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Notifier\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Sends notifications via one or more channels (email, SMS, ...)", - "homepage": "https://symfony.com", - "keywords": [ - "notification", - "notifier" - ], - "support": { - "source": "https://github.com/symfony/notifier/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/options-resolver", - "version": "v5.4.21", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "4fe5cf6ede71096839f0e4b4444d65dd3a7c1eb9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/4fe5cf6ede71096839f0e4b4444d65dd3a7c1eb9", - "reference": "4fe5cf6ede71096839f0e4b4444d65dd3a7c1eb9", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php73": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], - "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.4.21" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-02-14T08:03:56+00:00" - }, - { - "name": "symfony/password-hasher", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/password-hasher.git", - "reference": "23b9782de5d06a7e61101558d3e887100fbf8f93" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/password-hasher/zipball/23b9782de5d06a7e61101558d3e887100fbf8f93", - "reference": "23b9782de5d06a7e61101558d3e887100fbf8f93", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.15" - }, - "conflict": { - "symfony/security-core": "<5.3" - }, - "require-dev": { - "symfony/console": "^5.3|^6.0", - "symfony/security-core": "^5.3|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\PasswordHasher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Robin Chalas", - "email": "robin.chalas@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides password hashing utilities", - "homepage": "https://symfony.com", - "keywords": [ - "hashing", - "password" - ], - "support": { - "source": "https://github.com/symfony/password-hasher/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-intl-icu", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-icu.git", - "reference": "07094a28851a49107f3ab4f9120ca2975a64b6e1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/07094a28851a49107f3ab4f9120ca2975a64b6e1", - "reference": "07094a28851a49107f3ab4f9120ca2975a64b6e1", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance and support of other locales than \"en\"" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Icu\\": "" - }, - "classmap": [ - "Resources/stubs" - ], - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's ICU-related data and classes", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "icu", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.29.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:12:16+00:00" - }, - { - "name": "symfony/polyfill-intl-idn", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a287ed7475f85bf6f61890146edbc932c0fff919" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919", - "reference": "a287ed7475f85bf6f61890146edbc932c0fff919", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "21bd091060673a1177ae842c0ef8fe30893114d2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/21bd091060673a1177ae842c0ef8fe30893114d2", - "reference": "21bd091060673a1177ae842c0ef8fe30893114d2", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.29.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-php81", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d", - "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/process", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "cbc28e34015ad50166fc2f9c8962d28d0fe861eb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/cbc28e34015ad50166fc2f9c8962d28d0fe861eb", - "reference": "cbc28e34015ad50166fc2f9c8962d28d0fe861eb", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/property-access", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/property-access.git", - "reference": "f1341758d8046cfff0ac748a0cad238f917191d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/f1341758d8046cfff0ac748a0cad238f917191d4", - "reference": "f1341758d8046cfff0ac748a0cad238f917191d4", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/property-info": "^5.2|^6.0" - }, - "require-dev": { - "symfony/cache": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/cache-implementation": "To cache access methods." - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\PropertyAccess\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides functions to read and write from/to an object or array using a simple string notation", - "homepage": "https://symfony.com", - "keywords": [ - "access", - "array", - "extraction", - "index", - "injection", - "object", - "property", - "property-path", - "reflection" - ], - "support": { - "source": "https://github.com/symfony/property-access/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/property-info", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/property-info.git", - "reference": "d30d48f366ad2bfbf521256be85eb1c182c29198" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/d30d48f366ad2bfbf521256be85eb1c182c29198", - "reference": "d30d48f366ad2bfbf521256be85eb1c182c29198", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/string": "^5.1|^6.0" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/dependency-injection": "<4.4" - }, - "require-dev": { - "doctrine/annotations": "^1.10.4|^2", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "phpstan/phpdoc-parser": "^1.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" - }, - "suggest": { - "phpdocumentor/reflection-docblock": "To use the PHPDoc", - "psr/cache-implementation": "To cache results", - "symfony/doctrine-bridge": "To use Doctrine metadata", - "symfony/serializer": "To use Serializer metadata" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\PropertyInfo\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kévin Dunglas", - "email": "dunglas@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Extracts information about PHP class' properties using metadata of popular sources", - "homepage": "https://symfony.com", - "keywords": [ - "doctrine", - "phpdoc", - "property", - "symfony", - "type", - "validator" - ], - "support": { - "source": "https://github.com/symfony/property-info/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T15:43:50+00:00" - }, - { - "name": "symfony/redis-messenger", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/redis-messenger.git", - "reference": "d00f1bbaefe83468c0afb3f6773258cefca13bb8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/redis-messenger/zipball/d00f1bbaefe83468c0afb3f6773258cefca13bb8", - "reference": "d00f1bbaefe83468c0afb3f6773258cefca13bb8", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/messenger": "^5.1|^6.0" - }, - "require-dev": { - "symfony/property-access": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" - }, - "type": "symfony-messenger-bridge", - "autoload": { - "psr-4": { - "Symfony\\Component\\Messenger\\Bridge\\Redis\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Redis extension Messenger Bridge", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/redis-messenger/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/routing", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "86c5a06a61ddaf17efa1403542e3d7146af96203" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/86c5a06a61ddaf17efa1403542e3d7146af96203", - "reference": "86c5a06a61ddaf17efa1403542e3d7146af96203", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/annotations": "<1.12", - "symfony/config": "<5.3", - "symfony/dependency-injection": "<4.4", - "symfony/yaml": "<4.4" - }, - "require-dev": { - "doctrine/annotations": "^1.12|^2", - "psr/log": "^1|^2|^3", - "symfony/config": "^5.3|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Routing\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Maps an HTTP request to a set of configuration variables", - "homepage": "https://symfony.com", - "keywords": [ - "router", - "routing", - "uri", - "url" - ], - "support": { - "source": "https://github.com/symfony/routing/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-30T13:10:15+00:00" - }, - { - "name": "symfony/runtime", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/runtime.git", - "reference": "a32a623d71fc0f699a2a196377b3b85c840bd39a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/a32a623d71fc0f699a2a196377b3b85c840bd39a", - "reference": "a32a623d71fc0f699a2a196377b3b85c840bd39a", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0|^2.0", - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.15" - }, - "conflict": { - "symfony/dotenv": "<5.1" - }, - "require-dev": { - "composer/composer": "^1.0.2|^2.0", - "symfony/console": "^4.4.30|^5.4.9|^6.0.9", - "symfony/dotenv": "^5.1|^6.0", - "symfony/http-foundation": "^4.4.30|^5.3.7|^6.0", - "symfony/http-kernel": "^4.4.30|^5.3.7|^6.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Symfony\\Component\\Runtime\\Internal\\ComposerPlugin" - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Runtime\\": "", - "Symfony\\Runtime\\Symfony\\Component\\": "Internal/" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Enables decoupling PHP applications from global state", - "homepage": "https://symfony.com", - "keywords": [ - "runtime" - ], - "support": { - "source": "https://github.com/symfony/runtime/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/security-bundle", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/security-bundle.git", - "reference": "ed194715515a87d0f9c80b8696baf37ae18beb81" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/ed194715515a87d0f9c80b8696baf37ae18beb81", - "reference": "ed194715515a87d0f9c80b8696baf37ae18beb81", - "shasum": "" - }, - "require": { - "ext-xml": "*", - "php": ">=7.2.5", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher": "^5.1|^6.0", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/http-kernel": "^5.3|^6.0", - "symfony/password-hasher": "^5.3|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/security-core": "^5.4|^6.0", - "symfony/security-csrf": "^4.4|^5.0|^6.0", - "symfony/security-guard": "^5.3", - "symfony/security-http": "^5.4.30|^6.3.6", - "symfony/service-contracts": "^1.10|^2|^3" - }, - "conflict": { - "symfony/browser-kit": "<4.4", - "symfony/console": "<4.4", - "symfony/framework-bundle": "<4.4", - "symfony/ldap": "<5.1", - "symfony/twig-bundle": "<4.4" - }, - "require-dev": { - "doctrine/annotations": "^1.10.4|^2", - "symfony/asset": "^4.4|^5.0|^6.0", - "symfony/browser-kit": "^4.4|^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/form": "^4.4|^5.0|^6.0", - "symfony/framework-bundle": "^5.3|^6.0", - "symfony/ldap": "^5.3|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/twig-bridge": "^4.4|^5.0|^6.0", - "symfony/twig-bundle": "^4.4|^5.0|^6.0", - "symfony/validator": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Symfony\\Bundle\\SecurityBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/security-bundle/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/security-core", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/security-core.git", - "reference": "3cbacefb2a350ed39950f93c8a054c2eb625fb69" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/3cbacefb2a350ed39950f93c8a054c2eb625fb69", - "reference": "3cbacefb2a350ed39950f93c8a054c2eb625fb69", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^1.1|^2|^3", - "symfony/password-hasher": "^5.3|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1.6|^2|^3" - }, - "conflict": { - "symfony/event-dispatcher": "<4.4", - "symfony/http-foundation": "<5.3", - "symfony/ldap": "<4.4", - "symfony/security-guard": "<4.4", - "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3", - "symfony/validator": "<5.2" - }, - "require-dev": { - "psr/cache": "^1.0|^2.0|^3.0", - "psr/container": "^1.0|^2.0", - "psr/log": "^1|^2|^3", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/ldap": "^4.4|^5.0|^6.0", - "symfony/translation": "^5.4.35|~6.3.12|^6.4.3", - "symfony/validator": "^5.2|^6.0" - }, - "suggest": { - "psr/container-implementation": "To instantiate the Security class", - "symfony/event-dispatcher": "", - "symfony/expression-language": "For using the expression voter", - "symfony/http-foundation": "", - "symfony/ldap": "For using LDAP integration", - "symfony/validator": "For using the user password constraint" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Security\\Core\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Security Component - Core Library", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/security-core/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/security-csrf", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/security-csrf.git", - "reference": "6728ed79d7f9aae3b86fca7ea554f1c46bae1e0b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/security-csrf/zipball/6728ed79d7f9aae3b86fca7ea554f1c46bae1e0b", - "reference": "6728ed79d7f9aae3b86fca7ea554f1c46bae1e0b", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/security-core": "^4.4|^5.0|^6.0" - }, - "conflict": { - "symfony/http-foundation": "<5.3" - }, - "require-dev": { - "symfony/http-foundation": "^5.3|^6.0" - }, - "suggest": { - "symfony/http-foundation": "For using the class SessionTokenStorage." - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Security\\Csrf\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Security Component - CSRF Library", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/security-csrf/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/security-guard", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/security-guard.git", - "reference": "b6fb8c88f7cd544db761de2d1c3618cbc5c1b9e7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/security-guard/zipball/b6fb8c88f7cd544db761de2d1c3618cbc5c1b9e7", - "reference": "b6fb8c88f7cd544db761de2d1c3618cbc5c1b9e7", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.15", - "symfony/security-core": "^5.0", - "symfony/security-http": "^5.3" - }, - "require-dev": { - "psr/log": "^1|^2|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Security\\Guard\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Security Component - Guard", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/security-guard/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/security-http", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/security-http.git", - "reference": "274a6aef49a0e1707bcb57217251885be749b6d8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/274a6aef49a0e1707bcb57217251885be749b6d8", - "reference": "274a6aef49a0e1707bcb57217251885be749b6d8", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/http-kernel": "^5.3|^6.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/property-access": "^4.4|^5.0|^6.0", - "symfony/security-core": "^5.4.19|~6.0.19|~6.1.11|^6.2.5", - "symfony/service-contracts": "^1.10|^2|^3" - }, - "conflict": { - "symfony/event-dispatcher": "<4.3", - "symfony/security-bundle": "<5.3", - "symfony/security-csrf": "<4.4" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/security-csrf": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs", - "symfony/security-csrf": "For using tokens to protect authentication/logout attempts" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Security\\Http\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Security Component - HTTP Integration", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/security-http/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/serializer", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/serializer.git", - "reference": "02acd86290077dab2f12ae91b3e9f141c079d84c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/02acd86290077dab2f12ae91b3e9f141c079d84c", - "reference": "02acd86290077dab2f12ae91b3e9f141c079d84c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/annotations": "<1.12", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/dependency-injection": "<4.4", - "symfony/property-access": "<5.4", - "symfony/property-info": "<5.4.24|>=6,<6.2.11", - "symfony/uid": "<5.3", - "symfony/yaml": "<4.4" - }, - "require-dev": { - "doctrine/annotations": "^1.12|^2", - "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/form": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/property-access": "^5.4.26|^6.3", - "symfony/property-info": "^5.4.24|^6.2.11", - "symfony/uid": "^5.3|^6.0", - "symfony/validator": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0", - "symfony/var-exporter": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/cache-implementation": "For using the metadata cache.", - "symfony/config": "For using the XML mapping loader.", - "symfony/mime": "For using a MIME type guesser within the DataUriNormalizer.", - "symfony/property-access": "For using the ObjectNormalizer.", - "symfony/property-info": "To deserialize relations.", - "symfony/var-exporter": "For using the metadata compiler.", - "symfony/yaml": "For using the default YAML mapping loader." - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Serializer\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/serializer/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-30T08:00:51+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-30T19:17:29+00:00" - }, - { - "name": "symfony/stopwatch", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "887762aa99ff16f65dc8b48aafead415f942d407" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/887762aa99ff16f65dc8b48aafead415f942d407", - "reference": "887762aa99ff16f65dc8b48aafead415f942d407", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/service-contracts": "^1|^2|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a way to profile code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/string", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "c209c4d0559acce1c9a2067612cfb5d35756edc2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/c209c4d0559acce1c9a2067612cfb5d35756edc2", - "reference": "c209c4d0559acce1c9a2067612cfb5d35756edc2", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" - }, - "conflict": { - "symfony/translation-contracts": ">=3.0" - }, - "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/translation", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "77d7d1e46f52827585e65e6cd6f52a2542e59c72" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/77d7d1e46f52827585e65e6cd6f52a2542e59c72", - "reference": "77d7d1e46f52827585e65e6cd6f52a2542e59c72", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation-contracts": "^2.3" - }, - "conflict": { - "symfony/config": "<4.4", - "symfony/console": "<5.3", - "symfony/dependency-injection": "<5.0", - "symfony/http-kernel": "<5.0", - "symfony/twig-bundle": "<5.0", - "symfony/yaml": "<4.4" - }, - "provide": { - "symfony/translation-implementation": "2.3" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client-contracts": "^1.1|^2.0|^3.0", - "symfony/http-kernel": "^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", - "symfony/polyfill-intl-icu": "^1.21", - "symfony/service-contracts": "^1.1.2|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to internationalize your application", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/translation/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/translation-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/translation-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Translation\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to translation", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-06-27T16:58:25+00:00" - }, - { - "name": "symfony/twig-bridge", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/twig-bridge.git", - "reference": "e6ae362b7c4f1d6e99f61f59b0c93b9f027b4c73" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/e6ae362b7c4f1d6e99f61f59b0c93b9f027b4c73", - "reference": "e6ae362b7c4f1d6e99f61f59b0c93b9f027b4c73", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16", - "symfony/translation-contracts": "^1.1|^2|^3", - "twig/twig": "^2.13|^3.0.4" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/console": "<5.3", - "symfony/form": "<5.4.21|>=6,<6.2.7", - "symfony/http-foundation": "<5.3", - "symfony/http-kernel": "<4.4", - "symfony/translation": "<5.2", - "symfony/workflow": "<5.2" - }, - "require-dev": { - "doctrine/annotations": "^1.12|^2", - "egulias/email-validator": "^2.1.10|^3|^4", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/asset": "^4.4|^5.0|^6.0", - "symfony/console": "^5.3|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/form": "^5.4.21|^6.2.7", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", - "symfony/mime": "^5.2|^6.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/property-info": "^4.4|^5.1|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/security-acl": "^2.8|^3.0", - "symfony/security-core": "^4.4|^5.0|^6.0", - "symfony/security-csrf": "^4.4|^5.0|^6.0", - "symfony/security-http": "^4.4|^5.0|^6.0", - "symfony/serializer": "^5.4.35|~6.3.12|^6.4.3", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^5.2|^6.0", - "symfony/web-link": "^4.4|^5.0|^6.0", - "symfony/workflow": "^5.2|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0", - "twig/cssinliner-extra": "^2.12|^3", - "twig/inky-extra": "^2.12|^3", - "twig/markdown-extra": "^2.12|^3" - }, - "suggest": { - "symfony/asset": "For using the AssetExtension", - "symfony/expression-language": "For using the ExpressionExtension", - "symfony/finder": "", - "symfony/form": "For using the FormExtension", - "symfony/http-kernel": "For using the HttpKernelExtension", - "symfony/routing": "For using the RoutingExtension", - "symfony/security-core": "For using the SecurityExtension", - "symfony/security-csrf": "For using the CsrfExtension", - "symfony/security-http": "For using the LogoutUrlExtension", - "symfony/stopwatch": "For using the StopwatchExtension", - "symfony/translation": "For using the TranslationExtension", - "symfony/var-dumper": "For using the DumpExtension", - "symfony/web-link": "For using the WebLinkExtension", - "symfony/yaml": "For using the YamlExtension" - }, - "type": "symfony-bridge", - "autoload": { - "psr-4": { - "Symfony\\Bridge\\Twig\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides integration for Twig with various Symfony components", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-30T08:00:51+00:00" - }, - { - "name": "symfony/twig-bundle", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/twig-bundle.git", - "reference": "f59b91e23c7c790e71a187c3fa4aefdc5391d682" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/f59b91e23c7c790e71a187c3fa4aefdc5391d682", - "reference": "f59b91e23c7c790e71a187c3fa4aefdc5391d682", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^5.0|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/twig-bridge": "^5.3|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "conflict": { - "symfony/dependency-injection": "<5.3", - "symfony/framework-bundle": "<5.0", - "symfony/service-contracts": ">=3.0", - "symfony/translation": "<5.0" - }, - "require-dev": { - "doctrine/annotations": "^1.10.4|^2", - "doctrine/cache": "^1.0|^2.0", - "symfony/asset": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/form": "^4.4|^5.0|^6.0", - "symfony/framework-bundle": "^5.0|^6.0", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0", - "symfony/translation": "^5.0|^6.0", - "symfony/web-link": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Symfony\\Bundle\\TwigBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a tight integration of Twig into the Symfony full-stack framework", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/validator", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/validator.git", - "reference": "4993e9b5b17e23e7ad9dc2e3b31412f5c4679385" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/4993e9b5b17e23e7ad9dc2e3b31412f5c4679385", - "reference": "4993e9b5b17e23e7ad9dc2e3b31412f5c4679385", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22", - "symfony/translation-contracts": "^1.1|^2|^3" - }, - "conflict": { - "doctrine/annotations": "<1.13", - "doctrine/cache": "<1.11", - "doctrine/lexer": "<1.1", - "symfony/dependency-injection": "<4.4", - "symfony/expression-language": "<5.1", - "symfony/http-kernel": "<4.4", - "symfony/intl": "<4.4", - "symfony/property-info": "<5.3", - "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3", - "symfony/yaml": "<4.4" - }, - "require-dev": { - "doctrine/annotations": "^1.13|^2", - "doctrine/cache": "^1.11|^2.0", - "egulias/email-validator": "^2.1.10|^3|^4", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^5.1|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/property-access": "^4.4|^5.0|^6.0", - "symfony/property-info": "^5.3|^6.0", - "symfony/translation": "^5.4.35|~6.3.12|^6.4.3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "egulias/email-validator": "Strict (RFC compliant) email validation", - "psr/cache-implementation": "For using the mapping cache.", - "symfony/config": "", - "symfony/expression-language": "For using the Expression validator and the ExpressionLanguageSyntax constraints", - "symfony/http-foundation": "", - "symfony/intl": "", - "symfony/property-access": "For accessing properties within comparison constraints", - "symfony/property-info": "To automatically add NotNull and Type constraints", - "symfony/translation": "For translating validation errors.", - "symfony/yaml": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Validator\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to validate values", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/validator/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-25T22:32:56+00:00" - }, - { - "name": "symfony/var-dumper", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "ce4685b30e47d94dfc990c5566285ff99ddf012b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/ce4685b30e47d94dfc990c5566285ff99ddf012b", - "reference": "ce4685b30e47d94dfc990c5566285ff99ddf012b", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/console": "<4.4" - }, - "require-dev": { - "ext-iconv": "*", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" - }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", - "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T14:28:09+00:00" - }, - { - "name": "symfony/var-exporter", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-exporter.git", - "reference": "abb0a151b62d6b07e816487e20040464af96cae7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/abb0a151b62d6b07e816487e20040464af96cae7", - "reference": "abb0a151b62d6b07e816487e20040464af96cae7", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "symfony/var-dumper": "^4.4.9|^5.0.9|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\VarExporter\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows exporting any serializable PHP data structure to plain PHP code", - "homepage": "https://symfony.com", - "keywords": [ - "clone", - "construct", - "export", - "hydrate", - "instantiate", - "serialize" - ], - "support": { - "source": "https://github.com/symfony/var-exporter/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/web-link", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/web-link.git", - "reference": "5eb4930dd8bdf233d32e4e3c2dfa8241051b6320" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/web-link/zipball/5eb4930dd8bdf233d32e4e3c2dfa8241051b6320", - "reference": "5eb4930dd8bdf233d32e4e3c2dfa8241051b6320", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/link": "^1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/http-kernel": "<5.3" - }, - "provide": { - "psr/link-implementation": "1.0" - }, - "require-dev": { - "symfony/http-kernel": "^5.3|^6.0" - }, - "suggest": { - "symfony/http-kernel": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\WebLink\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kévin Dunglas", - "email": "dunglas@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Manages links between resources", - "homepage": "https://symfony.com", - "keywords": [ - "dns-prefetch", - "http", - "http2", - "link", - "performance", - "prefetch", - "preload", - "prerender", - "psr13", - "push" - ], - "support": { - "source": "https://github.com/symfony/web-link/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/yaml", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "e78db7f5c70a21f0417a31f414c4a95fe76c07e4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e78db7f5c70a21f0417a31f414c4a95fe76c07e4", - "reference": "e78db7f5c70a21f0417a31f414c4a95fe76c07e4", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/console": "<5.3" - }, - "require-dev": { - "symfony/console": "^5.3|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "bin": [ - "Resources/bin/yaml-lint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "twig/extra-bundle", - "version": "v3.8.0", - "source": { - "type": "git", - "url": "https://github.com/twigphp/twig-extra-bundle.git", - "reference": "32807183753de0388c8e59f7ac2d13bb47311140" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/32807183753de0388c8e59f7ac2d13bb47311140", - "reference": "32807183753de0388c8e59f7ac2d13bb47311140", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/framework-bundle": "^5.4|^6.0|^7.0", - "symfony/twig-bundle": "^5.4|^6.0|^7.0", - "twig/twig": "^3.0" - }, - "require-dev": { - "league/commonmark": "^1.0|^2.0", - "symfony/phpunit-bridge": "^6.4|^7.0", - "twig/cache-extra": "^3.0", - "twig/cssinliner-extra": "^2.12|^3.0", - "twig/html-extra": "^2.12|^3.0", - "twig/inky-extra": "^2.12|^3.0", - "twig/intl-extra": "^2.12|^3.0", - "twig/markdown-extra": "^2.12|^3.0", - "twig/string-extra": "^2.12|^3.0" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Twig\\Extra\\TwigExtraBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - } - ], - "description": "A Symfony bundle for extra Twig extensions", - "homepage": "https://twig.symfony.com", - "keywords": [ - "bundle", - "extra", - "twig" - ], - "support": { - "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.8.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-11-21T14:02:01+00:00" - }, - { - "name": "twig/twig", - "version": "v3.8.0", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.8.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-11-21T18:54:41+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "zircote/swagger-php", - "version": "3.3.7", - "source": { - "type": "git", - "url": "https://github.com/zircote/swagger-php.git", - "reference": "e8c3bb316e385e93a0c7e8b2aa0681079244c381" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/e8c3bb316e385e93a0c7e8b2aa0681079244c381", - "reference": "e8c3bb316e385e93a0c7e8b2aa0681079244c381", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.7", - "ext-json": "*", - "php": ">=7.2", - "psr/log": "^1.1 || ^2.0 || ^3.0", - "symfony/finder": ">=2.2", - "symfony/yaml": ">=3.3" - }, - "require-dev": { - "composer/package-versions-deprecated": "1.11.99.2", - "friendsofphp/php-cs-fixer": "^2.17 || ^3.0", - "phpunit/phpunit": ">=8.5.14" - }, - "bin": [ - "bin/openapi" - ], - "type": "library", - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "OpenApi\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Robert Allen", - "email": "zircote@gmail.com" - }, - { - "name": "Bob Fanger", - "email": "bfanger@gmail.com", - "homepage": "https://bfanger.nl" - }, - { - "name": "Martin Rademacher", - "email": "mano@radebatz.net", - "homepage": "https://radebatz.net" - } - ], - "description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations", - "homepage": "https://github.com/zircote/swagger-php/", - "keywords": [ - "api", - "json", - "rest", - "service discovery" - ], - "support": { - "issues": "https://github.com/zircote/swagger-php/issues", - "source": "https://github.com/zircote/swagger-php/tree/3.3.7" - }, - "time": "2023-01-03T21:17:10+00:00" - } - ], - "packages-dev": [ - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.18.0", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" - }, - "time": "2023-12-10T21:03:43+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "7.0.15", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "819f92bba8b001d4363065928088de22f25a3a48" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48", - "reference": "819f92bba8b001d4363065928088de22f25a3a48", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": ">=7.2", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.3 || ^4.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^4.2.2", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1.3" - }, - "require-dev": { - "phpunit/phpunit": "^8.2.2" - }, - "suggest": { - "ext-xdebug": "^2.7.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-07-26T12:20:09+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", - "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:42:26+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" - }, - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "2.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:20:02+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "3.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "9c1da83261628cb24b6a6df371b6e312b3954768" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9c1da83261628cb24b6a6df371b6e312b3954768", - "reference": "9c1da83261628cb24b6a6df371b6e312b3954768", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "abandoned": true, - "time": "2021-07-26T12:15:06+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "8.5.36", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9652df58e06a681429d8cfdaec3c43d6de581d5a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9652df58e06a681429d8cfdaec3c43d6de581d5a", - "reference": "9652df58e06a681429d8cfdaec3c43d6de581d5a", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.0", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.2", - "phpunit/php-code-coverage": "^7.0.12", - "phpunit/php-file-iterator": "^2.0.4", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1.2", - "sebastian/comparator": "^3.0.5", - "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.3", - "sebastian/exporter": "^3.1.5", - "sebastian/global-state": "^3.0.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0.1", - "sebastian/type": "^1.1.3", - "sebastian/version": "^2.0.1" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage", - "phpunit/php-invoker": "To allow enforcing time limits" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "8.5-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.36" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2023-12-01T16:52:15+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:15:22+00:00" - }, - { - "name": "sebastian/comparator", - "version": "3.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dc7ceb4a24aede938c7af2a9ed1de09609ca770", - "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:31:48+00:00" - }, - { - "name": "sebastian/diff", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "6296a0c086dd0117c1b78b059374d7fcbe7545ae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/6296a0c086dd0117c1b78b059374d7fcbe7545ae", - "reference": "6296a0c086dd0117c1b78b059374d7fcbe7545ae", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:30:20+00:00" - }, - { - "name": "sebastian/environment", - "version": "4.2.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:53:42+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/73a9676f2833b9a7c36968f9d882589cd75511e6", - "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6", - "shasum": "" - }, - "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:00:17+00:00" - }, - { - "name": "sebastian/global-state", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "66783ce213de415b451b904bfef9dda0cf9aeae0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/66783ce213de415b451b904bfef9dda0cf9aeae0", - "reference": "66783ce213de415b451b904bfef9dda0cf9aeae0", - "shasum": "" - }, - "require": { - "php": ">=7.2", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^8.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-02T09:23:32+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "shasum": "" - }, - "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:40:27+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:37:18+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:34:24+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:30:19+00:00" - }, - { - "name": "sebastian/type", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:25:11+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" - }, - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "symfony/browser-kit", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "2f6f979b579ed1c051465c3c2fb81daf5bb4a002" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/2f6f979b579ed1c051465c3c2fb81daf5bb4a002", - "reference": "2f6f979b579ed1c051465c3c2fb81daf5bb4a002", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/browser-kit/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/css-selector", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "9e615d367e2bed41f633abb383948c96a2dbbfae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/9e615d367e2bed41f633abb383948c96a2dbbfae", - "reference": "9e615d367e2bed41f633abb383948c96a2dbbfae", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Converts CSS selectors to XPath expressions", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/debug-bundle", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug-bundle.git", - "reference": "dfecbdbad82e59002c3fcf2322ec3a65f96d4f95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/dfecbdbad82e59002c3fcf2322ec3a65f96d4f95", - "reference": "dfecbdbad82e59002c3fcf2322ec3a65f96d4f95", - "shasum": "" - }, - "require": { - "ext-xml": "*", - "php": ">=7.2.5", - "symfony/http-kernel": "^4.4|^5.0|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/twig-bridge": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "conflict": { - "symfony/config": "<4.4", - "symfony/dependency-injection": "<5.2" - }, - "require-dev": { - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/web-profiler-bundle": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "For service container configuration", - "symfony/dependency-injection": "For using as a service from the container" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Symfony\\Bundle\\DebugBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/debug-bundle/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/dom-crawler", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "e3b4806f88abf106a411847a78619a542e71de29" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/e3b4806f88abf106a411847a78619a542e71de29", - "reference": "e3b4806f88abf106a411847a78619a542e71de29", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "masterminds/html5": "<2.6" - }, - "require-dev": { - "masterminds/html5": "^2.6", - "symfony/css-selector": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/css-selector": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases DOM navigation for HTML and XML documents", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/maker-bundle", - "version": "v1.43.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/maker-bundle.git", - "reference": "e3f9a1d9e0f4968f68454403e820dffc7db38a59" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/e3f9a1d9e0f4968f68454403e820dffc7db38a59", - "reference": "e3f9a1d9e0f4968f68454403e820dffc7db38a59", - "shasum": "" - }, - "require": { - "doctrine/inflector": "^2.0", - "nikic/php-parser": "^4.11", - "php": ">=7.2.5", - "symfony/config": "^5.4.7|^6.0", - "symfony/console": "^5.4.7|^6.0", - "symfony/dependency-injection": "^5.4.7|^6.0", - "symfony/deprecation-contracts": "^2.2|^3", - "symfony/filesystem": "^5.4.7|^6.0", - "symfony/finder": "^5.4.3|^6.0", - "symfony/framework-bundle": "^5.4.7|^6.0", - "symfony/http-kernel": "^5.4.7|^6.0" - }, - "conflict": { - "doctrine/orm": "<2.10" - }, - "require-dev": { - "composer/semver": "^3.0", - "doctrine/doctrine-bundle": "^2.4", - "doctrine/orm": "^2.10.0", - "symfony/http-client": "^5.4.7|^6.0", - "symfony/phpunit-bridge": "^5.4.7|^6.0", - "symfony/polyfill-php80": "^1.16.0", - "symfony/process": "^5.4.7|^6.0", - "symfony/security-core": "^5.4.7|^6.0", - "symfony/yaml": "^5.4.3|^6.0", - "twig/twig": "^2.0|^3.0" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-main": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Bundle\\MakerBundle\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.", - "homepage": "https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html", - "keywords": [ - "code generator", - "generator", - "scaffold", - "scaffolding" - ], - "support": { - "issues": "https://github.com/symfony/maker-bundle/issues", - "source": "https://github.com/symfony/maker-bundle/tree/v1.43.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-17T15:46:50+00:00" - }, - { - "name": "symfony/phpunit-bridge", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "2a0216076da723aeabebef57bbb5009d0ee920f6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/2a0216076da723aeabebef57bbb5009d0ee920f6", - "reference": "2a0216076da723aeabebef57bbb5009d0ee920f6", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/deprecation-contracts": "^2.1|^3" - }, - "conflict": { - "phpunit/phpunit": "<7.5|9.1.2" - }, - "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" - }, - "bin": [ - "bin/simple-phpunit" - ], - "type": "symfony-bridge", - "extra": { - "thanks": { - "name": "phpunit/phpunit", - "url": "https://github.com/sebastianbergmann/phpunit" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Bridge\\PhpUnit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides utilities for PHPUnit, especially user deprecation notices management", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T13:51:25+00:00" - }, - { - "name": "symfony/web-profiler-bundle", - "version": "v5.4.35", - "source": { - "type": "git", - "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "d71d64739a77ba23e7797d5ed3226796751df058" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/d71d64739a77ba23e7797d5ed3226796751df058", - "reference": "d71d64739a77ba23e7797d5ed3226796751df058", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/framework-bundle": "^5.3|^6.0,<6.4", - "symfony/http-kernel": "^5.3|^6.0", - "symfony/polyfill-php80": "^1.16", - "symfony/routing": "^4.4|^5.0|^6.0", - "symfony/twig-bundle": "^4.4|^5.0|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "conflict": { - "symfony/dependency-injection": "<5.2", - "symfony/form": "<4.4", - "symfony/mailer": "<5.4", - "symfony/messenger": "<4.4" - }, - "require-dev": { - "symfony/browser-kit": "^4.4|^5.0|^6.0", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Symfony\\Bundle\\WebProfilerBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a development tool that gives detailed information about the execution of any request", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v5.4.35" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T15:43:50+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.2", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.2" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2023-11-20T00:12:19+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": true, - "prefer-lowest": false, - "platform": { - "php": ">=7.2.0", - "ext-ctype": "*", - "ext-iconv": "*" - }, - "platform-dev": [], - "platform-overrides": { - "php": "7.2.24" - }, - "plugin-api-version": "2.6.0" -} diff --git a/config/bundles.php b/config/bundles.php deleted file mode 100644 index bf96f99..0000000 --- a/config/bundles.php +++ /dev/null @@ -1,15 +0,0 @@ - ['all' => true], - Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], - Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], - Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], - Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], - Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], - Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], - Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], - Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], - Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], - App\OgBootBundle\OgBootBundle::class => ['all' => true], -]; diff --git a/config/packages/cache.yaml b/config/packages/cache.yaml deleted file mode 100644 index 6899b72..0000000 --- a/config/packages/cache.yaml +++ /dev/null @@ -1,19 +0,0 @@ -framework: - cache: - # Unique name of your app: used to compute stable namespaces for cache keys. - #prefix_seed: your_vendor_name/app_name - - # The "app" cache stores to the filesystem by default. - # The data in this cache should persist between deploys. - # Other options include: - - # Redis - #app: cache.adapter.redis - #default_redis_provider: redis://localhost - - # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) - #app: cache.adapter.apcu - - # Namespaced pools use the above "app" backend by default - #pools: - #my.dedicated.cache: null diff --git a/config/packages/debug.yaml b/config/packages/debug.yaml deleted file mode 100644 index ad874af..0000000 --- a/config/packages/debug.yaml +++ /dev/null @@ -1,5 +0,0 @@ -when@dev: - debug: - # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. - # See the "server:dump" command to start a new server. - dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml deleted file mode 100644 index 1bb885e..0000000 --- a/config/packages/doctrine.yaml +++ /dev/null @@ -1,43 +0,0 @@ -doctrine: - dbal: - url: '%env(resolve:DATABASE_URL)%' - - # IMPORTANT: You MUST configure your server version, - # either here or in the DATABASE_URL env var (see .env file) - #server_version: '15' - orm: - auto_generate_proxy_classes: true - naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware - auto_mapping: true - mappings: - App: - is_bundle: false - dir: '%kernel.project_dir%/src/Entity' - prefix: 'App\Entity' - alias: App - -when@test: - doctrine: - dbal: - # "TEST_TOKEN" is typically set by ParaTest - dbname_suffix: '_test%env(default::TEST_TOKEN)%' - -when@prod: - doctrine: - orm: - auto_generate_proxy_classes: false - proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' - query_cache_driver: - type: pool - pool: doctrine.system_cache_pool - result_cache_driver: - type: pool - pool: doctrine.result_cache_pool - - framework: - cache: - pools: - doctrine.result_cache_pool: - adapter: cache.app - doctrine.system_cache_pool: - adapter: cache.system diff --git a/config/packages/doctrine_migrations.yaml b/config/packages/doctrine_migrations.yaml deleted file mode 100644 index 29231d9..0000000 --- a/config/packages/doctrine_migrations.yaml +++ /dev/null @@ -1,6 +0,0 @@ -doctrine_migrations: - migrations_paths: - # namespace is arbitrary but should be different from App\Migrations - # as migrations classes should NOT be autoloaded - 'DoctrineMigrations': '%kernel.project_dir%/migrations' - enable_profiler: false diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml deleted file mode 100644 index 7853e9e..0000000 --- a/config/packages/framework.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# see https://symfony.com/doc/current/reference/configuration/framework.html -framework: - secret: '%env(APP_SECRET)%' - #csrf_protection: true - http_method_override: false - - # Enables session support. Note that the session will ONLY be started if you read or write from it. - # Remove or comment this section to explicitly disable session support. - session: - handler_id: null - cookie_secure: auto - cookie_samesite: lax - storage_factory_id: session.storage.factory.native - - #esi: true - #fragments: true - php_errors: - log: true - -when@test: - framework: - test: true - session: - storage_factory_id: session.storage.factory.mock_file diff --git a/config/packages/mailer.yaml b/config/packages/mailer.yaml deleted file mode 100644 index 56a650d..0000000 --- a/config/packages/mailer.yaml +++ /dev/null @@ -1,3 +0,0 @@ -framework: - mailer: - dsn: '%env(MAILER_DSN)%' diff --git a/config/packages/messenger.yaml b/config/packages/messenger.yaml deleted file mode 100644 index 587083a..0000000 --- a/config/packages/messenger.yaml +++ /dev/null @@ -1,24 +0,0 @@ -framework: - messenger: - failure_transport: failed - - transports: - # https://symfony.com/doc/current/messenger.html#transport-configuration - async: - dsn: '%env(MESSENGER_TRANSPORT_DSN)%' - options: - use_notify: true - check_delayed_interval: 60000 - retry_strategy: - max_retries: 3 - multiplier: 2 - failed: 'doctrine://default?queue_name=failed' - # sync: 'sync://' - - routing: - Symfony\Component\Mailer\Messenger\SendEmailMessage: async - Symfony\Component\Notifier\Message\ChatMessage: async - Symfony\Component\Notifier\Message\SmsMessage: async - - # Route your messages to the transports - # 'App\Message\YourMessage': async diff --git a/config/packages/monolog.yaml b/config/packages/monolog.yaml deleted file mode 100644 index 8c9efa9..0000000 --- a/config/packages/monolog.yaml +++ /dev/null @@ -1,61 +0,0 @@ -monolog: - channels: - - deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists - -when@dev: - monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug - channels: ["!event"] - # uncomment to get logging in your browser - # you may have to allow bigger header sizes in your Web server configuration - #firephp: - # type: firephp - # level: info - #chromephp: - # type: chromephp - # level: info - console: - type: console - process_psr_3_messages: false - channels: ["!event", "!doctrine", "!console"] - -when@test: - monolog: - handlers: - main: - type: fingers_crossed - action_level: error - handler: nested - excluded_http_codes: [404, 405] - channels: ["!event"] - nested: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug - -when@prod: - monolog: - handlers: - main: - type: fingers_crossed - action_level: error - handler: nested - excluded_http_codes: [404, 405] - buffer_size: 50 # How many messages should be saved? Prevent memory leaks - nested: - type: stream - path: php://stderr - level: debug - formatter: monolog.formatter.json - console: - type: console - process_psr_3_messages: false - channels: ["!event", "!doctrine"] - deprecation: - type: stream - channels: [deprecation] - path: php://stderr diff --git a/config/packages/notifier.yaml b/config/packages/notifier.yaml deleted file mode 100644 index d02f986..0000000 --- a/config/packages/notifier.yaml +++ /dev/null @@ -1,12 +0,0 @@ -framework: - notifier: - chatter_transports: - texter_transports: - channel_policy: - # use chat/slack, chat/telegram, sms/twilio or sms/nexmo - urgent: ['email'] - high: ['email'] - medium: ['email'] - low: ['email'] - admin_recipients: - - { email: admin@example.com } diff --git a/config/packages/routing.yaml b/config/packages/routing.yaml deleted file mode 100644 index 4b766ce..0000000 --- a/config/packages/routing.yaml +++ /dev/null @@ -1,12 +0,0 @@ -framework: - router: - utf8: true - - # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. - # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands - #default_uri: http://localhost - -when@prod: - framework: - router: - strict_requirements: null diff --git a/config/packages/security.yaml b/config/packages/security.yaml deleted file mode 100644 index 789a9ac..0000000 --- a/config/packages/security.yaml +++ /dev/null @@ -1,40 +0,0 @@ -security: - enable_authenticator_manager: true - # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords - password_hashers: - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' - # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider - providers: - users_in_memory: { memory: null } - firewalls: - dev: - pattern: ^/(_(profiler|wdt)|css|images|js)/ - security: false - main: - lazy: true - provider: users_in_memory - - # activate different ways to authenticate - # https://symfony.com/doc/current/security.html#the-firewall - - # https://symfony.com/doc/current/security/impersonating_user.html - # switch_user: true - - # Easy way to control access for large sections of your site - # Note: Only the *first* access control that matches will be used - access_control: - # - { path: ^/admin, roles: ROLE_ADMIN } - # - { path: ^/profile, roles: ROLE_USER } - -when@test: - security: - password_hashers: - # By default, password hashers are resource intensive and take time. This is - # important to generate secure password hashes. In tests however, secure hashes - # are not important, waste resources and increase test times. The following - # reduces the work factor to the lowest possible values. - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: - algorithm: auto - cost: 4 # Lowest possible value for bcrypt - time_cost: 3 # Lowest possible value for argon - memory_cost: 10 # Lowest possible value for argon diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml deleted file mode 100644 index abb76aa..0000000 --- a/config/packages/translation.yaml +++ /dev/null @@ -1,13 +0,0 @@ -framework: - default_locale: en - translator: - default_path: '%kernel.project_dir%/translations' - fallbacks: - - en -# providers: -# crowdin: -# dsn: '%env(CROWDIN_DSN)%' -# loco: -# dsn: '%env(LOCO_DSN)%' -# lokalise: -# dsn: '%env(LOKALISE_DSN)%' diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml deleted file mode 100644 index f9f4cc5..0000000 --- a/config/packages/twig.yaml +++ /dev/null @@ -1,6 +0,0 @@ -twig: - default_path: '%kernel.project_dir%/templates' - -when@test: - twig: - strict_variables: true diff --git a/config/packages/validator.yaml b/config/packages/validator.yaml deleted file mode 100644 index 0201281..0000000 --- a/config/packages/validator.yaml +++ /dev/null @@ -1,13 +0,0 @@ -framework: - validation: - email_validation_mode: html5 - - # Enables validator auto-mapping support. - # For instance, basic validation constraints will be inferred from Doctrine's metadata. - #auto_mapping: - # App\Entity\: [] - -when@test: - framework: - validation: - not_compromised_password: false diff --git a/config/packages/web_profiler.yaml b/config/packages/web_profiler.yaml deleted file mode 100644 index 17893da..0000000 --- a/config/packages/web_profiler.yaml +++ /dev/null @@ -1,15 +0,0 @@ -when@dev: - web_profiler: - toolbar: true - intercept_redirects: false - - framework: - profiler: { only_exceptions: false } - -when@test: - web_profiler: - toolbar: false - intercept_redirects: false - - framework: - profiler: { collect: false } diff --git a/config/preload.php b/config/preload.php deleted file mode 100644 index 5ebcdb2..0000000 --- a/config/preload.php +++ /dev/null @@ -1,5 +0,0 @@ -= DEBUG_MEDIO) { - sprintf(msglog, "%s:%s", tbMensajes[8], script); - infoDebug(msglog); - } - - /* Crea matriz de los argumentos */ - nargs=splitCadena(argumentos,parametros,32); - for(i=nargs;i= DEBUG_ALTO) { - sprintf(msglog, "%s: #%d-%s", tbMensajes[9],i+1,argumentos[i]); - infoDebug(msglog); - } - } - /* Elimina fichero de retorno */ - if(salida!=(char*)NULL){ - f = fopen("/tmp/_retinterface_","w" ); - if (f==NULL){ // Error de eliminacin - scriptLog(modulo,10); - resul=8; - scriptLog(modulo,resul); - return(resul); - } - fclose(f); - } - /* Compone linea de comando */ - if(parametros){ - strcat(script," "); - strcat(script,parametros); - } - /* LLamada funcin interface */ - resul=system(script); - if(resul){ - scriptLog(modulo,10); - scriptLog(modulo,resul); - return(resul); - } - /* Lee fichero de retorno */ - if(salida!=(char*)NULL){ - f = fopen("/tmp/_retinterface_","rb" ); - if (f==NULL){ // Error de apertura - scriptLog(modulo,10); - resul=9; - scriptLog(modulo,resul); - return(resul); - } - else{ - fseek (f ,0,SEEK_END); // Obtiene tamao del fichero. - lSize = ftell (f); - rewind (f); - if(lSize>LONGITUD_SCRIPTSALIDA){ - scriptLog(modulo,10); - resul=11; - scriptLog(modulo,resul); - return(resul); - } - fread (salida,1,lSize,f); // Lee contenido del fichero - rTrim(salida); - fclose(f); - } - } - /* Muestra informacin de retorno */ - if(salida!=(char*)NULL){ - if(ndebug>2){ - sprintf(msglog,"Informacin devuelta %s",salida); - infoDebug(msglog); - } - } - return(resul); -} -//______________________________________________________________________________________________________ -// Funcin: interfaceAdmin -// -// Descripcin: -// Esta funcin es la puerta de comunicacin entre el mdulo de administracin y el motor de clonacin. -// La Aplicacin de administracin utiliza una interface para ejecutar funciones del motor de clonacin; -// esta interface llamar a la API del motor con lo que cambiando el comportamiento de esta interface -// podremos hacer llamadas a otras API de clonacin y de esta manera probar distintos motores. -// -// Parmetros: -// - script: Nombre del mdulo,funcin o script de la interface -// - parametros: Parmetros que se le pasarn a la interface -// - salida: Recoge la salida que genera la llamada a la interface - -// Devuelve: -// Cdigo de error de la ejecucin al mdulo , funcin o script de la interface -// -// Especificaciones: -// El parmetro salida recoge la salida desde el procedimiento hijo que se genera en la ejecucin de ste -// siempre que sea distinto de NULL, esto es, si al llamar a la funcin este parmetro es NULL no se -// recoger dicha salida. -//______________________________________________________________________________________________________ - -int interfaceAdmin( char *script,char* parametros,char* salida) -{ - int descr[2]; /* Descriptores de E y S de la turbera */ - int bytesleidos; /* Bytes leidos en el mensaje */ - int estado; - pid_t pid; - char buffer[LONBLK]; // Buffer de lectura de fichero - pipe (descr); - int i,nargs,resul; - int lon; // Longitud de cadena - char msglog[LONSUC]; // Mensaje de registro de sucesos - char *argumentos[MAXARGS]; - char modulo[] = "interfaceAdmin()"; - if (ndebug>= DEBUG_MEDIO) { - sprintf(msglog, "%s:%s", tbMensajes[8], script); - infoDebug(msglog); - } - - /* Crea matriz de los argumentos */ - nargs=splitCadena(argumentos,parametros,32); - for(i=nargs;i= DEBUG_ALTO) { - // Truncar la cadena si es mayor que el tamao de la lnea de log. - sprintf(msglog, "%s: #%d-", tbMensajes[9], i+1); - lon = strlen (msglog); - if (lon + strlen (argumentos[i]) < LONSUC) { - strcat (msglog, argumentos[i]); - } - else - { - strncat (msglog, argumentos[i], LONSUC - lon - 4); - strcat (msglog, "..."); - } - infoDebug(msglog); - } - } - - if((pid=fork())==0) - { - //_______________________________________________________________ - - /* Proceso hijo que ejecuta la funcin de interface */ - - close (descr[LEER]); - dup2 (descr[ESCRIBIR], 1); - close (descr[ESCRIBIR]); - resul=execv(script,argumentos); - //resul=execlp (script, script, argumentos[0],argumentos[1],NULL); - exit(resul); - - /* Fin de proceso hijo */ - //_______________________________________________________________ - } - else - { - //_______________________________________________________________ - - /* Proceso padre que espera la ejecucin del hijo */ - - if (pid ==-1){ // Error en la creacin del proceso hijo - scriptLog(modulo,10); - resul=13; - scriptLog(modulo,resul); - return(resul); - } - close (descr[ESCRIBIR]); - bytesleidos = read (descr[LEER], buffer, LONBLK-1); - while(bytesleidos>0){ - if(salida!=(char*)NULL){ // Si se solicita retorno de informacin... - buffer[bytesleidos]='\0'; - // Error si se supera el tamao mximo de cadena de salida. - if(strlen(buffer)+strlen(salida)>LONGITUD_SCRIPTSALIDA){ - scriptLog(modulo,10); - resul=11; - scriptLog(modulo,resul); - return(resul); - } - rTrim(buffer); - strcat(salida,buffer); - } - bytesleidos = read (descr[LEER], buffer, LONBLK-1); - } - close (descr[LEER]); - //kill(pid,SIGQUIT); - waitpid(pid,&estado,0); - resul=WEXITSTATUS(estado); - if(resul){ - scriptLog(modulo,10); - scriptLog(modulo,resul); - return(resul); - } - /* Fin de proceso padre */ - //_______________________________________________________________ - } - - /* Muestra informacin de retorno */ - if(salida!=(char*)NULL){ - if(ndebug>2){ - // Truncar la cadena si es mayor que el tamao de la lnea de log. - strcpy(msglog,"Informacion devuelta "); - lon = strlen (msglog); - if (lon + strlen (salida) < LONSUC) { - strcat (msglog, salida); - } - else - { - strncat (msglog, salida, LONSUC-lon-4); - strcat (msglog, "..."); - } - infoDebug(msglog); - } - } - return(resul); -} -//______________________________________________________________________________________________________ -// Funcin: scriptLog -// -// Descripcin: -// Registra los sucesos de errores de scripts en el fichero de log -// Parametros: -// - modulo: Mdulo donde se produjo el error -// - coderr : Cdigo del mensaje de error del script -//______________________________________________________________________________________________________ -void scriptLog(const char *modulo,int coderr) -{ - char msglog[LONSUC]; - - if(coderr>>>>>>>>>>>>>>>>>>>>>>>>> - char msglog[LONSTD]; - char modulo[] = "cuestionCache()"; - - sprintf(interface,"%s/%s",pathinterface,"procesaCache"); - sprintf(parametros,"%s %s","procesaCache",tam); - - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s",tbErrores[88]); - errorInfo(modulo,msglog); - return(FALSE); - } - - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: cargaPaginaWeb -// -// Descripcin: -// Muestra una pgina web usando el browser -// Parmetros: -// urp: Direccin url de la pgina -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -// ________________________________________________________________________________________________________ -int cargaPaginaWeb(char *url) -{ - pid_t pidbrowser; // Identificador del proceso que se crea para mostrar una pgina web con el browser - int resul=0; - char* argumentos[4]; - char modulo[] = "cargaPaginaWeb()"; - - // Destruye los procesos del Browser y lanza uno nuevo. - system("pkill -9 browser"); - - sprintf(interface,"/opt/opengnsys/bin/browser"); - sprintf(parametros,"browser -qws %s",url); - - splitCadena(argumentos,parametros,' '); // Crea matriz de los argumentos del scripts - argumentos[3]=NULL; - if((pidbrowser=fork())==0){ - /* Proceso hijo que ejecuta el script */ - resul=execv(interface,argumentos); - exit(resul); - } - else { - if (pidbrowser ==-1){ - scriptLog(modulo,10); - resul=13; - scriptLog(modulo,resul); - return(resul); - } - } - return(resul); -} -//________________________________________________________________________________________________________ -// Funcin: muestraMenu -// -// Descripcin: -// Muestra el menu inicial del cliente -// Parmetros: -// Ninguno -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//________________________________________________________________________________________________________ -void muestraMenu() -{ - cargaPaginaWeb(urlmenu); -} -//______________________________________________________________________________________________________ -// Funcin: muestraMensaje -// -// Descripcin: -// Muestra un mensaje en pantalla -// Parmetros: -// - idx: Indice del mensaje -// - msg: Descripcin Mensaje -// ________________________________________________________________________________________________________ -void muestraMensaje(int idx,char*msg) -{ - char *msgpan,url[250]; - - if(msg){ - msgpan=URLEncode(msg); - sprintf(url,"%s?msg=%s",urlmsg,msgpan); // Url de la pgina de mensajes - liberaMemoria(msgpan); - } - else - sprintf(url,"%s?idx=%d",urlmsg,idx); // Url de la pgina de mensajes - cargaPaginaWeb(url); -} -//______________________________________________________________________________________________________ -// Funcin: InclusionCliente -// Descripcin: -// Abre una sesin en el servidor de administracin y registra al cliente en el sistema -// Parmetros: -// Ninguno -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN inclusionCliente(TRAMA* ptrTrama) -{ - int lon; // Longitud de cadena - char msglog[LONSUC]; // Mensaje de registro de sucesos - char *cfg; // Datos de configuracin - SOCKET socket_c; - char modulo[] = "inclusionCliente()"; - - cfg=LeeConfiguracion(); - - if(!cfg){ // No se puede recuperar la configuracin del cliente - errorLog(modulo,36,FALSE); - errorLog(modulo,37,FALSE); - return(FALSE); - } - if (ndebug>= DEBUG_ALTO) { - // Truncar la cadena si es mayor que el tamao de la lnea de log. - sprintf(msglog, "%s", tbMensajes[14]); - lon = strlen (msglog); - if (lon + strlen (cfg) < LONSUC) { - strcat (msglog, cfg); - } - else - { - strncat (msglog, cfg, LONSUC - lon - 4); - strcat (msglog, "..."); - } - infoDebug(msglog); - } - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=InclusionCliente\r"); // Nombre de la funcin a ejecutar en el servidor - lon+=sprintf(ptrTrama->parametros+lon,"cfg=%s\r",cfg); // Configuracin de los Sistemas Operativos del cliente - liberaMemoria(cfg); - - if(!enviaMensajeServidor(&socket_c,ptrTrama,MSG_PETICION)){ - errorLog(modulo,37,FALSE); - return(FALSE); - } - ptrTrama=recibeMensaje(&socket_c); - if(!ptrTrama){ - errorLog(modulo,45,FALSE); - return(FALSE); - } - - close(socket_c); - - if(!gestionaTrama(ptrTrama)){ // Anlisis de la trama - errorLog(modulo,39,FALSE); - return(FALSE); - } - - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: RESPUESTA_InclusionCliente -// -// Descripcin: -// Respuesta del servidor de administracin a la peticin de inicio -// enviando los datos identificativos del cliente y otras configuraciones -// Parmetros: -// - ptrTrama: Trama recibida por el servidor con el contenido y los parmetros -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN RESPUESTA_InclusionCliente(TRAMA* ptrTrama) -{ - char* res; - char modulo[] = "RESPUESTA_InclusionCliente()"; - - res=copiaParametro("res",ptrTrama); // Resultado del proceso de inclusin - if(atoi(res)==0){ // Error en el proceso de inclusin - liberaMemoria(res); - errorLog(modulo,41,FALSE); - return (FALSE); - } - liberaMemoria(res); - - idordenador=copiaParametro("ido",ptrTrama); // Identificador del ordenador - nombreordenador=copiaParametro("npc",ptrTrama); // Nombre del ordenador - cache=copiaParametro("che",ptrTrama); // Tamao de la cach reservada al cliente - idproautoexec=copiaParametro("exe",ptrTrama); // Procedimento de inicio (Autoexec) - idcentro=copiaParametro("idc",ptrTrama); // Identificador de la Unidad Organizativa - idaula=copiaParametro("ida",ptrTrama); // Identificador del aula - - if(idordenador==NULL || nombreordenador==NULL){ - errorLog(modulo,40,FALSE); - return (FALSE); - } - return(TRUE); -} -//______________________________________________________________________________________________________ -// -// Funcin: LeeConfiguracion -// Descripcin: -// Abre una sesin en el servidor de administracin y registra al cliente en el sistema -// Parmetros: -// Ninguno -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ - -char* LeeConfiguracion() -{ - char* parametroscfg; - char modulo[] = "LeeConfiguracion()"; - int herrorcfg; - - // Reservar memoria para los datos de cofiguracin. - parametroscfg=(char*)reservaMemoria(LONGITUD_SCRIPTSALIDA); - if(!parametroscfg){ - errorLog(modulo,3,FALSE); - return(NULL); - } - // Ejecutar script y obtener datos. - sprintf(interface,"%s/%s",pathinterface,"getConfiguration"); - herrorcfg=interfaceAdmin(interface,NULL,parametroscfg); - - if(herrorcfg){ // No se puede recuperar la configuracin del cliente - liberaMemoria(parametroscfg); - errorLog(modulo,36,FALSE); - return(NULL); - } - return(parametroscfg); -} -//________________________________________________________________________________________________________ -// Funcin: autoexecCliente -// -// Descripcin: -// Solicita procedimiento de autoexec para el cliebnte -// Parmetros: -// Ninguno -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//________________________________________________________________________________________________________ -BOOLEAN autoexecCliente(TRAMA* ptrTrama) -{ - SOCKET socket_c; - char modulo[] = "autoexecCliente()"; - - initParametros(ptrTrama,0); - sprintf(ptrTrama->parametros,"nfn=AutoexecCliente\rexe=%s\r",idproautoexec); - - if(!enviaMensajeServidor(&socket_c,ptrTrama,MSG_PETICION)){ - errorLog(modulo,42,FALSE); - return(FALSE); - } - ptrTrama=recibeMensaje(&socket_c); - if(!ptrTrama){ - errorLog(modulo,45,FALSE); - return(FALSE); - } - - close(socket_c); - - if(!gestionaTrama(ptrTrama)){ // Anlisis de la trama - errorLog(modulo,39,FALSE); - return(FALSE); - } - - return(TRUE); -} -//________________________________________________________________________________________________________ -// Funcin: autoexecCliente -// -// Descripcin: -// Ejecuta un script de autoexec personalizado en todos los inicios para el cliente -// Parmetros: -// Ninguno -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//________________________________________________________________________________________________________ -BOOLEAN RESPUESTA_AutoexecCliente(TRAMA* ptrTrama) -{ - SOCKET socket_c; - char *res,*nfl; - char modulo[] = "RESPUESTA_AutoexecCliente()"; - - res=copiaParametro("res",ptrTrama); - if(atoi(res)==0){ // Error en el proceso de autoexec - liberaMemoria(res); - return (FALSE); - } - liberaMemoria(res); - - nfl=copiaParametro("nfl",ptrTrama); - initParametros(ptrTrama,0); - sprintf(ptrTrama->parametros,"nfn=enviaArchivo\rnfl=%s\r",nfl); - liberaMemoria(nfl); - - /* Enva peticin */ - if(!enviaMensajeServidor(&socket_c,ptrTrama,MSG_PETICION)){ - errorLog(modulo,42,FALSE); - return(FALSE); - } - /* Nombre del archivo destino (local)*/ - char fileautoexec[LONPRM]; - sprintf(fileautoexec,"/tmp/_autoexec_%s",IPlocal); - - /* Recibe archivo */ - if(!recArchivo(&socket_c,fileautoexec)){ - errorLog(modulo,58, FALSE); - close(socket_c); - return(FALSE); - } - - close(socket_c); - - /* Ejecuta archivo */ - ejecutaArchivo(fileautoexec,ptrTrama); - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: comandosPendientes -// -// Descripcin: -// Bsqueda de acciones pendientes en el servidor de administracin -// Parmetros: -// Ninguno -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN comandosPendientes(TRAMA* ptrTrama) -{ - SOCKET socket_c; - char modulo[] = "comandosPendientes()"; - - CMDPTES=TRUE; - initParametros(ptrTrama,0); - - while(CMDPTES){ - sprintf(ptrTrama->parametros,"nfn=ComandosPendientes\r"); - if(!enviaMensajeServidor(&socket_c,ptrTrama,MSG_PETICION)){ - errorLog(modulo,42,FALSE); - return(FALSE); - } - ptrTrama=recibeMensaje(&socket_c); - if(!ptrTrama){ - errorLog(modulo,45,FALSE); - return(FALSE); - } - - close(socket_c); - - if(!gestionaTrama(ptrTrama)){ // Anlisis de la trama - errorLog(modulo,39,FALSE); - return(FALSE); - } - } - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: NoComandosPtes -// -// Descripcin: -// Conmuta el switch de los comandos pendientes y lo pone a false -// Parmetros: -// - ptrTrama: contenido del mensaje -// Devuelve: -// TRUE siempre -// Especificaciones: -// Cuando se ejecuta esta funcin se sale del bucle que recupera los comandos pendientes en el -// servidor y el cliente pasa a a estar disponible para recibir comandos desde el ste. -//______________________________________________________________________________________________________ -BOOLEAN NoComandosPtes(TRAMA* ptrTrama) -{ - CMDPTES=FALSE; // Corta el bucle de comandos pendientes - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: ProcesaComandos -// -// Descripcin: -// Espera comando desde el Servidor de Administracin para ejecutarlos -// Parmetros: -// Ninguno -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -// ________________________________________________________________________________________________________ -void procesaComandos(TRAMA* ptrTrama) -{ - int lon; - SOCKET socket_c; - char modulo[] = "procesaComandos()"; - - initParametros(ptrTrama,0); - while(TRUE){ - lon=sprintf(ptrTrama->parametros,"nfn=DisponibilidadComandos\r"); - lon+=sprintf(ptrTrama->parametros+lon,"tpc=%s\r",CLIENTE_OPENGNSYS); // Activar disponibilidad - if(!enviaMensajeServidor(&socket_c,ptrTrama,MSG_INFORMACION)){ - errorLog(modulo,43,FALSE); - return; - } - infoLog(19); // Disponibilidad de cliente activada - ptrTrama=recibeMensaje(&socket_c); - if(!ptrTrama){ - errorLog(modulo,46,FALSE); - return; - } - - close(socket_c); - - if(!gestionaTrama(ptrTrama)){ // Anlisis de la trama - errorLog(modulo,39,FALSE); - return; - } - if(!comandosPendientes(ptrTrama)){ - errorLog(modulo,42,FALSE); - } - } -} -//______________________________________________________________________________________________________ -// Funcin: Actualizar -// -// Descripcin: -// Actualiza los datos de un ordenador como si volviera a solicitar la entrada -// en el sistema al servidor de administracin -// Parmetros: -// ptrTrama: contenido del mensajede -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN Actualizar(TRAMA* ptrTrama) -{ - char msglog[LONSTD]; // Mensaje de log - char *cfg; // Cadena de datos de configuracin - int lon; // Longitud de cadena - char modulo[] = "Actualizar()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - muestraMensaje(1,NULL); - if(!comandosPendientes(ptrTrama)){ - errorLog(modulo,84,FALSE); - return(FALSE); - } - - cfg=LeeConfiguracion(); - herror=0; - if(!cfg){ // No se puede recuperar la configuracin del cliente - errorLog(modulo,36,FALSE); - herror=3; - } - // Envia Configuracion al servidor - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_Configurar"); - lon+=sprintf(ptrTrama->parametros+lon,"cfg=%s\r",cfg); // Configuracin de los Sistemas Operativos del cliente - respuestaEjecucionComando(ptrTrama,herror,0); - - muestraMenu(); - - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: Purgar -// -// Descripcin: -// Detiene la ejecucin del browser -// Parmetros: -// ptrTrama: contenido del mensajede -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -int Purgar(TRAMA* ptrTrama) -{ - - exit(EXIT_SUCCESS); -} -//______________________________________________________________________________________________________ -// Funcin: Sondeo -// -// Descripcin: -// Enva al servidor una confirmacin de que est dentro del sistema -// Parmetros: -// ptrTrama: contenido del mensajede -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN Sondeo(TRAMA* ptrTrama) -{ - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: ConsolaRemota -// -// Descripcin: -// Ejecuta un comando de la Shell y envia el eco al servidor (Consola remota) -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN ConsolaRemota(TRAMA* ptrTrama) -{ - SOCKET socket_c; - char *nfn,*scp,*aux,ecosrc[LONPRM],ecodst[LONPRM],msglog[LONSTD];; - char modulo[] = "ConsolaRemota()"; - - /* Nombre del archivo de script */ - char filescript[LONPRM]; - sprintf(filescript,"/tmp/_script_%s",IPlocal); - - aux=copiaParametro("scp",ptrTrama); - scp=URLDecode(aux); - escribeArchivo(filescript,scp); - liberaMemoria(aux); - liberaMemoria(scp); - - nfn=copiaParametro("nfn",ptrTrama); - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(ecosrc,"/tmp/_econsola_%s",IPlocal); - sprintf(parametros,"%s %s %s",nfn,filescript,ecosrc); - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - } - else{ - /* Enva fichero de inventario al servidor */ - sprintf(ecodst,"/tmp/_Seconsola_%s",IPlocal); // Nombre que tendra el archivo en el Servidor - initParametros(ptrTrama,0); - sprintf(ptrTrama->parametros,"nfn=recibeArchivo\rnfl=%s\r",ecodst); - if(!enviaMensajeServidor(&socket_c,ptrTrama,MSG_COMANDO)){ - errorLog(modulo,42,FALSE); - return(FALSE); - } - /* Espera seal para comenzar el envo */ - liberaMemoria(ptrTrama); - recibeFlag(&socket_c,ptrTrama); - /* Enva archivo */ - if(!sendArchivo(&socket_c,ecosrc)){ - errorLog(modulo,57, FALSE); - herror=12; // Error de envo de fichero por la red - } - close(socket_c); - } - liberaMemoria(nfn); - return(TRUE); -} -//_____________________________________________________________________________________________________ -// Funcin: Comando -// -// Descripcin: -// COmando personalizado enviado desde el servidor -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//_____________________________________________________________________________________________________ -BOOLEAN Comando(TRAMA* ptrTrama) -{ - char *ids,*nfn,msglog[LONSTD]; - char modulo[] = "Comando()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - - sprintf(interface,"%s/%s",pathinterface,nfn); - herror=interfaceAdmin(interface,NULL,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - } - /* Envia respuesta de ejecucucin del comando */ - initParametros(ptrTrama,0); - sprintf(ptrTrama->parametros,"nfn=RESPUESTA_%s\r",nfn); - respuestaEjecucionComando(ptrTrama,herror,ids); - liberaMemoria(nfn); - liberaMemoria(ids); - return(TRUE); -} -//_____________________________________________________________________________________________________ -// Funcin: Arrancar -// -// Descripcin: -// Responde a un comando de encendido por la red -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//_____________________________________________________________________________________________________ -BOOLEAN Arrancar(TRAMA* ptrTrama) -{ - int lon; - char *ids,msglog[LONSTD]; - char modulo[] = "Arrancar()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - - ids=copiaParametro("ids",ptrTrama); - - /* Envia respuesta de ejecucucin del script */ - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_Arrancar"); - lon+=sprintf(ptrTrama->parametros+lon,"tpc=%s\r",CLIENTE_OPENGNSYS); - respuestaEjecucionComando(ptrTrama,0,ids); - liberaMemoria(ids); - return(TRUE); -} -//_____________________________________________________________________________________________________ -// Funcin: Apagar -// -// Descripcin: -// Apaga el cliente -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//_____________________________________________________________________________________________________ -BOOLEAN Apagar(TRAMA* ptrTrama) -{ - char *ids,*nfn,msglog[LONSTD]; - char modulo[] = "Apagar()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - - initParametros(ptrTrama,0); - sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_Apagar"); - respuestaEjecucionComando(ptrTrama,0,ids); - - sprintf(interface,"%s/%s",pathinterface,nfn); - herror=interfaceAdmin(interface,NULL,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - liberaMemoria(nfn); - liberaMemoria(ids); - errorInfo(modulo,msglog); - return(FALSE); - } - liberaMemoria(nfn); - liberaMemoria(ids); - return(TRUE); -} -//_____________________________________________________________________________________________________ -// Funcin: Reiniciar -// -// Descripcin: -// Apaga el cliente -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//_____________________________________________________________________________________________________ -BOOLEAN Reiniciar(TRAMA* ptrTrama) -{ - char *nfn,*ids,msglog[LONSTD]; - char modulo[] = "Reiniciar()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - - initParametros(ptrTrama,0); - sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_Reiniciar"); - respuestaEjecucionComando(ptrTrama,0,ids); - - sprintf(interface,"%s/%s",pathinterface,nfn); - herror=interfaceAdmin(interface,NULL,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - liberaMemoria(nfn); - liberaMemoria(ids); - errorInfo(modulo,msglog); - return(FALSE); - } - liberaMemoria(nfn); - liberaMemoria(ids); - return(TRUE); -} -//_____________________________________________________________________________________________________ -// Funcin: IniciarSesion -// -// Descripcin: -// Inicia sesin en el Sistema Operativo de una de las particiones -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//_____________________________________________________________________________________________________ -BOOLEAN IniciarSesion(TRAMA* ptrTrama) -{ - char *nfn,*ids,*disk,*par,msglog[LONSTD]; - char modulo[] = "IniciarSesion()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - disk=copiaParametro("dsk",ptrTrama); - par=copiaParametro("par",ptrTrama); - - initParametros(ptrTrama,0); - sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_IniciarSesion"); - respuestaEjecucionComando(ptrTrama,0,ids); - liberaMemoria(ids); - - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(parametros,"%s %s %s",nfn,disk,par); - liberaMemoria(par); - - herror=interfaceAdmin(interface,parametros,NULL); - - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - liberaMemoria(nfn); - errorInfo(modulo,msglog); - return(FALSE); - } - liberaMemoria(nfn); - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: CrearImagen -// -// Descripcin: -// Crea una imagen de una particin -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN CrearImagen(TRAMA* ptrTrama) -{ - int lon; - char *nfn,*dsk,*par,*cpt,*idi,*ipr,*nci,*ids,msglog[LONSTD]; - char modulo[] = "CrearImagen()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - - dsk=copiaParametro("dsk",ptrTrama); // Disco - par=copiaParametro("par",ptrTrama); // Nmero de particin - cpt=copiaParametro("cpt",ptrTrama); // Cdigo de la particin - idi=copiaParametro("idi",ptrTrama); // Identificador de la imagen - nci=copiaParametro("nci",ptrTrama); // Nombre cannico de la imagen - ipr=copiaParametro("ipr",ptrTrama); // Ip del repositorio - - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - muestraMensaje(7,NULL); - - if(InventariandoSoftware(ptrTrama,FALSE,"InventarioSoftware")){ // Crea inventario Software previamente - muestraMensaje(2,NULL); - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(parametros,"%s %s %s %s %s",nfn,dsk,par,nci,ipr); - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - muestraMensaje(10,NULL); - } - else - muestraMensaje(9,NULL); - } - else{ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - } - - /* Envia respuesta de ejecucin de la funcin de interface */ - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_CrearImagen"); - lon+=sprintf(ptrTrama->parametros+lon,"idi=%s\r",idi); // Identificador de la imagen - lon+=sprintf(ptrTrama->parametros+lon,"dsk=%s\r",dsk); // Nmero de disco - lon+=sprintf(ptrTrama->parametros+lon,"par=%s\r",par); // Nmero de particin de donde se cre - lon+=sprintf(ptrTrama->parametros+lon,"cpt=%s\r",cpt); // Tipo o cdigo de particin - lon+=sprintf(ptrTrama->parametros+lon,"ipr=%s\r",ipr); // Ip del repositorio donde se aloj - respuestaEjecucionComando(ptrTrama,herror,ids); - - liberaMemoria(dsk); - liberaMemoria(par); - liberaMemoria(cpt); - liberaMemoria(idi); - liberaMemoria(nci); - liberaMemoria(ipr); - liberaMemoria(nfn); - liberaMemoria(ids); - - muestraMenu(); - - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: CrearImagenBasica -// -// Descripcin: -// Crea una imagen bsica a travers dela sincronizacin -// Parmetros: -// ptrTrama: contenido del mensaje -// -// FDevuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN CrearImagenBasica(TRAMA* ptrTrama) -{ - int lon; - char *nfn,*dsk,*par,*cpt,*idi,*nci,*rti,*ipr,*msy,*whl,*eli,*cmp,*bpi,*cpc,*bpc,*nba,*ids,msglog[LONSTD]; - char modulo[] = "CrearImagenBasica()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - nfn=copiaParametro("nfn",ptrTrama); - dsk=copiaParametro("dsk",ptrTrama); // Disco - par=copiaParametro("par",ptrTrama); // Nmero de particin - cpt=copiaParametro("cpt",ptrTrama); // Tipo de particin - idi=copiaParametro("idi",ptrTrama); // Identificador de la imagen - nci=copiaParametro("nci",ptrTrama); // Nombre cannico de la imagen - rti=copiaParametro("rti",ptrTrama); // Ruta de origen de la imagen - ipr=copiaParametro("ipr",ptrTrama); // Ip del repositorio - - msy=copiaParametro("msy",ptrTrama); // Mtodo de sincronizacin - - whl=copiaParametro("whl",ptrTrama); // Envo del fichero completo si hay diferencias - eli=copiaParametro("eli",ptrTrama); // Elimiar archivos en destino que no estn en origen - cmp=copiaParametro("cmp",ptrTrama); // Comprimir antes de enviar - - bpi=copiaParametro("bpi",ptrTrama); // Borrar la imagen antes de crearla - cpc=copiaParametro("cpc",ptrTrama); // Copiar tambin imagen a la cache - bpc=copiaParametro("bpc",ptrTrama); // Borrarla de la cache antes de copiarla en ella - nba=copiaParametro("nba",ptrTrama); // No borrar archivos en destino - - muestraMensaje(7,NULL); // Creando Inventario Software - if(InventariandoSoftware(ptrTrama,FALSE,"InventarioSoftware")){ // Crea inventario Software previamente - muestraMensaje(30,NULL);// Creando Imagen Bsica, por favor espere... - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(parametros,"%s %s %s %s %s %s%s%s %s%s%s%s %s %s",nfn,dsk,par,nci,ipr,whl,eli,cmp,bpi,cpc,bpc,nba,msy,rti); - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - muestraMensaje(29,NULL);// Ha habido algn error en el proceso de creacin de imagen bsica - } - else - muestraMensaje(28,NULL);// El proceso de creacin de imagen bsica ha terminado correctamente - } - else{ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - } - - ids=copiaParametro("ids",ptrTrama); // Identificador de la sesin - - /* Envia respuesta de ejecucin de la funcin de interface */ - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_CrearImagenBasica"); - lon+=sprintf(ptrTrama->parametros+lon,"idi=%s\r",idi); // Identificador de la imagen - lon+=sprintf(ptrTrama->parametros+lon,"dsk=%s\r",dsk); // Nmero de disco - lon+=sprintf(ptrTrama->parametros+lon,"par=%s\r",par); // Nmero de particin de donde se cre - lon+=sprintf(ptrTrama->parametros+lon,"cpt=%s\r",cpt); // Tipo o cdigo de particin - lon+=sprintf(ptrTrama->parametros+lon,"ipr=%s\r",ipr); // Ip del repositorio donde se aloj - respuestaEjecucionComando(ptrTrama,herror,ids); - - liberaMemoria(nfn); - liberaMemoria(dsk); - liberaMemoria(par); - liberaMemoria(cpt); - liberaMemoria(idi); - liberaMemoria(nci); - liberaMemoria(rti); - liberaMemoria(ipr); - - liberaMemoria(msy); - - liberaMemoria(whl); - liberaMemoria(eli); - liberaMemoria(cmp); - - liberaMemoria(bpi); - liberaMemoria(cpc); - liberaMemoria(bpc); - liberaMemoria(nba); - liberaMemoria(ids); - - muestraMenu(); - - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: CrearSoftIncremental -// -// Descripcin: -// Crea una software incremental comparando una particin con una imagen bsica -// Parmetros: -// ptrTrama: contenido del mensaje -// -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN CrearSoftIncremental(TRAMA* ptrTrama) -{ - int lon; - char *nfn,*dsk,*par,*idi,*idf,*ipr,*nci,*rti,*ncf,*msy,*whl,*eli,*cmp,*bpi,*cpc,*bpc,*nba,*ids,msglog[LONSTD]; - char modulo[] = "CrearSoftIncremental()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - nfn=copiaParametro("nfn",ptrTrama); - - dsk=copiaParametro("dsk",ptrTrama); // Disco - par=copiaParametro("par",ptrTrama); // Nmero de particin - idi=copiaParametro("idi",ptrTrama); // Identificador de la imagen - nci=copiaParametro("nci",ptrTrama); // Nombre cannico de la imagen - rti=copiaParametro("rti",ptrTrama); // Ruta de origen de la imagen - ipr=copiaParametro("ipr",ptrTrama); // Ip del repositorio - idf=copiaParametro("idf",ptrTrama); // Identificador de la imagen diferencial - ncf=copiaParametro("ncf",ptrTrama); // Nombre cannico de la imagen diferencial - - msy=copiaParametro("msy",ptrTrama); // Mtodo de sincronizacin - - whl=copiaParametro("whl",ptrTrama); // Envo del fichero completo si hay diferencias - eli=copiaParametro("eli",ptrTrama); // Elimiar archivos en destino que no estn en origen - cmp=copiaParametro("cmp",ptrTrama); // Comprimir antes de enviar - - bpi=copiaParametro("bpi",ptrTrama); // Borrar la imagen antes de crearla - cpc=copiaParametro("cpc",ptrTrama); // Copiar tambin imagen a la cache - bpc=copiaParametro("bpc",ptrTrama); // Borrarla de la cache antes de copiarla en ella - nba=copiaParametro("nba",ptrTrama); // No borrar archivos en destino - - muestraMensaje(7,NULL); // Creando Inventario Software - if(InventariandoSoftware(ptrTrama,FALSE,"InventarioSoftware")){ // Crea inventario Software previamente - muestraMensaje(25,NULL);// Creando Imagen Incremental, por favor espere... - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(parametros,"%s %s %s %s %s %s %s%s%s %s%s%s%s %s %s",nfn,dsk,par,nci,ipr,ncf,whl,eli,cmp,bpi,cpc,bpc,nba,msy,rti); - - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - muestraMensaje(27,NULL);// Ha habido algn error en el proceso de creacin de imagen bsica - } - else - muestraMensaje(26,NULL);// El proceso de creacin de imagen incremental ha terminado correctamente - } - else{ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - } - - ids=copiaParametro("ids",ptrTrama); // Identificador de la sesin - - /* Envia respuesta de ejecucin de la funcin de interface */ - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_CrearSoftIncremental"); - lon+=sprintf(ptrTrama->parametros+lon,"idf=%s\r",idf); // Identificador de la imagen incremental - lon+=sprintf(ptrTrama->parametros+lon,"dsk=%s\r",dsk); // Nmero de disco - lon+=sprintf(ptrTrama->parametros+lon,"par=%s\r",par); // Nmero de particin - respuestaEjecucionComando(ptrTrama,herror,ids); - - liberaMemoria(nfn); - liberaMemoria(dsk); - liberaMemoria(par); - liberaMemoria(idi); - liberaMemoria(nci); - liberaMemoria(rti); - liberaMemoria(ipr); - liberaMemoria(idf); - liberaMemoria(ncf); - liberaMemoria(msy); - liberaMemoria(whl); - liberaMemoria(eli); - liberaMemoria(cmp); - liberaMemoria(bpi); - liberaMemoria(cpc); - liberaMemoria(bpc); - liberaMemoria(nba); - liberaMemoria(ids); - - muestraMenu(); - - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: RestaurarImagen -// -// Descripcin: -// Restaura una imagen en una particin -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En bpccaso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN RestaurarImagen(TRAMA* ptrTrama) -{ - int lon; - char *nfn,*dsk,*par,*idi,*ipr,*ifs,*cfg,*nci,*ids,*ptc,msglog[LONSTD]; - char modulo[] = "RestaurarImagen()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - - dsk=copiaParametro("dsk",ptrTrama); - par=copiaParametro("par",ptrTrama); - idi=copiaParametro("idi",ptrTrama); - ipr=copiaParametro("ipr",ptrTrama); - nci=copiaParametro("nci",ptrTrama); - ifs=copiaParametro("ifs",ptrTrama); - ptc=copiaParametro("ptc",ptrTrama); - - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - muestraMensaje(3,NULL); - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(parametros,"%s %s %s %s %s %s",nfn,dsk,par,nci,ipr,ptc); - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - muestraMensaje(12,NULL); - } - else - muestraMensaje(11,NULL); - - /* Obtener nueva configuracin */ - cfg=LeeConfiguracion(); - if(!cfg){ // No se puede recuperar la configuracin del cliente - errorLog(modulo,36,FALSE); - } - - /* Envia respuesta de ejecucin de la funcin de interface */ - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_RestaurarImagen"); - lon+=sprintf(ptrTrama->parametros+lon,"idi=%s\r",idi); // Identificador de la imagen - lon+=sprintf(ptrTrama->parametros+lon,"dsk=%s\r",dsk); // Nmero de disco - lon+=sprintf(ptrTrama->parametros+lon,"par=%s\r",par); // Nmero de particin - lon+=sprintf(ptrTrama->parametros+lon,"ifs=%s\r",ifs); // Identificador del perfil software - lon+=sprintf(ptrTrama->parametros+lon,"cfg=%s\r",cfg); // Configuracin de discos - respuestaEjecucionComando(ptrTrama,herror,ids); - - liberaMemoria(nfn); - liberaMemoria(dsk); - liberaMemoria(par); - liberaMemoria(idi); - liberaMemoria(nci); - liberaMemoria(ipr); - liberaMemoria(ifs); - liberaMemoria(cfg); - liberaMemoria(ptc); - liberaMemoria(ids); - - muestraMenu(); - - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: RestaurarImagenBasica -// -// Descripcin: -// Restaura una imagen bsica en una particin -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN RestaurarImagenBasica(TRAMA* ptrTrama) -{ - int lon; - char *nfn,*dsk,*par,*idi,*ipr,*met,*nci,*rti,*ifs,*cfg,*msy,*whl,*eli,*cmp,*tpt,*bpi,*cpc,*bpc,*nba,*ids,msglog[LONSTD]; - char modulo[] = "RestaurarImagenBasica()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - dsk=copiaParametro("dsk",ptrTrama); - par=copiaParametro("par",ptrTrama); - idi=copiaParametro("idi",ptrTrama); - ipr=copiaParametro("ipr",ptrTrama); - met=copiaParametro("met",ptrTrama); // Mtodo de clonacin 0= desde cach 1= desde repositorio - nci=copiaParametro("nci",ptrTrama); - rti=copiaParametro("rti",ptrTrama); // Ruta de origen de la imagen - ifs=copiaParametro("ifs",ptrTrama); - - tpt=copiaParametro("tpt",ptrTrama); // Tipo de trasnmisin unicast o multicast - msy=copiaParametro("msy",ptrTrama); // Metodo de sincronizacion - - whl=copiaParametro("whl",ptrTrama); // Envo del fichero completo si hay diferencias - eli=copiaParametro("eli",ptrTrama); // Elimiar archivos en destino que no estn en origen - cmp=copiaParametro("cmp",ptrTrama); // Comprimir antes de enviar - - bpi=copiaParametro("bpi",ptrTrama); // Borrar la imagen antes de crearla - cpc=copiaParametro("cpc",ptrTrama); // Copiar tambin imagen a la cache - bpc=copiaParametro("bpc",ptrTrama); // Borrarla de la cache antes de copiarla en ella - nba=copiaParametro("nba",ptrTrama); // No borrar archivos en destino - - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - muestraMensaje(31,NULL); - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(parametros,"%s %s %s %s %s %s %s%s%s %s%s%s%s %s %s %s",nfn,dsk,par,nci,ipr,tpt,whl,eli,cmp,bpi,cpc,bpc,nba,met,msy,rti); - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - muestraMensaje(33,NULL); - } - else - muestraMensaje(32,NULL); - - /* Obtener nueva configuracin */ - cfg=LeeConfiguracion(); - if(!cfg){ // No se puede recuperar la configuracin del cliente - errorLog(modulo,36,FALSE); - } - - /* Envia respuesta de ejecucin de la funcin de interface */ - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_RestaurarImagenBasica"); - lon+=sprintf(ptrTrama->parametros+lon,"idi=%s\r",idi); // Identificador de la imagen - lon+=sprintf(ptrTrama->parametros+lon,"dsk=%s\r",dsk); // Nmero de disco - lon+=sprintf(ptrTrama->parametros+lon,"par=%s\r",par); // Nmero de particin - lon+=sprintf(ptrTrama->parametros+lon,"ifs=%s\r",ifs); // Identificador del perfil software - lon+=sprintf(ptrTrama->parametros+lon,"cfg=%s\r",cfg); // Configuracin de discos - respuestaEjecucionComando(ptrTrama,herror,ids); - - liberaMemoria(nfn); - liberaMemoria(dsk); - liberaMemoria(par); - liberaMemoria(idi); - liberaMemoria(nci); - liberaMemoria(rti); - liberaMemoria(ifs); - liberaMemoria(cfg); - liberaMemoria(ipr); - liberaMemoria(met); - - liberaMemoria(tpt); - liberaMemoria(msy); - - liberaMemoria(whl); - liberaMemoria(eli); - liberaMemoria(cmp); - - liberaMemoria(bpi); - liberaMemoria(cpc); - liberaMemoria(bpc); - liberaMemoria(nba); - liberaMemoria(ids); - - muestraMenu(); - - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: RestaurarSoftIncremental -// -// Descripcin: -// Restaura software incremental en una particin -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN RestaurarSoftIncremental(TRAMA* ptrTrama) -{ - int lon; - char *nfn,*dsk,*par,*idi,*ipr,*met,*ifs,*nci,*rti,*idf,*ncf,*msy,*whl,*eli,*cmp,*tpt,*bpi,*cpc,*bpc,*nba,*ids,msglog[LONSTD]; - char modulo[] = "RestaurarSoftIncremental()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - dsk=copiaParametro("dsk",ptrTrama); - par=copiaParametro("par",ptrTrama); - idi=copiaParametro("idi",ptrTrama); - idf=copiaParametro("idf",ptrTrama); - ipr=copiaParametro("ipr",ptrTrama); - met=copiaParametro("met",ptrTrama); // Mtodo de clonacin 0= desde cach 1= desde repositorio - ifs=copiaParametro("ifs",ptrTrama); - nci=copiaParametro("nci",ptrTrama); - rti=copiaParametro("rti",ptrTrama); // Ruta de origen de la imagen - ncf=copiaParametro("ncf",ptrTrama); - - tpt=copiaParametro("tpt",ptrTrama); // Tipo de trasnmisin unicast o multicast - msy=copiaParametro("msy",ptrTrama); // Metodo de sincronizacion - - whl=copiaParametro("whl",ptrTrama); // Envo del fichero completo si hay diferencias - eli=copiaParametro("eli",ptrTrama); // Elimiar archivos en destino que no estn en origen - cmp=copiaParametro("cmp",ptrTrama); // Comprimir antes de enviar - - bpi=copiaParametro("bpi",ptrTrama); // Borrar la imagen antes de crearla - cpc=copiaParametro("cpc",ptrTrama); // Copiar tambin imagen a la cache - bpc=copiaParametro("bpc",ptrTrama); // Borrarla de la cache antes de copiarla en ella - nba=copiaParametro("nba",ptrTrama); // No borrar archivos en destino - - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - muestraMensaje(31,NULL); - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(parametros,"%s %s %s %s %s %s %s %s%s%s %s%s%s%s %s %s %s",nfn,dsk,par,nci,ipr,ncf,tpt,whl,eli,cmp,bpi,cpc,bpc,nba,met,msy,rti); - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - muestraMensaje(35,NULL); - } - else - muestraMensaje(34,NULL); - - /* Envia respuesta de ejecucin de la funcin de interface */ - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_RestaurarSoftIncremental"); - lon+=sprintf(ptrTrama->parametros+lon,"idi=%s\r",idf); // Identificador de la imagen incremental (Forzada a idi) - lon+=sprintf(ptrTrama->parametros+lon,"dsk=%s\r",dsk); // Nmero de disco - lon+=sprintf(ptrTrama->parametros+lon,"par=%s\r",par); // Nmero de particin - lon+=sprintf(ptrTrama->parametros+lon,"ifs=%s\r",ifs); // Identificador del perfil software - - respuestaEjecucionComando(ptrTrama,herror,ids); - - liberaMemoria(nfn); - liberaMemoria(dsk); - liberaMemoria(par); - liberaMemoria(idi); - liberaMemoria(idf); - liberaMemoria(nci); - liberaMemoria(rti); - liberaMemoria(ncf); - liberaMemoria(ifs); - liberaMemoria(ipr); - liberaMemoria(met); - - liberaMemoria(tpt); - liberaMemoria(msy); - - liberaMemoria(whl); - liberaMemoria(eli); - liberaMemoria(cmp); - - liberaMemoria(bpi); - liberaMemoria(cpc); - liberaMemoria(bpc); - liberaMemoria(nba); - liberaMemoria(ids); - - muestraMenu(); - - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: Configurar -// -// Descripcin: -// Configura la tabla de particiones y formatea -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN Configurar(TRAMA* ptrTrama) -{ - int lon; - char *nfn,*dsk,*cfg,*ids,msglog[LONSTD]; - char modulo[] = "Configurar()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - - dsk=copiaParametro("dsk",ptrTrama); - cfg=copiaParametro("cfg",ptrTrama); - /* Sustituir caracteres */ - sustituir(cfg,'\n','$'); - sustituir(cfg,'\t','#'); - - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - muestraMensaje(4,NULL); - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(parametros,"%s %s %s",nfn,dsk,cfg); - - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - muestraMensaje(13,NULL); - } - else - muestraMensaje(14,NULL); - - cfg=LeeConfiguracion(); - if(!cfg){ // No se puede recuperar la configuracin del cliente - errorLog(modulo,36,FALSE); - return(FALSE); - } - - /* Envia respuesta de ejecucin del comando*/ - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_Configurar"); - lon+=sprintf(ptrTrama->parametros+lon,"cfg=%s\r",cfg); // Configuracin de los Sistemas Operativos del cliente - respuestaEjecucionComando(ptrTrama,herror,ids); - - liberaMemoria(dsk); - liberaMemoria(cfg); - liberaMemoria(nfn); - liberaMemoria(ids); - - muestraMenu(); - - return(TRUE); -} -// ________________________________________________________________________________________________________ -// Funcin: InventarioHardware -// -// Descripcin: -// Envia al servidor el nombre del archivo de inventario de su hardware para posteriormente -// esperar que ste lo solicite y enviarlo por la red. -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN InventarioHardware(TRAMA* ptrTrama) -{ - int lon; - SOCKET socket_c; - char *nfn,*ids,msglog[LONSTD],hrdsrc[LONPRM],hrddst[LONPRM]; - char modulo[] = "InventarioHardware()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - muestraMensaje(6,NULL); - - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(hrdsrc,"/tmp/Chrd-%s",IPlocal); // Nombre que tendra el archivo de inventario - sprintf(parametros,"%s %s",nfn,hrdsrc); - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - muestraMensaje(18,NULL); - } - else{ - /* Enva fichero de inventario al servidor */ - sprintf(hrddst,"/tmp/Shrd-%s",IPlocal); // Nombre que tendra el archivo en el Servidor - initParametros(ptrTrama,0); - sprintf(ptrTrama->parametros,"nfn=recibeArchivo\rnfl=%s\r",hrddst); - if(!enviaMensajeServidor(&socket_c,ptrTrama,MSG_COMANDO)){ - liberaMemoria(nfn); - liberaMemoria(ids); - errorLog(modulo,42,FALSE); - return(FALSE); - } - /* Espera seal para comenzar el envo */ - liberaMemoria(ptrTrama); - recibeFlag(&socket_c,ptrTrama); - /* Enva archivo */ - if(!sendArchivo(&socket_c,hrdsrc)){ - errorLog(modulo,57, FALSE); - herror=12; // Error de envo de fichero por la red - } - close(socket_c); - muestraMensaje(17,NULL); - } - - /* Envia respuesta de ejecucin de la funcin de interface */ - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_InventarioHardware"); - lon+=sprintf(ptrTrama->parametros+lon,"hrd=%s\r",hrddst); - respuestaEjecucionComando(ptrTrama,herror,ids); - liberaMemoria(nfn); - liberaMemoria(ids); - - muestraMenu(); - - return(TRUE); -} -// ________________________________________________________________________________________________________ -// Funcin: InventarioSoftware -// -// Descripcin: -// Crea el inventario software de un sistema operativo instalado en una particin. -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN InventarioSoftware(TRAMA* ptrTrama) -{ - char *nfn,*ids,msglog[LONSTD]; - char modulo[] = "InventarioSoftware()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - nfn=copiaParametro("nfn",ptrTrama); - ids=copiaParametro("ids",ptrTrama); - muestraMensaje(7,NULL); - InventariandoSoftware(ptrTrama,TRUE,nfn); - respuestaEjecucionComando(ptrTrama,herror,ids); - liberaMemoria(nfn); - liberaMemoria(ids); - muestraMenu(); - return(TRUE); -} -// ________________________________________________________________________________________________________ -// -// Funcin: InventariandoSoftware -// -// Descripcin: -// Envia al servidor el nombre del archivo de inventario de su software para posteriormente -// esperar que ste lo solicite y enviarlo por la red. -// Parmetros: -// ptrTrama: contenido del mensaje -// sw: switch que indica si la funcin es llamada por el comando InventarioSoftware(true) o CrearImagen(false) -// nfn: Nombre de la funcin del Interface que implementa el comando -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN InventariandoSoftware(TRAMA* ptrTrama,BOOLEAN sw,char *nfn) -{ - int lon; - SOCKET socket_c; - char *dsk,*par,msglog[LONSTD],sftsrc[LONPRM],sftdst[LONPRM]; - char modulo[] = "InventariandoSoftware()"; - - dsk=copiaParametro("dsk",ptrTrama); // Disco - par=copiaParametro("par",ptrTrama); - - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(sftsrc,"/tmp/CSft-%s-%s",IPlocal,par); // Nombre que tendra el archivo de inventario - sprintf(parametros,"%s %s %s %s",nfn,dsk,par,sftsrc); - - herror=interfaceAdmin(interface,parametros,NULL); - herror=0; - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - muestraMensaje(20,NULL); - } - else{ - /* Enva fichero de inventario al servidor */ - sprintf(sftdst,"/tmp/Ssft-%s-%s",IPlocal,par); // Nombre que tendra el archivo en el Servidor - initParametros(ptrTrama,0); - - sprintf(ptrTrama->parametros,"nfn=recibeArchivo\rnfl=%s\r",sftdst); - if(!enviaMensajeServidor(&socket_c,ptrTrama,MSG_COMANDO)){ - errorLog(modulo,42,FALSE); - liberaMemoria(dsk); - liberaMemoria(par); - return(FALSE); - } - /* Espera seal para comenzar el envo */ - liberaMemoria(ptrTrama); - if(!recibeFlag(&socket_c,ptrTrama)){ - errorLog(modulo,17,FALSE); - } - /* Enva archivo */ - if(!sendArchivo(&socket_c,sftsrc)){ - errorLog(modulo,57, FALSE); - herror=12; // Error de envo de fichero por la red - } - close(socket_c); - muestraMensaje(19,NULL); - } - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_InventarioSoftware"); - lon+=sprintf(ptrTrama->parametros+lon,"par=%s\r",par); - lon+=sprintf(ptrTrama->parametros+lon,"sft=%s\r",sftdst); - if(!sw) - respuestaEjecucionComando(ptrTrama,herror,"0"); - - liberaMemoria(dsk); - liberaMemoria(par); - return(TRUE); -} -// ________________________________________________________________________________________________________ -// Funcin: EjecutarScript -// -// Descripcin: -// Ejecuta cdigo de script -// Parmetros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//______________________________________________________________________________________________________ -BOOLEAN EjecutarScript(TRAMA* ptrTrama) -{ - int lon; - char *nfn,*aux,*ids,*scp,*cfg,msglog[LONSTD]; - char modulo[] = "EjecutarScript()"; - - if (ndebug>=DEBUG_MAXIMO) { - sprintf(msglog, "%s:%s",tbMensajes[21],modulo); - infoDebug(msglog); - } - aux=copiaParametro("scp",ptrTrama); - scp=URLDecode(aux); - - - muestraMensaje(8,NULL); - /* Nombre del archivo de script */ - char filescript[LONPRM]; - sprintf(filescript,"/tmp/_script_%s",IPlocal); - escribeArchivo(filescript,scp); - nfn=copiaParametro("nfn",ptrTrama); - sprintf(interface,"%s/%s",pathinterface,nfn); - sprintf(parametros,"%s %s",nfn,filescript); - herror=interfaceAdmin(interface,parametros,NULL); - if(herror){ - sprintf(msglog,"%s:%s",tbErrores[86],nfn); - errorInfo(modulo,msglog); - muestraMensaje(21,NULL); - } - else - muestraMensaje(22,NULL); - - // Toma configuracin de particiones - cfg=LeeConfiguracion(); - if(!cfg){ // No se puede recuperar la configuracin del cliente - errorLog(modulo,36,FALSE); - herror=36; - } - - ids=copiaParametro("ids",ptrTrama); - - //herror=ejecutarCodigoBash(scp); - initParametros(ptrTrama,0); - lon=sprintf(ptrTrama->parametros,"nfn=%s\r","RESPUESTA_EjecutarScript"); - lon+=sprintf(ptrTrama->parametros+lon,"cfg=%s\r",cfg); // Configuracin de los Sistemas Operativos del cliente - respuestaEjecucionComando(ptrTrama,herror,ids); - - liberaMemoria(nfn); - liberaMemoria(ids); - liberaMemoria(aux); - liberaMemoria(scp); - liberaMemoria(cfg); - - muestraMenu(); - - return(TRUE); -} -//______________________________________________________________________________________________________ -// Funcin: respuestaEjecucionComando -// -// Descripcin: -// Envia una respuesta a una ejecucion de comando al servidor de Administracin -// Parmetros: -// - ptrTrama: contenido del mensaje -// - res: Resultado de la ejecucin (Cdigo de error devuelto por el script ejecutado) -// - ids: Identificador de la sesion (En caso de no haber seguimiento es NULO) -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -// ________________________________________________________________________________________________________ -BOOLEAN respuestaEjecucionComando(TRAMA* ptrTrama,int res,char *ids) -{ - int lon; - SOCKET socket_c; - char modulo[] = "respuestaEjecucionComando()"; - - lon=strlen(ptrTrama->parametros); - if(ids){ // Existe seguimiento - lon+=sprintf(ptrTrama->parametros+lon,"ids=%s\r",ids); // Aade identificador de la sesin - } - if (res==0){ // Resultado satisfactorio - lon+=sprintf(ptrTrama->parametros+lon,"res=%s\r","1"); - lon+=sprintf(ptrTrama->parametros+lon,"der=%s\r",""); - } - else{ // Algn error - lon+=sprintf(ptrTrama->parametros+lon,"res=%s\r","2"); - if(res>MAXERRORSCRIPT) - lon+=sprintf(ptrTrama->parametros+lon,"der=%s (Error de script:%d)\r",tbErroresScripts[0],res);// Descripcin del error - else - lon+=sprintf(ptrTrama->parametros+lon,"der=%s\r",tbErroresScripts[res]);// Descripcin del error - } - if(!(enviaMensajeServidor(&socket_c,ptrTrama,MSG_NOTIFICACION))){ - errorLog(modulo,44,FALSE); - return(FALSE); - } - - close(socket_c); - return(TRUE); -} -// ________________________________________________________________________________________________________ -// Funcin: gestionaTrama -// -// Descripcin: -// Procesa las tramas recibidas. -// Parametros: -// ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -// ________________________________________________________________________________________________________ -BOOLEAN gestionaTrama(TRAMA *ptrTrama) -{ - int i, res; - char *nfn; - char modulo[] = "gestionaTrama()"; - - INTROaFINCAD(ptrTrama); - nfn = copiaParametro("nfn", ptrTrama); // Toma nombre de funcin - for (i = 0; i < MAXIMAS_FUNCIONES; i++) { // Recorre funciones que procesan las tramas - res = strcmp(tbfuncionesClient[i].nf, nfn); - if (res == 0) { // Encontrada la funcin que procesa el mensaje - liberaMemoria(nfn); - return(tbfuncionesClient[i].fptr(ptrTrama)); // Invoca la funcin - } - } - - liberaMemoria(nfn); - - /* Slo puede ser un comando personalizado - if (ptrTrama->tipo==MSG_COMANDO) - return(Comando(ptrTrama)); - */ - errorLog(modulo, 18, FALSE); - return (FALSE); -} -//________________________________________________________________________________________________________ -// Funcin: ejecutaArchivo -// -// Descripcin: -// Ejecuta los comando contenido en un archivo (cada comando y sus parametros separados por un -// salto de linea. -// Parmetros: -// filecmd: Nombre del archivo de comandos -// ptrTrama: Puntero a una estructura TRAMA usada en las comunicaciones por red (No debe ser NULL) -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -//________________________________________________________________________________________________________ -BOOLEAN ejecutaArchivo(char* filecmd,TRAMA *ptrTrama) -{ - char* buffer,*lineas[MAXIMAS_LINEAS]; - int i,numlin; - char modulo[] = "ejecutaArchivo()"; - - buffer=leeArchivo(filecmd); - if(buffer){ - numlin = splitCadena(lineas, buffer, '@'); - initParametros(ptrTrama,0); - for (i = 0; i < numlin; i++) { - if(strlen(lineas[i])>0){ - strcpy(ptrTrama->parametros,lineas[i]); - //strcat(ptrTrama->parametros,"\rMCDJ@"); // Fin de trama - if(!gestionaTrama(ptrTrama)){ // Anlisis de la trama - errorLog(modulo,39,FALSE); - //return(FALSE); - } - } - } - } - liberaMemoria(buffer); - return(TRUE); -} - -BOOLEAN EjecutaComandosPendientes(TRAMA* ptrTrama) -{ - return(TRUE); -} - -//______________________________________________________________________________________________________ -// Funcin: enviaMensajeServidor -// -// Descripcin: -// Envia un mensaje al servidor de Administracin -// Parmetros: -// - socket_c: (Salida) Socket utilizado para el envo -// - ptrTrama: contenido del mensaje -// - tipo: Tipo de mensaje -// C=Comando, N=Respuesta a un comando, P=Peticion,R=Respuesta a una peticin, I=Informacion -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algn error -// ________________________________________________________________________________________________________ -BOOLEAN enviaMensajeServidor(SOCKET *socket_c,TRAMA *ptrTrama,char tipo) -{ - int lon; - char modulo[] = "enviaMensajeServidor()"; - - *socket_c=abreConexion(); - if(*socket_c==INVALID_SOCKET){ - errorLog(modulo,38,FALSE); // Error de conexin con el servidor - return(FALSE); - } - ptrTrama->arroba='@'; // Cabecera de la trama - strncpy(ptrTrama->identificador,"JMMLCAMDJ_MCDJ",14); // identificador de la trama - ptrTrama->tipo=tipo; // Tipo de mensaje - lon=strlen(ptrTrama->parametros); // Compone la trama - lon+=sprintf(ptrTrama->parametros+lon,"iph=%s\r",IPlocal); // Ip del ordenador - lon+=sprintf(ptrTrama->parametros+lon,"ido=%s\r",idordenador); // Identificador del ordenador - lon+=sprintf(ptrTrama->parametros+lon,"npc=%s\r",nombreordenador); // Nombre del ordenador - lon+=sprintf(ptrTrama->parametros+lon,"idc=%s\r",idcentro); // Identificador del centro - lon+=sprintf(ptrTrama->parametros+lon,"ida=%s\r",idaula); // Identificador del aula - - if (!mandaTrama(socket_c,ptrTrama)) { - errorLog(modulo,26,FALSE); - return (FALSE); - } - return(TRUE); -} -// ******************************************************************************************************** -// PROGRAMA PRINCIPAL (CLIENTE) -// ******************************************************************************************************** -int main(int argc, char *argv[]) -{ - TRAMA *ptrTrama; - char modulo[] = "main()"; - - ptrTrama=(TRAMA *)reservaMemoria(sizeof(TRAMA)); - if (ptrTrama == NULL) { // No hay memoria suficiente para el bufer de las tramas - errorLog(modulo, 3, FALSE); - exit(EXIT_FAILURE); - } - /*-------------------------------------------------------------------------------------------------------- - Validacin de parmetros de ejecucin y fichero de configuracin - ---------------------------------------------------------------------------------------------------------*/ - if (!validacionParametros(argc, argv,3)) // Valida parmetros de ejecucin - exit(EXIT_FAILURE); - - if (!tomaConfiguracion(szPathFileCfg)) // Toma parametros de configuracin - exit(EXIT_FAILURE); - /*-------------------------------------------------------------------------------------------------------- - Carga catlogo de funciones que procesan las tramas - ---------------------------------------------------------------------------------------------------------*/ - int cf = 0; - - strcpy(tbfuncionesClient[cf].nf, "RESPUESTA_AutoexecCliente"); - tbfuncionesClient[cf++].fptr = &RESPUESTA_AutoexecCliente; - - strcpy(tbfuncionesClient[cf].nf, "RESPUESTA_InclusionCliente"); - tbfuncionesClient[cf++].fptr = &RESPUESTA_InclusionCliente; - - strcpy(tbfuncionesClient[cf].nf, "NoComandosPtes"); - tbfuncionesClient[cf++].fptr = &NoComandosPtes; - - strcpy(tbfuncionesClient[cf].nf, "Actualizar"); - tbfuncionesClient[cf++].fptr = &Actualizar; - - strcpy(tbfuncionesClient[cf].nf, "Purgar"); - tbfuncionesClient[cf++].fptr = &Purgar; - - strcpy(tbfuncionesClient[cf].nf, "ConsolaRemota"); - tbfuncionesClient[cf++].fptr = &ConsolaRemota; - - strcpy(tbfuncionesClient[cf].nf, "Sondeo"); - tbfuncionesClient[cf++].fptr = &Sondeo; - - strcpy(tbfuncionesClient[cf].nf, "Arrancar"); - tbfuncionesClient[cf++].fptr = &Arrancar; - - strcpy(tbfuncionesClient[cf].nf, "Apagar"); - tbfuncionesClient[cf++].fptr = &Apagar; - - strcpy(tbfuncionesClient[cf].nf, "Reiniciar"); - tbfuncionesClient[cf++].fptr = &Reiniciar; - - strcpy(tbfuncionesClient[cf].nf, "IniciarSesion"); - tbfuncionesClient[cf++].fptr = &IniciarSesion; - - strcpy(tbfuncionesClient[cf].nf, "CrearImagen"); - tbfuncionesClient[cf++].fptr = &CrearImagen; - - strcpy(tbfuncionesClient[cf].nf, "CrearImagenBasica"); - tbfuncionesClient[cf++].fptr = &CrearImagenBasica; - - strcpy(tbfuncionesClient[cf].nf, "CrearSoftIncremental"); - tbfuncionesClient[cf++].fptr = &CrearSoftIncremental; - - strcpy(tbfuncionesClient[cf].nf, "RestaurarImagen"); - tbfuncionesClient[cf++].fptr = &RestaurarImagen; - - strcpy(tbfuncionesClient[cf].nf, "RestaurarImagenBasica"); - tbfuncionesClient[cf++].fptr = &RestaurarImagenBasica; - - strcpy(tbfuncionesClient[cf].nf, "RestaurarSoftIncremental"); - tbfuncionesClient[cf++].fptr = &RestaurarSoftIncremental; - - - strcpy(tbfuncionesClient[cf].nf, "Configurar"); - tbfuncionesClient[cf++].fptr = &Configurar; - - strcpy(tbfuncionesClient[cf].nf, "EjecutarScript"); - tbfuncionesClient[cf++].fptr = &EjecutarScript; - - strcpy(tbfuncionesClient[cf].nf, "InventarioHardware"); - tbfuncionesClient[cf++].fptr = &InventarioHardware; - - strcpy(tbfuncionesClient[cf].nf, "InventarioSoftware"); - tbfuncionesClient[cf++].fptr = &InventarioSoftware; - - strcpy(tbfuncionesClient[cf].nf, "EjecutaComandosPendientes"); - tbfuncionesClient[cf++].fptr = &EjecutaComandosPendientes; - - /*-------------------------------------------------------------------------------------------------------- - Toma direccin IP del cliente - ---------------------------------------------------------------------------------------------------------*/ - if(!tomaIPlocal()){ // Error al recuperar la IP local - errorLog(modulo,0,FALSE); - exit(EXIT_FAILURE); - } - /*-------------------------------------------------------------------------------------------------------- - Inicio de sesin - ---------------------------------------------------------------------------------------------------------*/ - infoLog(1); // Inicio de sesin - infoLog(3); // Abriendo sesin en el servidor de Administracin; - /*-------------------------------------------------------------------------------------------------------- - Inclusin del cliente en el sistema - ---------------------------------------------------------------------------------------------------------*/ - if(!inclusionCliente(ptrTrama)){ // Ha habido algn problema al abrir sesin - errorLog(modulo,0,FALSE); - exit(EXIT_FAILURE); - } - infoLog(4); // Cliente iniciado - - /*-------------------------------------------------------------------------------------------------------- - Procesamiento de la cache - ---------------------------------------------------------------------------------------------------------*/ - infoLog(23); // Abriendo sesin en el servidor de Administracin; - if(!cuestionCache(cache)){ - errorLog(modulo,0,FALSE); - exit(EXIT_FAILURE); - } - /*-------------------------------------------------------------------------------------------------------- - Ejecucin del autoexec - ---------------------------------------------------------------------------------------------------------*/ - if(atoi(idproautoexec)>0){ // Ejecucin de procedimiento Autoexec - infoLog(5); - if(!autoexecCliente(ptrTrama)){ // Ejecucin fichero autoexec - errorLog(modulo,0,FALSE); - exit(EXIT_FAILURE); - } - } - /*-------------------------------------------------------------------------------------------------------- - Comandos pendientes - ---------------------------------------------------------------------------------------------------------*/ - infoLog(6); // Procesa comandos pendientes - if(!comandosPendientes(ptrTrama)){ // Ejecucin de acciones pendientes - errorLog(modulo,0,FALSE); - exit(EXIT_FAILURE); - } - infoLog(7); // Acciones pendientes procesadas - /*-------------------------------------------------------------------------------------------------------- - Bucle de recepcin de comandos - ---------------------------------------------------------------------------------------------------------*/ - muestraMenu(); - procesaComandos(ptrTrama); // Bucle para procesar comandos interactivos - /*-------------------------------------------------------------------------------------------------------- - Fin de la sesin - ---------------------------------------------------------------------------------------------------------*/ - exit(EXIT_SUCCESS); -} diff --git a/native/Sources/Clients/ogAdmClient/sources/ogAdmClient.h b/native/Sources/Clients/ogAdmClient/sources/ogAdmClient.h deleted file mode 100644 index da0a5c3..0000000 --- a/native/Sources/Clients/ogAdmClient/sources/ogAdmClient.h +++ /dev/null @@ -1,183 +0,0 @@ -// ******************************************************************************************************** -// Cliernte: ogAdmClient -// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla -// Fecha Creación: Marzo-2010 -// Fecha Última modificación: Marzo-2010 -// Nombre del fichero: ogAdmClient.h -// Descripción :Este fichero implementa el cliente general del sistema -// ******************************************************************************************************** -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ogAdmLib.h" -// ________________________________________________________________________________________________________ -// Variables globales -// ________________________________________________________________________________________________________ -char *idordenador; // Identificador del ordenador -char *nombreordenador; // Nombre del ordenador -char *cache; // Tamaño de la caché -char *idproautoexec; // Identificador del procedimiento de autoexec -char *idcentro; // Identificador de la Unidad Organizativa -char *idaula; // Identificador del aula -char IPlocal[LONIP]; // Ip local - -char servidoradm[LONPRM]; // Dirección IP del servidor de administración -char puerto[LONPRM]; // Puerto de comunicación -char pathinterface[LONPRM]; // Path donde está la interface entre la administración y el módulo de clonación - -char interface[LONFUN]; // Nombre del módulo,función o script de la interface con el módulo de administración -char parametros[LONSTD]; // Parámetros para la llamada -int herror; - -BOOLEAN CMDPTES; // Para bucle de comandos pendientes - - -char urlmenu[MAXLONURL]; // Url de la pagina de menu para el browser -char urlmsg[MAXLONURL]; // Url de la página de mensajed para el browser - - -typedef struct{ // Estructura usada para referenciar las funciones que procesan las tramas - char nf[LONFUN]; // Nombre de la función - BOOLEAN (*fptr)(TRAMA*); // Puntero a la función que procesa la trama -}MSGFUN; -MSGFUN tbfuncionesClient[MAXIMAS_FUNCIONES]; -// ________________________________________________________________________________________________________ -// Tabla de errores de la ejecución de los scripts -// ________________________________________________________________________________________________________ -char* tbErroresScripts[]={"Se han generado errores desconocidos. No se puede continuar la ejecución de este módulo",\ - "001-Formato de ejecución incorrecto.",\ - "002-Fichero o dispositivo no encontrado",\ - "003-Error en partición de disco",\ - "004-Partición o fichero bloqueado",\ - "005-Error al crear o restaurar una imagen",\ - "006-Sin sistema operativo",\ - "007-Programa o función BOOLEAN no ejecutable",\ - "008-Error en la creación del archivo de eco para consola remota",\ - "009-Error en la lectura del archivo temporal de intercambio",\ - "010-Error al ejecutar la llamada a la interface de administración",\ - "011-La información retornada por la interface de administración excede de la longitud permitida",\ - "012-Error en el envío de fichero por la red",\ - "013-Error en la creación del proceso hijo",\ - "014-Error de escritura en destino",\ - "015-Sin Cache en el Cliente",\ - "016-No hay espacio en la cache para almacenar fichero-imagen",\ - "017-Error al Reducir el Sistema Archivos",\ - "018-Error al Expandir el Sistema Archivos",\ - "019-Valor fuera de rango o no válido.",\ - "020-Sistema de archivos desconocido o no se puede montar",\ - "021-Error en partición de caché local",\ - "022-El disco indicado no contiene una particion GPT",\ - "023-Error no definido",\ - "024-Error no definido",\ - "025-Error no definido",\ - "026-Error no definido",\ - "027-Error no definido",\ - "028-Error no definido",\ - "029-Error no definido",\ - "030-Error al restaurar imagen - Imagen mas grande que particion",\ - "031-Error al realizar el comando updateCache",\ - "032-Error al formatear",\ - "033-Archivo de imagen corrupto o de otra versión de partclone",\ - "034-Error no definido",\ - "035-Error no definido",\ - "036-Error no definido",\ - "037-Error no definido",\ - "038-Error no definido",\ - "039-Error no definido",\ - "040-Error imprevisto no definido",\ - "041-Error no definido",\ - "042-Error no definido",\ - "043-Error no definido",\ - "044-Error no definido",\ - "045-Error no definido",\ - "046-Error no definido",\ - "047-Error no definido",\ - "048-Error no definido",\ - "049-Error no definido",\ - "050-Error en la generación de sintaxis de transferenica unicast",\ - "051-Error en envio UNICAST de una particion",\ - "052-Error en envio UNICAST de un fichero",\ - "053-Error en la recepcion UNICAST de una particion",\ - "054-Error en la recepcion UNICAST de un fichero",\ - "055-Error en la generacion de sintaxis de transferenica Multicast",\ - "056-Error en envio MULTICAST de un fichero",\ - "057-Error en la recepcion MULTICAST de un fichero",\ - "058-Error en envio MULTICAST de una particion",\ - "059-Error en la recepcion MULTICAST de una particion",\ - "060-Error en la conexion de una sesion UNICAST|MULTICAST con el MASTER",\ - "061-Error no definido",\ - "062-Error no definido",\ - "063-Error no definido",\ - "064-Error no definido",\ - "065-Error no definido",\ - "066-Error no definido",\ - "067-Error no definido",\ - "068-Error no definido",\ - "069-Error no definido",\ - "070-Error al montar una imagen sincronizada.",\ - "071-Imagen no sincronizable (es monolitica).",\ - "072-Error al desmontar la imagen.",\ - "073-No se detectan diferencias entre la imagen basica y la particion.",\ - "074-Error al sincronizar, puede afectar la creacion/restauracion de la imagen.",\ - "Error desconocido " - }; - #define MAXERRORSCRIPT 74 // Error máximo cometido -// ________________________________________________________________________________________________________ -// Prototipo de funciones -// ________________________________________________________________________________________________________ -BOOLEAN autoexecCliente(TRAMA*); -BOOLEAN RESPUESTA_AutoexecCliente(TRAMA*); -void procesaComandos(TRAMA*); - -BOOLEAN tomaConfiguracion(char*); -BOOLEAN tomaIPlocal(void); -void scriptLog(const char *,int ); - -BOOLEAN gestionaTrama(TRAMA *); -BOOLEAN inclusionCliente(); -char* LeeConfiguracion(); -BOOLEAN RESPUESTA_InclusionCliente(TRAMA *); - -BOOLEAN comandosPendientes(TRAMA*); -BOOLEAN NoComandosPtes(TRAMA *); - -BOOLEAN respuestaEjecucionComando(TRAMA *,int,char*); -BOOLEAN Sondeo(TRAMA *); -BOOLEAN Actualizar(TRAMA *); -int Purgar(TRAMA* ); - -BOOLEAN ConsolaRemota(TRAMA*); - -BOOLEAN Arrancar(TRAMA *); -BOOLEAN Apagar(TRAMA *); -BOOLEAN Reiniciar(TRAMA *); -BOOLEAN IniciarSesion(TRAMA *); -BOOLEAN CrearImagen(TRAMA *); -BOOLEAN CrearImagenBasica(TRAMA *); -BOOLEAN CrearSoftIncremental(TRAMA*); - -BOOLEAN InventarioHardware(TRAMA *); -BOOLEAN InventariandoSoftware(TRAMA *,BOOLEAN,char*); -BOOLEAN EjecutarScript(TRAMA *); -BOOLEAN ejecutaArchivo(char*,TRAMA*); - -BOOLEAN cuestionCache(char*); -int cargaPaginaWeb(char *); -void muestraMenu(void); -void muestraMensaje(int idx,char*); - -BOOLEAN enviaMensajeServidor(SOCKET *,TRAMA *,char); - - - - diff --git a/native/Sources/Clients/ogAdmClient/sources/ogAdmClient.o b/native/Sources/Clients/ogAdmClient/sources/ogAdmClient.o deleted file mode 100644 index e8bec648fc91775dbf0b298384526615646aa621..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108604 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 DNc6y) diff --git a/native/Sources/Clients/ogagent/ogagent-oglive_3.0.0-20190520_amd64.buildinfo b/native/Sources/Clients/ogagent/ogagent-oglive_3.0.0-20190520_amd64.buildinfo deleted file mode 100644 index d2b2392..0000000 --- a/native/Sources/Clients/ogagent/ogagent-oglive_3.0.0-20190520_amd64.buildinfo +++ /dev/null @@ -1,182 +0,0 @@ -Format: 1.0 -Source: ogagent-oglive -Binary: ogagent-oglive -Architecture: all -Version: 3.0.0-20190520 -Checksums-Md5: - 7252ce182f6ea1d1c6d569046770cbad 33624 ogagent-oglive_3.0.0-20190520_all.deb -Checksums-Sha1: - 5c1a1984bc7598e33a4271d11954352a77adda50 33624 ogagent-oglive_3.0.0-20190520_all.deb -Checksums-Sha256: - 94624c2751b6178a9476ee57a5f59c98f516cf5795f849383418a4182644dd0d 33624 ogagent-oglive_3.0.0-20190520_all.deb -Build-Origin: Ubuntu -Build-Architecture: amd64 -Build-Date: Thu, 14 Dec 2023 08:28:43 +0100 -Build-Tainted-By: - merged-usr-via-aliased-dirs - usr-local-has-configs - usr-local-has-libraries - usr-local-has-programs -Installed-Build-Depends: - autoconf (= 2.71-2), - automake (= 1:1.16.5-1.3), - autopoint (= 0.21-4ubuntu4), - autotools-dev (= 20220109.1), - base-files (= 12ubuntu4.4), - base-passwd (= 3.5.52build1), - bash (= 5.1-6ubuntu1), - binutils (= 2.38-4ubuntu2.4), - binutils-common (= 2.38-4ubuntu2.4), - binutils-x86-64-linux-gnu (= 2.38-4ubuntu2.4), - bsdextrautils (= 2.37.2-4ubuntu3), - bsdutils (= 1:2.37.2-4ubuntu3), - build-essential (= 12.9ubuntu3), - bzip2 (= 1.0.8-5build1), - coreutils (= 8.32-4.1ubuntu1), - cpp (= 4:11.2.0-1ubuntu1), - cpp-11 (= 11.4.0-1ubuntu1~22.04), - dash (= 0.5.11+git20210903+057cd650a4ed-3build1), - debconf (= 1.5.79ubuntu1), - debhelper (= 13.6ubuntu1), - debianutils (= 5.5-1ubuntu2), - debugedit (= 1:5.0-4build1), - dh-autoreconf (= 20), - dh-strip-nondeterminism (= 1.13.0-1), - diffutils (= 1:3.8-0ubuntu2), - dpkg (= 1.21.1ubuntu2.2), - dpkg-dev (= 1.21.1ubuntu2.2), - dwz (= 0.14-1build2), - file (= 1:5.41-3ubuntu0.1), - findutils (= 4.8.0-1ubuntu3), - g++ (= 4:11.2.0-1ubuntu1), - g++-11 (= 11.4.0-1ubuntu1~22.04), - gawk (= 1:5.1.0-1ubuntu0.1), - gcc (= 4:11.2.0-1ubuntu1), - gcc-11 (= 11.4.0-1ubuntu1~22.04), - gcc-11-base (= 11.4.0-1ubuntu1~22.04), - gcc-12-base (= 12.3.0-1ubuntu1~22.04), - gettext (= 0.21-4ubuntu4), - gettext-base (= 0.21-4ubuntu4), - grep (= 3.7-1build1), - groff-base (= 1.22.4-8build1), - gzip (= 1.10-4ubuntu4.1), - hostname (= 3.23ubuntu2), - init-system-helpers (= 1.62), - install-info (= 6.8-4build1), - intltool-debian (= 0.35.0+20060710.5), - libacl1 (= 2.3.1-1), - libarchive-zip-perl (= 1.68-1), - libasan6 (= 11.4.0-1ubuntu1~22.04), - libatomic1 (= 12.3.0-1ubuntu1~22.04), - libattr1 (= 1:2.5.1-1build1), - libaudit-common (= 1:3.0.7-1build1), - libaudit1 (= 1:3.0.7-1build1), - libbinutils (= 2.38-4ubuntu2.4), - libblkid1 (= 2.37.2-4ubuntu3), - libbz2-1.0 (= 1.0.8-5build1), - libc-bin (= 2.35-0ubuntu3.5), - libc-dev-bin (= 2.35-0ubuntu3.5), - libc6 (= 2.35-0ubuntu3.5), - libc6-dev (= 2.35-0ubuntu3.5), - libcap-ng0 (= 0.7.9-2.2build3), - libcap2 (= 1:2.44-1ubuntu0.22.04.1), - libcc1-0 (= 12.3.0-1ubuntu1~22.04), - libcom-err2 (= 1.46.5-2ubuntu1.1), - libcrypt-dev (= 1:4.4.27-1), - libcrypt1 (= 1:4.4.27-1), - libctf-nobfd0 (= 2.38-4ubuntu2.4), - libctf0 (= 2.38-4ubuntu2.4), - libdb5.3 (= 5.3.28+dfsg1-0.8ubuntu3), - libdebconfclient0 (= 0.261ubuntu1), - libdebhelper-perl (= 13.6ubuntu1), - libdpkg-perl (= 1.21.1ubuntu2.2), - libdw1 (= 0.186-1build1), - libelf1 (= 0.186-1build1), - libfile-stripnondeterminism-perl (= 1.13.0-1), - libgcc-11-dev (= 11.4.0-1ubuntu1~22.04), - libgcc-s1 (= 12.3.0-1ubuntu1~22.04), - libgcrypt20 (= 1.9.4-3ubuntu3), - libgdbm-compat4 (= 1.23-1), - libgdbm6 (= 1.23-1), - libgmp10 (= 2:6.2.1+dfsg-3ubuntu1), - libgomp1 (= 12.3.0-1ubuntu1~22.04), - libgpg-error0 (= 1.43-3), - libgssapi-krb5-2 (= 1.19.2-2ubuntu0.3), - libicu70 (= 70.1-2), - libisl23 (= 0.24-2build1), - libitm1 (= 12.3.0-1ubuntu1~22.04), - libk5crypto3 (= 1.19.2-2ubuntu0.3), - libkeyutils1 (= 1.6.1-2ubuntu3), - libkrb5-3 (= 1.19.2-2ubuntu0.3), - libkrb5support0 (= 1.19.2-2ubuntu0.3), - liblsan0 (= 12.3.0-1ubuntu1~22.04), - liblz4-1 (= 1.9.3-2build2), - liblzma5 (= 5.2.5-2ubuntu1), - libmagic-mgc (= 1:5.41-3ubuntu0.1), - libmagic1 (= 1:5.41-3ubuntu0.1), - libmount1 (= 2.37.2-4ubuntu3), - libmpc3 (= 1.2.1-2build1), - libmpfr6 (= 4.1.0-3build3), - libnsl-dev (= 1.3.0-2build2), - libnsl2 (= 1.3.0-2build2), - libpam-modules (= 1.4.0-11ubuntu2.3), - libpam-modules-bin (= 1.4.0-11ubuntu2.3), - libpam-runtime (= 1.4.0-11ubuntu2.3), - libpam0g (= 1.4.0-11ubuntu2.3), - libpcre2-8-0 (= 10.40-1+ubuntu22.04.1+deb.sury.org+1), - libpcre3 (= 2:8.45-1+ubuntu22.04.1+deb.sury.org+1), - libperl5.34 (= 5.34.0-3ubuntu1.3), - libpipeline1 (= 1.5.5-1), - libquadmath0 (= 12.3.0-1ubuntu1~22.04), - libreadline8 (= 8.1.2-1), - libseccomp2 (= 2.5.3-2ubuntu2), - libselinux1 (= 3.3-1build2), - libsigsegv2 (= 2.13-1ubuntu3), - libsmartcols1 (= 2.37.2-4ubuntu3), - libssl3 (= 3.0.2-0ubuntu1.12), - libstdc++-11-dev (= 11.4.0-1ubuntu1~22.04), - libstdc++6 (= 12.3.0-1ubuntu1~22.04), - libsub-override-perl (= 0.09-2), - libsystemd0 (= 249.11-0ubuntu3.11), - libtinfo6 (= 6.3-2ubuntu0.1), - libtirpc-common (= 1.3.2-2ubuntu0.1), - libtirpc-dev (= 1.3.2-2ubuntu0.1), - libtirpc3 (= 1.3.2-2ubuntu0.1), - libtool (= 2.4.6-15build2), - libtsan0 (= 11.4.0-1ubuntu1~22.04), - libubsan1 (= 12.3.0-1ubuntu1~22.04), - libuchardet0 (= 0.0.7-1build2), - libudev1 (= 249.11-0ubuntu3.11), - libunistring2 (= 1.0-1), - libuuid1 (= 2.37.2-4ubuntu3), - libxml2 (= 2.9.14+dfsg-0.1+ubuntu22.04.1+deb.sury.org+1), - libzstd1 (= 1.4.8+dfsg-3build1), - linux-libc-dev (= 5.15.0-91.101), - login (= 1:4.8.1-2ubuntu2.1), - lsb-base (= 11.1.0ubuntu4), - lto-disabled-list (= 24), - m4 (= 1.4.18-5ubuntu2), - make (= 4.3-4.1build1), - man-db (= 2.10.2-1), - mawk (= 1.3.4.20200120-3), - ncurses-base (= 6.3-2ubuntu0.1), - ncurses-bin (= 6.3-2ubuntu0.1), - patch (= 2.7.6-7build2), - perl (= 5.34.0-3ubuntu1.3), - perl-base (= 5.34.0-3ubuntu1.3), - perl-modules-5.34 (= 5.34.0-3ubuntu1.3), - po-debconf (= 1.0.21+nmu1), - readline-common (= 8.1.2-1), - rpcsvc-proto (= 1.4.2-0ubuntu6), - sed (= 4.8-1ubuntu2), - sensible-utils (= 0.0.17), - sysvinit-utils (= 3.01-1ubuntu1), - tar (= 1.34+dfsg-1ubuntu0.1.22.04.2), - util-linux (= 2.37.2-4ubuntu3), - xz-utils (= 5.2.5-2ubuntu1), - zlib1g (= 1:1.2.11.dfsg-2ubuntu9.2) -Environment: - DEB_BUILD_OPTIONS="parallel=8" - DEB_BUILD_PROFILES="noudeb" - LANG="C" - SOURCE_DATE_EPOCH="1529319600" diff --git a/native/Sources/Clients/ogagent/ogagent-oglive_3.0.0-20190520_amd64.changes b/native/Sources/Clients/ogagent/ogagent-oglive_3.0.0-20190520_amd64.changes deleted file mode 100644 index 9ab26d9..0000000 --- a/native/Sources/Clients/ogagent/ogagent-oglive_3.0.0-20190520_amd64.changes +++ /dev/null @@ -1,26 +0,0 @@ -Format: 1.8 -Date: Mon, 18 Jun 2018 13:00:00 +0200 -Source: ogagent-oglive -Binary: ogagent-oglive -Built-For-Profiles: noudeb -Architecture: all -Version: 3.0.0-20190520 -Distribution: unstable -Urgency: medium -Maintainer: Ramón M. Gómez -Changed-By: Ramón M. Gómez -Description: - ogagent-oglive - OpenGnsys Agent for ogLive client -Changes: - ogagent-oglive (3.0.0-20190520) unstable; urgency=medium - . - * OGAgent for ogLive compatible with OpenGnsys 3 web API -Checksums-Sha1: - 5c1a1984bc7598e33a4271d11954352a77adda50 33624 ogagent-oglive_3.0.0-20190520_all.deb - ec6e0da3760059612e014f25297cb73e887697cd 5954 ogagent-oglive_3.0.0-20190520_amd64.buildinfo -Checksums-Sha256: - 94624c2751b6178a9476ee57a5f59c98f516cf5795f849383418a4182644dd0d 33624 ogagent-oglive_3.0.0-20190520_all.deb - 7f2a8b5e25860c53c72b1a438010be022b3bf1c0a05f3562b570f9e940bfd450 5954 ogagent-oglive_3.0.0-20190520_amd64.buildinfo -Files: - 7252ce182f6ea1d1c6d569046770cbad 33624 admin optional ogagent-oglive_3.0.0-20190520_all.deb - 9f2806400070d5df4bb4c54a010dde70 5954 admin optional ogagent-oglive_3.0.0-20190520_amd64.buildinfo diff --git a/native/Sources/Clients/ogagent/oglive/build-stamp b/native/Sources/Clients/ogagent/oglive/build-stamp deleted file mode 100644 index e69de29..0000000 diff --git a/native/Sources/Clients/ogagent/oglive/configure-stamp b/native/Sources/Clients/ogagent/oglive/configure-stamp deleted file mode 100644 index e69de29..0000000 diff --git a/native/Sources/Clients/ogagent/oglive/debian/.debhelper/generated/ogagent-oglive/installed-by-dh_installdocs b/native/Sources/Clients/ogagent/oglive/debian/.debhelper/generated/ogagent-oglive/installed-by-dh_installdocs deleted file mode 100644 index e4fdbda..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/.debhelper/generated/ogagent-oglive/installed-by-dh_installdocs +++ /dev/null @@ -1 +0,0 @@ -./readme.txt diff --git a/native/Sources/Clients/ogagent/oglive/debian/files b/native/Sources/Clients/ogagent/oglive/debian/files deleted file mode 100644 index a00cd9d..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/files +++ /dev/null @@ -1,2 +0,0 @@ -ogagent-oglive_3.0.0-20190520_all.deb admin optional -ogagent-oglive_3.0.0-20190520_amd64.buildinfo admin optional diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.debhelper.log b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.debhelper.log deleted file mode 100644 index 908d06e..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive.debhelper.log +++ /dev/null @@ -1,14 +0,0 @@ -dh_prep -dh_installdirs -dh_installchangelogs -dh_installdocs -dh_installdebconf -dh_installinit -dh_compress -dh_link -dh_fixperms -dh_installdeb -dh_shlibdeps -dh_gencontrol -dh_md5sums -dh_builddeb diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/control b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/control deleted file mode 100644 index 719a066..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/control +++ /dev/null @@ -1,11 +0,0 @@ -Package: ogagent-oglive -Version: 3.0.0-20190520 -Architecture: all -Maintainer: Ramón M. Gómez -Installed-Size: 235 -Depends: python-requests (>= 0.8.2), python-six (>= 1.1), python-prctl (>= 1.1.1), python (>= 2.7), libxss1 -Section: admin -Priority: optional -Homepage: https://opengnsys.es -Description: OpenGnsys Agent for ogLive client - This package provides the required components to allow this machine to work on an environment managed by OpenGnsys. diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/md5sums b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/md5sums deleted file mode 100644 index 319e0b8..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/md5sums +++ /dev/null @@ -1,44 +0,0 @@ -a645b097e936b35a0f9d667320b97c7a usr/bin/ogagent -f1af20ebcb0f9ca24050f5440b280971 usr/share/OGAgent/cfg/ogagent.cfg -1a151168042f9f91b0ac9de8d81b1cc9 usr/share/OGAgent/cfg/ogclient.cfg -e9b55cf5a2893cb205ece2914ad2cec6 usr/share/OGAgent/opengnsys/RESTApi.py -b2145636e0e9f3733a07dc3c864ffba6 usr/share/OGAgent/opengnsys/__init__.py -a53a553fabe11b358872eaa6f15e51c7 usr/share/OGAgent/opengnsys/certs.py -b6b6d13dffe43f62019489701bd3dceb usr/share/OGAgent/opengnsys/config.py -5ddd04ea082b0030b56dbac442817ba2 usr/share/OGAgent/opengnsys/httpserver.py -a7ceb4791ea9a1e5074f37c27aaef65b usr/share/OGAgent/opengnsys/ipc.py -08005cd7b24449275157d9c560f729c0 usr/share/OGAgent/opengnsys/linux/OGAgentService.py -3ef55d64ebda86651c3d882c5c649389 usr/share/OGAgent/opengnsys/linux/__init__.py -e5c7da55543002f86d591d243383563a usr/share/OGAgent/opengnsys/linux/daemon.py -86e7674bddc118fc1ceb9cd1de7d5912 usr/share/OGAgent/opengnsys/linux/log.py -18c89c3f7a9d457f99c1e1bb9e3297dd usr/share/OGAgent/opengnsys/linux/operations.py -f287da160c9d182e8a11d86bce27fd69 usr/share/OGAgent/opengnsys/linux/renamer/__init__.py -dee0a162a35e6bf8b18e39c4b8c4922c usr/share/OGAgent/opengnsys/linux/renamer/debian.py -39cc52a365eac6caec10dbad16d3dceb usr/share/OGAgent/opengnsys/linux/renamer/opensuse.py -b97470f7d68208d837659fcd6b910c9f usr/share/OGAgent/opengnsys/linux/renamer/redhat.py -f2255a73b7801f9596a9929358d75629 usr/share/OGAgent/opengnsys/loader.py -8d648c5a0cbb79e69c3d0eeee8247226 usr/share/OGAgent/opengnsys/log.py -cc21354530aecfd1376d0a9efcedc0f5 usr/share/OGAgent/opengnsys/macos/__init__.py -5ed4af5272fe5f5e4bd1f5ad591edd3a usr/share/OGAgent/opengnsys/macos/operations.py -d41d8cd98f00b204e9800998ecf8427e usr/share/OGAgent/opengnsys/modules/__init__.py -5be7cd44bb941107cbe67345982cce22 usr/share/OGAgent/opengnsys/modules/client/OpenGnSys/__init__.py -d41d8cd98f00b204e9800998ecf8427e usr/share/OGAgent/opengnsys/modules/client/__init__.py -a7903db8bd76249a4e0ee72147542ecd usr/share/OGAgent/opengnsys/modules/server/OpenGnSys/__init__.py -d41d8cd98f00b204e9800998ecf8427e usr/share/OGAgent/opengnsys/modules/server/__init__.py -3ef55d64ebda86651c3d882c5c649389 usr/share/OGAgent/opengnsys/oglive/__init__.py -e5c7da55543002f86d591d243383563a usr/share/OGAgent/opengnsys/oglive/daemon.py -a316e2cd56e19eb453215d441f9589aa usr/share/OGAgent/opengnsys/oglive/operations.py -7b20385f798d849dd546eb1c25149d76 usr/share/OGAgent/opengnsys/operations.py -e5c055043531b106f345f63ffbfb3b44 usr/share/OGAgent/opengnsys/scriptThread.py -296542d5654398781ec99c1ec2c0db7f usr/share/OGAgent/opengnsys/service.py -68b513e35d67d3e783947ec35b60cfa7 usr/share/OGAgent/opengnsys/utils.py -99858b8c43e60816ce3d50888f6bdf21 usr/share/OGAgent/opengnsys/windows/OGAgentService.py -5c4822b3e4cef2d5ec2225ad8adcfeed usr/share/OGAgent/opengnsys/windows/__init__.py -737d49175b1b4045adfd3bd8a66b66b7 usr/share/OGAgent/opengnsys/windows/log.py -f6e9945941b22caf066617abc25b47e2 usr/share/OGAgent/opengnsys/windows/operations.py -4eeead8170a612cedd5ac16f043695f8 usr/share/OGAgent/opengnsys/workers/__init__.py -7e0279e56a6730c74c4b525916a06f57 usr/share/OGAgent/opengnsys/workers/client_worker.py -6e6ff6fc0e398882d8696ae00cf48bb9 usr/share/OGAgent/opengnsys/workers/server_worker.py -c25929437b6c40d24ff965ca588dd089 usr/share/doc/ogagent-oglive/changelog.Debian.gz -172f344346f66d22a8bf1888b1811c06 usr/share/doc/ogagent-oglive/copyright -a7d8603a8cfc9a45e52129a5e5e8664b usr/share/doc/ogagent-oglive/readme.txt diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/postinst b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/postinst deleted file mode 100755 index 8fc04b5..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/postinst +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -. /usr/share/debconf/confmodule - -set -e -case "$1" in - configure) - chmod 600 /usr/share/OGAgent/cfg/ogagent.cfg - ;; - abort-upgrade|abort-remove|abort-deconfigure) - ;; - - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - - - -exit 0 diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/postrm b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/postrm deleted file mode 100755 index a46fa48..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/DEBIAN/postrm +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -e - -. /usr/share/debconf/confmodule - -set -e - -if [ "$1" = "purge" ] ; then - rm -rf /usr/share/OGAgent || true > /dev/null 2>&1 -fi - diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/etc/ogagent/ogagent.cfg b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/etc/ogagent/ogagent.cfg deleted file mode 120000 index 8a21b68..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/etc/ogagent/ogagent.cfg +++ /dev/null @@ -1 +0,0 @@ -/usr/share/OGAgent/cfg/ogagent.cfg \ No newline at end of file diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/etc/ogagent/ogclient.cfg b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/etc/ogagent/ogclient.cfg deleted file mode 120000 index c7dfd07..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/etc/ogagent/ogclient.cfg +++ /dev/null @@ -1 +0,0 @@ -/usr/share/OGAgent/cfg/ogclient.cfg \ No newline at end of file diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/bin/ogagent b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/bin/ogagent deleted file mode 100755 index 1bcc29b..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/bin/ogagent +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -FOLDER=/usr/share/OGAgent - -cd $FOLDER -python -m opengnsys.linux.OGAgentService $@ diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/cfg/ogagent.cfg b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/cfg/ogagent.cfg deleted file mode 100644 index 2c84cb1..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/cfg/ogagent.cfg +++ /dev/null @@ -1,27 +0,0 @@ -[opengnsys] -# Listen address & port of REST -address=0.0.0.0 -port=8000 - -# This is a comma separated list of paths where to look for modules to load -path=test_modules/server - -# Remote OpenGnsys Service (please change IP address) -remote=https://192.168.2.10/opengnsys/rest/v3 -# Alternate OpenGnsys Service (comment out to enable this option) -#altremote=https://10.0.2.2/opengnsys/rest - -# Security tokens (please change) -client=xxxxx -secret=yyyyy - -# Log Level, if ommited, will be set to INFO -log=DEBUG - -# Module specific -# The sections must match the module name -# This section will be passes on activation to module -#[Sample1] -#value1=Mariete -#value2=Yo -#remote=https://172.27.0.1:9999/rest diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/cfg/ogclient.cfg b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/cfg/ogclient.cfg deleted file mode 100644 index d39fc96..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/cfg/ogclient.cfg +++ /dev/null @@ -1,11 +0,0 @@ -[opengnsys] -# Log Level, if ommited, will be set to INFO -log=DEBUG - -# Module specific -# The sections must match the module name -# This section will be passes on activation to module -#[Sample1] -#value1=Mariete -#value2=Yo -#remote=https://172.27.0.1:9999/rest diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/RESTApi.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/RESTApi.py deleted file mode 100644 index 39c23a9..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/RESTApi.py +++ /dev/null @@ -1,196 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 201 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" -@author: Adolfo Gómez, dkmaster at dkmon dot com -""" - -# pylint: disable-msg=E1101,W0703 - -from __future__ import unicode_literals - -import requests -import logging -import json -import warnings - -from .log import logger - -from .utils import exceptionToMessage - -VERIFY_CERT = False # Do not check server certificate -TIMEOUT = 5 # Connection timout, in seconds - - -class RESTError(Exception): - ERRCODE = 0 - - -class ConnectionError(RESTError): - ERRCODE = -1 - - -# Disable warnings log messages -try: - import urllib3 # @UnusedImport -except Exception: - from requests.packages import urllib3 # @Reimport - -try: - urllib3.disable_warnings() # @UndefinedVariable - warnings.simplefilter("ignore") -except Exception: - pass # In fact, isn't too important, but wil log warns to logging file - - -class REST(object): - """ - Simple interface to remote REST apis. - The constructor expects the "base url" as parameter, that is, the url that will be common on all REST requests - Remember that this is a helper for "easy of use". You can provide your owns using requests lib for example. - Examples: - v = REST('https://example.com/rest/v1/') (Can omit trailing / if desired) - v.sendMessage('hello?param1=1¶m2=2') - This will generate a GET message to https://example.com/rest/v1/hello?param1=1¶m2=2, and return the - deserialized JSON result or an exception - v.sendMessage('hello?param1=1¶m2=2', {'name': 'mario' }) - This will generate a POST message to https://example.com/rest/v1/hello?param1=1¶m2=2, with json encoded - body {'name': 'mario' }, and also returns - the deserialized JSON result or raises an exception in case of error - """ - access_token = None - refresh_token = None - - def __init__(self, url): - """ - Initializes the REST helper - url is the full url of the REST API Base, as for example "https://example.com/rest/v1". - @param url The url of the REST API Base. The trailing '/' can be included or omitted, as desired. - """ - self.endpoint = url - - if self.endpoint[-1] != '/': - self.endpoint += '/' - - # Some OSs ships very old python requests lib implementations, workaround them... - try: - self.newerRequestLib = requests.__version__.split('.')[0] >= '1' - except Exception: - self.newerRequestLib = False # I no version, guess this must be an old requests - - # Disable logging requests messages except for errors, ... - logging.getLogger("requests").setLevel(logging.CRITICAL) - # Tries to disable all warnings - try: - warnings.simplefilter("ignore") # Disables all warnings - except Exception: - pass - - def _getUrl(self, method): - """ - Internal method - Composes the URL based on "method" - @param method: Method to append to base url for composition - """ - url = self.endpoint + method - - return url - - def _request(self, url, data=None, patch=False): - """ - Launches the request - @param url: The url to obtain - @param data: if None, the request will be sent as a GET request. If != None, the request will be sent as a POST, - with data serialized as JSON in the body. - """ - try: - # Prepare the header content - headers = {'content-type': 'application/json'} - if self.access_token is not None: - headers['Authorization'] = 'Bearer ' + self.access_token - - if data is None: - logger.debug('REST - Requesting using GET (no data provided) {}'.format(url)) - # Old requests version does not support verify, but it do not checks ssl certificate by default - if self.newerRequestLib: - r = requests.get(url, verify=VERIFY_CERT, timeout=TIMEOUT) - else: - r = requests.get(url) - else: # POST / PATCH - if patch is False: - logger.debug('REST - Requesting using POST {}, data: {}'.format(url, data)) - if self.newerRequestLib: - r = requests.post(url, data=data, headers=headers, - verify=VERIFY_CERT, timeout=TIMEOUT) - else: - r = requests.post(url, data=data, headers=headers) - else: - logger.debug('REST - Requesting using PATCH {}, data: {}'.format(url, data)) - if self.newerRequestLib: - r = requests.patch(url, data=data, headers=headers, - verify=VERIFY_CERT, timeout=TIMEOUT) - else: - r = requests.patch(url, data=data, headers=headers) - - # la respuesta puede venir sin contenido, para ello miramos el codigo devuelto - if r.status_code == 204: - r = json.loads('{"response": "OK"}') - else: - r = json.loads(r.content) # Using instead of r.json() to make compatible with old requests lib versions - logger.debug("REST - response - " + json.dumps(r)) - except requests.exceptions.RequestException as e: - raise ConnectionError(e) - except Exception as e: - raise ConnectionError(exceptionToMessage(e)) - - return r - - def sendMessage(self, msg, data=None, processData=True, patch=False): - """ - Sends a message to remote REST server - @param data: if None or omitted, message will be a GET, else it will send a POST - @param processData: if True, data will be serialized to json before sending, else, data will be sent as "raw" - """ - logger.debug('Invoking post message {} with data {}'.format(msg, data)) - - if processData and data is not None: - data = json.dumps(data) - - url = self._getUrl(msg) - logger.debug('Requesting {}'.format(url)) - - return self._request(url, data, patch) - - def set_authorization_headers(self, access_token = None, refresh_token = None): - """ - Set access token and refresh token for use in REST Api with authorization headers - @param access_token: if None, no authorization headers will be sent in each request, else, and authorization header will be sent - @param refresh_token: if not None, when access_token expires, it will be used to obtain new access token - """ - self.access_token = access_token - self.refresh_token = refresh_token \ No newline at end of file diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/__init__.py deleted file mode 100644 index 02e7c34..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/__init__.py +++ /dev/null @@ -1,60 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" -@author: Adolfo Gómez, dkmaster at dkmon dot com -""" -from __future__ import unicode_literals - -# On centos, old six release does not includes byte2int, nor six.PY2 -import six - -import modules -from RESTApi import REST, RESTError - -try: - with open('VERSION', 'r') as v: - VERSION = v.read() -except IOError: - VERSION = '1.1.0' - -__title__ = 'OpenGnsys Agent' -__version__ = VERSION -__build__ = 0x010750 -__author__ = 'Adolfo Gómez' -__license__ = "BSD 3-clause" -__copyright__ = "Copyright VirtualCable S.L.U." - -if not hasattr(six, 'byte2int'): - if six.PY3: - import operator - six.byte2int = operator.itemgetter(0) - else: - def _byte2int(bs): - return ord(bs[0]) - six.byte2int = _byte2int diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/certs.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/certs.py deleted file mode 100644 index e4c070e..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/certs.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' - -from tempfile import gettempdir -from os.path import exists, join - -CERTFILE = 'OGAgent.pem' - - -def createSelfSignedCert(force=False): - - certFile = join(gettempdir(), CERTFILE) - - if exists(certFile) and not force: - return certFile - - certData = '''-----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb50K3mIznNklz -yVAD7xSQOSJQ6+NPXj7U9/4zLZ+TvmbQ7RqUUsxbfxHbeRnoYTWV2nKk4+tHqmvz -ujLSS/loFhTSMqtrLn7rowSYJoQhKOUkAiQlWkqCfItWgL5pJopDpNHFul9Rn3ds -PMWQTiGeUNR4Y3RnBhr1Q1BsqAzf4m6zFUmgLPPmVLdF4uJ3Tuz8TSy2gWLs5aSr -5do4WamwUfYjRSVMJECmwjUM4rQ8SQgg0sHBeBuDUGNBvBQFac1G7qUcMReeu8Zr -DUtMsXma/l4rA8NB5CRmTrQbTBF4l+jb2BDFebDqDUK1Oqs9X35yOQfDOAFYHiix -PX0IsXOZAgMBAAECggEBAJi3000RrIUZUp6Ph0gzPMuCjDEEwWiQA7CPNX1gpb8O -dp0WhkDhUroWIaICYPSXtOwUTtVjRqivMoxPy1Thg3EIoGC/rdeSdlXRHMEGicwJ -yVyalFnatr5Xzg5wkxVh4XMd0zeDt7e3JD7s0QLo5lm1CEzd77qz6lhzFic5/1KX -bzdULtTlq60dazg2hEbcS4OmM1UMCtRVDAsOIUIZPL0M9j1C1d1iEdYnh2xshKeG -/GOfo95xsgdMlGjtv3hUT5ryKVoEsu+36rGb4VfhPfUvvoVbRx5QZpW+QvxaYh5E -Fi0JEROozFwG31Y++8El7J3yQko8cFBa1lYYUwwpNAECgYEAykT+GiM2YxJ4uVF1 -OoKiE9BD53i0IG5j87lGPnWqzEwYBwnqjEKDTou+uzMGz3MDV56UEFNho7wUWh28 -LpEkjJB9QgbsugjxIBr4JoL/rYk036e/6+U8I95lvYWrzb+rBMIkRDYI7kbQD/mQ -piYUpuCkTymNAu2RisK6bBzJslkCgYEAxVE23OQvkCeOV8hJNPZGpJ1mDS+TiOow -oOScMZmZpail181eYbAfMsCr7ri812lSj98NvA2GNVLpddil6LtS1cQ5p36lFBtV -xQUMZiFz4qVbEak+izL+vPaev/mXXsOcibAIQ+qI/0txFpNhJjpaaSy6vRCBYFmc -8pgSoBnBI0ECgYAUKCn2atnpp5aWSTLYgNosBU4vDA1PShD14dnJMaqyr0aZtPhF -v/8b3btFJoGgPMLxgWEZ+2U4ju6sSFhPf7FXvLJu2QfQRkHZRDbEh7t5DLpTK4Fp -va9vl6Ml7uM/HsGpOLuqfIQJUs87OFCc7iCSvMJDDU37I7ekT2GKkpfbCQKBgBrE -0NeY0WcSJrp7/oqD2sOcYurpCG/rrZs2SIZmGzUhMxaa0vIXzbO59dlWELB8pmnE -Tf20K//x9qA5OxDe0PcVPukdQlH+/1zSOYNliG44FqnHtyd1TJ/gKVtMBiAiE4uO -aSClod5Yosf4SJbCFd/s5Iyfv52NqsAyp1w3Aj/BAoGAVCnEiGUfyHlIR+UH4zZW -GXJMeqdZLfcEIszMxLePkml4gUQhoq9oIs/Kw+L1DDxUwzkXN4BNTlFbOSu9gzK1 -dhuIUGfS6RPL88U+ivC3A0y2jT43oUMqe3hiRt360UQ1GXzp2dMnR9odSRB1wHoO -IOjEBZ8341/c9ZHc5PCGAG8= ------END PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIID7zCCAtegAwIBAgIJAIrEIthCfxUCMA0GCSqGSIb3DQEBCwUAMIGNMQswCQYD -VQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMREwDwYDVQQHDAhBbGNvcmNvbjEMMAoG -A1UECgwDVURTMQ4wDAYDVQQLDAVBY3RvcjESMBAGA1UEAwwJVURTIEFjdG9yMSgw -JgYJKoZIhvcNAQkBFhlzdXBwb3J0QHVkc2VudGVycHJpc2UuY29tMB4XDTE0MTAy -NjIzNDEyNFoXDTI0MTAyMzIzNDEyNFowgY0xCzAJBgNVBAYTAkVTMQ8wDQYDVQQI -DAZNYWRyaWQxETAPBgNVBAcMCEFsY29yY29uMQwwCgYDVQQKDANVRFMxDjAMBgNV -BAsMBUFjdG9yMRIwEAYDVQQDDAlVRFMgQWN0b3IxKDAmBgkqhkiG9w0BCQEWGXN1 -cHBvcnRAdWRzZW50ZXJwcmlzZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCb50K3mIznNklzyVAD7xSQOSJQ6+NPXj7U9/4zLZ+TvmbQ7RqUUsxb -fxHbeRnoYTWV2nKk4+tHqmvzujLSS/loFhTSMqtrLn7rowSYJoQhKOUkAiQlWkqC -fItWgL5pJopDpNHFul9Rn3dsPMWQTiGeUNR4Y3RnBhr1Q1BsqAzf4m6zFUmgLPPm -VLdF4uJ3Tuz8TSy2gWLs5aSr5do4WamwUfYjRSVMJECmwjUM4rQ8SQgg0sHBeBuD -UGNBvBQFac1G7qUcMReeu8ZrDUtMsXma/l4rA8NB5CRmTrQbTBF4l+jb2BDFebDq -DUK1Oqs9X35yOQfDOAFYHiixPX0IsXOZAgMBAAGjUDBOMB0GA1UdDgQWBBRShS90 -5lJTNvYPIEqP3GxWwG5iiDAfBgNVHSMEGDAWgBRShS905lJTNvYPIEqP3GxWwG5i -iDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAU0Sp4gXhQmRVzq+7+ -vRFUkQuPj4Ga/d9r5Wrbg3hck3+5pwe9/7APoq0P/M0DBhQpiJKjrD6ydUevC+Y/ -43ZOJPhMlNw0o6TdQxOkX6FDwQanLLs7sfvJvqtVzYn3nuRFKT3dvl7Zg44QMw2M -ay42q59fAcpB4LaDx/i7gOYSS5eca3lYW7j7YSr/+ozXK2KlgUkuCUHN95lOq+dF -trmV9mjzM4CNPZqKSE7kpHRywgrXGPCO000NvEGSYf82AtgRSFKiU8NWLQSEPdcB -k//2dsQZw2cRZ8DrC2B6Tb3M+3+CA6wVyqfqZh1SZva3LfGvq/C+u+ItguzPqNpI -xtvM ------END CERTIFICATE-----''' - with open(certFile, "wt") as f: - f.write(certData) - - return certFile diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/config.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/config.py deleted file mode 100644 index d1f3ede..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/config.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -# pylint: disable=unused-wildcard-import, wildcard-import -from __future__ import unicode_literals - -from ConfigParser import SafeConfigParser - -config = None - -def readConfig(client=False): - ''' - Reads configuration file - If client is False, will read ogagent.cfg as configuration - If client is True, will read ogclient.cfg as configuration - - This is this way so we can protect ogagent.cfg against reading for non admin users on all platforms. - ''' - cfg = SafeConfigParser() - if client is True: - fname = 'ogclient.cfg' - else: - fname = 'ogagent.cfg' - - if len(cfg.read('cfg/{}'.format(fname))) == 0: - # No configuration found - return None - - return cfg - \ No newline at end of file diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/httpserver.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/httpserver.py deleted file mode 100644 index a06ae6b..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/httpserver.py +++ /dev/null @@ -1,150 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2015 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -# pylint: disable=unused-wildcard-import,wildcard-import -from __future__ import unicode_literals, print_function - -# Pydev can't parse "six.moves.xxxx" because it is loaded lazy -import six -from six.moves.socketserver import ThreadingMixIn # @UnresolvedImport -from six.moves.BaseHTTPServer import BaseHTTPRequestHandler # @UnresolvedImport -from six.moves.BaseHTTPServer import HTTPServer # @UnresolvedImport -from six.moves.urllib.parse import unquote # @UnresolvedImport - -import json -import threading -import ssl - -from .utils import exceptionToMessage -from .certs import createSelfSignedCert -from .log import logger - -class HTTPServerHandler(BaseHTTPRequestHandler): - service = None - protocol_version = 'HTTP/1.0' - server_version = 'OpenGnsys Agent Server' - sys_version = '' - - def sendJsonError(self, code, message): - self.send_response(code) - self.send_header('Content-type', 'application/json') - self.end_headers() - self.wfile.write(json.dumps({'error': message})) - return - - def sendJsonResponse(self, data): - self.send_response(200) - data = json.dumps(data) - self.send_header('Content-type', 'application/json') - self.send_header('Content-Length', len(data)) - self.end_headers() - # Send the html message - self.wfile.write(data) - - - # parseURL - def parseUrl(self): - # Very simple path & params splitter - path = self.path.split('?')[0][1:].split('/') - - try: - params = dict((v[0], unquote(v[1])) for v in (v.split('=') for v in self.path.split('?')[1].split('&'))) - except Exception: - params = {} - - for v in self.service.modules: - if v.name == path[0]: # Case Sensitive!!!! - return (v, path[1:], params) - - return (None, path, params) - - def notifyMessage(self, module, path, getParams, postParams): - ''' - Locates witch module will process the message based on path (first folder on url path) - ''' - try: - data = module.processServerMessage(path, getParams, postParams, self) - self.sendJsonResponse(data) - except Exception as e: - logger.exception() - self.sendJsonError(500, exceptionToMessage(e)) - - def do_GET(self): - module, path, params = self.parseUrl() - - self.notifyMessage(module, path, params, None) - - def do_POST(self): - module, path, getParams = self.parseUrl() - - # Tries to get JSON content (UTF-8 encoded) - try: - length = int(self.headers.getheader('content-length')) - content = self.rfile.read(length).decode('utf-8') - logger.debug('length: {}, content >>{}<<'.format(length, content)) - postParams = json.loads(content) - except Exception as e: - self.sendJsonError(500, exceptionToMessage(e)) - - self.notifyMessage(module, path, getParams, postParams) - - - def log_error(self, fmt, *args): - logger.error('HTTP ' + fmt % args) - - def log_message(self, fmt, *args): - logger.info('HTTP ' + fmt % args) - - -class HTTPThreadingServer(ThreadingMixIn, HTTPServer): - pass - -class HTTPServerThread(threading.Thread): - def __init__(self, address, service): - super(self.__class__, self).__init__() - - HTTPServerHandler.service = service # Keep tracking of service so we can intercact with it - - self.certFile = createSelfSignedCert() - self.server = HTTPThreadingServer(address, HTTPServerHandler) - self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.certFile, server_side=True) - - logger.debug('Initialized HTTPS Server thread on {}'.format(address)) - - def getServerUrl(self): - return 'https://{}:{}/'.format(self.server.server_address[0], self.server.server_address[1]) - - def stop(self): - self.server.shutdown() - - def run(self): - self.server.serve_forever() - - diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/ipc.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/ipc.py deleted file mode 100644 index dd3663d..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/ipc.py +++ /dev/null @@ -1,423 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -import socket -import threading -import six -import traceback -import json - -from opengnsys.utils import toUnicode -from opengnsys.log import logger - -# The IPC Server will wait for connections from clients -# Clients will open socket, and wait for data from server -# The messages sent (from server) will be the following (subject to future changes): -# Message_id Data Action -# ------------ -------- -------------------------- -# MSG_LOGOFF None Logout user from session -# MSG_MESSAGE message,level Display a message with level (INFO, WARN, ERROR, FATAL) # TODO: Include level, right now only has message -# MSG_POPUP title,message Display a popup box with a title -# MSG_SCRIPT python script Execute an specific python script INSIDE CLIENT environment (this messages is not sent right now) -# The messages received (sent from client) will be the following: -# Message_id Data Action -# ------------ -------- -------------------------- -# REQ_LOGOUT Logout user from session -# REQ_INFORMATION None Request information from ipc server (maybe configuration parameters in a near future) -# REQ_LOGIN python script Execute an specific python script INSIDE CLIENT environment (this messages is not sent right now) -# -# All messages are in the form: -# BYTE -# 0 1-2 3 4 ... -# MSG_ID DATA_LENGTH (little endian) Data (can be 0 length) -# With a previos "MAGIC" header in fron of each message - -# Client messages -MSG_LOGOFF = 0xA1 # Request log off from an user -MSG_MESSAGE = 0xB2 -MSG_POPUP = 0xB3 -MSG_SCRIPT = 0xC3 - -# Request messages -REQ_MESSAGE = 0xD4 -REQ_POPUP = 0xD5 -REQ_LOGIN = 0xE5 -REQ_LOGOUT = 0xF6 - -# Reverse msgs dict for debugging -REV_DICT = { - MSG_LOGOFF: 'MSG_LOGOFF', - MSG_MESSAGE: 'MSG_MESSAGE', - MSG_POPUP: 'MSG_POPUP', - MSG_SCRIPT: 'MSG_SCRIPT', - REQ_LOGIN: 'REQ_LOGIN', - REQ_LOGOUT: 'REQ_LOGOUT', - REQ_MESSAGE: 'REQ_MESSAGE' -} - -MAGIC = b'\x4F\x47\x41\x00' # OGA in hexa with a padded 0 to the right - - -# States for client processor -ST_SECOND_BYTE = 0x01 -ST_RECEIVING = 0x02 -ST_PROCESS_MESSAGE = 0x02 - - -class ClientProcessor(threading.Thread): - def __init__(self, parent, clientSocket): - super(self.__class__, self).__init__() - self.parent = parent - self.clientSocket = clientSocket - self.running = False - self.messages = six.moves.queue.Queue(32) # @UndefinedVariable - - def stop(self): - logger.debug('Stoping client processor') - self.running = False - - def processRequest(self, msg, data): - logger.debug('Got Client message {}={}'.format(msg, REV_DICT.get(msg))) - if self.parent.clientMessageProcessor is not None: - self.parent.clientMessageProcessor(msg, data) - - def run(self): - self.running = True - self.clientSocket.setblocking(0) - - state = None - recv_msg = None - recv_data = None - while self.running: - try: - counter = 1024 - while counter > 0: # So we process at least the incoming queue every XX bytes readed - counter -= 1 - b = self.clientSocket.recv(1) - if b == b'': - # Client disconnected - self.running = False - break - buf = six.byte2int(b) # Empty buffer, this is set as non-blocking - if state is None: - if buf in (REQ_MESSAGE, REQ_LOGIN, REQ_LOGOUT): - logger.debug('State set to {}'.format(buf)) - state = buf - recv_msg = buf - continue # Get next byte - else: - logger.debug('Got unexpected data {}'.format(buf)) - elif state in (REQ_MESSAGE, REQ_LOGIN, REQ_LOGOUT): - logger.debug('First length byte is {}'.format(buf)) - msg_len = buf - state = ST_SECOND_BYTE - continue - elif state == ST_SECOND_BYTE: - msg_len += buf << 8 - logger.debug('Second length byte is {}, len is {}'.format(buf, msg_len)) - if msg_len == 0: - self.processRequest(recv_msg, None) - state = None - break - state = ST_RECEIVING - recv_data = b'' - continue - elif state == ST_RECEIVING: - recv_data += six.int2byte(buf) - msg_len -= 1 - if msg_len == 0: - self.processRequest(recv_msg, recv_data) - recv_data = None - state = None - break - else: - logger.debug('Got invalid message from request: {}, state: {}'.format(buf, state)) - except socket.error as e: - # If no data is present, no problem at all, pass to check messages - pass - except Exception as e: - tb = traceback.format_exc() - logger.error('Error: {}, trace: {}'.format(e, tb)) - - if self.running is False: - break - - try: - msg = self.messages.get(block=True, timeout=1) - except six.moves.queue.Empty: # No message got in time @UndefinedVariable - continue - - logger.debug('Got message {}={}'.format(msg, REV_DICT.get(msg[0]))) - - try: - m = msg[1] if msg[1] is not None else b'' - l = len(m) - data = MAGIC + six.int2byte(msg[0]) + six.int2byte(l & 0xFF) + six.int2byte(l >> 8) + m - try: - self.clientSocket.sendall(data) - except socket.error as e: - # Send data error - logger.debug('Socket connection is no more available: {}'.format(e.args)) - self.running = False - except Exception as e: - logger.error('Invalid message in queue: {}'.format(e)) - - logger.debug('Client processor stopped') - try: - self.clientSocket.close() - except Exception: - pass # If can't close, nothing happens, just end thread - - -class ServerIPC(threading.Thread): - - def __init__(self, listenPort, clientMessageProcessor=None): - super(self.__class__, self).__init__() - self.port = listenPort - self.running = False - self.serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.threads = [] - self.clientMessageProcessor = clientMessageProcessor - - def stop(self): - logger.debug('Stopping Server IPC') - self.running = False - for t in self.threads: - t.stop() - socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect(('localhost', self.port)) - self.serverSocket.close() - - for t in self.threads: - t.join() - - def sendMessage(self, msgId, msgData): - ''' - Notify message to all listening threads - ''' - logger.debug('Sending message {}({}),{} to all clients'.format(msgId, REV_DICT.get(msgId), msgData)) - - # Convert to bytes so length is correctly calculated - if isinstance(msgData, six.text_type): - msgData = msgData.encode('utf8') - - for t in self.threads: - if t.isAlive(): - logger.debug('Sending to {}'.format(t)) - t.messages.put((msgId, msgData)) - - def sendLoggofMessage(self): - self.sendMessage(MSG_LOGOFF, '') - - def sendMessageMessage(self, message): - self.sendMessage(MSG_MESSAGE, message) - - def sendPopupMessage(self, title, message): - self.sendMessage(MSG_POPUP, {'title':title, 'message':message}) - - def sendScriptMessage(self, script): - self.sendMessage(MSG_SCRIPT, script) - - def cleanupFinishedThreads(self): - ''' - Cleans up current threads list - ''' - aliveThreads = [] - for t in self.threads: - if t.isAlive(): - logger.debug('Thread {} is alive'.format(t)) - aliveThreads.append(t) - self.threads[:] = aliveThreads - - def run(self): - self.running = True - - self.serverSocket.bind(('localhost', self.port)) - self.serverSocket.setblocking(1) - self.serverSocket.listen(4) - - while True: - try: - (clientSocket, address) = self.serverSocket.accept() - # Stop processing if thread is mean to stop - if self.running is False: - break - logger.debug('Got connection from {}'.format(address)) - - self.cleanupFinishedThreads() # House keeping - - logger.debug('Starting new thread, current: {}'.format(self.threads)) - t = ClientProcessor(self, clientSocket) - self.threads.append(t) - t.start() - except Exception as e: - logger.error('Got an exception on Server ipc thread: {}'.format(e)) - - -class ClientIPC(threading.Thread): - def __init__(self, listenPort): - super(ClientIPC, self).__init__() - self.port = listenPort - self.running = False - self.clientSocket = None - self.messages = six.moves.queue.Queue(32) # @UndefinedVariable - - self.connect() - - def stop(self): - self.running = False - - def getMessage(self): - while self.running: - try: - return self.messages.get(timeout=1) - except six.moves.queue.Empty: # @UndefinedVariable - continue - - return None - - def sendRequestMessage(self, msg, data=None): - logger.debug('Sending request for msg: {}({}), {}'.format(msg, REV_DICT.get(msg), data)) - if data is None: - data = b'' - - if isinstance(data, six.text_type): # Convert to bytes if necessary - data = data.encode('utf-8') - - l = len(data) - msg = six.int2byte(msg) + six.int2byte(l & 0xFF) + six.int2byte(l >> 8) + data - self.clientSocket.sendall(msg) - - def sendLogin(self, username, language): - self.sendRequestMessage(REQ_LOGIN, username+','+language) - - def sendLogout(self, username): - self.sendRequestMessage(REQ_LOGOUT, username) - - def sendMessage(self, module, message, data=None): - ''' - Sends a message "message" with data (data will be encoded as json, so ensure that it is serializable) - @param module: Module that will receive this message - @param message: Message to send. This message is "customized", and understand by modules - @param data: Data to be send as message companion - ''' - msg = '\0'.join((module, message, json.dumps(data))) - self.sendRequestMessage(REQ_MESSAGE, msg) - - def messageReceived(self): - ''' - Override this method to automatically get notified on new message - received. Message is at self.messages queue - ''' - pass - - def receiveBytes(self, number): - msg = b'' - while self.running and len(msg) < number: - try: - buf = self.clientSocket.recv(number - len(msg)) - if buf == b'': - logger.debug('Buf {}, msg {}({})'.format(buf, msg, REV_DICT.get(msg))) - self.running = False - break - msg += buf - except socket.timeout: - pass - - if self.running is False: - logger.debug('Not running, returning None') - return None - return msg - - def connect(self): - self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.clientSocket.connect(('localhost', self.port)) - self.clientSocket.settimeout(2) # Static, custom socket timeout of 2 seconds for local connection (no network) - - def run(self): - self.running = True - - while self.running: - try: - msg = b'' - # We look for magic message header - while self.running: # Wait for MAGIC - try: - buf = self.clientSocket.recv(len(MAGIC) - len(msg)) - if buf == b'': - self.running = False - break - msg += buf - if len(msg) != len(MAGIC): - continue # Do not have message - if msg != MAGIC: # Skip first byte an continue searchong - msg = msg[1:] - continue - break - except socket.timeout: # Timeout is here so we can get stop thread - continue - - if self.running is False: - break - - # Now we get message basic data (msg + datalen) - msg = bytearray(self.receiveBytes(3)) - - # We have the magic header, here comes the message itself - if msg is None: - continue - - msgId = msg[0] - dataLen = msg[1] + (msg[2] << 8) - if msgId not in (MSG_LOGOFF, MSG_MESSAGE, MSG_SCRIPT): - raise Exception('Invalid message id: {}'.format(msgId)) - - data = self.receiveBytes(dataLen) - if data is None: - continue - - self.messages.put((msgId, data)) - self.messageReceived() - - except socket.error as e: - logger.error('Communication with server got an error: {}'.format(toUnicode(e.strerror))) - self.running = False - return - except Exception as e: - tb = traceback.format_exc() - logger.error('Error: {}, trace: {}'.format(e, tb)) - - try: - self.clientSocket.close() - except Exception: - pass # If can't close, nothing happens, just end thread - diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/OGAgentService.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/OGAgentService.py deleted file mode 100644 index 29a238d..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/OGAgentService.py +++ /dev/null @@ -1,147 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -from opengnsys.service import CommonService -from opengnsys.service import IPC_PORT -from opengnsys import ipc - -from opengnsys.log import logger - -from opengnsys.linux.daemon import Daemon - -import sys -import signal -import json - -try: - from prctl import set_proctitle # @UnresolvedImport -except Exception: # Platform may not include prctl, so in case it's not available, we let the "name" as is - def set_proctitle(_): - pass - - -class OGAgentSvc(Daemon, CommonService): - def __init__(self, args=None): - Daemon.__init__(self, '/var/run/opengnsys-agent.pid') - CommonService.__init__(self) - - def run(self): - logger.debug('** Running Daemon **') - set_proctitle('OGAgent') - - self.initialize() - - # Call modules initialization - # They are called in sequence, no threading is done at this point, so ensure modules onActivate always returns - - - # ********************* - # * Main Service loop * - # ********************* - # Counter used to check ip changes only once every 10 seconds, for - # example - try: - while self.isAlive: - # In milliseconds, will break - self.doWait(1000) - except (KeyboardInterrupt, SystemExit) as e: - logger.error('Requested exit of main loop') - except Exception as e: - logger.exception() - logger.error('Caught exception on main loop: {}'.format(e)) - - self.terminate() - - self.notifyStop() - - def signal_handler(self, signal, frame): - self.isAlive = False - sys.stderr.write("signal handler: {}".format(signal)) - - -def usage(): - sys.stderr.write("usage: {} start|stop|restart|fg|login 'username'|logout 'username'|message 'module' 'message' 'json'\n".format(sys.argv[0])) - sys.exit(2) - -if __name__ == '__main__': - logger.setLevel('INFO') - - if len(sys.argv) == 5 and sys.argv[1] == 'message': - logger.debug('Running client opengnsys') - client = None - try: - client = ipc.ClientIPC(IPC_PORT) - client.sendMessage(sys.argv[2], sys.argv[3], json.loads(sys.argv[4])) - sys.exit(0) - except Exception as e: - logger.error(e) - - - if len(sys.argv) == 3 and sys.argv[1] in ('login', 'logout'): - logger.debug('Running client opengnsys') - client = None - try: - client = ipc.ClientIPC(IPC_PORT) - if 'login' == sys.argv[1]: - client.sendLogin(sys.argv[2]) - sys.exit(0) - elif 'logout' == sys.argv[1]: - client.sendLogout(sys.argv[2]) - sys.exit(0) - else: - usage() - except Exception as e: - logger.error(e) - elif len(sys.argv) != 2: - usage() - - logger.debug('Executing actor') - daemon = OGAgentSvc() - - signal.signal(signal.SIGTERM, daemon.signal_handler) - signal.signal(signal.SIGINT, daemon.signal_handler) - - if len(sys.argv) == 2: - if 'start' == sys.argv[1]: - daemon.start() - elif 'stop' == sys.argv[1]: - daemon.stop() - elif 'restart' == sys.argv[1]: - daemon.restart() - elif 'fg' == sys.argv[1]: - daemon.run() - else: - usage() - sys.exit(0) - else: - usage() diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/__init__.py deleted file mode 100644 index 3a98c78..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/daemon.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/daemon.py deleted file mode 100644 index 3753808..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/daemon.py +++ /dev/null @@ -1,182 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' -@author: : http://www.jejik.com/authors/sander_marechal/ -@see: : http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ -''' - -from __future__ import unicode_literals -import sys -import os -import time -import atexit -from opengnsys.log import logger - -from signal import SIGTERM - - -class Daemon: - """ - A generic daemon class. - - Usage: subclass the Daemon class and override the run() method - """ - def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): - self.stdin = stdin - self.stdout = stdout - self.stderr = stderr - self.pidfile = pidfile - - def daemonize(self): - """ - do the UNIX double-fork magic, see Stevens' "Advanced - Programming in the UNIX Environment" for details (ISBN 0201563177) - http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 - """ - try: - pid = os.fork() - if pid > 0: - # exit first parent - sys.exit(0) - except OSError as e: - logger.error("fork #1 error: {}".format(e)) - sys.stderr.write("fork #1 failed: {}\n".format(e)) - sys.exit(1) - - # decouple from parent environment - os.chdir("/") - os.setsid() - os.umask(0) - - # do second fork - try: - pid = os.fork() - if pid > 0: - # exit from second parent - sys.exit(0) - except OSError as e: - logger.error("fork #2 error: {}".format(e)) - sys.stderr.write("fork #2 failed: {}\n".format(e)) - sys.exit(1) - - # redirect standard file descriptors - sys.stdout.flush() - sys.stderr.flush() - si = open(self.stdin, 'r') - so = open(self.stdout, 'a+') - se = open(self.stderr, 'a+', 0) - os.dup2(si.fileno(), sys.stdin.fileno()) - os.dup2(so.fileno(), sys.stdout.fileno()) - os.dup2(se.fileno(), sys.stderr.fileno()) - - # write pidfile - atexit.register(self.delpid) - pid = str(os.getpid()) - with open(self.pidfile, 'w+') as f: - f.write("{}\n".format(pid)) - - def delpid(self): - try: - os.remove(self.pidfile) - except Exception: - # Not found/not permissions or whatever... - pass - - def start(self): - """ - Start the daemon - """ - logger.debug('Starting daemon') - # Check for a pidfile to see if the daemon already runs - try: - pf = open(self.pidfile, 'r') - pid = int(pf.read().strip()) - pf.close() - except IOError: - pid = None - - if pid: - message = "pidfile {} already exist. Daemon already running?\n".format(pid) - logger.error(message) - sys.stderr.write(message) - sys.exit(1) - - # Start the daemon - self.daemonize() - try: - self.run() - except Exception as e: - logger.error('Exception running process: {}'.format(e)) - - if os.path.exists(self.pidfile): - os.remove(self.pidfile) - - def stop(self): - """ - Stop the daemon - """ - # Get the pid from the pidfile - try: - pf = open(self.pidfile, 'r') - pid = int(pf.read().strip()) - pf.close() - except IOError: - pid = None - - if pid is None: - message = "pidfile {} does not exist. Daemon not running?\n".format(self.pidfile) - logger.info(message) - # sys.stderr.write(message) - return # not an error in a restart - - # Try killing the daemon process - try: - for i in range(10): - os.kill(pid, SIGTERM) - time.sleep(1) - except OSError as err: - if err.errno == 3: # No such process - if os.path.exists(self.pidfile): - os.remove(self.pidfile) - else: - sys.stderr.write(err) - sys.exit(1) - - def restart(self): - """ - Restart the daemon - """ - self.stop() - self.start() - - # Overridables - def run(self): - """ - You should override this method when you subclass Daemon. It will be called after the process has been - daemonized by start() or restart(). - """ diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/log.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/log.py deleted file mode 100644 index dc54e19..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/log.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -import logging -import os -import tempfile -import six - -# Valid logging levels, from UDS Broker (uds.core.utils.log) -OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in six.moves.xrange(6)) # @UndefinedVariable - - -class LocalLogger(object): - def __init__(self): - # tempdir is different for "user application" and "service" - # service wil get c:\windows\temp, while user will get c:\users\XXX\temp - # Try to open logger at /var/log path - # If it fails (access denied normally), will try to open one at user's home folder, and if - # agaim it fails, open it at the tmpPath - - for logDir in ('/var/log', os.path.expanduser('~'), tempfile.gettempdir()): - try: - fname = os.path.join(logDir, 'opengnsys.log') - logging.basicConfig( - filename=fname, - filemode='a', - format='%(levelname)s %(asctime)s %(message)s', - level=logging.DEBUG - ) - self.logger = logging.getLogger('opengnsys') - os.chmod(fname, 0o0600) - return - except Exception: - pass - - # Logger can't be set - self.logger = None - - def log(self, level, message): - # Debug messages are logged to a file - # our loglevels are 10000 (other), 20000 (debug), .... - # logging levels are 10 (debug), 20 (info) - # OTHER = logging.NOTSET - self.logger.log(int(level / 1000) - 10, message) - - def isWindows(self): - return False - - def isLinux(self): - return True diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/operations.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/operations.py deleted file mode 100644 index db6ea18..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/operations.py +++ /dev/null @@ -1,286 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -import socket -import platform -import fcntl -import os -import locale -import ctypes # @UnusedImport -import ctypes.util -import subprocess -import struct -import array -import six -from opengnsys import utils -from .renamer import rename - - -def _getMacAddr(ifname): - ''' - Returns the mac address of an interface - Mac is returned as unicode utf-8 encoded - ''' - if isinstance(ifname, list): - return dict([(name, _getMacAddr(name)) for name in ifname]) - if isinstance(ifname, six.text_type): - ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7) - try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - info = bytearray(fcntl.ioctl(s.fileno(), 0x8927, struct.pack(str('256s'), ifname[:15]))) - return six.text_type(''.join(['%02x:' % char for char in info[18:24]])[:-1]) - except Exception: - return None - - -def _getIpAddr(ifname): - ''' - Returns the ip address of an interface - Ip is returned as unicode utf-8 encoded - ''' - if isinstance(ifname, list): - return dict([(name, _getIpAddr(name)) for name in ifname]) - if isinstance(ifname, six.text_type): - ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7) - try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - return six.text_type(socket.inet_ntoa(fcntl.ioctl( - s.fileno(), - 0x8915, # SIOCGIFADDR - struct.pack(str('256s'), ifname[:15]) - )[20:24])) - except Exception: - return None - - -def _getInterfaces(): - ''' - Returns a list of interfaces names coded in utf-8 - ''' - max_possible = 128 # arbitrary. raise if needed. - space = max_possible * 16 - if platform.architecture()[0] == '32bit': - offset, length = 32, 32 - elif platform.architecture()[0] == '64bit': - offset, length = 16, 40 - else: - raise OSError('Unknown arquitecture {0}'.format(platform.architecture()[0])) - - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - names = array.array(str('B'), b'\0' * space) - outbytes = struct.unpack(str('iL'), fcntl.ioctl( - s.fileno(), - 0x8912, # SIOCGIFCONF - struct.pack(str('iL'), space, names.buffer_info()[0]) - ))[0] - namestr = names.tostring() - # return namestr, outbytes - return [namestr[i:i + offset].split(b'\0', 1)[0].decode('utf-8') for i in range(0, outbytes, length)] - - -def _getIpAndMac(ifname): - ip, mac = _getIpAddr(ifname), _getMacAddr(ifname) - return (ip, mac) - - -def getComputerName(): - ''' - Returns computer name, with no domain - ''' - return socket.gethostname().split('.')[0] - - -def getNetworkInfo(): - ''' - Obtains a list of network interfaces - @return: A "generator" of elements, that are dict-as-object, with this elements: - name: Name of the interface - mac: mac of the interface - ip: ip of the interface - ''' - for ifname in _getInterfaces(): - ip, mac = _getIpAndMac(ifname) - if mac != '00:00:00:00:00:00': # Skips local interfaces - yield utils.Bunch(name=ifname, mac=mac, ip=ip) - - -def getDomainName(): - return '' - - -def getLinuxVersion(): - lv = platform.linux_distribution() - return lv[0] + ', ' + lv[1] - - -def reboot(flags=0): - ''' - Simple reboot using os command - ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - subprocess.call(['/sbin/reboot']) - - -def poweroff(flags=0): - ''' - Simple poweroff using os command - ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - subprocess.call(['/sbin/poweroff']) - - -def logoff(): - ''' - Kills all curent user processes, which must send a logogof - caveat: If the user has other sessions, will also disconnect from them - ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - subprocess.call(['/usr/bin/pkill', '-u', os.environ['USER']]) - - -def renameComputer(newName): - rename(newName) - - -def joinDomain(domain, ou, account, password, executeInOneStep=False): - pass - - -def changeUserPassword(user, oldPassword, newPassword): - ''' - Simple password change for user using command line - ''' - os.system('echo "{1}\n{1}" | /usr/bin/passwd {0} 2> /dev/null'.format(user, newPassword)) - - -class XScreenSaverInfo(ctypes.Structure): - _fields_ = [('window', ctypes.c_long), - ('state', ctypes.c_int), - ('kind', ctypes.c_int), - ('til_or_since', ctypes.c_ulong), - ('idle', ctypes.c_ulong), - ('eventMask', ctypes.c_ulong)] - -# Initialize xlib & xss -try: - xlibPath = ctypes.util.find_library('X11') - xssPath = ctypes.util.find_library('Xss') - xlib = ctypes.cdll.LoadLibrary(xlibPath) - xss = ctypes.cdll.LoadLibrary(xssPath) - - # Fix result type to XScreenSaverInfo Structure - xss.XScreenSaverQueryExtension.restype = ctypes.c_int - xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo) # Result in a XScreenSaverInfo structure -except Exception: # Libraries not accesible, not found or whatever.. - xlib = xss = None - - -def initIdleDuration(atLeastSeconds): - ''' - On linux we set the screensaver to at least required seconds, or we never will get "idle" - ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - subprocess.call(['/usr/bin/xset', 's', '{}'.format(atLeastSeconds + 30)]) - # And now reset it - subprocess.call(['/usr/bin/xset', 's', 'reset']) - - -def getIdleDuration(): - ''' - Returns idle duration, in seconds - ''' - if xlib is None or xss is None: - return 0 # Libraries not available - - # production code might want to not hardcode the offset 16... - display = xlib.XOpenDisplay(None) - - event_base = ctypes.c_int() - error_base = ctypes.c_int() - - available = xss.XScreenSaverQueryExtension(display, ctypes.byref(event_base), ctypes.byref(error_base)) - if available != 1: - return 0 # No screen saver is available, no way of getting idle - - info = xss.XScreenSaverAllocInfo() - xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), info) - - if info.contents.state != 0: - return 3600 * 100 * 1000 # If screen saver is active, return a high enough value - - return info.contents.idle / 1000.0 - - -def getCurrentUser(): - ''' - Returns current logged in user - ''' - return os.environ['USER'] - - -def getSessionLanguage(): - ''' - Returns the user's session language - ''' - return locale.getdefaultlocale()[0] - - -def showPopup(title, message): - ''' - Displays a message box on user's session (during 1 min). - ''' - return subprocess.call('zenity --info --timeout 60 --title "{}" --text "{}"'.format(title, message), shell=True) - - -def get_etc_path(): - """ - :return: - Returns etc directory path. - """ - return os.sep + 'etc' diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/__init__.py deleted file mode 100644 index 27198dc..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/__init__.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -import platform -import os -import sys -import pkgutil - -from opengnsys.log import logger - -renamers = {} - - -# Renamers now are for IPv4 only addresses -def rename(newName): - distribution = platform.linux_distribution()[0].lower().strip() - if distribution in renamers: - return renamers[distribution](newName) - - # Try Debian renamer, simplest one - logger.info('Renamer for platform "{0}" not found, tryin debian renamer'.format(distribution)) - return renamers['debian'](newName) - - -# Do load of packages -def _init(): - pkgpath = os.path.dirname(sys.modules[__name__].__file__) - for _, name, _ in pkgutil.iter_modules([pkgpath]): - __import__(__name__ + '.' + name, globals(), locals()) - -_init() \ No newline at end of file diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/debian.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/debian.py deleted file mode 100644 index be55e00..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/debian.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -from opengnsys.linux.renamer import renamers -from opengnsys.log import logger - -import os - - -def rename(newName): - ''' - Debian renamer - Expects new host name on newName - Host does not needs to be rebooted after renaming - ''' - logger.debug('using Debian renamer') - - with open('/etc/hostname', 'w') as hostname: - hostname.write(newName) - - # Force system new name - os.system('/bin/hostname %s' % newName) - - # add name to "hosts" - with open('/etc/hosts', 'r') as hosts: - lines = hosts.readlines() - with open('/etc/hosts', 'w') as hosts: - hosts.write("127.0.1.1\t%s\n" % newName) - for l in lines: - if l[:9] == '127.0.1.1': # Skips existing 127.0.1.1. if it already exists - continue - hosts.write(l) - - return True - -# All names in lower case -renamers['debian'] = rename -renamers['ubuntu'] = rename diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/opensuse.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/opensuse.py deleted file mode 100644 index a2d29a5..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/opensuse.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -from opengnsys.linux.renamer import renamers -from opengnsys.log import logger - -import os - - -def rename(newName): - ''' - RH, Centos, Fedora Renamer - Expects new host name on newName - Host does not needs to be rebooted after renaming - ''' - logger.debug('using SUSE renamer') - - with open('/etc/hostname', 'w') as hostname: - hostname.write(newName) - - # Force system new name - os.system('/bin/hostname %s' % newName) - - # add name to "hosts" - with open('/etc/hosts', 'r') as hosts: - lines = hosts.readlines() - with open('/etc/hosts', 'w') as hosts: - hosts.write("127.0.1.1\t{}\n".format(newName)) - for l in lines: - if l[:9] != '127.0.1.1': # Skips existing 127.0.1.1. if it already exists - hosts.write(l) - - return True - -# All names in lower case -renamers['opensuse'] = rename -renamers['suse'] = rename diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/redhat.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/redhat.py deleted file mode 100644 index 8821a81..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/linux/renamer/redhat.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -from opengnsys.linux.renamer import renamers -from opengnsys.log import logger - -import os - - -def rename(newName): - ''' - RH, Centos, Fedora Renamer - Expects new host name on newName - Host does not needs to be rebooted after renaming - ''' - logger.debug('using RH renamer') - - with open('/etc/hostname', 'w') as hostname: - hostname.write(newName) - - # Force system new name - os.system('/bin/hostname %s' % newName) - - # add name to "hosts" - with open('/etc/hosts', 'r') as hosts: - lines = hosts.readlines() - with open('/etc/hosts', 'w') as hosts: - hosts.write("127.0.1.1\t{}\n".format(newName)) - for l in lines: - if l[:9] != '127.0.1.1': # Skips existing 127.0.1.1. if it already exists - hosts.write(l) - - with open('/etc/sysconfig/network', 'r') as net: - lines = net.readlines() - with open('/etc/sysconfig/network', 'w') as net: - net.write('HOSTNAME={}\n'.format(newName)) - for l in lines: - if l[:8] != 'HOSTNAME': - net.write(l) - - return True - -# All names in lower case -renamers['centos linux'] = rename -renamers['fedora'] = rename diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/loader.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/loader.py deleted file mode 100644 index 23988fc..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/loader.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -# pylint: disable=unused-wildcard-import,wildcard-import - -# This is a simple module loader, so we can add "external opengnsys" modules as addons -# Modules under "opengsnsys/modules" are always autoloaded -from __future__ import unicode_literals - -import pkgutil -import os.path - -from opengnsys.workers import ServerWorker -from opengnsys.workers import ClientWorker -from .log import logger - - -def loadModules(controller, client=False): - ''' - Load own provided modules plus the modules that are in the configuration path. - The loading order is not defined (they are loaded as found, because modules MUST be "standalone" modules - @param service: The service that: - * Holds the configuration - * Will be used to initialize modules. - ''' - - ogModules = [] - - if client is False: - from opengnsys.modules.server import OpenGnSys # @UnusedImport - from .modules import server # @UnusedImport, just used to ensure opengnsys modules are initialized - modPath = 'opengnsys.modules.server' - modType = ServerWorker - else: - from opengnsys.modules.client import OpenGnSys # @UnusedImport @Reimport - from .modules import client # @UnusedImport, just used to ensure opengnsys modules are initialized - modPath = 'opengnsys.modules.client' - modType = ClientWorker - - def addCls(cls): - logger.debug('Found module class {}'.format(cls)) - try: - if cls.name is None: - # Error, cls has no name - # Log the issue and - logger.error('Class {} has no name attribute'.format(cls)) - return - ogModules.append(cls(controller)) - except Exception as e: - logger.error('Error loading module {}'.format(e)) - - def recursiveAdd(p): - subcls = p.__subclasses__() - - if len(subcls) == 0: - addCls(p) - else: - for c in subcls: - recursiveAdd(c) - - def doLoad(paths): - for (module_loader, name, ispkg) in pkgutil.iter_modules(paths, modPath + '.'): - if ispkg: - logger.debug('Found module package {}'.format(name)) - module_loader.find_module(name).load_module(name) - - - if controller.config.has_option('opengnsys', 'path') is True: - paths = tuple(os.path.abspath(v) for v in controller.config.get('opengnsys', 'path').split(',')) - else: - paths = () - - # paths += (os.path.dirname(sys.modules[modPath].__file__),) - - logger.debug('Loading modules from {}'.format(paths)) - - # Load modules - doLoad(paths) - - # Add to list of available modules - recursiveAdd(modType) - - return ogModules diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/log.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/log.py deleted file mode 100644 index e34c087..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/log.py +++ /dev/null @@ -1,103 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -import traceback -import sys -import six - -if sys.platform == 'win32': - from opengnsys.windows.log import LocalLogger # @UnusedImport -else: - from opengnsys.linux.log import LocalLogger # @Reimport - -# Valid logging levels, from UDS Broker (uds.core.utils.log) -OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in six.moves.xrange(6)) # @UndefinedVariable - -_levelName = { - 'OTHER': OTHER, - 'DEBUG': DEBUG, - 'INFO': INFO, - 'WARN': WARN, - 'ERROR': ERROR, - 'FATAL': FATAL -} - -class Logger(object): - def __init__(self): - self.logLevel = INFO - self.logger = LocalLogger() - - def setLevel(self, level): - ''' - Sets log level filter (minimum level required for a log message to be processed) - :param level: Any message with a level below this will be filtered out - ''' - if isinstance(level, six.string_types): - level = _levelName.get(level, INFO) - - self.logLevel = level # Ensures level is an integer or fails - - def log(self, level, message): - if level < self.logLevel: # Skip not wanted messages - return - - self.logger.log(level, message) - - def debug(self, message): - self.log(DEBUG, message) - - def warn(self, message): - self.log(WARN, message) - - def info(self, message): - self.log(INFO, message) - - def error(self, message): - self.log(ERROR, message) - - def fatal(self, message): - self.log(FATAL, message) - - def exception(self): - try: - tb = traceback.format_exc() - except Exception: - tb = '(could not get traceback!)' - - self.log(DEBUG, tb) - - def flush(self): - pass - - -logger = Logger() diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/macos/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/macos/__init__.py deleted file mode 100644 index ee5ba4c..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/macos/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Ramón M. Gómez, ramongomez at us dot es -''' -from __future__ import unicode_literals diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/macos/operations.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/macos/operations.py deleted file mode 100644 index 286b6d8..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/macos/operations.py +++ /dev/null @@ -1,263 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -import socket -import platform -import fcntl -import os -import locale -import ctypes # @UnusedImport -import ctypes.util -import subprocess -import struct -import array -import six -from opengnsys import utils -import netifaces - - -def _getMacAddr(ifname): - ''' - Returns the mac address of an interface - Mac is returned as unicode utf-8 encoded - ''' - if isinstance(ifname, list): - return dict([(name, _getMacAddr(name)) for name in ifname]) - if isinstance(ifname, six.text_type): - ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7) - try: - return netifaces.ifaddresses(ifname)[18][0]['addr'] - except Exception: - return None - - -def _getIpAddr(ifname): - ''' - Returns the IP address of an interface - IP is returned as unicode utf-8 encoded - ''' - if isinstance(ifname, list): - return dict([(name, _getIpAddr(name)) for name in ifname]) - if isinstance(ifname, six.text_type): - ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7) - try: - return netifaces.ifaddresses(ifname)[2][0]['addr'] - except Exception: - return None - - -def _getInterfaces(): - ''' - Returns a list of interfaces names - ''' - return netifaces.interfaces() - - -def _getIpAndMac(ifname): - ip, mac = _getIpAddr(ifname), _getMacAddr(ifname) - return (ip, mac) - - -def getComputerName(): - ''' - Returns computer name, with no domain - ''' - return socket.gethostname().split('.')[0] - - -def getNetworkInfo(): - ''' - Obtains a list of network interfaces - @return: A "generator" of elements, that are dict-as-object, with this elements: - name: Name of the interface - mac: mac of the interface - ip: ip of the interface - ''' - for ifname in _getInterfaces(): - ip, mac = _getIpAndMac(ifname) - if mac != None and ip != None: # Skips local interfaces - yield utils.Bunch(name=ifname, mac=mac, ip=ip) - - -def getDomainName(): - return '' - - -def getMacosVersion(): - return 'macOS {}'.format(platform.mac_ver()[0]) - - -def reboot(flags=0): - ''' - Simple reboot command - ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - # Exec reboot command - subprocess.call('/sbin/shutdown -r now', shell=True) - - -def poweroff(flags=0): - ''' - Simple poweroff command - ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - # Exec shutdown command - subprocess.call('/sbin/shutdown -h now', shell=True) - - -def logoff(): - ''' - Simple logout using AppleScript - ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - # Exec logout using AppleSctipt - subprocess.call('/usr/bin/osascript -e \'tell app "System Events" to «event aevtrlgo»\'', shell=True) - - -def renameComputer(newName): - rename(newName) - - -def joinDomain(domain, ou, account, password, executeInOneStep=False): - pass - - -def changeUserPassword(user, oldPassword, newPassword): - ''' - Simple password change for user using command line - ''' - os.system('echo "{1}\n{1}" | /usr/bin/passwd {0} 2> /dev/null'.format(user, newPassword)) - - -class XScreenSaverInfo(ctypes.Structure): - _fields_ = [('window', ctypes.c_long), - ('state', ctypes.c_int), - ('kind', ctypes.c_int), - ('til_or_since', ctypes.c_ulong), - ('idle', ctypes.c_ulong), - ('eventMask', ctypes.c_ulong)] - -# Initialize xlib & xss -try: - xlibPath = ctypes.util.find_library('X11') - xssPath = ctypes.util.find_library('Xss') - xlib = ctypes.cdll.LoadLibrary(xlibPath) - xss = ctypes.cdll.LoadLibrary(xssPath) - - # Fix result type to XScreenSaverInfo Structure - xss.XScreenSaverQueryExtension.restype = ctypes.c_int - xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo) # Result in a XScreenSaverInfo structure -except Exception: # Libraries not accesible, not found or whatever.. - xlib = xss = None - - -def initIdleDuration(atLeastSeconds): - ''' - On linux we set the screensaver to at least required seconds, or we never will get "idle" - ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - subprocess.call(['/usr/bin/xset', 's', '{}'.format(atLeastSeconds + 30)]) - # And now reset it - subprocess.call(['/usr/bin/xset', 's', 'reset']) - - -def getIdleDuration(): - ''' - Returns idle duration, in seconds - ''' - if xlib is None or xss is None: - return 0 # Libraries not available - - # production code might want to not hardcode the offset 16... - display = xlib.XOpenDisplay(None) - - event_base = ctypes.c_int() - error_base = ctypes.c_int() - - available = xss.XScreenSaverQueryExtension(display, ctypes.byref(event_base), ctypes.byref(error_base)) - if available != 1: - return 0 # No screen saver is available, no way of getting idle - - info = xss.XScreenSaverAllocInfo() - xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), info) - - if info.contents.state != 0: - return 3600 * 100 * 1000 # If screen saver is active, return a high enough value - - return info.contents.idle / 1000.0 - - -def getCurrentUser(): - ''' - Returns current logged in user - ''' - return os.environ['USER'] - - -def getSessionLanguage(): - ''' - Returns the user's session language - ''' - return locale.getdefaultlocale()[0] - - -def showPopup(title, message): - ''' - Displays a message box on user's session (during 1 min). - ''' - # Show a dialog using AppleSctipt - return subprocess.call('/usr/bin/osascript -e \'display notification "{}" with title "{}"\''.format(message, title), shell=True) - - -def get_etc_path(): - """ - :return: - Returns etc directory path. - """ - return os.sep + 'etc' diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/client/OpenGnSys/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/client/OpenGnSys/__init__.py deleted file mode 100644 index b840d92..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/client/OpenGnSys/__init__.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' -@author: Ramón M. Gómez, ramongomez at us dot es -''' -from __future__ import unicode_literals - -from opengnsys.workers import ClientWorker - -from opengnsys import operations -from opengnsys.log import logger -from opengnsys.scriptThread import ScriptExecutorThread - -class OpenGnSysWorker(ClientWorker): - name = 'opengnsys' - - def onActivation(self): - logger.debug('Activate invoked') - - def onDeactivation(self): - logger.debug('Deactivate invoked') - - # Processes script execution - def process_script(self, jsonParams): - logger.debug('Processed message: script({})'.format(jsonParams)) - thr = ScriptExecutorThread(jsonParams['code']) - thr.start() - #self.sendServerMessage('script', {'op', 'launched'}) - - def process_logoff(self, jsonParams): - logger.debug('Processed message: logoff({})'.format(jsonParams)) - operations.logoff() - - def process_popup(self, jsonParams): - logger.debug('Processed message: popup({})'.format(jsonParams)) - ret = operations.showPopup(jsonParams['title'], jsonParams['message']) - #self.sendServerMessage('popup', {'op', ret}) - diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/client/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/client/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/server/OpenGnSys/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/server/OpenGnSys/__init__.py deleted file mode 100644 index 223875a..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/server/OpenGnSys/__init__.py +++ /dev/null @@ -1,558 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" -@author: Ramón M. Gómez, ramongomez at us dot es -""" -from __future__ import unicode_literals - -import os -import random -import shutil -import string -import subprocess -import threading -import time -import urllib -import signal - -from opengnsys import REST -from opengnsys import operations -from opengnsys.log import logger -from opengnsys.workers import ServerWorker -from six.moves.urllib import parse - - -# Check authorization header decorator -def check_secret(fnc): - """ - Decorator to check for received secret key and raise exception if it isn't valid. - """ - def wrapper(*args, **kwargs): - try: - this, path, get_params, post_params, server = args # @UnusedVariable - if this.random == server.headers['Authorization']: - fnc(*args, **kwargs) - else: - raise Exception('Unauthorized operation') - except Exception as e: - logger.error(e) - raise Exception(e) - - return wrapper - - -# Error handler decorator. -def catch_background_error(fnc): - def wrapper(*args, **kwargs): - this = args[0] - try: - fnc(*args, **kwargs) - except Exception as e: - this.REST.sendMessage('error?id={}'.format(kwargs.get('requestId', 'error')), {'error': '{}'.format(e)}) - - return wrapper - - -def check_locked_partition(sync=False): - """ - Decorator to check if a partition is locked - """ - - def outer(fnc): - def wrapper(*args, **kwargs): - part_id = 'None' - try: - this, path, get_params, post_params, server = args # @UnusedVariable - part_id = post_params['disk'] + post_params['part'] - if this.locked.get(part_id, False): - this.locked[part_id] = True - fnc(*args, **kwargs) - else: - return 'partition locked' - except Exception as e: - this.locked[part_id] = False - return 'error {}'.format(e) - finally: - if sync is True: - this.locked[part_id] = False - logger.debug('Lock status: {} {}'.format(fnc, this.locked)) - - return wrapper - - return outer - - -class OpenGnSysWorker(ServerWorker): - name = 'opengnsys' - interface = None # Bound interface for OpenGnsys - REST = None # REST object - logged_in = False # User session flag - browser = {} # Browser info - commands = [] # Running commands - random = None # Random string for secure connections - length = 32 # Random string length - access_token = refresh_token = None # Server authorization tokens - grant_type = 'http://opengnsys.es/grants/og_client' - - def _launch_browser(self, url): - """ - Launchs the Browser with specified URL - :param url: URL to show - """ - logger.debug('Launching browser with URL: {}'.format(url)) - # Trying to kill an old browser - try: - os.kill(self.browser['process'].pid, signal.SIGKILL) - except OSError: - logger.warn('Cannot kill the old browser process') - except KeyError: - # There is no previous browser - pass - self.browser['url'] = url - self.browser['process'] = subprocess.Popen(['browser', '-qws', url]) - - def _task_command(self, route, code, op_id, send_config=False): - """ - Task to execute a command and return results to a server URI - :param route: server callback REST route to return results - :param code: code to execute - :param op_id: operation id. - """ - menu_url = '' - # Show execution tacking log, if OGAgent runs on ogLive - os_type = operations.os_type.lower() - if os_type == 'oglive': - menu_url = self.browser['url'] - self._launch_browser('http://localhost/cgi-bin/httpd-log.sh') - # Execute the code - (stat, out, err) = operations.exec_command(code) - # Remove command from the list - for c in self.commands: - if c.getName() == op_id: - self.commands.remove(c) - # Remove the REST API prefix, if needed - if route.startswith(self.REST.endpoint): - route = route[len(self.REST.endpoint):] - # Send back exit status and outputs (base64-encoded) - self.REST.sendMessage(route, {'mac': self.interface.mac, 'ip': self.interface.ip, 'trace': op_id, - 'status': stat, 'output': out.encode('base64'), 'error': err.encode('base64')}) - # Show latest menu, if OGAgent runs on ogLive - if os_type == 'oglive': - # Send configuration data, if needed - if send_config: - self.REST.sendMessage('clients/configs', {'mac': self.interface.mac, 'ip': self.interface.ip, - 'config': operations.get_configuration()}) - self._launch_browser(menu_url) - - def onActivation(self): - """ - Sends OGAgent activation notification to OpenGnsys server - """ - t = 0 - # Generate random secret to send on activation - self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(self.length)) - # Ensure cfg has required configuration variables or an exception will be thrown - url = self.service.config.get('opengnsys', 'remote') - server_client = self.service.config.get('opengnsys', 'client') - server_secret = self.service.config.get('opengnsys', 'secret') - if operations.os_type == 'ogLive' and 'oglive' in os.environ: - # Replacing server IP if its running on ogLive client - logger.debug('Activating on ogLive client, new server is {}'.format(os.environ['oglive'])) - url = parse.urlsplit(url)._replace(netloc=os.environ['oglive']).geturl() - if not url.endswith(os.path.sep): - url += os.path.sep - self.REST = REST(url) - # Get network interfaces until they are active or timeout (5 minutes) - for t in range(0, 300): - try: - self.interface = list(operations.getNetworkInfo())[0] # Get first network interface - except Exception as e: - # Wait 1 sec. and retry - time.sleep(1) - finally: - # Exit loop if interface is active - if self.interface: - if t > 0: - logger.debug("Fetch connection data after {} tries".format(t)) - break - # Raise error after timeout - if not self.interface: - raise e - # Compose login route - login_route = 'oauth/v2/token?client_id=' + server_client + '&client_secret=' + server_secret + \ - '&grant_type=' + self.grant_type + '&ip=' + self.interface.ip + '&mac=' + self.interface.mac + \ - '&token=' + self.random - # Send initialization login message - response = None - # Loop to send initialization message - for t in range(0, 100): - try: - try: - # New web compatibility. - self.REST = REST(self.service.config.get('opengnsys', 'remote')) - response = self.REST.sendMessage(login_route) - break - except: - # Trying to initialize on alternative server, if defined - # (used in "exam mode" from the University of Seville) - self.REST = REST(self.service.config.get('opengnsys', 'altremote')) - response = self.REST.sendMessage(login_route) - break - except: - time.sleep(3) - # Raise error after timeout or authentication failure - if 0 < t < 100: - logger.debug('Successful connection after {} tries'.format(t)) - elif t == 100: - raise Exception('Initialization error: Cannot connect to remote server') - if response['access_token'] is None: - raise Exception('Initialization error: Cannot obtain access token') - # Read access tokens - self.access_token = response['access_token'] - self.refresh_token = response['refresh_token'] - # Once authenticated with the server, change the API URL for private request - self.REST = REST(url + 'api/private') - # Set authorization tokens in the REST object, so in each request this token will be used - self.REST.set_authorization_headers(self.access_token, self.refresh_token) - # Completing OGAgent initialization process - os_type = operations.os_type.lower() - if os_type == 'oglive': - # Create HTML file (TEMPORARY) - message = """ - - - - -

- OpenGnsys 3 -
-

- - -""" - f = open('/tmp/init.html', 'w') - f.write(message) - f.close() - # Launching Browser - self._launch_browser('/tmp/init.html') - self.REST.sendMessage('clients/statuses', {'mac': self.interface.mac, 'ip': self.interface.ip, - 'status': 'initializing'}) - # Send configuration message - self.REST.sendMessage('clients/configs', {'mac': self.interface.mac, 'ip': self.interface.ip, - 'config': operations.get_configuration()}) - # Launching new Browser with client's menu - # menu_url = self.REST.sendMessage('menus?mac' + self.interface.mac + '&ip=' + self.interface.ip) - menu_url = '/opt/opengnsys/log/' + self.interface.ip + '.info.html' # TEMPORARY menu - self._launch_browser(menu_url) - else: - # Delete marking files - for f in ['ogboot.me', 'ogboot.firstboot', 'ogboot.secondboot']: - try: - os.remove(os.sep + f) - except OSError: - pass - # Copy file "HostsFile.FirstOctetOfIPAddress" to "HostsFile", if it exists - # (used in "exam mode" from the University of Seville) - hosts_file = os.path.join(operations.get_etc_path(), 'hosts') - new_file = hosts_file + '.' + self.interface.ip.split('.')[0] - if os.path.isfile(new_file): - shutil.copy2(new_file, hosts_file) - # Return status message - self.REST.sendMessage('clients/statuses', {'mac': self.interface.mac, 'ip': self.interface.ip, - 'status': os_type}) - - def onDeactivation(self): - """ - Sends OGAgent stopping notification to OpenGnsys server - """ - logger.debug('onDeactivation') - self.REST.sendMessage('clients/statuses', {'mac': self.interface.mac, 'ip': self.interface.ip, - 'ostype': operations.os_type, 'osversion': operations.os_version, - 'status': 'off'}) - - def processClientMessage(self, message, data): - logger.debug('Got OpenGnsys message from client: {}, data {}'.format(message, data)) - - def onLogin(self, data): - """ - Sends session login notification to OpenGnsys server - """ - user, sep, language = data.partition(',') - logger.debug('Received login for {} with language {}'.format(user, language)) - self.logged_in = True - self.REST.sendMessage('ogagent/loggedin', {'ip': self.interface.ip, 'user': user, 'language': language, - 'ostype': operations.os_type, 'osversion': operations.os_version}) - - def onLogout(self, user): - """ - Sends session logout notification to OpenGnsys server - """ - logger.debug('Received logout for {}'.format(user)) - self.logged_in = False - self.REST.sendMessage('ogagent/loggedout', {'ip': self.interface.ip, 'user': user}) - - def process_ogclient(self, path, get_params, post_params, server): - """ - This method can be overridden to provide your own message processor, or better you can - implement a method that is called exactly as "process_" + path[0] (module name has been removed from path - array) and this default processMessage will invoke it - * Example: - Imagine this invocation url (no matter if GET or POST): http://example.com:9999/Sample/mazinger/Z - The HTTP Server will remove "Sample" from path, parse arguments and invoke this method as this: - module.processMessage(["mazinger","Z"], get_params, post_params) - - This method will process "mazinger", and look for a "self" method that is called "process_mazinger", - and invoke it this way: - return self.process_mazinger(["Z"], get_params, post_params) - - In the case path is empty (that is, the path is composed only by the module name, like in - "http://example.com/Sample", the "process" method will be invoked directly - - The methods must return data that can be serialized to json (i.e. Objects are not serializable to json, - basic type are) - """ - if not path: - return "ok" - try: - operation = getattr(self, 'ogclient_' + path[0]) - except Exception: - raise Exception('Message processor for "{}" not found'.format(path[0])) - return operation(path[1:], get_params, post_params) - - def process_status(self, path, get_params, post_params, server): - """ - Returns client status (OS type or execution status) and login status - :param path: - :param get_params: - :param post_params: - :param server: - :return: JSON object {"status": "status_code", "loggedin": boolean} - """ - res = {'loggedin': self.logged_in} - try: - res['status'] = operations.os_type.lower() - except KeyError: - res['status'] = '' - # Check if OpenGnsys Client is busy - if res['status'] == 'oglive' and len(self.commands) > 0: - res['status'] = 'busy' - return res - - @check_secret - def process_reboot(self, path, get_params, post_params, server): - """ - Launches a system reboot operation - :param path: - :param get_params: - :param post_params: - :param server: authorization header - :return: JSON object {"op": "launched"} - """ - logger.debug('Received reboot operation') - - # Rebooting thread - def rebt(): - operations.reboot() - - threading.Thread(target=rebt).start() - return {'op': 'launched'} - - @check_secret - def process_poweroff(self, path, get_params, post_params, server): - """ - Launches a system power off operation - :param path: - :param get_params: - :param post_params: - :param server: authorization header - :return: JSON object {"op": "launched"} - """ - logger.debug('Received poweroff operation') - - # Powering off thread - def pwoff(): - time.sleep(2) - operations.poweroff() - - threading.Thread(target=pwoff).start() - return {'op': 'launched'} - - @check_secret - def process_script(self, path, get_params, post_params, server): - """ - Processes an script execution (script should be encoded in base64) - :param path: - :param get_params: - :param post_params: JSON object {"redirect_uri, "uri", "script": "commands", "id": trace_id} - :param server: authorization header - :return: JSON object {"op": "launched"} or {"error": "message"} - """ - logger.debug('Processing script request') - # Processing data - try: - script = urllib.unquote(post_params.get('script').decode('base64')).decode('utf8') - op_id = post_params.get('id') - route = post_params.get('redirectUri') - send_config = (post_params.get('sendConfig', 'false') == 'true') - # Check if the thread id. exists - for c in self.commands: - if c.getName() == str(op_id): - raise Exception('Task id. already exists: {}'.format(op_id)) - if post_params.get('client', 'false') == 'false': - # Launching a new thread - thr = threading.Thread(name=op_id, target=self._task_command, args=(route, script, op_id, send_config)) - thr.start() - self.commands.append(thr) - else: - # Executing as normal user - self.sendClientMessage('script', {'code': script}) - except Exception as e: - logger.error('Got exception {}'.format(e)) - return {'error': e} - return {'op': 'launched'} - - @check_secret - def process_logoff(self, path, get_params, post_params, server): - """ - Closes user session - """ - logger.debug('Received logoff operation') - # Send log off message to OGAgent client - self.sendClientMessage('logoff', {}) - return {'op': 'sent to client'} - - @check_secret - def process_popup(self, path, get_params, post_params, server): - """ - Shows a message popup on the user's session - """ - logger.debug('Received message operation') - # Send popup message to OGAgent client - self.sendClientMessage('popup', post_params) - return {'op': 'launched'} - - def process_client_popup(self, params): - self.REST.sendMessage('popup_done', params) - - @check_secret - def process_config(self, path, get_params, post_params, server): - """ - Returns client configuration - :param path: - :param get_params: - :param post_params: - :param server: authorization header - :return: JSON object - """ - serialno = '' # Serial number - storage = [] # Storage configuration - warnings = 0 # Number of warnings - logger.debug('Received getconfig operation') - # Processing data - for row in operations.get_configuration().split(';'): - cols = row.split(':') - if len(cols) == 1: - if cols[0] != '': - # Serial number - serialno = cols[0] - else: - # Skip blank rows - pass - elif len(cols) == 7: - disk, npart, tpart, fs, opsys, size, usage = cols - try: - if int(npart) == 0: - # Disk information - storage.append({'disk': int(disk), 'parttable': int(tpart), 'size': int(size)}) - else: - # Partition information - storage.append({'disk': int(disk), 'partition': int(npart), 'parttype': tpart, - 'filesystem': fs, 'operatingsystem': opsys, 'size': int(size), - 'usage': int(usage)}) - except ValueError: - logger.warn('Configuration parameter error: {}'.format(cols)) - warnings += 1 - else: - # Log warnings - logger.warn('Configuration data error: {}'.format(cols)) - warnings += 1 - # Return configuration data and count of warnings - return {'serialno': serialno, 'storage': storage, 'warnings': warnings} - - @check_secret - def process_execinfo(self, path, get_params, post_params, server): - """ - Returns running commands information - :param path: - :param get_params: - :param post_params: - :param server: authorization header - :return: JSON array: [["callback_url", "commands", trace_id], ...] - """ - data = [] - logger.debug('Received execinfo operation') - # Return the arguments of all running threads - for c in self.commands: - if c.is_alive(): - data.append(c.__dict__['_Thread__args']) - return data - - @check_secret - def process_stopcmd(self, path, get_params, post_params, server): - """ - Stops a running process identified by its trace id. - :param path: - :param get_params: - :param post_params: JSON object {"trace": trace_id} - :param server: authorization header - :return: JSON object: {"stopped": trace_id} - """ - logger.debug('Received stopcmd operation with params {}:'.format(post_params)) - # Find operation id. and stop the thread - op_id = post_params.get('trace') - for c in self.commands: - if c.is_alive() and c.getName() == str(op_id): - c._Thread__stop() - return {"stopped": op_id} - return {} diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/server/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/modules/server/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/__init__.py deleted file mode 100644 index 3a98c78..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/daemon.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/daemon.py deleted file mode 100644 index 3753808..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/daemon.py +++ /dev/null @@ -1,182 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' -@author: : http://www.jejik.com/authors/sander_marechal/ -@see: : http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ -''' - -from __future__ import unicode_literals -import sys -import os -import time -import atexit -from opengnsys.log import logger - -from signal import SIGTERM - - -class Daemon: - """ - A generic daemon class. - - Usage: subclass the Daemon class and override the run() method - """ - def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): - self.stdin = stdin - self.stdout = stdout - self.stderr = stderr - self.pidfile = pidfile - - def daemonize(self): - """ - do the UNIX double-fork magic, see Stevens' "Advanced - Programming in the UNIX Environment" for details (ISBN 0201563177) - http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 - """ - try: - pid = os.fork() - if pid > 0: - # exit first parent - sys.exit(0) - except OSError as e: - logger.error("fork #1 error: {}".format(e)) - sys.stderr.write("fork #1 failed: {}\n".format(e)) - sys.exit(1) - - # decouple from parent environment - os.chdir("/") - os.setsid() - os.umask(0) - - # do second fork - try: - pid = os.fork() - if pid > 0: - # exit from second parent - sys.exit(0) - except OSError as e: - logger.error("fork #2 error: {}".format(e)) - sys.stderr.write("fork #2 failed: {}\n".format(e)) - sys.exit(1) - - # redirect standard file descriptors - sys.stdout.flush() - sys.stderr.flush() - si = open(self.stdin, 'r') - so = open(self.stdout, 'a+') - se = open(self.stderr, 'a+', 0) - os.dup2(si.fileno(), sys.stdin.fileno()) - os.dup2(so.fileno(), sys.stdout.fileno()) - os.dup2(se.fileno(), sys.stderr.fileno()) - - # write pidfile - atexit.register(self.delpid) - pid = str(os.getpid()) - with open(self.pidfile, 'w+') as f: - f.write("{}\n".format(pid)) - - def delpid(self): - try: - os.remove(self.pidfile) - except Exception: - # Not found/not permissions or whatever... - pass - - def start(self): - """ - Start the daemon - """ - logger.debug('Starting daemon') - # Check for a pidfile to see if the daemon already runs - try: - pf = open(self.pidfile, 'r') - pid = int(pf.read().strip()) - pf.close() - except IOError: - pid = None - - if pid: - message = "pidfile {} already exist. Daemon already running?\n".format(pid) - logger.error(message) - sys.stderr.write(message) - sys.exit(1) - - # Start the daemon - self.daemonize() - try: - self.run() - except Exception as e: - logger.error('Exception running process: {}'.format(e)) - - if os.path.exists(self.pidfile): - os.remove(self.pidfile) - - def stop(self): - """ - Stop the daemon - """ - # Get the pid from the pidfile - try: - pf = open(self.pidfile, 'r') - pid = int(pf.read().strip()) - pf.close() - except IOError: - pid = None - - if pid is None: - message = "pidfile {} does not exist. Daemon not running?\n".format(self.pidfile) - logger.info(message) - # sys.stderr.write(message) - return # not an error in a restart - - # Try killing the daemon process - try: - for i in range(10): - os.kill(pid, SIGTERM) - time.sleep(1) - except OSError as err: - if err.errno == 3: # No such process - if os.path.exists(self.pidfile): - os.remove(self.pidfile) - else: - sys.stderr.write(err) - sys.exit(1) - - def restart(self): - """ - Restart the daemon - """ - self.stop() - self.start() - - # Overridables - def run(self): - """ - You should override this method when you subclass Daemon. It will be called after the process has been - daemonized by start() or restart(). - """ diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/operations.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/operations.py deleted file mode 100644 index 8c74f74..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/oglive/operations.py +++ /dev/null @@ -1,258 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" -@author: Ramón M. Gómez, ramongomez at us dot es -""" -from __future__ import unicode_literals - -import socket -import platform -import os -import fcntl -import subprocess -import struct -import array -import six -import chardet -from opengnsys import utils -from opengnsys.log import logger - - -def _getMacAddr(ifname): - """ - Returns the mac address of an interface - Mac is returned as unicode utf-8 encoded - """ - if isinstance(ifname, list): - return dict([(name, _getMacAddr(name)) for name in ifname]) - if isinstance(ifname, six.text_type): - ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7) - try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - info = bytearray(fcntl.ioctl(s.fileno(), 0x8927, struct.pack(str('256s'), ifname[:15]))) - return six.text_type(''.join(['%02x:' % char for char in info[18:24]])[:-1]) - except Exception: - return None - - -def _getIpAddr(ifname): - """ - Returns the ip address of an interface - Ip is returned as unicode utf-8 encoded - """ - if isinstance(ifname, list): - return dict([(name, _getIpAddr(name)) for name in ifname]) - if isinstance(ifname, six.text_type): - ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7) - try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - return six.text_type(socket.inet_ntoa(fcntl.ioctl( - s.fileno(), - 0x8915, # SIOCGIFADDR - struct.pack(str('256s'), ifname[:15]) - )[20:24])) - except Exception: - return None - - -def _getInterfaces(): - """ - Returns a list of interfaces names coded in utf-8 - """ - max_possible = 128 # arbitrary. raise if needed. - space = max_possible * 16 - if platform.architecture()[0] == '32bit': - offset, length = 32, 32 - elif platform.architecture()[0] == '64bit': - offset, length = 16, 40 - else: - raise OSError('Unknown arquitecture {0}'.format(platform.architecture()[0])) - - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - names = array.array(str('B'), b'\0' * space) - outbytes = struct.unpack(str('iL'), fcntl.ioctl( - s.fileno(), - 0x8912, # SIOCGIFCONF - struct.pack(str('iL'), space, names.buffer_info()[0]) - ))[0] - namestr = names.tostring() - # return namestr, outbytes - return [namestr[i:i + offset].split(b'\0', 1)[0].decode('utf-8') for i in range(0, outbytes, length)] - - -def _getIpAndMac(ifname): - ip, mac = _getIpAddr(ifname), _getMacAddr(ifname) - return ip, mac - - -def _exec_ogcommand(ogcmd): - """ - Loads OpenGnsys environment variables, executes the command and returns the result - """ - ret = subprocess.check_output(ogcmd, shell=True) - return ret - - -def getComputerName(): - """ - Returns computer name, with no domain - """ - return socket.gethostname().split('.')[0] - - -def getNetworkInfo(): - """ - Obtains a list of network interfaces - :return: A "generator" of elements, that are dict-as-object, with this elements: - name: Name of the interface - mac: mac of the interface - ip: ip of the interface - """ - for ifname in _getInterfaces(): - ip, mac = _getIpAndMac(ifname) - if mac != '00:00:00:00:00:00': # Skips local interfaces - yield utils.Bunch(name=ifname, mac=mac, ip=ip) - - -def getDomainName(): - return '' - - -def get_oglive_version(): - """ - Returns ogLive Kernel version and architecture - :return: kernel version - """ - kv = platform.os.uname() - return kv[2] + ', ' + kv[4] - - -def reboot(): - """ - Simple reboot using OpenGnsys script - """ - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - _exec_ogcommand('/opt/opengnsys/scripts/reboot') - - -def poweroff(): - """ - Simple power off using OpenGnsys script - """ - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - _exec_ogcommand('/opt/opengnsys/scripts/poweroff') - - -def get_etc_path(): - """ - Returns etc directory path. - """ - return os.sep + 'etc' - - -def get_configuration(): - """ - Returns client's configuration - Warning: this operation may take some time - :return: - """ - try: - _exec_ogcommand('/opt/opengnsys/interfaceAdm/getConfiguration') - # Returns content of configuration file - cfgdata = open('/tmp/getconfig', 'r').read().strip() - except IOError: - cfgdata = '' - return cfgdata - - -def exec_command(cmd): - """ - Executing a shell command - :param cmd: - :return: object with components: - output: standard output - error: error output - exit: exit code - """ - proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, err) = proc.communicate() - try: - if out is not None: - encoding = chardet.detect(out)["encoding"] - if encoding is not None: - out = out.decode(encoding).encode("utf8") - if err is not None: - encoding = chardet.detect(err)["encoding"] - if encoding is not None: - err = err.decode(encoding).encode("utf8") - except Exception as e: - logger.debug("ERROR EXEC COMMAND: {}".format(str(e))) - - stat = proc.returncode - return stat, out, err - - -def get_hardware(): - """ - Returns client's hardware list - :return: - """ - try: - filepath = _exec_ogcommand('/opt/opengnsys/scripts/listHardwareInfo').strip() - # Returns content of configuration file, skipping the header line and newline characters - with open(filepath, 'r') as f: - harddata = map(str.strip, f.readlines()[1:]) - except IOError: - harddata = '' - return harddata - - -def get_software(disk, part): - """ - Returns software list installed on an operating system - :param disk: - :param part: - :return: - """ - try: - filepath = _exec_ogcommand('/opt/opengnsys/scripts/listSoftwareInfo {} {}'.format(disk, part)).strip() - # Returns content of configuration file, skipping the header line and newline characters - with open(filepath, 'r') as f: - softdata = map(str.strip, f.readlines()) - except IOError: - softdata = '' - return softdata diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/operations.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/operations.py deleted file mode 100644 index a81d0c0..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/operations.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" -@author: Ramón M. Gómez, ramongomez at us dot es -""" -# pylint: disable=unused-wildcard-import,wildcard-import - -from __future__ import unicode_literals - -import sys -import os - -# Importing platform operations and getting operating system data. -if sys.platform == 'win32': - from .windows.operations import * # @UnusedWildImport - os_type = 'Windows' - os_version = getWindowsVersion() -else: - if sys.platform == 'darwin': - from .macos.operations import * # @UnusedWildImport - os_type = 'MacOS' - os_version = getMacosVersion().replace(',', '') - else: - if os.path.exists('/scripts/oginit'): - from .oglive.operations import * # @UnusedWildImport - os_type = 'ogLive' - os_version = get_oglive_version().replace(',', '') - else: - from .linux.operations import * # @UnusedWildImport - os_type = 'Linux' - os_version = getLinuxVersion().replace(',', '') diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/scriptThread.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/scriptThread.py deleted file mode 100644 index 2a6779b..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/scriptThread.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 201 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' - -# pylint: disable-msg=E1101,W0703 - -from opengnsys.log import logger - -import threading -import six - - -class ScriptExecutorThread(threading.Thread): - def __init__(self, script): - super(ScriptExecutorThread, self).__init__() - self.script = script - - def run(self): - try: - logger.debug('Executing script: {}'.format(self.script)) - six.exec_(self.script, globals(), None) - except Exception as e: - logger.error('Error executing script: {}'.format(e)) diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/service.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/service.py deleted file mode 100644 index 4a44028..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/service.py +++ /dev/null @@ -1,249 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -from .log import logger -from .config import readConfig -from .utils import exceptionToMessage - -from . import ipc -from . import httpserver -from .loader import loadModules - -import socket -import time -import json -import six - -IPC_PORT = 10398 - - -class CommonService(object): - isAlive = True - ipc = None - httpServer = None - modules = None - - def __init__(self): - logger.info('----------------------------------------') - logger.info('Initializing OpenGnsys Agent') - - # Read configuration file before proceding & ensures minimal config is there - - self.config = readConfig() - - # Get opengnsys section as dict - cfg = dict(self.config.items('opengnsys')) - - # Set up log level - logger.setLevel(cfg.get('log', 'INFO')) - - - logger.debug('Loaded configuration from opengnsys.cfg:') - for section in self.config.sections(): - logger.debug('Section {} = {}'.format(section, self.config.items(section))) - - - if logger.logger.isWindows(): - # Logs will also go to windows event log for services - logger.logger.serviceLogger = True - - self.address = (cfg.get('address', '0.0.0.0'), int(cfg.get('port', '10997'))) - self.ipcport = int(cfg.get('ipc_port', IPC_PORT)) - - self.timeout = int(cfg.get('timeout', '20')) - - logger.debug('Socket timeout: {}'.format(self.timeout)) - socket.setdefaulttimeout(self.timeout) - - # Now load modules - self.modules = loadModules(self) - logger.debug('Modules: {}'.format(list(v.name for v in self.modules))) - - def stop(self): - ''' - Requests service termination - ''' - self.isAlive = False - - # ******************************** - # * Internal messages processors * - # ******************************** - def notifyLogin(self, username): - for v in self.modules: - try: - logger.debug('Notifying login of user {} to module {}'.format(username, v.name)) - v.onLogin(username) - except Exception as e: - logger.error('Got exception {} processing login message on {}'.format(e, v.name)) - - def notifyLogout(self, username): - for v in self.modules: - try: - logger.debug('Notifying logout of user {} to module {}'.format(username, v.name)) - v.onLogout(username) - except Exception as e: - logger.error('Got exception {} processing logout message on {}'.format(e, v.name)) - - def notifyMessage(self, data): - module, message, data = data.split('\0') - for v in self.modules: - if v.name == module: # Case Sensitive!!!! - try: - logger.debug('Notifying message {} to module {} with json data {}'.format(message, v.name, data)) - v.processClientMessage(message, json.loads(data)) - return - except Exception as e: - logger.error('Got exception {} processing generic message on {}'.format(e, v.name)) - - logger.error('Module {} not found, messsage {} not sent'.format(module, message)) - - - def clientMessageProcessor(self, msg, data): - ''' - Callback, invoked from IPC, on its own thread (not the main thread). - This thread will "block" communication with agent untill finished, but this should be no problem - ''' - logger.debug('Got message {}'.format(msg)) - - if msg == ipc.REQ_LOGIN: - self.notifyLogin(data) - elif msg == ipc.REQ_LOGOUT: - self.notifyLogout(data) - elif msg == ipc.REQ_MESSAGE: - self.notifyMessage(data) - - def initialize(self): - # ****************************************** - # * Initialize listeners, modules, etc... - # ****************************************** - - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - - logger.debug('Starting IPC listener at {}'.format(IPC_PORT)) - self.ipc = ipc.ServerIPC(self.ipcport, clientMessageProcessor=self.clientMessageProcessor) - self.ipc.start() - - # And http threaded server - self.httpServer = httpserver.HTTPServerThread(self.address, self) - self.httpServer.start() - - # And lastly invoke modules activation - validMods = [] - for mod in self.modules: - try: - logger.debug('Activating module {}'.format(mod.name)) - mod.activate() - validMods.append(mod) - except Exception as e: - logger.exception() - logger.error("Activation of {} failed: {}".format(mod.name, exceptionToMessage(e))) - - self.modules[:] = validMods # copy instead of assignment - - logger.debug('Modules after activation: {}'.format(list(v.name for v in self.modules))) - - def terminate(self): - # First invoke deactivate on modules - for mod in reversed(self.modules): - try: - logger.debug('Deactivating module {}'.format(mod.name)) - mod.deactivate() - except Exception as e: - logger.exception() - logger.error("Deactivation of {} failed: {}".format(mod.name, exceptionToMessage(e))) - - # Remove IPC threads - if self.ipc is not None: - try: - self.ipc.stop() - except Exception: - logger.error('Couln\'t stop ipc server') - - if self.httpServer is not None: - try: - self.httpServer.stop() - except Exception: - logger.error('Couln\'t stop RESTApi server') - - self.notifyStop() - - # **************************************** - # Methods that CAN BE overridden by agents - # **************************************** - def doWait(self, miliseconds): - ''' - Invoked to wait a bit - CAN be OVERRIDDEN - ''' - time.sleep(float(miliseconds) / 1000) - - def notifyStop(self): - ''' - Overridden to log stop - ''' - logger.info('Service is being stopped') - - # *************************************************** - # * Helpers, convenient methods to facilitate comms * - # *************************************************** - def sendClientMessage(self, toModule, message, data): - ''' - Sends a message to the clients using IPC - The data is converted to json, so ensure that it is serializable. - All IPC is asynchronous, so if you expect a response, this will be sent by client using another message - - @param toModule: Module that will receive this message - @param message: Message to send - @param data: data to send - ''' - self.ipc.sendMessageMessage('\0'.join((toModule, message, json.dumps(data)))) - - def sendScriptMessage(self, script): - ''' - Sends an script to be executed by client - ''' - self.ipc.sendScriptMessage(script) - - def sendLogoffMessage(self): - ''' - Sends a logoff message to client - ''' - self.ipc.sendLoggofMessage() - - def sendPopupMessage(self, title, message): - ''' - Sends a poup box to be displayed by client - ''' - self.ipc.sendPopupMessage(title, message) diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/utils.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/utils.py deleted file mode 100644 index 9480a6a..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/utils.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -import sys -import six - -if sys.platform == 'win32': - _fromEncoding = 'windows-1250' -else: - _fromEncoding = 'utf-8' - - -def toUnicode(msg): - try: - if not isinstance(msg, six.text_type): - if isinstance(msg, six.binary_type): - return msg.decode(_fromEncoding, 'ignore') - return six.text_type(msg) - else: - return msg - except Exception: - try: - return six.text_type(msg) - except Exception: - return '' - - -def exceptionToMessage(e): - msg = '' - for arg in e.args: - if isinstance(arg, Exception): - msg = msg + exceptionToMessage(arg) - else: - msg = msg + toUnicode(arg) + '. ' - return msg - - -class Bunch(dict): - def __init__(self, **kw): - dict.__init__(self, kw) - self.__dict__ = self - diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/OGAgentService.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/OGAgentService.py deleted file mode 100644 index 71716d7..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/OGAgentService.py +++ /dev/null @@ -1,124 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals -# pylint: disable=unused-wildcard-import, wildcard-import - -import win32serviceutil # @UnresolvedImport, pylint: disable=import-error -import win32service # @UnresolvedImport, pylint: disable=import-error -import win32security # @UnresolvedImport, pylint: disable=import-error -import win32net # @UnresolvedImport, pylint: disable=import-error -import win32event # @UnresolvedImport, pylint: disable=import-error -import win32com.client # @UnresolvedImport, @UnusedImport, pylint: disable=import-error -import pythoncom # @UnresolvedImport, pylint: disable=import-error -import servicemanager # @UnresolvedImport, pylint: disable=import-error -import os - -from opengnsys import operations -from opengnsys.service import CommonService - -from opengnsys.log import logger - -class OGAgentSvc(win32serviceutil.ServiceFramework, CommonService): - ''' - This class represents a Windows Service for managing Agent interactions - with OpenGnsys Server - ''' - _svc_name_ = "OGAgent" - _svc_display_name_ = "OpenGnsys Agent Service" - _svc_description_ = "OpenGnsys Agent for Operating Systems" - # 'System Event Notification' is the SENS service - _svc_deps_ = ['EventLog'] - - def __init__(self, args): - win32serviceutil.ServiceFramework.__init__(self, args) - CommonService.__init__(self) - self.hWaitStop = win32event.CreateEvent(None, 1, 0, None) - self._user = None - - def SvcStop(self): - self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) - self.isAlive = False - win32event.SetEvent(self.hWaitStop) - - SvcShutdown = SvcStop - - def notifyStop(self): - servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, - servicemanager.PYS_SERVICE_STOPPED, - (self._svc_name_, '')) - - def doWait(self, miliseconds): - win32event.WaitForSingleObject(self.hWaitStop, miliseconds) - - def SvcDoRun(self): - ''' - Main service loop - ''' - try: - logger.debug('running SvcDoRun') - servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, - servicemanager.PYS_SERVICE_STARTED, - (self._svc_name_, '')) - - # call the CoInitialize to allow the registration to run in an other - # thread - logger.debug('Initializing com...') - pythoncom.CoInitialize() - - # Initialize remaining service data - self.initialize() - except Exception: # Any init exception wil be caught, service must be then restarted - logger.exception() - logger.debug('Exiting service with failure status') - os._exit(-1) # pylint: disable=protected-access - - # ********************* - # * Main Service loop * - # ********************* - try: - while self.isAlive: - # Pumps & processes any waiting messages - pythoncom.PumpWaitingMessages() - win32event.WaitForSingleObject(self.hWaitStop, 1000) - except Exception as e: - logger.error('Caught exception on main loop: {}'.format(e)) - - logger.debug('Exited main loop, deregistering SENS') - - self.terminate() # Ends IPC servers - - self.notifyStop() - - -if __name__ == '__main__': - - win32serviceutil.HandleCommandLine(OGAgentSvc) diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/__init__.py deleted file mode 100644 index e662942..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -import os -import sys - -# Change to application directory. -os.chdir(os.path.dirname(sys.argv[0])) - diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/log.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/log.py deleted file mode 100644 index 745fd03..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/log.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -import servicemanager # @UnresolvedImport, pylint: disable=import-error -import logging -import os -import tempfile - -# Valid logging levels, from UDS Broker (uds.core.utils.log) -OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6)) - - -class LocalLogger(object): - def __init__(self): - # tempdir is different for "user application" and "service" - # service wil get c:\windows\temp, while user will get c:\users\XXX\temp - logging.basicConfig( - filename=os.path.join(tempfile.gettempdir(), 'opengnsys.log'), - filemode='a', - format='%(levelname)s %(asctime)s %(message)s', - level=logging.DEBUG - ) - self.logger = logging.getLogger('opengnsys') - self.serviceLogger = False - - def log(self, level, message): - # Debug messages are logged to a file - # our loglevels are 10000 (other), 20000 (debug), .... - # logging levels are 10 (debug), 20 (info) - # OTHER = logging.NOTSET - self.logger.log(level / 1000 - 10, message) - - if level < INFO or self.serviceLogger is False: # Only information and above will be on event log - return - - if level < WARN: # Info - servicemanager.LogInfoMsg(message) - elif level < ERROR: # WARN - servicemanager.LogWarningMsg(message) - else: # Error & Fatal - servicemanager.LogErrorMsg(message) - - def isWindows(self): - return True - - def isLinux(self): - return False diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/operations.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/operations.py deleted file mode 100644 index 95331fd..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/windows/operations.py +++ /dev/null @@ -1,269 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from __future__ import unicode_literals - -import os -import locale -import subprocess -import ctypes -from ctypes.wintypes import DWORD, LPCWSTR -import win32com.client # @UnresolvedImport, pylint: disable=import-error -import win32net # @UnresolvedImport, pylint: disable=import-error -import win32security # @UnresolvedImport, pylint: disable=import-error -import win32api # @UnresolvedImport, pylint: disable=import-error -import win32con # @UnresolvedImport, pylint: disable=import-error - -from opengnsys import utils -from opengnsys.log import logger - - -def getErrorMessage(res=0): - msg = win32api.FormatMessage(res) - return msg.decode('windows-1250', 'ignore') - - -def getComputerName(): - return win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname) - - -def getNetworkInfo(): - ''' - Obtains a list of network interfaces - @return: A "generator" of elements, that are dict-as-object, with this elements: - name: Name of the interface - mac: mac of the interface - ip: ip of the interface - ''' - obj = win32com.client.Dispatch("WbemScripting.SWbemLocator") - wmobj = obj.ConnectServer("localhost", "root\cimv2") - adapters = wmobj.ExecQuery("Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True") - try: - for obj in adapters: - if obj.DefaultIPGateway is None: # Skip adapters without default router - continue - for ip in obj.IPAddress: - if ':' in ip: # Is IPV6, skip this - continue - if ip is None or ip == '' or ip.startswith('169.254') or ip.startswith('0.'): # If single link ip, or no ip - continue - logger.debug('Net config found: {}=({}, {})'.format(obj.Caption, obj.MACAddress, ip)) - yield utils.Bunch(name=obj.Caption, mac=obj.MACAddress, ip=ip) - except Exception: - return - - -def getDomainName(): - ''' - Will return the domain name if we belong a domain, else None - (if part of a network group, will also return None) - ''' - # Status: - # 0 = Unknown - # 1 = Unjoined - # 2 = Workgroup - # 3 = Domain - domain, status = win32net.NetGetJoinInformation() - if status != 3: - domain = None - - return domain - - -def getWindowsVersion(): - ''' - Returns Windows version. - ''' - import _winreg - reg = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion') - try: - data = '{} {}'.format(_winreg.QueryValueEx(reg, 'ProductName')[0], _winreg.QueryValueEx(reg, 'ReleaseId')[0]) - except Exception: - data = '{} {}'.format(_winreg.QueryValueEx(reg, 'ProductName')[0], _winreg.QueryValueEx(reg, 'CurrentBuildNumber')[0]) - reg.Close() - return data - - -EWX_LOGOFF = 0x00000000 -EWX_SHUTDOWN = 0x00000001 -EWX_REBOOT = 0x00000002 -EWX_FORCE = 0x00000004 -EWX_POWEROFF = 0x00000008 -EWX_FORCEIFHUNG = 0x00000010 - - -def reboot(flags=EWX_FORCEIFHUNG | EWX_REBOOT): - hproc = win32api.GetCurrentProcess() - htok = win32security.OpenProcessToken(hproc, win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY) - privs = ((win32security.LookupPrivilegeValue(None, win32security.SE_SHUTDOWN_NAME), win32security.SE_PRIVILEGE_ENABLED),) - win32security.AdjustTokenPrivileges(htok, 0, privs) - win32api.ExitWindowsEx(flags, 0) - -def poweroff(flags=0): - ''' - Simple poweroff command. - ''' - reboot(flags=EWX_FORCEIFHUNG | EWX_SHUTDOWN) - -def logoff(): - win32api.ExitWindowsEx(EWX_LOGOFF) - - -def renameComputer(newName): - # Needs admin privileges to work - if ctypes.windll.kernel32.SetComputerNameExW(DWORD(win32con.ComputerNamePhysicalDnsHostname), LPCWSTR(newName)) == 0: # @UndefinedVariable - # win32api.FormatMessage -> returns error string - # win32api.GetLastError -> returns error code - # (just put this comment here to remember to log this when logger is available) - error = getErrorMessage() - computerName = win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname) - raise Exception('Error renaming computer from {} to {}: {}'.format(computerName, newName, error)) - - -NETSETUP_JOIN_DOMAIN = 0x00000001 -NETSETUP_ACCT_CREATE = 0x00000002 -NETSETUP_ACCT_DELETE = 0x00000004 -NETSETUP_WIN9X_UPGRADE = 0x00000010 -NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x00000020 -NETSETUP_JOIN_UNSECURE = 0x00000040 -NETSETUP_MACHINE_PWD_PASSED = 0x00000080 -NETSETUP_JOIN_WITH_NEW_NAME = 0x00000400 -NETSETUP_DEFER_SPN_SET = 0x1000000 - - -def joinDomain(domain, ou, account, password, executeInOneStep=False): - ''' - Joins machine to a windows domain - :param domain: Domain to join to - :param ou: Ou that will hold machine - :param account: Account used to join domain - :param password: Password of account used to join domain - :param executeInOneStep: If true, means that this machine has been renamed and wants to add NETSETUP_JOIN_WITH_NEW_NAME to request so we can do rename/join in one step. - ''' - # If account do not have domain, include it - if '@' not in account and '\\' not in account: - if '.' in domain: - account = account + '@' + domain - else: - account = domain + '\\' + account - - # Do log - flags = NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN - - if executeInOneStep: - flags |= NETSETUP_JOIN_WITH_NEW_NAME - - flags = DWORD(flags) - - domain = LPCWSTR(domain) - - # Must be in format "ou=.., ..., dc=...," - ou = LPCWSTR(ou) if ou is not None and ou != '' else None - account = LPCWSTR(account) - password = LPCWSTR(password) - - res = ctypes.windll.netapi32.NetJoinDomain(None, domain, ou, account, password, flags) - # Machine found in another ou, use it and warn this on log - if res == 2224: - flags = DWORD(NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN) - res = ctypes.windll.netapi32.NetJoinDomain(None, domain, None, account, password, flags) - if res != 0: - # Log the error - error = getErrorMessage(res) - if res == 1355: - error = "DC Is not reachable" - print('{} {}'.format(res, error)) - raise Exception('Error joining domain {}, with credentials {}/*****{}: {}, {}'.format(domain.value, account.value, ', under OU {}'.format(ou.value) if ou.value is not None else '', res, error)) - - -def changeUserPassword(user, oldPassword, newPassword): - computerName = LPCWSTR(getComputerName()) - user = LPCWSTR(user) - oldPassword = LPCWSTR(oldPassword) - newPassword = LPCWSTR(newPassword) - - res = ctypes.windll.netapi32.NetUserChangePassword(computerName, user, oldPassword, newPassword) - - if res != 0: - # Log the error, and raise exception to parent - error = getErrorMessage() - raise Exception('Error changing password for user {}: {}'.format(user.value, error)) - - -class LASTINPUTINFO(ctypes.Structure): - _fields_ = [ - ('cbSize', ctypes.c_uint), - ('dwTime', ctypes.c_uint), - ] - - -def initIdleDuration(atLeastSeconds): - ''' - In windows, there is no need to set screensaver - ''' - pass - - -def getIdleDuration(): - lastInputInfo = LASTINPUTINFO() - lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo) - ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo)) - millis = ctypes.windll.kernel32.GetTickCount() - lastInputInfo.dwTime # @UndefinedVariable - return millis / 1000.0 - - -def getCurrentUser(): - ''' - Returns current logged in username - ''' - return os.environ['USERNAME'] - - -def getSessionLanguage(): - ''' - Returns the user's session language - ''' - return locale.getdefaultlocale()[0] - - -def showPopup(title, message): - ''' - Displays a message box on user's session (during 1 min). - ''' - return subprocess.call('mshta "javascript:var sh=new ActiveXObject(\'WScript.Shell\'); sh.Popup( \'{}\', 60, \'{}\', 64); close()"'.format(message.encode('unicode_escape'), title.encode('unicode_escape')), shell=True) - - -def get_etc_path(): - """ - :return: - Returns etc directory path. - """ - return os.path.join('C:', os.sep, 'Windows', 'System32', 'drivers', 'etc') diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/__init__.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/__init__.py deleted file mode 100644 index f2bcd7d..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .server_worker import ServerWorker -from .client_worker import ClientWorker diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/client_worker.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/client_worker.py deleted file mode 100644 index 6a08380..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/client_worker.py +++ /dev/null @@ -1,114 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -# pylint: disable=unused-wildcard-import,wildcard-import -from __future__ import unicode_literals - -class ClientWorker(object): - ''' - A ServerWorker is a server module that "works" for service - Most method are invoked inside their own thread, except onActivation & onDeactivation. - This two methods are invoked inside main service thread, take that into account when creating them - - * You must provide a module name (override name on your class), so we can identify the module by a "valid" name. - A valid name is like a valid python variable (do not use spaces, etc...) - * The name of the module is used as REST message destination id: - https://sampleserver:8888/[name]/.... - Remember that module names and REST path are case sensitive!!! - - ''' - name = None - service = None - - def __init__(self, service): - self.service = service - - def activate(self): - ''' - Convenient method to wrap onActivation, so we can include easyly custom common logic for activation in a future - ''' - self.onActivation() - - def deactivate(self): - ''' - Convenient method to wrap onActivation, so we can include easyly custom common logic for deactivation in a future - ''' - self.onDeactivation() - - def processMessage(self, message, params): - ''' - This method can be overriden to provide your own message proccessor, or better you can - implement a method that is called "process_" + message and this default processMessage will invoke it - * Example: - We got a message from OGAgent "Mazinger", with json params - module.processMessage("mazinger", jsonParams) - - This method will process "mazinguer", and look for a "self" method that is called "process_mazinger", and invoke it this way: - return self.process_mazinger(jsonParams) - - The methods must return data that can be serialized to json (i.e. Ojects are not serializable to json, basic type are) - ''' - try: - operation = getattr(self, 'process_' + message) - except Exception: - raise Exception('Message processor for "{}" not found'.format(message)) - - return operation(params) - - def onActivation(self): - ''' - Invoked by Service for activation. - This MUST be overridden by modules! - This method is invoked inside main thread, so if it "hangs", complete service will hang - This should be no problem, but be advised about this - ''' - pass - - def onDeactivation(self): - ''' - Invoked by Service before unloading service - This MUST be overridden by modules! - This method is invoked inside main thread, so if it "hangs", complete service will hang - This should be no problem, but be advised about this - ''' - pass - - # ************************************* - # * Helper, convenient helper methods * - # ************************************* - def sendServerMessage(self, message, data): - ''' - Sends a message to connected ipc clients - By convenience, it uses the "current" moduel name as destination module name also. - If you need to send a message to a different module, you can use self.service.sendClientMessage(module, message, data) instead - og this helmer - ''' - self.service.ipc.sendMessage(self.name, message, data) - \ No newline at end of file diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/server_worker.py b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/server_worker.py deleted file mode 100644 index 141d657..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/OGAgent/opengnsys/workers/server_worker.py +++ /dev/null @@ -1,186 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2014 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -# pylint: disable=unused-wildcard-import,wildcard-import -from __future__ import unicode_literals - -class ServerWorker(object): - ''' - A ServerWorker is a server module that "works" for service - Most method are invoked inside their own thread, except onActivation & onDeactivation. - This two methods are invoked inside main service thread, take that into account when creating them - - * You must provide a module name (override name on your class), so we can identify the module by a "valid" name. - A valid name is like a valid python variable (do not use spaces, etc...) - * The name of the module is used as REST message destination id: - https://sampleserver:8888/[name]/.... - Remember that module names and REST path are case sensitive!!! - - ''' - name = None - service = None - locked = False - - def __init__(self, service): - self.service = service - - def activate(self): - ''' - Convenient method to wrap onActivation, so we can include easyly custom common logic for activation in a future - ''' - self.onActivation() - - def deactivate(self): - ''' - Convenient method to wrap onActivation, so we can include easyly custom common logic for deactivation in a future - ''' - self.onDeactivation() - - def process(self, getParams, postParams, server): - ''' - This method is invoked on a message received with an empty path (that means a message with only the module name, like in "http://example.com/Sample" - Override it if you expect messages with that pattern - - Overriden method must return data that can be serialized to json (i.e. Ojects are not serializable to json, basic type are) - ''' - raise NotImplementedError('Generic message processor is not supported') - - def processServerMessage(self, path, getParams, postParams, server): - ''' - This method can be overriden to provide your own message proccessor, or better you can - implement a method that is called exactly as "process_" + path[0] (module name has been removed from path array) and this default processMessage will invoke it - * Example: - Imagine this invocation url (no matter if GET or POST): http://example.com:9999/Sample/mazinger/Z - The HTTP Server will remove "Sample" from path, parse arguments and invoke this method as this: - module.processMessage(["mazinger","Z"], getParams, postParams) - - This method will process "mazinguer", and look for a "self" method that is called "process_mazinger", and invoke it this way: - return self.process_mazinger(["Z"], getParams, postParams) - - In the case path is empty (that is, the path is composed only by the module name, like in "http://example.com/Sample", the "process" method - will be invoked directly - - The methods must return data that can be serialized to json (i.e. Ojects are not serializable to json, basic type are) - ''' - if self.locked is True: - raise Exception('system is busy') - - if len(path) == 0: - return self.process(getParams, postParams, server) - try: - operation = getattr(self, 'process_' + path[0]) - except Exception: - raise Exception('Message processor for "{}" not found'.format(path[0])) - - return operation(path[1:], getParams, postParams, server) - - - def processClientMessage(self, message, data): - ''' - Invoked by Service when a client message is received (A message from user space Agent) - - This method can be overriden to provide your own message proccessor, or better you can - implement a method that is called exactly "process_client_" + message (module name has been removed from path) and this default processMessage will invoke it - * Example: - We got a message from OGAgent "Mazinger", with json params - module.processClientMessage("mazinger", jsonParams) - - This method will process "mazinguer", and look for a "self" method that is called "process_client_mazinger", and invoke it this way: - self.process_client_mazinger(jsonParams) - - The methods returns nothing (client communications are done asynchronously) - ''' - try: - operation = getattr(self, 'process_client_' + message) - except Exception: - raise Exception('Message processor for "{}" not found'.format(message)) - - operation(data) - - # raise NotImplementedError('Got a client message but no proccessor is implemented') - - - def onActivation(self): - ''' - Invoked by Service for activation. - This MUST be overridden by modules! - This method is invoked inside main thread, so if it "hangs", complete service will hang - This should be no problem, but be advised about this - ''' - pass - - def onDeactivation(self): - ''' - Invoked by Service before unloading service - This MUST be overridden by modules! - This method is invoked inside main thread, so if it "hangs", complete service will hang - This should be no problem, but be advised about this - ''' - pass - - - def onLogin(self, user): - ''' - Invoked by Service when an user login is detected - This CAN be overridden by modules - This method is invoked whenever the client (user space agent) notifies the server (Service) that a user has logged in. - This method is run on its own thread - ''' - pass - - def onLogout(self, user): - ''' - Invoked by Service when an user login is detected - This CAN be overridden by modules - This method is invoked whenever the client (user space agent) notifies the server (Service) that a user has logged in. - This method is run on its own thread - ''' - pass - - # ************************************* - # * Helper, convenient helper methods * - # ************************************* - def sendClientMessage(self, message, data): - ''' - Sends a message to connected ipc clients - By convenience, it uses the "current" moduel name as destination module name also. - If you need to send a message to a different module, you can use self.service.sendClientMessage(module, message, data) instead - og this helmer - ''' - self.service.sendClientMessage(self.name, message, data) - - def sendScriptMessage(self, script): - self.service.sendScriptMessage(script) - - def sendLogoffMessage(self): - self.service.sendLogoffMessage() - - def sendPopupMessage(self): - self.service.sendPopupMessage() diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/changelog.Debian.gz b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/changelog.Debian.gz deleted file mode 100644 index 3de583a4b48f59370190c92ee0e98b42de3fa4ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 214 zcmV;{04e_;iwFP!000021FeoRP6IIzMf*L)FA1>L?5-jO2#8dxAfg~{;Do_gTF*pl znN65u++B7t1elf*=Ur(KNEU8vE_;1ElXUI_9QgcVsmL|v&lidvbfIODs_V5_^ z!{<;{z%|zW;!NXHN2q%}FKjjJZ1M%c-ls3DcPzccLqvx?Z?Je<7PhYOZo}y)SQ+%E zW8fdmM;la}mj5|N!|@4NsobKO;Ux=-)C^5`PgH^%YKf{!FZ>%*?l0}7_~h+?kq5Ss Q&3_900xCgIkx>Bv0QLc6n*aa+ diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/copyright b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/copyright deleted file mode 100644 index 7b6ef31..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/copyright +++ /dev/null @@ -1,26 +0,0 @@ -Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 -Name: ogagent -Maintainer: Ramón M. Gómez -Source: https://opengnsys.es - -Copyright: 2014 Virtual Cable S.L.U. -License: BSD-3-clause - -License: GPL-2+ -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., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -. -On Debian systems, the full text of the GNU General Public -License version 2 can be found in the file -`/usr/share/common-licenses/GPL-2'. diff --git a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/readme.txt b/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/readme.txt deleted file mode 100644 index a2771de..0000000 --- a/native/Sources/Clients/ogagent/oglive/debian/ogagent-oglive/usr/share/doc/ogagent-oglive/readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -OGAgent is the agent intended for OpengGnsys interaction. - -Please, visit https://opengnsys.es for more information diff --git a/native/Sources/Includes/Database.cpp b/native/Sources/Includes/Database.cpp deleted file mode 100644 index 84add9c..0000000 --- a/native/Sources/Includes/Database.cpp +++ /dev/null @@ -1,203 +0,0 @@ -// ******************************************************************************************************** -// Nombre del fichero: Database.cpp -// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla -// Fecha Creación: Marzo-2010 -// Fecha Última modificación: Marzo-2010 -// Descripción: -// Fichero de implementación de la clase Database para funciones de manipulación -// de bases de datos sobre un Servidor Mysql -// ******************************************************************************************************** -#include "Database.h" -// __________________________________________________________________________ -void ErrorHandler(Herror hr, char* ErrStr) -{ - sprintf(ErrStr,"Error:\n"); - sprintf(ErrStr,"%sCode = %d\n",ErrStr ,hr.nError); - sprintf(ErrStr,"%sDescription = %s",ErrStr, (char*) hr.dError); -} -// __________________________________________________________________________ -Database::Database() -{ - m_Cnn=NULL; - sprintf(m_ErrStr,"NULL POINTER"); -} -// __________________________________________________________________________ -void Database::GetErrorErrStr(char* ErrStr) -{ - sprintf(ErrStr,"%s",m_ErrStr); -} -// __________________________________________________________________________ -void Table::GetErrorErrStr(char* ErrStr) -{ - sprintf(ErrStr,"%s",m_ErrStr); -} -// __________________________________________________________________________ -bool Database::Open(char* UserName, char* Pwd,char* server,char*Bd) -{ - Herror hr; - m_Cnn=mysql_init(NULL); - if(m_Cnn==NULL){ - hr.nError=0; - strcpy(hr.dError,"Error en la Creación del objeto MYSQL"); - ErrorHandler(hr,m_ErrStr); - return(false); // Fallo de inicializaci� - } - - if(!mysql_real_connect(m_Cnn, server,UserName,Pwd,Bd, MYSQL_PORT,NULL,0)){ - mysql_error(m_Cnn); - hr.nError=mysql_errno(m_Cnn); - strcpy(hr.dError,mysql_error(m_Cnn)); - ErrorHandler(hr,m_ErrStr); - return(false); // Fallo de conexi� - } - hr.nError=0; - strcpy(hr.dError,"Success"); - ErrorHandler(hr,m_ErrStr); - return (true); -} -// __________________________________________________________________________ -bool Database::Close() -{ - mysql_close(m_Cnn); - return(true); -} -// __________________________________________________________________________ -bool Database::Execute(char* CmdStr) -{ - Herror hr; - if (mysql_query(m_Cnn,CmdStr)){ // Ejecuta la consulta - mysql_error(m_Cnn); - hr.nError=mysql_errno(m_Cnn); - strcpy(hr.dError,mysql_error(m_Cnn)); - ErrorHandler(hr,m_ErrStr); - mysql_close(m_Cnn); - return(false); // Fallo de conexión - } - hr.nError=0; - strcpy(hr.dError,"Success"); - ErrorHandler(hr,m_ErrStr); - return (true); -} -// __________________________________________________________________________ -bool Database::Execute(char* CmdStr, Table& Tbl) -{ - Herror hr; - if (mysql_query(m_Cnn,CmdStr)) { // Ejecuta la consulta - mysql_error(m_Cnn); - hr.nError=mysql_errno(m_Cnn); - strcpy(hr.dError,mysql_error(m_Cnn)); - ErrorHandler(hr,m_ErrStr); - mysql_close(m_Cnn); - return(false); // Fallo de conexi� - } - - hr.nError=0; - strcpy(hr.dError,"Success"); - ErrorHandler(hr,m_ErrStr); - - Tbl.m_Rec = mysql_store_result(m_Cnn) ; // Toma el recordset - if(Tbl.m_Rec){ - Tbl.row=mysql_fetch_row(Tbl.m_Rec); - Tbl.fields = mysql_fetch_fields(Tbl.m_Rec); - Tbl.num_fields = mysql_num_fields(Tbl.m_Rec); - Tbl.numreg=mysql_num_rows(Tbl.m_Rec); - Tbl.eof=Tbl.numreg==0; // Consulta vacia - } - return (true); -} -// __________________________________________________________________________ -void Database::liberaResult (Table& Tbl) { - //Free resources after mysql_store_result - mysql_free_result(Tbl.m_Rec); -} -// __________________________________________________________________________ -Table::Table() -{ - m_Rec=NULL; -} -// __________________________________________________________________________ -bool Table::ISEOF() -{ - return(eof); -} -// __________________________________________________________________________ -bool Table::Get(const char* FieldName, char *FieldValue) -{ - char * aux; - aux=tomadato(FieldName); - if(aux) - strcpy(FieldValue,aux); - else - strcpy(FieldValue,""); - return(true); -} -// __________________________________________________________________________ -bool Table::Get(const char* FieldName,int &FieldValue) -{ - char *aux; - aux=tomadato(FieldName); - if(aux) - FieldValue=atoi(aux); - else - FieldValue=0; - return(true); -} -// __________________________________________________________________________ -bool Table::Get(const char* FieldName,char &FieldValue) -{ - char *aux; - aux=tomadato(FieldName); - FieldValue=aux[0]; - return(true); -} -// __________________________________________________________________________ -char* Table::tomadato(const char* FieldName) -{ - Herror hr; - unsigned int i; - - for(i = 0; i < num_fields; i++){ - if(strcmp((char*)fields[i].name,FieldName)==0){ - sprintf(m_ErrStr,"Success"); - return((char*)row[i]); - } - } - hr.nError=-1; - strcpy(hr.dError,"El nombre del campo no existe"); - ErrorHandler(hr,m_ErrStr); - return(NULL); // No existe el nombre del campo en la tabla -} -// __________________________________________________________________________ - -bool Table::MoveNext() -{ - eof=false; - row=mysql_fetch_row(m_Rec); - if(row==NULL){ - if(!mysql_eof(m_Rec)) - return(false); // Fallo de lectura - else - eof=true; // Fin de fichero - } - return (true); -} -// __________________________________________________________________________ -bool Table::MoveFirst() -{ - my_ulonglong auxnumreg; - - auxnumreg=0; - mysql_data_seek(m_Rec,auxnumreg); - return (MoveNext()); -} -// __________________________________________________________________________ -bool Table::MoveLast() -{ - my_ulonglong auxnumreg; - auxnumreg=numreg; - auxnumreg--; - if(auxnumreg<0) auxnumreg=0; // Principio de fichero - mysql_data_seek(m_Rec,auxnumreg); - return (MoveNext()); - return (true); -} diff --git a/native/Sources/Includes/Database.h b/native/Sources/Includes/Database.h deleted file mode 100644 index ae318bd..0000000 --- a/native/Sources/Includes/Database.h +++ /dev/null @@ -1,61 +0,0 @@ -// ****************************************************************************************************** -// Aplicación HIDRA -// Copyright 2004 Jos�Manuel Alonso. Todos los derechos reservados. -// Fichero: Database.h -// Descripción: -// Fichero de cabecera de la clase Database para implementar funciones de manipulaci� -// de bases de datos sobre un Servidor Mysql -// ****************************************************************************************************** -#include -#include -#include -#include -// __________________________________________________________________________ -class Database; -class Table; -// __________________________________________________________________________ -class Database -{ -public: - MYSQL *m_Cnn; - char m_ErrStr[500]; - Database(); - bool Open(char* UserName, char* Pwd,char* server,char*Database); - bool OpenTbl(int Mode, char* CmdStr, Table& Tbl); - bool Close(void); - bool Execute(char* CmdStr); - bool Execute(char* CmdStr, Table& Tbl); - void liberaResult(Table& Tbl); - void GetErrorErrStr(char* ErrStr); -}; -// __________________________________________________________________________ -class Table{ - char* tomadato(const char* FieldName); -public: - bool eof,bof; - MYSQL_RES * m_Rec ; - MYSQL_FIELD *fields; - unsigned int num_fields; - MYSQL_ROW row ; - MYSQL_ROW_OFFSET ptr; - my_ulonglong numreg; - char m_ErrStr[500]; - Table(); - void GetErrorErrStr(char* ErrStr); - bool ISEOF(); - bool MoveNext(); - bool MovePrevious(); - bool MoveFirst(); - bool MoveLast(); - - bool Get(const char* FieldName, char* FieldValue); - bool Get(const char* FieldName,int &FieldValue); - bool Get(const char* FieldName,char &FieldValue); -}; -// __________________________________________________________________________ -class Herror -{ -public: - int nError; // C�igo del error - char dError[500]; // Descripción del error -}; diff --git a/native/Sources/Includes/Database.o b/native/Sources/Includes/Database.o deleted file mode 100644 index aa88a5778e924e7938bd4aa3c7f503492dcef433..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33312 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 KStpsZtpA^wM;@X8 diff --git a/native/Sources/Includes/ogAdmLib.c b/native/Sources/Includes/ogAdmLib.c deleted file mode 100644 index 2f0a714..0000000 --- a/native/Sources/Includes/ogAdmLib.c +++ /dev/null @@ -1,1074 +0,0 @@ -// ************************************************************************************************************************************************** -// Libreria: ogAdmLib -// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla -// Fecha Creación: Marzo-2010 -// Fecha Última modificación: Marzo-2010 -// Nombre del fichero: ogAdmLib.c -// Descripción: Este fichero implementa una libreria de funciones para uso común de los servicios -// ************************************************************************************************************************************************** -// ________________________________________________________________________________________________________ -// Función: tomaHora -// -// Descripción: -// Devuelve la hora del sistema -// Parametros: -// Ninguno -// ________________________________________________________________________________________________________ -struct tm * tomaHora() -{ - time_t rawtime; - time ( &rawtime ); - return(localtime(&rawtime)); -} -// ________________________________________________________________________________________________________ -// Función: registraLog -// -// Descripción: -// Registra los eventos en un fichero log ya sean errores o información -// Parametros: -// - fileLog : Ruta completa del archivo de log -// - msg : Descripción del error -// - swe: Switch que indica si se debe recuperar además el literal de error del propio sistema operativo -// ________________________________________________________________________________________________________ -void registraLog(const char* filelog,const char *msg,int swe) -{ - FILE *flog; - struct tm * t; - - t = tomaHora(); - flog=fopen(filelog,"at"); - if(swe) - fprintf (flog,"%02d/%02d/%d %02d:%02d %s: %s\n",t->tm_mday,t->tm_mon+1,t->tm_year+1900,t->tm_hour,t->tm_min,msg,strerror(errno)); - else - fprintf (flog,"%02d/%02d/%d %02d:%02d %s\n",t->tm_mday,t->tm_mon+1,t->tm_year+1900,t->tm_hour,t->tm_min,msg); - fclose(flog); -} -// ________________________________________________________________________________________________________ -// Función: errorLog -// -// Descripción: -// Registra los sucesos de errores preestablecidos en el fichero de log -// Parametros: -// - coderr : Código del mensaje de error -// - swe: Switch que indica si se debe recuperar además el literal de error del propio sistema operativo -// ________________________________________________________________________________________________________ -void errorLog(const char *modulo, int coderr, int swe) { - char msglog[LONSUC]; - - sprintf(msglog, "*** Error: %s. Módulo %s", tbErrores[coderr], modulo); - registraLog(szPathFileLog, msglog, swe); -} -// ________________________________________________________________________________________________________ -// Función: errorInfo -// -// Descripción: -// Registra los sucesos de errores dinámicos en el fichero de log -// Parametros: -// - msgerr : Descripción del error -// - swe: Switch que indica si se debe recuperar además el literal de error del propio sistema operativo -// ________________________________________________________________________________________________________ -void errorInfo(const char *modulo, char *msgerr) { - char msglog[LONSUC]; - - sprintf(msglog, "*** Error: %s. Módulo %s", msgerr, modulo); - registraLog(szPathFileLog, msglog, FALSE); -} -// ________________________________________________________________________________________________________ -// Función: infoLog -// -// Descripción: -// Registra los sucesos de información en el fichero de log -// Parametros: -// - coderr : Código del mensaje de información -// ________________________________________________________________________________________________________ -void infoLog(int codinf) { - char msglog[LONSUC]; - - sprintf(msglog, "*** Info: %s", tbMensajes[codinf]); - registraLog(szPathFileLog, msglog, FALSE); -} -// ________________________________________________________________________________________________________ -// Función: infoDebug -// -// Descripción: -// Registra los mensajes de debugs en el fichero de log -// Parametros: -// - msgdeb : Descripción del mensaje de información -// ________________________________________________________________________________________________________ -void infoDebug(char* msgdeb) { - char msglog[LONSUC+15]; // Cadena de registro (reserva caracteres para el prefijo). - - sprintf(msglog, "*** Debug: %d-%s", ndebug, msgdeb); - registraLog(szPathFileLog, msglog, FALSE); -} -//______________________________________________________________________________________________________ -// Función: ValidacionParametros -// -// Descripción: -// Valida que los parametros de ejecución del programa sean correctos -// Parámetros: -// - argc: Número de argumentos -// - argv: Puntero a cada argumento -// - eje: Tipo de ejecutable (1=Servicio,2=Repositorio o 3=Cliente) -// Devuelve: -// - TRUE si los argumentos pasados son correctos -// - FALSE en caso contrario -// Especificaciones: -// La sintaxis de los argumentos es la siguiente -// -f Archivo de configuración del servicio -// -l Archivo de logs -// -d Nivel de debuger (mensages que se escribirán en el archivo de logs) -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -//______________________________________________________________________________________________________ -BOOLEAN validacionParametros(int argc, char*argv[],int eje) { - int i; - char modulo[] = "validacionParametros()"; - - switch(eje){ - case 1: // Administrador - strcpy(szPathFileCfg, "ogAdmServer.cfg"); // Valores por defecto de archivos - strcpy(szPathFileLog, "ogAdmServer.log"); // de configuración y de logs - break; - case 2: // Repositorio - strcpy(szPathFileCfg, "ogAdmRepo.cfg"); // Valores por defecto de archivos - strcpy(szPathFileLog, "ogAdmRepo.log"); // de configuración y de logs - break; - case 3: // Cliente OpenGnsys - strcpy(szPathFileCfg, "ogAdmClient.cfg"); // Valores por defecto de archivos - strcpy(szPathFileLog, "ogAdmClient.log"); // de configuración y de logs - break; - case 4: // Servicios DHCP,BOOTP Y TFTP - strcpy(szPathFileCfg, "ogAdmBoot.cfg"); // Valores por defecto de archivos - strcpy(szPathFileLog, "ogAdmBoot.log"); // de configuración y de logs - break; - case 5: // Agente - strcpy(szPathFileCfg, "ogAdmAgent.cfg"); // Valores por defecto de archivos - strcpy(szPathFileLog, "ogAdmAgent.log"); // de configuración y de logs - break; - case 6: // Agente - strcpy(szPathFileCfg, "ogAdmWinClient.cfg"); // Valores por defecto de archivos - strcpy(szPathFileLog, "ogAdmWinClient.log"); // de configuración y de logs - break; - case 7: // Agente - strcpy(szPathFileCfg, "ogAdmnxClient.cfg"); // Valores por defecto de archivos - strcpy(szPathFileLog, "ogAdmLnxClient.log"); // de configuración y de logs - break; - } - - ndebug = 1; // Nivel de debuger por defecto - - for (i = 1; (i + 1) < argc; i += 2) { - if (argv[i][0] == '-') { - switch (tolower(argv[i][1])) { - case 'f': - if (argv[i + 1] != NULL) - strcpy(szPathFileCfg, argv[i + 1]); - else { - errorLog(modulo, 10, FALSE); - return (FALSE); - } - break; - case 'l': - if (argv[i + 1] != NULL) - strcpy(szPathFileLog, argv[i + 1]); - else { - errorLog(modulo, 11, FALSE); - return (FALSE); - } - break; - case 'd': - if (argv[i + 1] != NULL) { - ndebug = atoi(argv[i + 1]); - if (ndebug < 1) - ndebug = 1; // Por defecto el nivel de debug es 1 - } else - ndebug = 1; // Por defecto el nivel de debug es 1 - break; - default: - errorLog(modulo, 12, FALSE); - exit(EXIT_FAILURE); - break; - } - } - } - return (TRUE); -} -//______________________________________________________________________________________________________ -// Función: reservaMemoria -// -// Descripción: -// Reserva memoria para una variable -// Parámetros: -// - lon: Longitud en bytes de la reserva -// Devuelve: -// Un puntero a la zona de memoria reservada que ha sido previamente rellena con zeros o nulos -//______________________________________________________________________________________________________ -char* reservaMemoria(int lon) -{ - char *mem; - - mem=(char*)malloc(lon); - if(mem!=NULL) - memset(mem,0,lon); - return(mem); -} -//______________________________________________________________________________________________________ -// Función: ampliaMemoria -// -// Descripción: -// Amplia memoria para una variable -// Parámetros: -// - ptr: Puntero al buffer de memoria que se quiere ampliar -// - lon: Longitud en bytes de la amplicación -// Devuelve: -// Un puntero a la zona de memoria reservada que ha sido previamente rellena con zeros o nulos -//______________________________________________________________________________________________________ -char* ampliaMemoria(char* ptr,int lon) -{ - char *mem; - - mem=(char*)realloc(ptr,lon*sizeof(char*)); - if(mem!=NULL) - return(mem); - return(NULL); -} -//______________________________________________________________________________________________________ -// Función: liberaMemoria -// -// Descripción: -// Libera memoria para una variable -// Parámetros: -// - ptr: Puntero al buffer de memoria que se quiere liberar -// Devuelve: -// Nada -//______________________________________________________________________________________________________ -void liberaMemoria(void* ptr) -{ - if(ptr){ - free (ptr); - } -} -// ________________________________________________________________________________________________________ -// Función: splitCadena -// -// Descripción: -// Trocea una cadena según un carácter delimitador -// Parámetros: -// - trozos: Array de punteros a cadenas -// - cadena: Cadena a trocear -// - chd: Carácter delimitador -// Devuelve: -// Número de trozos en que se divide la cadena -// ________________________________________________________________________________________________________ -int splitCadena(char **trozos,char *cadena, char chd) -{ - int w=0; - if(cadena==NULL) return(w); - - trozos[w++]=cadena; - while(*cadena!='\0'){ - if(*cadena==chd){ - *cadena='\0'; - if(*(cadena+1)!='\0') - trozos[w++]=cadena+1; - } - cadena++; - } - return(w); // Devuelve el número de trozos -} -// ________________________________________________________________________________________________________ -// Función: sustituir -// -// Descripción: -// Sustituye las apariciones de un caracter por otro en una cadena -// Parámetros: -// - cadena: Cadena a convertir -// - cho: Caracter a sustituir -// - chs: Caracter sustituto -// ________________________________________________________________________________________________________ -void sustituir(char *cadena,char cho,char chs) -{ - int x=0; - - while(cadena[x]!=0) { - if (cadena[x]==cho) - cadena[x]=chs; - x++; - } -} -// ________________________________________________________________________________________________________ -// Función: escaparCadena -// -// Descripción: -// Sustituye las apariciones de un caracter comila simple ' por \' -// Parámetros: -// - cadena: Cadena a escapar -// Devuelve: -// La cadena con las comillas simples sustituidas por \' -// ________________________________________________________________________________________________________ -char* escaparCadena(char *cadena) -{ - int b,c; - char *buffer; - - buffer = (char*) reservaMemoria(strlen(cadena)*2); // Toma memoria para el buffer de conversión - if (buffer == NULL) { // No hay memoria suficiente para el buffer - return (FALSE); - } - - c=b=0; - while(cadena[c]!=0) { - if (cadena[c]=='\''){ - buffer[b++]='\\'; - buffer[b++]='\''; - } - else{ - buffer[b++]=cadena[c]; - } - c++; - } - return(buffer); -} -// ________________________________________________________________________________________________________ -// Función: StrToUpper -// -// Descripción: -// Convierta una cadena en mayúsculas -// Parámetros: -// - cadena: Cadena a convertir -// ________________________________________________________________________________________________________ -char* StrToUpper(char *cadena) -{ - int x=0; - - while(cadena[x]!=0) { - if (cadena[x] >= 'a' && cadena[x] <= 'z') { - cadena[x] -= 32; - } - x++; - } - return(cadena); -} -// ________________________________________________________________________________________________________ -// Función: StrToUpper -// -// Descripción: -// Convierta una cadena en mayúsculas -// Parámetros: -// - cadena: Cadena a convertir -// ________________________________________________________________________________________________________ -char* StrToLower(char *cadena) -{ - int x=0; - - while(cadena[x]!=0) { - if (cadena[x] >= 'A' && cadena[x] <= 'Z') { - cadena[x] += 32; - } - x++; - } - return(cadena); -} -// ________________________________________________________________________________________________________ -// Función: INTROaFINCAD -// -// Descripción: -// Cambia caracteres INTROS por fin de cadena ('\0') en una trama -// Parametros: -// - parametros: Puntero a los parametros de la trama -// - lon: Longitud de la cadena de parametros -// ________________________________________________________________________________________________________ -void INTROaFINCAD(TRAMA* ptrTrama) -{ - char *i,*a,*b; - - a=ptrTrama->parametros; - b=a+ptrTrama->lonprm; - for(i=a;iparametros; - b=a+ptrTrama->lonprm; - for(i=a;iparametros; - b=a+ptrTrama->lonprm; - for(pos=a;pos=0;i--){ - if(cadena[i]<32) - cadena[i]='\0'; - else - return(cadena); - } - return(cadena); -} -// ________________________________________________________________________________________________________ -// Función: mandaTrama -// -// Descripción: -// Envía una trama por la red -// Parametros: -// - sock : El socket del host al que se dirige la trama -// - trama: El contenido de la trama -// - lon: Longitud de la parte de parametros de la trama que se va a mandar -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -BOOLEAN mandaTrama(SOCKET *sock, TRAMA* ptrTrama) -{ - int lonprm; - char *buffer,hlonprm[LONHEXPRM+1]; - BOOLEAN res; - - lonprm=strlen(ptrTrama->parametros); - sprintf(hlonprm,"%05X",LONGITUD_CABECERATRAMA+LONHEXPRM+lonprm); // Convierte en hexadecimal la longitud - - buffer=reservaMemoria(LONGITUD_CABECERATRAMA+LONHEXPRM+lonprm); // Longitud total de la trama - if(buffer==NULL) - return(FALSE); - memcpy(buffer,ptrTrama,LONGITUD_CABECERATRAMA); // Copia cabecera de trama - memcpy(&buffer[LONGITUD_CABECERATRAMA],hlonprm,LONHEXPRM); // Copia longitud de la trama - memcpy(&buffer[LONGITUD_CABECERATRAMA+LONHEXPRM],ptrTrama->parametros,lonprm); - res=sendData(sock,buffer,LONGITUD_CABECERATRAMA+LONHEXPRM+lonprm); - liberaMemoria(buffer); - return (res); -} -// ________________________________________________________________________________________________________ -// Función: sendData -// -// Descripción: -// Envía datos por la red a través de un socket -// Parametros: -// - sock : El socket por donde se envía -// - datos: El contenido a enviar -// - lon: Cantidad de bites a enviar -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -BOOLEAN sendData(SOCKET *sock, char* datos,int lon) -{ - int idx,ret; - idx = 0; - while (lon > 0) { - ret = send(*sock,&datos[idx],lon, 0); - if (ret == 0) { // Conexión cerrada por parte del cliente (Graceful close) - break; - } - else{ - if (ret == SOCKET_ERROR) - return (FALSE); - } - lon -= ret; - idx += ret; - } - return (TRUE); -} -// ________________________________________________________________________________________________________ -// Función: recibeTrama -// -// Descripción: -// Recibe una trama por la red -// Parametros: -// - sock : El socket del cliente -// - trama: El buffer para recibir la trama -// Devuelve: -// Un puntero a una estrucutra TRAMA o NULL si ha habido algún error -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -TRAMA* recibeTrama(SOCKET *sock) -{ - int ret,lon,lSize; - char *buffer,*bufferd,bloque[LONBLK],*hlonprm; - TRAMA * ptrTrama; - - lon=lSize=0; - do{ - if(!recData(sock,bloque,LONBLK,&ret)) // Lee bloque - return(NULL); - - if (lon==0 && lSize==0 && ret==0) // Comprueba trama válida - return(NULL); - - if(lSize==0){ // Comprueba tipo de trama y longitud total de los parámetros - if (strncmp(bloque, "@JMMLCAMDJ_MCDJ",15)!=0) - return(NULL); // No se reconoce la trama - hlonprm=reservaMemoria(LONHEXPRM+1); - if(!hlonprm) return(NULL); - memcpy(hlonprm,&bloque[LONGITUD_CABECERATRAMA],LONHEXPRM); - lSize=strtol(hlonprm,NULL,16); // Longitud total de la trama con los parametros encriptados - liberaMemoria(hlonprm); - buffer=(char*)reservaMemoria(lSize); // Toma memoria para la trama completa - if(!buffer) - return(NULL); - } - - if(ret>0){ // Datos recibidos - memcpy(&buffer[lon],bloque,ret); // Añade bloque - lon+=ret; - } - }while(lonparametros,bufferd,lon); - liberaMemoria((char*)buffer); - ptrTrama->lonprm=lon; // Almacena longitud de los parámetros ya desencriptados - return(ptrTrama); -} -// ________________________________________________________________________________________________________ -// Función: recData -// -// Descripción: -// Recibe datos por la red a través de un socket -// Parametros: -// - sock : El socket por el que se reciben los datos -// - datos: El buffer donde se almacenan -// - lon: Cantidad máxima de bites a recibir -// - ret: Cantidad de bites recibidos (Parámetro de salida) -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -BOOLEAN recData(SOCKET *sock, char* buffer,int lon,int* ret) -{ - *ret = 0; - - while (TRUE) { // Bucle para recibir datos del cliente - *ret = recv(*sock,buffer, lon, 0); - if (*ret == 0) // Conexión cerrada por parte del cliente (Graceful close) - break; - else { - if (*ret == SOCKET_ERROR) { - return (FALSE); - } else - // Datos recibidos - break; - } - } - return(TRUE); -} -//______________________________________________________________________________________________________ -// Función: enviaFlag -// -// Descripción: -// Envia una señal de sincronización -// Parámetros: -// - socket_c: (Salida) Socket utilizado para el envío (operativo) -// - ptrTrama: contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -BOOLEAN enviaFlag(SOCKET *socket_c,TRAMA *ptrTrama) -{ - char modulo[] = "enviaFlag()"; - if (!mandaTrama(socket_c,ptrTrama)) { - errorLog(modulo,26,FALSE); - return (FALSE); - } - return(TRUE); -} -//______________________________________________________________________________________________________ -// Función: recibeFlag -// -// Descripción: -// Recibe una señal de sincronización -// Parámetros: -// - socket_c: Socket utilizadopara la recepción (operativo) -// - ptrTrama: (Salida) Contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -BOOLEAN recibeFlag(SOCKET *socket_c,TRAMA *ptrTrama) -{ - ptrTrama=recibeTrama(socket_c); - if(!ptrTrama){ - return(FALSE); - } - return(TRUE); -} -//______________________________________________________________________________________________________ -// Función: URLEncode -// -// Descripción: -// Codifica una cadena en UrlEncode -// Parámetros: -// - src: La cadena a decodificar -// Devuelve: -// La cadena decodificada -// ________________________________________________________________________________________________________ -char* URLEncode(char *src) -{ - char *dest; - int i,j=0,lon; - - lon=strlen(src); - dest=(char*)reservaMemoria(lon*2); // Reserva buffer para la cadena - for(i=0;i0) - fwrite(buffer,1,lon,f); // Lee el contenido del fichero - }while(lon>0); // Bucle para recibir datos del cliente - fclose(f); - return(TRUE); -} -//______________________________________________________________________________________________________ -// Función: initParammetros -// -// Descripción: -// Libera memoria del buffer de los parametros de la trama y vuelve a reservar espacio -// Parámetros: -// - parametros : Puntero a la zona donde están los parametros de una trama -// - lon : Tamaño de la nueva reserva de espacio para los parametros -// Devuelve: -// Un puntero a la nueva zona de memoria o NULL si ha habido algún error -// Especificaciones: -// En caso de que el parámetro lon valga cero el tamaño a reservar será el estandar -//______________________________________________________________________________________________________ -BOOLEAN initParametros(TRAMA* ptrTrama,int lon) -{ - if(lon==0) lon=LONGITUD_PARAMETROS; - ptrTrama->parametros=(char*)ampliaMemoria(ptrTrama->parametros,lon); - if(!ptrTrama->parametros) - return(FALSE); - else - return(TRUE); -} -//______________________________________________________________________________________________________ -// Función: TCPConnect -// -// Descripción: -// Crea un socket y lo conecta a un servidor -// Parámetros: -// - ips : La Dirección IP del servidor -// - port : Puerto para la comunicación -// Devuelve: -// Un socket para comunicaciones por protocolo TCP -//______________________________________________________________________________________________________ -SOCKET TCPConnect(char *ips,char* port) -{ - SOCKET s; - struct sockaddr_in server; - char modulo[] = "TCPConnect()"; - - s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (s == INVALID_SOCKET){ - return (INVALID_SOCKET); - } - server.sin_family = AF_INET; - server.sin_port = htons((short)atoi(port)); - server.sin_addr.s_addr = inet_addr(ips); - - if (connect(s, (struct sockaddr *)&server, sizeof(server)) == INVALID_SOCKET){ - errorLog(modulo,38,TRUE); - return (INVALID_SOCKET); - } - - return(s); -} -//______________________________________________________________________________________________________ -// Función: AbreConexion -// -// Descripción: -// Abre la conexión entre el cliente y el servidor de administración -// Parámetros: -// - Ninguno -// Devuelve: -// Un socket de cliente para comunicaciones -//______________________________________________________________________________________________________ -SOCKET abreConexion(void) -{ - int swloop=0; - SOCKET s; - - while(swlooparroba='@'; // Cabecera de la trama - strncpy(ptrTrama->identificador,"JMMLCAMDJ_MCDJ",14); // identificador de la trama - ptrTrama->tipo=tipo; // Tipo de mensaje - - if (!mandaTrama(socket_c,ptrTrama)) { - errorLog(modulo,26,FALSE); - return (FALSE); - } - return(TRUE); -} -//______________________________________________________________________________________________________ -// Función: recibeMensaje -// -// Descripción: -// Recibe un mensaje del servidor de Administración -// Parámetros: -// - socket_c: Socket utilizadopara la recepción -// - ptrTrama: (Salida) Contenido del mensaje -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -TRAMA* recibeMensaje(SOCKET *socket_c) -{ - TRAMA* ptrTrama; - char modulo[] = "recibeMensaje()"; - - ptrTrama=recibeTrama(socket_c); - if(!ptrTrama){ - errorLog(modulo,17,FALSE); - return(NULL); - } - return(ptrTrama); -} - -// ________________________________________________________________________________________________________ diff --git a/native/Sources/Includes/ogAdmLib.h b/native/Sources/Includes/ogAdmLib.h deleted file mode 100644 index 42279a3..0000000 --- a/native/Sources/Includes/ogAdmLib.h +++ /dev/null @@ -1,319 +0,0 @@ -// ************************************************************************************************************************************************** -// Libreria: ogAdmLib -// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla -// Fecha Creación: Marzo-2010 -// Fecha Última modificación: Marzo-2010 -// Nombre del fichero: ogAdmLib.h -// Descripción: Este fichero implementa el archivo de cabecera de la libreria ogAdmLib -// ************************************************************************************************************************************************** -// ________________________________________________________________________________________________________ -// Valores definidos -// ________________________________________________________________________________________________________ -#define LONSTD 1024 // Longitud de memoria estandar -#define LONINT 16 // Longitud de memoria estandar para un número entero -#define LONFIL 1024 // Longitud de memoria estandar para nombres de archivo completos (incluido path) -#define LONIP 16 // Longitud de memoria estandar para cadenas que contiene una dirección IP -#define LONMAC 16 // Longitud de memoria estandar para cadenas que contiene una dirección MAC -#define LONSQL 8192 // Longitud de memoria estandar para una sentencia SQL -#define LONPRM 4098 // Longitud estandar de los parámetros del fichero de configuración del servicio -#define LONSCP 4098 // Longitud estandar de los parámetros de las tramas -#define LONFUN 512 // Longitud estandar de los nombres de las funciones que procesan las tramas -#define LONSUC 4098 // Longitud de los mensajes de sucesos -#define LONBLK 8192 // Longitud de los paquetes de tramas leidos cada vez -#define MAXPRM 20 // Máximo número de parámeros del fichero de configuración del servicio -#define MAXPAR 128 // Maximo numero de particiones manejadas por el sistema, ahora con GPT es 128 -#define MAXLONURL 1024 // Longitud máxima de una dirección url con parámetros - -#define LONHEXPRM 5 // Longitud del campo que contiene el tamaño de la cadena de parámetros -#define LONGITUD_CABECERATRAMA 16 // Longitud de la cabecera de las tramas -#define LONGITUD_PARAMETROS 8192 // Longitud estandar de la información de la trama (parámetros) -#define MAXCMD_PARAMETROS 200 // Máximo número de parámetros de una trama - -#define MAXIMOS_CLIENTES 4000 // Máximo número de conexiones con ordenadores clientes -#define MAXIMAS_FUNCIONES LONSTD // Máximo número de funciones que procesan los mensajes entre servicio y clientes -#define MAXIMAS_LINEAS 3000 // Longitud máxima de lineas en un archivo de comandos - -#define AUTOINCORPORACION_OFF 0x0000 // Los ordenadores no se pueden dar de alta automáticamente -#define AUTOINCORPORACION_ONA 0x0001 // Los ordenadores se pueden dar de alta automáticamente si existe el aula -#define AUTOINCORPORACION_ONX 0x0002 // Los ordenadores se pueden dar de alta automáticamentee y si no existe el aula la crea - -#define DEBUG_BAJO 1 // Nivel de debug bajo -#define DEBUG_MEDIO 2 // Nivel de debug medio -#define DEBUG_ALTO 3 // Nivel de debug alto -#define DEBUG_MAXIMO 4 // Nivel de debug máximo - -#define CLIENTE_OCUPADO "BSY" // Cliente ocupado -#define CLIENTE_APAGADO "OFF" // Cliente apagado -#define CLIENTE_INICIANDO "INI" // Cliente iniciando - -#define CLIENTE_OPENGNSYS "OPG" // Cliente Opengnsys - -#define CLIENTE_WIN "WIN" // Cliente Windows genérico -#define CLIENTE_WNT "WNT" // Windows NT -#define CLIENTE_W2K "W2K" // Windows 2000 -#define CLIENTE_WS2 "WS2" // Windows Server 2003 -#define CLIENTE_WXP "WXP" // Cliente Windows XP -#define CLIENTE_W95 "W95" // Windows 95 -#define CLIENTE_W98 "W98" // Windows 98 -#define CLIENTE_WML "WML" // Windows Milenium -#define CLIENTE_MS2 "MS2" // MsDos -#define CLIENTE_WVI "WVI" // Cliente Windows Vista -#define CLIENTE_WI7 "WI7" // Cliente Windows 7 - -#define CLIENTE_LNX "LNX" // Cliente Linux - -#define ACCION_SINRESULTADO 0 // Sin resultado -#define ACCION_EXITOSA 1 // Finalizada con éxito -#define ACCION_FALLIDA 2 // Finalizada con errores - -#define ACCION_INICIADA 1 // Acción activa -#define ACCION_DETENIDA 2 // Acción momentanemente parada -#define ACCION_FINALIZADA 3 // Accion finalizada - -#define EJECUCION_COMANDO 1 -#define EJECUCION_PROCEDIMIENTO 2 -#define EJECUCION_TAREA 3 -#define EJECUCION_RESERVA 4 - -#define AMBITO_CENTROS 0x01 -#define AMBITO_GRUPOSAULAS 0x02 -#define AMBITO_AULAS 0x04 -#define AMBITO_GRUPOSORDENADORES 0x08 -#define AMBITO_ORDENADORES 0x10 - -// Código de los tipos de mensajes -#define MSG_COMANDO '1' // Mensaje del tipo comando -#define MSG_NOTIFICACION '2' // Respuesta a la ejecución un comando -#define MSG_PETICION '3' // Petición de cualquier actuación -#define MSG_RESPUESTA '4' // Respuesta a una petición -#define MSG_INFORMACION '5' // Envío de cualquier información sin espera de confirmación o respuesta - -#define ANNOREF 2009 // Año de referencia base - -#define LONGITUD_SCRIPTSALIDA 131072 // Longitud máxima de la información devuelta por una función de interface -#define MAXARGS 16 // Número máximo de argumentos enviados a un scripts -#define MAXCNX 5 // Máximos intentos de conexión al servidor de Administración - -#define PUERTO_WAKEUP 9 // Puerto wake up - -#define MAXHARDWARE 128 // Máximos elementos hardware a detectar -#define MAXSOFTWARE 8096 // Máximos elementos software a detectar -// ________________________________________________________________________________________________________ -// Tipos definidos -// ________________________________________________________________________________________________________ -typedef unsigned long DWORD; -typedef unsigned short WORD; -typedef int BOOLEAN; -typedef char BYTE; -typedef int SOCKET; -typedef void* LPVOID; - -#define TRUE 1 -#define FALSE 0 - -#define SOCKET_ERROR (-1) -#define INVALID_SOCKET (SOCKET)(~0) - -#define LEER 0 -#define ESCRIBIR 1 - -#define CHARNULL '\0' - -// ________________________________________________________________________________________________________ -// Variables globales -// ________________________________________________________________________________________________________ -char szPathFileCfg[LONSTD],szPathFileLog[LONSTD]; -int ndebug; // Nivel de debuger - -typedef struct{ // Estructura de las tramas - char arroba; // Caracter arroba siempre - char identificador[14]; // Identificador de la trama, siempre JMMLCAMDJ_MCDJ - char tipo; // Tipo de mensaje - long lonprm; // Longitud en hexadecimal de los parámetros - char *parametros; // Parámetros de la trama -}TRAMA; -// ________________________________________________________________________________________________________ -// Tabla de errores -// ________________________________________________________________________________________________________ -const char* tbErrores[]={"Se han generado errores. No se puede continuar la ejecución de este módulo",\ - "001-El nombre del fichero de configuración del programa está vacío",\ - "002-No existe fichero de configuración del programa",\ - "003-No hay memoria suficiente para el buffer",\ - "004-Error en el fichero de configuración del programa. No se ha definido el parámetro SERVIDORADM",\ - "005-Error en el fichero de configuración del programa. No se ha definido el parámetro PUERTO",\ - "006-Error en el fichero de configuración del programa. No se ha definido el parámetro USUARIO",\ - "007-Error en el fichero de configuración del programa. No se ha definido el parámetro PASSWORD",\ - "008-Error en el fichero de configuración del programa. No se ha definido el parámetro DATASOURCE",\ - "009-Error en el fichero de configuración del programa. No se ha definido el parámetro CATALOG",\ - "010-Error en los parámetros de ejecución del programa. Debe especificar el fichero de configuración",\ - "011-Error en los parámetros de ejecución del programa. Debe especificar el fichero de log",\ - "012-Error de sintaxis en los parámetros de ejecución del programa: Debe especificar -f nombre_del_fichero_de_configuración_del_programa -l nombre_del_fichero_de_log_del_programa -d nivel de debug",\ - "013-Error al crear socket ***socket() fallo",\ - "014-Error al enlazar socket al interface ***bind() fallo",\ - "015-Error al acceptar conexión de clientes ***accept() fallo",\ - "016-Error al crear hebra de cliente en módulo main()",\ - "017-Error al recibir trama ***recv() fallo",\ - "018-No se reconoce el mensaje enviado",\ - "019-Trama recibida NO válida",\ - "020-No se puede establecer conexión con la base de datos",\ - "021-No se han podido recuperar los datos de la consulta o bien insertar, modificar o eliminar datos",\ - "022-El cliente no se ha sido dado de alta en la base de datos del sistema. Se rechaza su petición de inclusión",\ - "023-Ha habido algún problema en la incorporación automática del cliente",\ - "024-Ha habido algún problema en la actualización de la configuración del cliente",\ - "025-La tabla de clientes está llena, no pueden registrarse más clientes en el sistema",\ - "026-Error al enviar trama ***send() fallo",\ - "027-No se encuentra Repositorio del cliente",\ - "028-Ha ocurrido algún error al tomar las particiones",\ - "029-Ha ocurrido algún problema en el proceso de inclusión del cliente. Se rechaza su petición",\ - "030-Ha ocurrido algún problema en el proceso de respuesta al comando",\ - "031-No se ha encontrado la acción a notificar es posible que se haya eliminado el registro",\ - "032-Ha ocurrido algún problema en el envío del comando",\ - "033-Error en el fichero de configuración del programa. No se ha definido el parámetro PATHSCRIPTS",\ - "034-Error en el fichero de configuración del programa. No se ha definido el parámetro URLMENU",\ - "035-Error en el fichero de configuración del programa. No se ha definido el parámetro URLMSG",\ - "036-No se ha podido recuperar la configuración de las particiones del disco",\ - "037-Ha ocurrido algún problema en el proceso de inclusión del cliente",\ - "038-No se ha podido establecer conexión con el Servidor de Administración",\ - "039-Ha ocurrido algún problema al procesar la trama recibida",\ - "040-Se han recibido parámetros con valores no válidos",\ - "041-Ha ocurrido algún problema en el proceso de inclusión del cliente",\ - "042-Ha ocurrido algún problema al enviar una petición de comandos o tareas pendientes al Servidor de Administración",\ - "043-Ha ocurrido algún problema al enviar una petición de comandos interactivos al Servidor de Administración",\ - "044-Ha ocurrido algún problema al enviar una respuesta de comandos al servidor",\ - "045-Ha ocurrido algún problema al recibir una petición de comandos o tareas pendientes desde el Servidor de Administración",\ - "046-Ha ocurrido algún problema al recibir un comando interactivo desde el Servidor de Administración",\ - "047-El cliente no está registrado en la tabla de sockest del sistema",\ - "048-Error al configurar opción BROADCAST para socket: setsockopt(SO_BROADCAST)",\ - "049-Error al enviar trama magic packet",\ - "050-Ha ocurrido algún problema al enviar un fichero por la red",\ - "051-Error en el fichero de configuración del programa. No se ha definido el parámetro PATHLOGFIL",\ - "052-No se puede crear archivo temporal para ejecución de Comandos",\ - "053-Ha ocurrido algún problema al procesar el Inventario Hardware del cliente",\ - "054-Existe un tipo de hardware que no está registrado",\ - "055-Ha ocurrido algún problema al actualizar el hardware del cliente",\ - "056-Error en el fichero de configuración del programa. No se ha definido el parámetro PATHINTERFACE",\ - "057-Ha ocurrido algún problema al enviar un archivo por la red",\ - "058-Ha ocurrido algún problema al recibir un archivo por la red",\ - "059-Error al crear la hebra DHCP o BOOTP",\ - "060-Error al crear la hebra TFTP",\ - "061-Error al crear socket para servicio DHCP",\ - "062-Error al enlazar socket con interface para servicio DHCP",\ - "063-No hay puertos libres para la hebra del servicio",\ - "064-Error al crear estructura de control para protocolo DHCP",\ - "065-Error al recibir mensaje DHCP. Se para el servicio",\ - "066-Error al crear la hebra cliente DHCP",\ - "067-Error al crear socket para servicio BOOTP",\ - "068-Error al enlazar socket con interface para servicio BOOTP",\ - "069-Error al crear estructura de control para protocolo BOOTP",\ - "070-Error al recibir mensaje BOOTP. Se para el servicio",\ - "071-Error al crear la hebra cliente BOOTP",\ - "072-Error al crear socket para servicio TFTP",\ - "073-Error al enlazar socket con interface para servicio TFTP",\ - "074-Error al crear estructura de control para protocolo TFTP",\ - "075-Error al recibir mensaje TFTP. Se para el servicio",\ - "076-Error al crear la hebra cliente TFTP",\ - "077-No se encontró opción DHCP",\ - "078-ERROR TFTP",\ - "079-Error al recibir mensaje TFTP en hebra cliente",\ - "080-Error al recibir mensaje DHCP",\ - "081-Error al crear socket de usuario para hebra",\ - "082-Ha ocurrido algún problema al procesar el Inventario software del cliente",\ - "083-Ha ocurrido algún problema al actualizar el software del cliente",\ - "084-Ha ocurrido algún problema al reiniciar la sesión del cliente",\ - "085-No se ha podido recuperar la dirección IP del cliente",\ - "086-Error al ejecutar el comando",\ - "087-Error al leer o escribir el contenido del archivo de eco de consola remota",\ - "088-Ha habido algún problerma al procesar la caché",\ - "089-Error en el fichero de configuración del programa. No se ha definido el parámetro URLMENU",\ - "090-Error en el fichero de configuración del programa. No se ha definido el parámetro URLMSG",\ - "091-Ha habido algún problema al enviar un mensaje de tipo petición al Servidor",\ - "092-Error en el fichero de configuración del programa. No se ha definido el parámetro IPLOCAL",\ - "093-No se puede cargar la librería Windows para trabajar con sockets",\ - "094-Ha habido algún problerma al procesar la actualización después de crear una imagen",\ - "095-Ha habido algún problerma al procesar la actualización después de restaurar una imagen",\ - "096-Ha habido algún problerma al procesar la actualización después de crear un software incremental",\ - "097-Este fichero de log está obsoleto, este proceso usa ahora syslog para gestionar los mensajes de log",\ -}; -// ________________________________________________________________________________________________________ -// Tabla de mensajes -// ________________________________________________________________________________________________________ -const char* tbMensajes[]={"",\ - "001-Inicio de sesion",\ - "002-Petición de inclusión de cliente",\ - "003-Abriendo sesión en el servidor de Administración",\ - "004-Cliente iniciado",\ - "005-Ejecución de archivo Autoexec",\ - "006-Procesa comandos pendientes",\ - "007-Acciones pendientes procesadas",\ - "008-Ejecución del script",\ - "009-Parámetro del script",\ - "010-Ha ocurrido algún error en la creación del proceso hijo",\ - "011-Aviso: La información de salida del script excede de la longitud permitida. Puede haberse truncado",\ - "012-Información devuelta por el script",\ - "013-Estatus de finalización del script",\ - "014-Configuración de particiones",\ - "015-Enviando petición de inclusión en el sistema al Servidor de Administración",\ - "016-Recibiendo respuesta de inclusión desde el Servidor de Administración",\ - "017-Enviando petición de comandos o tareas pendientes al Servidor de Administración",\ - "018-Recibiendo respuesta de comandos o tareas pendientes desde el Servidor de Administración",\ - "019-Disponibilidad de comandos activada",\ - "020-Disponibilidad de comandos desactivada",\ - "021-Ejecución de comando",\ - "022-Sin eco",\ - "023-Procesando caché",\ - "024-Repositorio iniciado",\ - -}; -// ________________________________________________________________________________________________________ -// Prototipo de funciones -// ________________________________________________________________________________________________________ -struct tm * tomaHora(); -void registraLog(const char *,const char *,int ); -void errorLog(const char *,int ,int); -#define og_log(err, swe) errorLog(__FUNCTION__, err, swe) -void errorInfo(const char *,char *); -#define og_info(err) errorInfo(__FUNCTION__, err) -void infoLog(int); -void infoDebug(char*); -BOOLEAN validacionParametros(int,char**,int); -char* reservaMemoria(int); -char* ampliaMemoria(char*,int); -void liberaMemoria(void*); -BOOLEAN initParametros(TRAMA*,int); -int splitCadena(char **,char *, char); -void sustituir(char *,char ,char ); -char* StrToUpper(char *); -char* StrToLower(char *); -void INTROaFINCAD(TRAMA*); -void FINCADaINTRO(TRAMA*); -int cuentaIPES(char*); -char *tomaParametro(const char*,TRAMA*); -char *copiaParametro(const char*,TRAMA *); -BOOLEAN contieneIP(char *,char *); -char* rTrim(char *); -SOCKET TCPConnect(char *,char *); -SOCKET abreConexion(void); -BOOLEAN enviaMensaje(SOCKET *,TRAMA *,char); -TRAMA* recibeMensaje(SOCKET *); -BOOLEAN mandaTrama(SOCKET*,TRAMA*); -BOOLEAN sendData(SOCKET *, char* ,int ); -BOOLEAN enviaTrama(SOCKET *,TRAMA *); -TRAMA* recibeTrama(SOCKET*); -BOOLEAN recData(SOCKET *,char*,int,int*); -BOOLEAN sendFlag(SOCKET *, char* ,int ); -BOOLEAN recibeFlag(SOCKET*,TRAMA*); -char* URLEncode(char *); -char* URLDecode(char *); -char* leeArchivo(char*); -int lonArchivo(char *); -BOOLEAN escribeArchivo(char *,char*); -BOOLEAN sendArchivo(SOCKET *,char *); -BOOLEAN recArchivo(SOCKET *,char *); -SOCKET TCPConnect(char *,char*); - -#include /* for offsetof. */ - -#define container_of(ptr, type, member) ({ \ - typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) - diff --git a/native/Sources/Services/ogAdmAgent/Makefile b/native/Sources/Services/ogAdmAgent/Makefile deleted file mode 100644 index b9d39b9..0000000 --- a/native/Sources/Services/ogAdmAgent/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -# makefile - -# Nombre del proyecto -PROYECTO := ogAdmAgent - -# Directorio de instalación -INSTALL_DIR := /opt/opengnsys - -# Opciones de compilacion -CFLAGS := $(shell mysql_config --cflags) -CFLAGS += -g -Wall -I../../Includes -CPPFLAGS := $(CFLAGS) - -# Opciones de linkado -LDFLAGS := -Wl,--no-as-needed $(shell mysql_config --libs) -lpthread - -# Ficheros objetos -OBJS := ../../Includes/Database.o sources/ogAdmAgent.o - - -all: $(PROYECTO) - -$(PROYECTO): $(OBJS) - g++ $(LDFLAGS) $(OBJS) -o $(PROYECTO) - -install: $(PROYECTO) - cp $(PROYECTO) $(INSTALL_DIR)/sbin - cp $(PROYECTO).cfg $(INSTALL_DIR)/etc - -clean: - rm -f $(PROYECTO) $(OBJS) - -uninstall: clean - rm -f /usr/local/sbin/$(PROYECTO) /usr/local/etc/$(PROYECTO).cfg - -sources/%.o: sources/%.cpp - g++ $(CPPFLAGS) -c -o"$@" "$<" - -sources/%.o: sources/%.c - gcc $(CFLAGS) -c -o"$@" "$<" - - diff --git a/native/Sources/Services/ogAdmAgent/ogAdmAgent.cfg b/native/Sources/Services/ogAdmAgent/ogAdmAgent.cfg deleted file mode 100644 index 20126d3..0000000 --- a/native/Sources/Services/ogAdmAgent/ogAdmAgent.cfg +++ /dev/null @@ -1,7 +0,0 @@ -ServidorAdm=SERVERIP -PUERTO=2008 -USUARIO=DBUSER -PASSWORD=DBPASSWORD -datasource=localhost -CATALOG=DATABASE - diff --git a/native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.cpp b/native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.cpp deleted file mode 100644 index 6bf2351..0000000 --- a/native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.cpp +++ /dev/null @@ -1,914 +0,0 @@ -// ******************************************************************************************************** -// Servicio: ogAdmAgent -// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla -// Fecha Creación: Marzo-2010 -// Fecha Última modificación: Marzo-2010 -// Nombre del fichero: ogAdmAgent.cpp -// Descripción: Este fichero implementa el servicio agente del sistema. Revisa a intervalos -// regulares la base de datos para comprobar si existen acciones programadas. -// ******************************************************************************************************** -#include "ogAdmAgent.h" -#include "ogAdmLib.c" -//________________________________________________________________________________________________________ -// Función: tomaConfiguracion -// -// Descripción: -// Lee el fichero de configuración del servicio -// Parámetros: -// filecfg : Ruta completa al fichero de configuración -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -//________________________________________________________________________________________________________ -BOOLEAN tomaConfiguracion(char* filecfg) -{ - char modulo[] = "tomaConfiguracion()"; - - if (filecfg == NULL || strlen(filecfg) == 0) { - errorLog(modulo, 1, FALSE); // Fichero de configuración del servicio vacío - return (FALSE); - } - FILE *fcfg; - long lSize; - char * buffer, *lineas[MAXPRM], *dualparametro[2]; - int i, numlin, resul; - - fcfg = fopen(filecfg, "rt"); - if (fcfg == NULL) { - errorLog(modulo, 2, FALSE); // No existe fichero de configuración del servicio - return (FALSE); - } - - fseek(fcfg, 0, SEEK_END); - lSize = ftell(fcfg); // Obtiene tamaño del fichero. - rewind(fcfg); - buffer = (char*) reservaMemoria(lSize + 1); // Toma memoria para el buffer de lectura. - if (buffer == NULL) { // No hay memoria suficiente para el buffer - errorLog(modulo, 3, FALSE); - return (FALSE); - } - fread(buffer, 1, lSize, fcfg); // Lee contenido del fichero - buffer[lSize] = (char) NULL; - fclose(fcfg); - - servidoradm[0] = (char) NULL; //inicializar variables globales - puerto[0] = (char) NULL; - usuario[0] = (char) NULL; - pasguor[0] = (char) NULL; - datasource[0] = (char) NULL; - catalog[0] = (char) NULL; - - numlin = splitCadena(lineas, buffer, '\n'); - for (i = 0; i < numlin; i++) { - splitCadena(dualparametro, lineas[i], '='); - resul = strcmp(StrToUpper(dualparametro[0]), "SERVIDORADM"); - if (resul == 0) - strcpy(servidoradm, dualparametro[1]); - resul = strcmp(StrToUpper(dualparametro[0]), "PUERTO"); - if (resul == 0) - strcpy(puerto, dualparametro[1]); - resul = strcmp(StrToUpper(dualparametro[0]), "USUARIO"); - if (resul == 0) - strcpy(usuario, dualparametro[1]); - resul = strcmp(StrToUpper(dualparametro[0]), "PASSWORD"); - if (resul == 0) - strcpy(pasguor, dualparametro[1]); - resul = strcmp(StrToUpper(dualparametro[0]), "DATASOURCE"); - if (resul == 0) - strcpy(datasource, dualparametro[1]); - resul = strcmp(StrToUpper(dualparametro[0]), "CATALOG"); - if (resul == 0) - strcpy(catalog, dualparametro[1]); - } - if (servidoradm[0] == (char) NULL) { - errorLog(modulo, 4, FALSE); // Falta parámetro SERVIDORADM - return (FALSE); - } - if (puerto[0] == (char) NULL) { - errorLog(modulo, 5, FALSE); // Falta parámetro PUERTO - return (FALSE); - } - if (usuario[0] == (char) NULL) { - errorLog(modulo, 6, FALSE); // Falta parámetro USUARIO - return (FALSE); - } - if (pasguor[0] == (char) NULL) { - errorLog(modulo, 7, FALSE); // Falta parámetro PASSWORD - return (FALSE); - } - if (datasource[0] == (char) NULL) { - errorLog(modulo, 8, FALSE); // Falta parámetro DATASOURCE - return (FALSE); - } - if (catalog[0] == (char) NULL) { - errorLog(modulo, 9, FALSE); // Falta parámetro CATALOG - return (FALSE); - } - return (TRUE); -} -// ________________________________________________________________________________________________________ -// -// Función: diadelaSemana -// -// Descripción: -// Calcula el número del día de la semana que corresponde a una fecha -// Parámetros: -// - dia: Un día -// - mes: Un mes -// - anno: Un año -// Devuelve: -// El número del día de la semana: 1=Lunes, 2=martes ... 6=sábado 7=domingo -// ________________________________________________________________________________________________________ - -int diadelaSemana(WORD dia,WORD mes,WORD anno) -{ - int i,cont,dias_anuales; - int desplazamiento_dias=6; - int orddiasem; - - cont =0; - for (i=1900;i0) cociente++; - return(cociente); -} -// ________________________________________________________________________________________________________ -// -// Función: buscaAccion -// -// Descripción: -// Busca en la base de datos, acciones programadas -// Parámetros: -// - db: Objeto base de datos (operativo) -// - dia : Día actual del mes -// - mes : mes en curso -// - anno : Año en curso -// - hora : Hora actual -// - minutos : Minutos actuales -// - diasemana : Dia de la semana 1=lunes,2=martes ... ( 0 Domingo) -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ - -BOOLEAN buscaAccion(Database db,WORD dia,WORD mes,WORD anno,WORD hora,WORD minutos,WORD diasemana) -{ - char msglog[LONSTD], sqlstr[LONSQL]; - Table tbl; - BYTE swampm,bitsemana; - int ordsem,ordulsem,ordiasem_1,maxdias; - int sesionprog; - char modulo[] = "buscaAccion()"; - - /* Año de comienzo */ - anno=anno-ANNOREF; // - /* Preparación hora */ - if(hora>11){ - hora-=12; - swampm=1; // Es P.M. - } - else - swampm=0; // Es am - /* Preparación semana */ - if(diasemana==0) diasemana=7; // El domingo - - // Cuestión semanas - ordiasem_1=diadelaSemana(1,mes,anno+2009); - ordsem=semanadelMes(ordiasem_1,dia); // Calcula el número de la semana - if (mes!=2) // Toma el último día de ese mes - maxdias=dias_meses[mes]; - else{ - if (bisiesto(anno+ANNOREF)) - maxdias=29; - else - maxdias=28; - } - ordulsem=semanadelMes(ordiasem_1,maxdias); // Calcula el número de la última semana - bitsemana=HEX_semanas[ordsem]; - if(ordsem==ordulsem) // Si es la última semana del mes - bitsemana|=HEX_semanas[6]; - - sprintf(sqlstr,"SELECT DISTINCT idprogramacion,tipoaccion,identificador,sesion,idcentro,"\ - "tareas.descripcion as descritarea"\ - " FROM programaciones"\ - " LEFT OUTER JOIN tareas ON tareas.idtarea=programaciones.identificador"\ - " WHERE suspendida=0 "\ - " AND (annos & %d <> 0) "\ - " AND (meses & %d<>0) "\ - " AND ((diario & %d<>0) OR (dias & %d<>0) OR (semanas & %d<>0))"\ - " AND (horas & %d<>0) AND ampm=%d AND minutos=%d",\ - HEX_annos[anno],\ - HEX_meses[mes],\ - HEX_dias[dia],\ - HEX_diasemana[diasemana],\ - bitsemana,\ - HEX_horas[hora],\ - swampm,minutos); - - if (!db.Execute(sqlstr, tbl)) { // Error al leer - errorLog(modulo, 21, FALSE); - db.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(tbl.ISEOF()){ - return(TRUE); // No hay acciones programadas - } - - while(!tbl.ISEOF()){ - if(!tbl.Get("idprogramacion",idprogramacion)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(!tbl.Get("tipoaccion",tipoaccion)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(!tbl.Get("identificador",idtipoaccion)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(!tbl.Get("sesion",sesionprog)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(!tbl.Get("idcentro",idcentro)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - - if(tipoaccion==EJECUCION_COMANDO){ // Es una programación de un comando - return(ejecutarComando(db,idprogramacion,sesionprog)); - } - else{ - - if(tipoaccion==EJECUCION_TAREA){ - if(!tbl.Get("descritarea",descriaccion)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - return(ejecutarTarea(db,idprogramacion,idtipoaccion)); - } - else{ - if(tipoaccion==EJECUCION_RESERVA){ - EjecutarReserva(idtipoaccion,db); // Es una programación de un trabajo - } - } - } - tbl.MoveNext(); - } - return(TRUE); -} -// ________________________________________________________________________________________________________ -// -// Función: ejecutarComando -// -// Descripción: -// Ejecuta un comando programado -// Parámetros: -// - db: Objeto base de datos (operativo) -// - idcomando: Identificador del comando -// - sesion: Sesión correspondiente al comando cuando se grabó en la tabla acciones -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ - -BOOLEAN ejecutarComando(Database db,int idprogramacion,int sesion ) -{ - struct tm* st; - char msglog[LONSTD], sqlstr[LONSQL]; - char fechahorareg[24]; - char modulo[] = "ejecutarComando()"; - - st = tomaHora(); - sprintf(fechahorareg,"%d/%d/%d %d:%d:%d", st->tm_year + 1900, st->tm_mon + 1, - st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec); - - sprintf(sqlstr,"UPDATE acciones SET estado=%d,idprogramacion=%d,fechahorareg='%s'"\ - " WHERE sesion=%d", ACCION_INICIADA,idprogramacion,fechahorareg,sesion); - - if (!db.Execute(sqlstr)) { // Error al recuperar los datos - errorLog(modulo, 21, FALSE); - db.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - return(enviaPeticion(idprogramacion)); -} -// ________________________________________________________________________________________________________ -// -// Función: ejecutarProcedimiento -// -// Descripción: -// Ejecuta un procedimiento programado -// Parámetros: -// - db: Objeto base de datos (operativo) -// - idprocedimiento: Identificador del procedimiento -// - ambito: Ámbito de aplicación -// - idambito: Identificador del ámbito -// - restrambito: cadena con los identificadores de los ordenadores a los que se aplica la acción -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ - -BOOLEAN ejecutarProcedimiento(Database db,int idprocedimiento,int ambito,int idambito,char* restrambito) -{ - char msglog[LONSTD], sqlstr[LONSQL],*parametros; - Table tbl; - int procedimientoid,idcomando,lonprm; - char modulo[] = "ejecutarProcedimiento()"; - - sprintf(sqlstr,"SELECT idcomando,procedimientoid,parametros,length(parametros) as lonprm"\ - " FROM procedimientos_acciones"\ - " WHERE idprocedimiento=%d ORDER BY orden",idprocedimiento); - - if (!db.Execute(sqlstr, tbl)) { // Error al leer - errorLog(modulo, 21, FALSE); - db.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - - if(tbl.ISEOF()){ - return(TRUE); // No exustde tarea - } - while(!tbl.ISEOF()){ - if(!tbl.Get("procedimientoid",procedimientoid)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(procedimientoid>0){ // Procedimiento recursivo - if(!ejecutarProcedimiento(db,procedimientoid,ambito,idambito,restrambito)){ - return(false); - } - } - else{ - if(!tbl.Get("lonprm",lonprm)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - parametros = reservaMemoria(lonprm+1); // Reserva para almacenar los parametros del procedimiento - if (parametros == NULL) { - errorLog(modulo, 3, FALSE); - return (FALSE); - } - if(!tbl.Get("parametros",parametros)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - liberaMemoria(parametros); - return (FALSE); - } - if(!tbl.Get("idcomando",idcomando)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - - if(!insertaComando(db,idcomando,parametros,idprocedimiento,ambito,idambito,restrambito)) { - - liberaMemoria(parametros); - return(false); - } - liberaMemoria(parametros); - } - tbl.MoveNext(); - } - return(TRUE); -} -// ________________________________________________________________________________________________________ -// -// Función: ejecutarTarea -// -// Descripción: -// Ejecuta una tarea programada -// Parámetros: -// - db: Objeto base de datos (operativo) -// - idtarea: Identificador de la tarea -// - idprogramacion: Identificador de la programación -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ - -BOOLEAN ejecutarTarea(Database db, int idprogramacion, int idtarea) -{ - char msglog[LONSTD], sqlstr[LONSQL]; - Table tbl; - int tareaid,ambito,idambito,idprocedimiento,lonrestrambito; - char* restrambito; - char modulo[] = "ejecutarTarea()"; - - sprintf(sqlstr,"SELECT tareas_acciones.orden,tareas_acciones.idprocedimiento,tareas_acciones.tareaid,"\ - " tareas.ambito,tareas.idambito,tareas.restrambito,length(tareas.restrambito) as lonrestrambito"\ - " FROM tareas"\ - " INNER JOIN tareas_acciones ON tareas_acciones.idtarea=tareas.idtarea"\ - " WHERE tareas_acciones.idtarea=%d ORDER BY tareas_acciones.orden",idtarea); - - if (!db.Execute(sqlstr, tbl)) { // Error al leer - errorLog(modulo, 21, FALSE); - db.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - - if(tbl.ISEOF()){ - return(TRUE); // No existe tarea - } - - while(!tbl.ISEOF()){ - if(!tbl.Get("tareaid",tareaid)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(tareaid>0){ // Tarea recursiva - if(!ejecutarTarea(db,idprogramacion,tareaid)){ - return(false); - } - } - else{ - if(!tbl.Get("ambito",ambito)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(!tbl.Get("idambito",idambito)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(!tbl.Get("lonrestrambito",lonrestrambito)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - restrambito = reservaMemoria(lonrestrambito+1); - if (restrambito == NULL) { - errorLog(modulo, 3, FALSE); - return (FALSE); - } - if(!tbl.Get("restrambito",restrambito)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - liberaMemoria(restrambito); - return (FALSE); - } - liberaMemoria(restrambito); - RecopilaIpesMacs(db,ambito,idambito,restrambito); // Recopila Ipes del ámbito - if(!tbl.Get("idprocedimiento",idprocedimiento)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - sesion=time(NULL); - - if(!ejecutarProcedimiento(db,idprocedimiento,ambito,idambito,restrambito)) - return(FALSE); - } - tbl.MoveNext(); - } - return(enviaPeticion(idprogramacion)); -} -// ________________________________________________________________________________________________________ -// -// Función: ejecutarTarea -// -// Descripción: -// Registra un procedimiento para un ambito concreto -// Parámetros: -// - db: Objeto base de datos (operativo) -// - idcomando: Identificador del comando -// - idprocedimiento: Identificador del procedimiento -// - ambito: Ámbito de aplicación -// - idambito: Identificador del ámbito -// - restrambito: cadena con los identificadores de los ordenadores a los que se aplica la acción -// Devuelve: -// TRUE: Si el proceso es correcto -// FALSE: En caso de ocurrir algún error -//________________________________________________________________________________________________________ -BOOLEAN insertaComando(Database db,int idcomando,char*parametros,int idprocedimiento,int ambito,int idambito,char*restrambito) -{ - char msglog[LONSTD], sqlstr[LONSQL]; - struct tm* st; - char *auxID[MAXIMOS_CLIENTES],*auxIP[MAXIMOS_CLIENTES]; - char fechahorareg[24]; - int i; - char modulo[] = "insertaComando()"; - - if(concli==0) return(TRUE); // No hay ordenadores en el ámbito - - st = tomaHora(); - sprintf(fechahorareg,"%d/%d/%d %d:%d:%d", st->tm_year + 1900, st->tm_mon + 1, st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec); - - splitCadena(auxID,cadenaid,','); - splitCadena(auxIP,cadenaip,';'); - - for (i=0;iparametros,"nfn=envioProgramacion\r"); // Nombre de la función a ejecutar en el servidor - lon+=sprintf(ptrTrama->parametros+lon,"idp=%d\r",idprogramacion); // Configuración de los Sistemas Operativos del cliente - - if(!enviaMensaje(&socket_c,ptrTrama,MSG_PETICION)){ - errorLog(modulo,91,FALSE); - liberaMemoria(ptrTrama); - return(FALSE); - } - liberaMemoria(ptrTrama); - return(TRUE); -} -// _____________________________________________________________________________________________________________ -// -// Función: RecopilaIpesMacs -// -// Descripción : -// Recopila las IPes, las Macs y los identificadores de ordenadores de un ámbito determinado -// -// Especificaciones: -// Esta Función recibe tres parámatros: -// db : Un objeto Base de datos totalmente operativo -// ambito: Tipo de ámbito -// idambito: Identificador del ámbito -// Devuelve: -// Todas los identificadores de ordenadores , las ipes y las macs de los ordenadores que componen el ámbito -// Para ellos habrá que tener declarada tres variables globales : -// cadenaid,cadenaip y cadenamac -// _____________________________________________________________________________________________________________ - -BOOLEAN RecopilaIpesMacs(Database db,int ambito,int idambito,char *restrambito) -{ - char sqlstr[LONSQL]; - - concli=0; - /* Reserva memoria al meno para caracter nulo */ - cadenaid=(char*) reservaMemoria(1); - cadenaip=(char*) reservaMemoria(1); - cadenamac=(char*) reservaMemoria(1); - - switch(ambito){ - case AMBITO_CENTROS : - sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores INNER JOIN aulas WHERE ordenadores.idaula=aulas.idaula AND idcentro=%d ORDER BY ordenadores.idaula, ordenadores.ip;",idambito); - RecorreOrdenadores(db,sqlstr); - break; - case AMBITO_GRUPOSAULAS : - sprintf(sqlstr,"SELECT idgrupo FROM grupos WHERE idgrupo=%d AND tipo=%d",idambito,AMBITO_GRUPOSAULAS); - RecorreGruposAulas(db,sqlstr); - break; - case AMBITO_AULAS : - sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores WHERE idaula=%d ORDER BY ip;",idambito); - RecorreOrdenadores(db,sqlstr); - break; - case AMBITO_GRUPOSORDENADORES : - sprintf(sqlstr,"SELECT idgrupo FROM gruposordenadores WHERE idgrupo=%d",idambito); - RecorreGruposOrdenadores(db,sqlstr); - break; - case AMBITO_ORDENADORES : - sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores WHERE idordenador=%d",idambito); - RecorreOrdenadores(db,sqlstr); - break; - default: // Se trata de un conjunto aleatorio de ordenadores - sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores WHERE idordenador IN (%s)",restrambito); - RecorreOrdenadores(db,sqlstr); - - } - return (TRUE); -} -//________________________________________________________________________________________________________ - -BOOLEAN RecorreCentro(Database db, char* sqlstr) -{ - char msglog[LONSTD]; - Table tbl; - int idcentro; - char modulo[] = "RecorreCentro()"; - - if (!db.Execute(sqlstr, tbl)) { // Error al leer - errorLog(modulo, 21, FALSE); - db.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(!tbl.ISEOF()){ - if(!tbl.Get("idcentro",idcentro)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - sprintf(sqlstr,"SELECT idgrupo FROM grupos WHERE idcentro=%d AND grupoid=0 AND tipo=%d",idcentro,AMBITO_GRUPOSAULAS); - RecorreGruposAulas(db,sqlstr); - sprintf(sqlstr,"SELECT idaula FROM aulas WHERE idcentro=%d AND grupoid=0",idcentro); - RecorreAulas(db,sqlstr); - } - return (TRUE); -} -//________________________________________________________________________________________________________ - -BOOLEAN RecorreGruposAulas(Database db, char* sqlstr) -{ - char msglog[LONSTD]; - Table tbl; - int idgrupo; - char modulo[] = "RecorreGruposAulas()"; - - if (!db.Execute(sqlstr, tbl)) { // Error al leer - errorLog(modulo, 21, FALSE); - db.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - while(!tbl.ISEOF()){ - if(!tbl.Get("idgrupo",idgrupo)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - sprintf(sqlstr,"SELECT idgrupo FROM grupos WHERE grupoid=%d AND tipo=%d",idgrupo,AMBITO_GRUPOSAULAS); - RecorreGruposAulas(db,sqlstr); - sprintf(sqlstr,"SELECT idaula FROM aulas WHERE grupoid=%d",idgrupo); - RecorreAulas(db,sqlstr); - tbl.MoveNext(); - } - return (TRUE); -} -//________________________________________________________________________________________________________ - -BOOLEAN RecorreAulas(Database db, char* sqlstr) -{ - char msglog[LONSTD]; - Table tbl; - int idaula; - char modulo[] = "RecorreAulas()"; - - if (!db.Execute(sqlstr, tbl)) { // Error al leer - errorLog(modulo, 21, FALSE); - db.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - while(!tbl.ISEOF()){ - if(!tbl.Get("idaula",idaula)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - sprintf(sqlstr,"SELECT idgrupo FROM gruposordenadores WHERE idaula=%d AND grupoid=0",idaula); - RecorreGruposOrdenadores(db,sqlstr); - sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores WHERE idaula=%d AND grupoid=0",idaula); - RecorreOrdenadores(db,sqlstr); - tbl.MoveNext(); - } - return (TRUE); -} -//________________________________________________________________________________________________________ - -BOOLEAN RecorreGruposOrdenadores(Database db, char* sqlstr) -{ - char msglog[LONSTD]; - Table tbl; - int idgrupo; - char modulo[] = "RecorreGruposOrdenadores()"; - - if (!db.Execute(sqlstr, tbl)) { // Error al leer - errorLog(modulo, 21, FALSE); - db.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - while(!tbl.ISEOF()){ - if(!tbl.Get("idgrupo",idgrupo)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - sprintf(sqlstr,"SELECT idgrupo FROM gruposordenadores WHERE grupoid=%d",idgrupo); - RecorreGruposOrdenadores(db,sqlstr); - sprintf(sqlstr,"SELECT ip,mac,idordenador FROM ordenadores WHERE grupoid=%d",idgrupo); - RecorreOrdenadores(db,sqlstr); - tbl.MoveNext(); - } - return (TRUE); -} -//________________________________________________________________________________________________________ - -BOOLEAN RecorreOrdenadores(Database db, char* sqlstr) -{ - char msglog[LONSTD]; - Table tbl; - int idordenador,o,p,m,lon; - char ido[16],ip[LONIP],mac[LONMAC]; - char modulo[] = "RecorreOrdenadores()"; - - if (!db.Execute(sqlstr, tbl)) { // Error al leer - errorLog(modulo, 21, FALSE); - db.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - o=p=m=0; - while(!tbl.ISEOF()){ - if(!tbl.Get("idordenador",idordenador)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(!tbl.Get("ip",ip)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - if(!tbl.Get("mac",mac)){ - tbl.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - return (FALSE); - } - sprintf(ido,"%d",idordenador); - lon=strlen(ido); - if(lon>16) lon=16; - cadenaid=(char*) ampliaMemoria(cadenaid,o+lon+1); - memcpy(&cadenaid[o],ido,lon); - o+=lon; - cadenaid[o++]=','; - - lon=strlen(ip); - if(lon>16) lon=LONIP; - cadenaip=(char*) ampliaMemoria(cadenaip,p+lon+1); - memcpy(&cadenaip[p],ip,lon); - p+=lon; - cadenaip[p++]=';'; - - lon=strlen(mac); - if(lon>16) lon=LONMAC; - cadenamac=(char*) ampliaMemoria(cadenamac,m+lon+1); - memcpy(&cadenamac[m],mac,lon); - m+=lon; - cadenamac[m++]=';'; - - concli++; - tbl.MoveNext(); - } - if(o>0) o--; - if(p>0) p--; - if(m>0) m--; - cadenaid[o]='\0'; - cadenaip[p]='\0'; - cadenamac[m]='\0'; - - return (TRUE); -} -// ******************************************************************************************************** -// PROGRAMA PRINCIPAL (SERVICIO) -// ******************************************************************************************************** -int main(int argc, char *argv[]) -{ - int pseg; - char msglog[LONSTD]; - struct tm* st; - Database db; - char modulo[] = "main()"; - - /* Validación de parámetros de ejecución y lectura del fichero de configuración del servicio */ - - if (!validacionParametros(argc, argv, 5)) // Valida parámetros de ejecución - exit(EXIT_FAILURE); - - if (!tomaConfiguracion(szPathFileCfg)) { // Toma parametros de configuracion - exit(EXIT_FAILURE); - } - - /* Bucle principal del servicio */ - - while (TRUE){ - st = tomaHora(); - pseg=65-st->tm_sec; // Calcula segundos de inactividad de la hebra - sleep(pseg); - - // Toma la hora - st = tomaHora(); - - if (!db.Open(usuario, pasguor, datasource, catalog)) { // Error de conexion - errorLog(modulo, 20, FALSE); - db.GetErrorErrStr(msglog); - errorInfo(modulo, msglog); - exit(EXIT_FAILURE); - } - buscaAccion(db,st->tm_mday,st->tm_mon+1,st->tm_year+1900,st->tm_hour,st->tm_min,st->tm_wday ); - db.Close(); // Cierra conexión - } - exit(EXIT_SUCCESS); -} - - diff --git a/native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.h b/native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.h deleted file mode 100644 index b1c429b..0000000 --- a/native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.h +++ /dev/null @@ -1,81 +0,0 @@ -// ******************************************************************************************************** -// Servicio: ogAdmAgent -// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla -// Fecha Creación: Marzo-2010 -// Fecha Última modificación: Marzo-2010 -// Nombre del fichero: ogAdmAgent.h -// Descripción: Este fichero implementa el servicio agente del sistema. Revisa a intervalos -// regulares la base de datos para comprobar si existen acciones programadas. -// ******************************************************************************************************** -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "Database.h" -#include "ogAdmLib.h" - -// ________________________________________________________________________________________________________ -// -// Valores hexadecimales para consultas -// ________________________________________________________________________________________________________ - -WORD HEX_annos[]={0,0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000}; -WORD HEX_meses[]={0,0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100,0x0200,0x0400,0x0800}; -int HEX_dias[]={0,0x00000001,0x00000002,0x00000004,0x00000008,0x00000010,0x00000020,0x00000040,0x00000080,0x00000100,0x00000200, - 0x00000400,0x00000800,0x00001000,0x00002000,0x00004000,0x00008000,0x00010000,0x00020000,0x00040000,0x00080000, - 0x00100000,0x00200000,0x00400000,0x00800000,0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,0x20000000,0x40000000}; -WORD HEX_horas[]={0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100,0x0200,0x0400,0x0800 }; -BYTE HEX_diasemana[]={0,0x01,0x02,0x04,0x08,0x10,0x20,0x40}; -BYTE HEX_semanas[]={0,0x01,0x02,0x04,0x08,0x10,0x20}; -WORD dias_meses[]={0,31,28,31,30,31,30,31,31,30,31,30,31}; - -// ________________________________________________________________________________________________________ -// -// Variables globales -// ________________________________________________________________________________________________________ - -char servidoradm[LONPRM]; // Dirección IP del servidor de administración -char puerto[LONPRM]; // Puerto de comunicación -char usuario[LONPRM]; // Usuario de acceso a la base de datos -char pasguor[LONPRM]; // Password del usuario -char datasource[LONPRM]; // Dirección IP del gestor de base de datos -char catalog[LONPRM]; // Nombre de la base de datos - -int idprogramacion; -int tipoaccion,idtipoaccion; -char descriaccion[250]; -char *cadenaid; -char *cadenaip; -char *cadenamac; -int concli; -int sesion; -int idcentro; - -// ________________________________________________________________________________________________________ -// Prototipo de funciones -// ________________________________________________________________________________________________________ -BOOLEAN tomaConfiguracion(char*); -int diadelaSemana(WORD,WORD,WORD); -BOOLEAN bisiesto(WORD); -BOOLEAN buscaAccion(Database,WORD,WORD,WORD,WORD,WORD,WORD); -BOOLEAN ejecutarComando(Database,int,int ); -BOOLEAN ejecutarProcedimiento(Database,int,int,int,char*); -BOOLEAN ejecutarTarea(Database,int, int); -BOOLEAN insertaComando(Database,int,char*,int,int,int,char*); -BOOLEAN EjecutarReserva(int,Database); -BOOLEAN enviaPeticion(int); -BOOLEAN RecopilaIpesMacs(Database,int,int,char *); -BOOLEAN RecorreCentro(Database, char*); -BOOLEAN RecorreGruposAulas(Database, char*); -BOOLEAN RecorreAulas(Database, char*); -BOOLEAN RecorreGruposOrdenadores(Database, char*); -BOOLEAN RecorreOrdenadores(Database, char*); - diff --git a/native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.o b/native/Sources/Services/ogAdmAgent/sources/ogAdmAgent.o deleted file mode 100644 index ccf5962b7794d776c2a5ff8a01b60c970ecbc92f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127632 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 BTRFS -KERNEL=$(file -bkr /opt/opengnsys/tftpboot/ogclient/ogvmlinuz |awk '/Linux/ {for(i=1;i<=NF;i++) if($i~/version/) {v=$(i+1);printf("%d",v);sub(/[0-9]*\./,"",v);printf(".%02d",v)}}') -[ $KERNEL \< 3.07 ] && IMGFS="EXT4" || IMGFS=${IMGFS:-"BTRFS"} - -# Añade registro de incidencias. -function echolog () { - logger --tag $0 --priority local0.info "$*" - echo "$*" -} - -function mountImage () { - #@param 1 image_file - #@param 2 mount_dir - #@param 3 openciones mount - [ "$3" != "" ] && OPTMOUNT=" -o $3 " - # Si está montado nada que hacer - df |grep "$2$" 2>&1 >/dev/null && return 0 - # FS de la imagen segun el contenido del archivo .img - if file "$1" |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then - echolog "mount $OPTMOUNT -t ext4 $1 $2" - mount $OPTMOUNT -t ext4 $1 $2 - else - echolog "mount $OPTMOUNT -o compress=lzo $1 $2" - mount $OPTMOUNT -o compress=lzo "$1" "$2" - fi - # Si esta montado da error 32, lo damos como bueno - RETVAL=$? - [ $RETVAL -eq 32 ] && RETVAL=0 - return $RETVAL -} - - -PARM1=$(echo $PARM | cut -f1 -d" ") -PARM2=$(echo $PARM | cut -f2 -d" ") -PARM3=$(echo $PARM | cut -f3 -d" ") -PARM4=$(echo $PARM | cut -f4 -d" ") - -# Determinamos el tipo de sistema de fichero de las imagenes segun el kernel que tenga - - -case "$PARM1" in - START_MULTICAST) - #1 START_MULTICAST - #2 fichero a enviar - #3 opciones de multicast - FILE="$PARM2" - MCASTOPT="$PARM3" - echolog "Ejecutar $(which sendFileMcast) $FILE $MCASTOPT" - sendFileMcast $FILE $MCASTOPT |logger --tag $0 --priority local0.info - case $? in - 1) echolog "Parametros insuficientes" - exit 1 ;; - 2) echolog "Fichero no accesible" - exit 2 ;; - 3) echolog "Sesion multicast no valida" - exit 3 ;; - esac - ;; - CREATE_IMAGE) - # Creamos/Redimensionamos el fichero de imagen y lo montamos para que se pueda escribir sobre el - #1 CREATE_IMAGE - #2 nombre imagen - #3 tipo de imagen [ img | diff ] - #4 tamaño imagen - LOOPDEVICE=$(losetup -f) - DIRMOUNT="$REPODIR/mount/$PARM2" - if [ "$PARM3" == "img" ] ; then - IMGEXT="img" - else - IMGEXT="img.diff" - DIRMOUNT="$DIRMOUNT.diff" - fi - IMGFILE="$REPODIR/$PARM2.$IMGEXT" - IMGDIR="$(dirname $IMGFILE)" - [ -d $IMGDIR ] || mkdir -p $IMGDIR - mkdir -p "$DIRMOUNT" - - LOCKFILE="$IMGFILE.lock" - - SIZEREQUIRED="$PARM4" - - # Si existe la imagen hacemos copia de seguridad y la redimesionamos - if [ -f "$IMGFILE" ]; then - echolog "La imagen $IMGFILE ya existe." - # TODO modificar ogGetImageSize - IMGSIZE=$(ls -l --block-size=1024 $IMGFILE| cut -f5 -d" ") - - if [ "$BACKUP" == "true" -o "$BACKUP" == "TRUE" -o $IMGSIZE -lt $SIZEREQUIRED ]; then - # Si la imagen esta montada la desmonto - if [ -r "$DIRMOUNT/ogimg.info" ]; then - echolog "umount $DIRMOUNT" - umount "$DIRMOUNT" - [ $? -ne 0 ] && echolog "Error: No podemos desmontar la imagen para hacer copia de seguridad o redimensionar" && exit 1 - fi - fi - - # Copia de seguridad de la imagen - if [ "$BACKUP" == "true" -o "$BACKUP" == "TRUE" ]; then - echolog "Copia de seguridad de la imagen anterior" - echolog "cp $IMGFILE $IMGFILE.ant" - cp "$IMGFILE" "$IMGFILE.ant" - echolog mv -f "$IMGFILE.torrent" "$IMGFILE.torrent.ant" 2>/dev/null - mv -f "$IMGFILE.torrent" "$IMGFILE.torrent.ant" 2>/dev/null - fi - - # Redimensionamos la imagen al tamaño necesario - if [ $IMGSIZE -lt $SIZEREQUIRED ];then - echolog "Redimensionamos la imagen $IMGFILE al tamaño necesario: $SIZEREQUIRED" - echolog "truncate --size=\">$SIZEREQUIRED\"k $IMGFILE" - truncate --size=">$SIZEREQUIRED"k $IMGFILE 2>&1 |logger --tag $0 --priority local0.info - # FS de la imagen segun el contenido del archivo .img - if file "$IMGFILE" |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then - losetup $LOOPDEVICE "$IMGFILE" - echolog "resize2fs -f $LOOPDEVICE" - resize2fs -f $LOOPDEVICE |logger --tag $0 --priority local0.info - else - mount -o compress=lzo "$IMGFILE" "$DIRMOUNT" - echolog "btrfs filesystem resize max $DIRMOUNT" - btrfs filesystem resize max "$DIRMOUNT" 2>&1 |logger --tag $0 --priority local0.info - fi - fi - - - # Si no existe la imagen creamos el fichero. - else - echolog "Creamos la imagen $IMGFILE al tamaño necesario: $SIZEREQUIRED" - touch "$IMGFILE" - echolog "truncate --size=\">$SIZEREQUIRED\"k $IMGFILE" - truncate --size=">$SIZEREQUIRED"k $IMGFILE 2>&1 |logger --tag $0 --priority local0.info - #Formateamos imagen - echo losetup $LOOPDEVICE "$IMGFILE" - losetup $LOOPDEVICE "$IMGFILE" - if [ $IMGFS == "EXT4" ] ; then - echolog " mkfs.ext4 -i 4096 -b 4096 -L ${PARM2##*\/} $LOOPDEVICE" - mkfs.ext4 -i 4096 -b 4096 -L ${PARM2##*\/} $LOOPDEVICE - else - echolog mkfs.btrfs -L ${PARM2##*\/} $LOOPDEVICE - mkfs.btrfs -L ${PARM2##*\/} $LOOPDEVICE #&> $OGLOGCOMMAND - fi - fi - # Montamos la imagen. - mountImage "$IMGFILE" "$DIRMOUNT" - if [ $? -ne 0 ]; then - rmdir "$DIRMOUNT" - echolog "Error al crear/redimensionar la imagen" - exit 1 - fi - - #touch "$DIRMOUNT/ogimg.info" - echo "mounted"> "$LOCKFILE" - TIME2=$[SECONDS-TIME] - echolog "Fin creación/redimension de la imagen: $[TIME2/60]m $[TIME2%60]s" - # Si existe dispositivo loop lo borramos. - [ $LOOPDEVICE ] && losetup -a| grep $LOOPDEVICE &> /dev/null && losetup -d $LOOPDEVICE - # TODO: comprobar que no se quede el losetup bloqueado. - - ;; - MOUNT_IMAGE) - # Montamos el fichero imagen para que se pueda - #1 MOUNT_IMAGE - #2 nombre imagen - #3 tipo de imagen [ img | diff ] - DIRMOUNT="$REPODIR""mount/$PARM2" - if [ "$PARM3" == "img" ] ; then - IMGEXT="img" - else - IMGEXT="img.diff" - DIRMOUNT="$DIRMOUNT.diff" - fi - IMGFILE="$REPODIR/$PARM2.$IMGEXT" - echolog "Montamos la imagen $IMGFILE " - mkdir -p "$DIRMOUNT" - mountImage "$IMGFILE" "$DIRMOUNT" ro || (echolog "Error al montar la imagen"; exit 1) - ;; - UMOUNT_IMAGE) - # Desmontamos el fichero imagen. - # Si el directorio esta ocupado no se desmontará - #1 UMOUNT_IMAGE - #2 nombre imagen - #3 tipo de imagen [ img | diff ] - IMGTYPE="$PARM3" - DIRMOUNT="$REPODIR/mount/$PARM2" - if [ "$IMGTYPE" == "img" ]; then - IMGEXT="img" - else - DIRMOUNT="$DIRMOUNT.$IMGTYPE" - IMGEXT="img.diff" - fi - LOCKFILE="$REPODIR/$PARM2.$IMGEXT.lock" - echolog "Desmontamos la imagen $PARM2 $PARM3 " - umount $DIRMOUNT - rmdir $DIRMOUNT - [ -f $LOCKFILE ] && sed -i s/mounted//g $LOCKFILE - - ;; - REDUCE_IMAGE) - # Reduce el archivo de la imagen a tamaño datos + 500M - #1 REDUCE_IMAGE - #2 Nombre Imagen - #3 Tipo de imagen [ img |diff ] - DIRMOUNT="${REPODIR}mount/${PARM2}" - if [ "$PARM3" == "img" ] ; then - IMGEXT="img" - else - IMGEXT="img.diff" - DIRMOUNT="$DIRMOUNT.diff" - fi - IMGFILE="$REPODIR$PARM2.$IMGEXT" - LOCKFILE="$IMGFILE.lock" - [ ! -f $IMGFILE ] && echolog "Imagen $IMGFILE no existe" && exit 1 - - # Para imagenes EXT4 reduzco, para BTRFS solo desmonto. - if file $IMGFILE |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then - - [ -d $DIRMOUNT ] || mkdir $DIRMOUNT - mountImage "$IMGFILE" "$DIRMOUNT" || (echolog "Error al montar la imagen $IMGFILE"; exit 1) - - - # Si el espacio libre menor que 200Mb desmontamos la imagen y nos salimos - AVAILABLE=$(df -k|grep $DIRMOUNT|awk '{print $4}') - if [ $AVAILABLE -lt 200000 ]; then - echolog "reducir imagen REPO $PARM2 $IMGEXT. tamaño minimo, nada que hacer" - umount $DIRMOUNT || (echolog "Error al desmontar la imagen $IMGFILE"; exit 1) - else - - # Calculamos la diferencia entre el tamaño interno y externo - EXTSIZE=$(ls -l --block-size=1024 $IMGFILE | cut -f5 -d" ") - INTSIZE=$(df -k|grep "$DIRMOUNT"|awk '{print $2}') - let EDGESIZE=$EXTSIZE-$INTSIZE - - echolog "reducir imagen REPO $PARM2 $IMGEXT, tamaño final: $ENDSIZE" - umount $DIRMOUNT - LOOPDEVICE=$(losetup -f) - losetup $LOOPDEVICE "$IMGFILE" - - # Redimensiono sistema de ficheros - echolog "resize2fs -fpM $LOOPDEVICE " - resize2fs -fpM $LOOPDEVICE # 2>&1 |logger --tag $0 --priority local0.info - mountImage "$IMGFILE" "$DIRMOUNT" - # Calculamos el tamaño final del archivo - INTSIZE=$(df -k|grep "$DIRMOUNT"|awk '{print $2}') - let EXTSIZE=$INTSIZE+$EDGESIZE - umount $DIRMOUNT || (echolog "Error al desmontar la imagen $IMGFILE"; exit 1) - # Si existe dispositivo loop lo borramos. - [ $LOOPDEVICE ] && losetup -a| grep $LOOPDEVICE &> /dev/null && losetup -d $LOOPDEVICE - # Corto el archivo al tamaño del sistema de ficheros - echo "truncate --size=\"$EXTSIZE\"k $IMGFILE" - echolog "truncate --size=\"$EXTSIZE\"k $IMGFILE" - truncate --size="$EXTSIZE"k $IMGFILE - fi - else - umount $DIRMOUNT || (echolog "Error al desmontar la imagen $IMGFILE"; exit 1) - fi - rmdir $DIRMOUNT - echo "reduced" >$LOCKFILE - - ;; - default) - echolog "Solicitud con parametros \"$PARM\" no realizada, no registrada o con errores" - ;; -esac diff --git a/native/Sources/Services/ogAdmServer/Makefile b/native/Sources/Services/ogAdmServer/Makefile deleted file mode 100644 index 0c3360e..0000000 --- a/native/Sources/Services/ogAdmServer/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -# makefile - -# Nombre del proyecto -PROYECTO := ogAdmServer - -# Directorio de instalación -INSTALL_DIR := /opt/opengnsys - -# Opciones de compilacion -CFLAGS := $(shell mysql_config --cflags) -CFLAGS += -g -Wall -I../../Includes - -# Opciones de linkado -LDFLAGS := -Wl,--no-as-needed $(shell mysql_config --libs) -lev -ljansson -ldbi - -# Ficheros objetos -OBJS := sources/ogAdmServer.o sources/dbi.o - - -all: $(PROYECTO) - -$(PROYECTO): $(OBJS) - gcc $(LDFLAGS) $(CFLAGS) $(OBJS) -o $(PROYECTO) - -install: $(PROYECTO) - cp $(PROYECTO) $(INSTALL_DIR)/sbin - cp $(PROYECTO).cfg $(INSTALL_DIR)/etc - -clean: - rm -f $(PROYECTO) $(OBJS) - -uninstall: clean - rm -f /usr/local/sbin/$(PROYECTO) /usr/local/etc/$(PROYECTO).cfg - -sources/%.o: sources/%.c - gcc $(CFLAGS) -c -o"$@" "$<" - - diff --git a/native/Sources/Services/ogAdmServer/Sources-Services-.txt b/native/Sources/Services/ogAdmServer/Sources-Services-.txt deleted file mode 100644 index 8ed387e..0000000 --- a/native/Sources/Services/ogAdmServer/Sources-Services-.txt +++ /dev/null @@ -1 +0,0 @@ -Sources/Services/ogAdmServer \ No newline at end of file diff --git a/native/Sources/Services/ogAdmServer/ogAdmServer.cfg b/native/Sources/Services/ogAdmServer/ogAdmServer.cfg deleted file mode 100644 index 790b233..0000000 --- a/native/Sources/Services/ogAdmServer/ogAdmServer.cfg +++ /dev/null @@ -1,8 +0,0 @@ -ServidorAdm=SERVERIP -PUERTO=2008 -USUARIO=DBUSER -PASSWORD=DBPASSWORD -datasource=localhost -CATALOG=DATABASE -INTERFACE=eth0 -APITOKEN=REPOKEY diff --git a/native/Sources/Services/ogAdmServer/sources/dbi.c b/native/Sources/Services/ogAdmServer/sources/dbi.c deleted file mode 100644 index cc5c5b5..0000000 --- a/native/Sources/Services/ogAdmServer/sources/dbi.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "dbi.h" - -struct og_dbi *og_dbi_open(struct og_dbi_config *config) -{ - struct og_dbi *dbi; - - dbi = (struct og_dbi *)malloc(sizeof(struct og_dbi)); - if (!dbi) - return NULL; - - dbi_initialize_r(NULL, &dbi->inst); - dbi->conn = dbi_conn_new_r("mysql", dbi->inst); - if (!dbi->conn) { - free(dbi); - return NULL; - } - - dbi_conn_set_option(dbi->conn, "host", config->host); - dbi_conn_set_option(dbi->conn, "username", config->user); - dbi_conn_set_option(dbi->conn, "password", config->passwd); - dbi_conn_set_option(dbi->conn, "dbname", config->database); - dbi_conn_set_option(dbi->conn, "encoding", "UTF-8"); - - if (dbi_conn_connect(dbi->conn) < 0) { - free(dbi); - return NULL; - } - - return dbi; -} - -void og_dbi_close(struct og_dbi *dbi) -{ - dbi_conn_close(dbi->conn); - dbi_shutdown_r(dbi->inst); - free(dbi); -} diff --git a/native/Sources/Services/ogAdmServer/sources/dbi.h b/native/Sources/Services/ogAdmServer/sources/dbi.h deleted file mode 100644 index bbb91d7..0000000 --- a/native/Sources/Services/ogAdmServer/sources/dbi.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef __OG_DBI -#define __OG_DBI - -#include - -struct og_dbi_config { - const char *user; - const char *passwd; - const char *host; - const char *database; -}; - -struct og_dbi { - dbi_conn conn; - dbi_inst inst; -}; - -struct og_dbi *og_dbi_open(struct og_dbi_config *config); -void og_dbi_close(struct og_dbi *db); -#define OG_DB_IP_MAXLEN 15 - -#endif diff --git a/native/Sources/Services/ogAdmServer/sources/dbi.o b/native/Sources/Services/ogAdmServer/sources/dbi.o deleted file mode 100644 index f42f644975120dd17e4d0168e01b47973cd77f2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6728 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 -#include -#include -#include -#include -#include -#include -#include - -static char usuario[LONPRM]; // Usuario de acceso a la base de datos -static char pasguor[LONPRM]; // Password del usuario -static char datasource[LONPRM]; // Dirección IP del gestor de base de datos -static char catalog[LONPRM]; // Nombre de la base de datos -static char interface[LONPRM]; // Interface name -static char auth_token[LONPRM]; // API token - -static struct og_dbi_config dbi_config = { - .user = usuario, - .passwd = pasguor, - .host = datasource, - .database = catalog, -}; - -//________________________________________________________________________________________________________ -// Función: tomaConfiguracion -// -// Descripción: -// Lee el fichero de configuración del servicio -// Parámetros: -// filecfg : Ruta completa al fichero de configuración -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -//________________________________________________________________________________________________________ -static bool tomaConfiguracion(const char *filecfg) -{ - char buf[1024], *line; - char *key, *value; - FILE *fcfg; - - if (filecfg == NULL || strlen(filecfg) == 0) { - syslog(LOG_ERR, "No configuration file has been specified\n"); - return false; - } - - fcfg = fopen(filecfg, "rt"); - if (fcfg == NULL) { - syslog(LOG_ERR, "Cannot open configuration file `%s'\n", - filecfg); - return false; - } - - servidoradm[0] = '\0'; //inicializar variables globales - - line = fgets(buf, sizeof(buf), fcfg); - while (line != NULL) { - const char *delim = "="; - - line[strlen(line) - 1] = '\0'; - - key = strtok(line, delim); - value = strtok(NULL, delim); - - if (!strcmp(StrToUpper(key), "SERVIDORADM")) - snprintf(servidoradm, sizeof(servidoradm), "%s", value); - else if (!strcmp(StrToUpper(key), "PUERTO")) - snprintf(puerto, sizeof(puerto), "%s", value); - else if (!strcmp(StrToUpper(key), "USUARIO")) - snprintf(usuario, sizeof(usuario), "%s", value); - else if (!strcmp(StrToUpper(key), "PASSWORD")) - snprintf(pasguor, sizeof(pasguor), "%s", value); - else if (!strcmp(StrToUpper(key), "DATASOURCE")) - snprintf(datasource, sizeof(datasource), "%s", value); - else if (!strcmp(StrToUpper(key), "CATALOG")) - snprintf(catalog, sizeof(catalog), "%s", value); - else if (!strcmp(StrToUpper(key), "INTERFACE")) - snprintf(interface, sizeof(interface), "%s", value); - else if (!strcmp(StrToUpper(key), "APITOKEN")) - snprintf(auth_token, sizeof(auth_token), "%s", value); - - line = fgets(buf, sizeof(buf), fcfg); - } - - fclose(fcfg); - - if (!servidoradm[0]) { - syslog(LOG_ERR, "Missing SERVIDORADM in configuration file\n"); - return false; - } - if (!puerto[0]) { - syslog(LOG_ERR, "Missing PUERTO in configuration file\n"); - return false; - } - if (!usuario[0]) { - syslog(LOG_ERR, "Missing USUARIO in configuration file\n"); - return false; - } - if (!pasguor[0]) { - syslog(LOG_ERR, "Missing PASSWORD in configuration file\n"); - return false; - } - if (!datasource[0]) { - syslog(LOG_ERR, "Missing DATASOURCE in configuration file\n"); - return false; - } - if (!catalog[0]) { - syslog(LOG_ERR, "Missing CATALOG in configuration file\n"); - return false; - } - if (!interface[0]) - syslog(LOG_ERR, "Missing INTERFACE in configuration file\n"); - - return true; -} - -enum og_client_state { - OG_CLIENT_RECEIVING_HEADER = 0, - OG_CLIENT_RECEIVING_PAYLOAD, - OG_CLIENT_PROCESSING_REQUEST, -}; - -#define OG_MSG_REQUEST_MAXLEN 16384 - -/* Shut down connection if there is no complete message after 10 seconds. */ -#define OG_CLIENT_TIMEOUT 10 - -struct og_client { - struct ev_io io; - struct ev_timer timer; - struct sockaddr_in addr; - enum og_client_state state; - char buf[OG_MSG_REQUEST_MAXLEN]; - unsigned int buf_len; - unsigned int msg_len; - int keepalive_idx; - bool rest; - int content_length; - char auth_token[64]; -}; - -static inline int og_client_socket(const struct og_client *cli) -{ - return cli->io.fd; -} - -// ________________________________________________________________________________________________________ -// Función: clienteDisponible -// -// Descripción: -// Comprueba la disponibilidad del cliente para recibir comandos interactivos -// Parametros: -// - ip : La ip del cliente a buscar -// - idx: (Salida) Indice que ocupa el cliente, de estar ya registrado -// Devuelve: -// true: Si el cliente está disponible -// false: En caso contrario -// ________________________________________________________________________________________________________ -bool clienteDisponible(char *ip, int* idx) -{ - int estado; - - if (clienteExistente(ip, idx)) { - estado = strcmp(tbsockets[*idx].estado, CLIENTE_OCUPADO); // Cliente ocupado - if (estado == 0) - return false; - - estado = strcmp(tbsockets[*idx].estado, CLIENTE_APAGADO); // Cliente apagado - if (estado == 0) - return false; - - estado = strcmp(tbsockets[*idx].estado, CLIENTE_INICIANDO); // Cliente en proceso de inclusión - if (estado == 0) - return false; - - return true; // En caso contrario el cliente está disponible - } - return false; // Cliente no está registrado en el sistema -} -// ________________________________________________________________________________________________________ -// Función: clienteExistente -// -// Descripción: -// Comprueba si el cliente está registrado en la tabla de socket del sistema -// Parametros: -// - ip : La ip del cliente a buscar -// - idx:(Salida) Indice que ocupa el cliente, de estar ya registrado -// Devuelve: -// true: Si el cliente está registrado -// false: En caso contrario -// ________________________________________________________________________________________________________ -bool clienteExistente(char *ip, int* idx) -{ - int i; - for (i = 0; i < MAXIMOS_CLIENTES; i++) { - if (contieneIP(ip, tbsockets[i].ip)) { // Si existe la IP en la cadena - *idx = i; - return true; - } - } - return false; -} -// ________________________________________________________________________________________________________ -// Función: hayHueco -// -// Descripción: -// Esta función devuelve true o false dependiendo de que haya hueco en la tabla de sockets para un nuevo cliente. -// Parametros: -// - idx: Primer indice libre que se podrn utilizar -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool hayHueco(int *idx) -{ - int i; - - for (i = 0; i < MAXIMOS_CLIENTES; i++) { - if (strncmp(tbsockets[i].ip, "\0", 1) == 0) { // Hay un hueco - *idx = i; - return true; - } - } - return false; -} -// ________________________________________________________________________________________________________ -// Función: InclusionClienteWin -// -// Descripción: -// Esta función incorpora el socket de un nuevo cliente Windows o Linux a la tabla de clientes -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool InclusionClienteWinLnx(TRAMA *ptrTrama, struct og_client *cli) -{ - int socket_c = og_client_socket(cli); - int res,idordenador,lon; - char nombreordenador[LONFIL]; - - res = procesoInclusionClienteWinLnx(socket_c, ptrTrama, &idordenador, - nombreordenador); - - // Prepara la trama de respuesta - - initParametros(ptrTrama,0); - ptrTrama->tipo=MSG_RESPUESTA; - lon = sprintf(ptrTrama->parametros, "nfn=RESPUESTA_InclusionClienteWinLnx\r"); - lon += sprintf(ptrTrama->parametros + lon, "ido=%d\r", idordenador); - lon += sprintf(ptrTrama->parametros + lon, "npc=%s\r", nombreordenador); - lon += sprintf(ptrTrama->parametros + lon, "res=%d\r", res); - - if (!mandaTrama(&socket_c, ptrTrama)) { - syslog(LOG_ERR, "failed to send response to %s:%hu reason=%s\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), - strerror(errno)); - return false; - } - return true; -} -// ________________________________________________________________________________________________________ -// Función: procesoInclusionClienteWinLnx -// -// Descripción: -// Implementa el proceso de inclusión en el sistema del Cliente Windows o Linux -// Parámetros de entrada: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Parámetros de salida: -// - ido: Identificador del ordenador -// - nombreordenador: Nombre del ordenador -// Devuelve: -// Código del error producido en caso de ocurrir algún error, 0 si el proceso es correcto -// ________________________________________________________________________________________________________ -bool procesoInclusionClienteWinLnx(int socket_c, TRAMA *ptrTrama, int *idordenador, char *nombreordenador) - { - struct og_dbi *dbi; - const char *msglog; - dbi_result result; - char *iph; - - // Toma parámetros - iph = copiaParametro("iph",ptrTrama); // Toma ip - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - goto err_dbi_open; - } - - result = dbi_conn_queryf(dbi->conn, - "SELECT idordenador,nombreordenador FROM ordenadores " - " WHERE ordenadores.ip = '%s'", iph); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - goto err_query_fail; - } - - if (!dbi_result_next_row(result)) { - syslog(LOG_ERR, "client does not exist in database (%s:%d)\n", - __func__, __LINE__); - dbi_result_free(result); - goto err_query_fail; - } - - syslog(LOG_DEBUG, "Client %s requesting inclusion\n", iph); - - *idordenador = dbi_result_get_uint(result, "idordenador"); - nombreordenador = (char *)dbi_result_get_string(result, "nombreordenador"); - - dbi_result_free(result); - og_dbi_close(dbi); - - if (!registraCliente(iph)) { // Incluyendo al cliente en la tabla de sokets - liberaMemoria(iph); - syslog(LOG_ERR, "client table is full\n"); - return false; - } - liberaMemoria(iph); - return true; - -err_query_fail: - og_dbi_close(dbi); -err_dbi_open: - liberaMemoria(iph); - return false; -} -// ________________________________________________________________________________________________________ -// Función: InclusionCliente -// -// Descripción: -// Esta función incorpora el socket de un nuevo cliente a la tabla de clientes y le devuelve alguna de sus propiedades: -// nombre, identificador, tamaño de la caché , etc ... -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool InclusionCliente(TRAMA *ptrTrama, struct og_client *cli) -{ - int socket_c = og_client_socket(cli); - - if (!procesoInclusionCliente(cli, ptrTrama)) { - initParametros(ptrTrama,0); - strcpy(ptrTrama->parametros, "nfn=RESPUESTA_InclusionCliente\rres=0\r"); - if (!mandaTrama(&socket_c, ptrTrama)) { - syslog(LOG_ERR, "failed to send response to %s:%hu reason=%s\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), - strerror(errno)); - return false; - } - } - return true; -} -// ________________________________________________________________________________________________________ -// Función: procesoInclusionCliente -// -// Descripción: -// Implementa el proceso de inclusión en el sistema del Cliente -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -bool procesoInclusionCliente(struct og_client *cli, TRAMA *ptrTrama) -{ - int socket_c = og_client_socket(cli); - const char *msglog, *str; - struct og_dbi *dbi; - dbi_result result; - - char *iph, *cfg; - char nombreordenador[LONFIL]; - int lon, resul, idordenador, cache, idproautoexec, idaula, idcentro; - - // Toma parámetros - iph = copiaParametro("iph",ptrTrama); // Toma ip - cfg = copiaParametro("cfg",ptrTrama); // Toma configuracion - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - goto err_dbi_open; - } - - // Recupera los datos del cliente - result = dbi_conn_queryf(dbi->conn, - "SELECT ordenadores.*,aulas.idaula,centros.idcentro FROM ordenadores " - " INNER JOIN aulas ON aulas.idaula=ordenadores.idaula" - " INNER JOIN centros ON centros.idcentro=aulas.idcentro" - " WHERE ordenadores.ip = '%s'", iph); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - goto err_query_fail; - } - - if (!dbi_result_next_row(result)) { - syslog(LOG_ERR, "client does not exist in database (%s:%d)\n", - __func__, __LINE__); - dbi_result_free(result); - goto err_query_fail; - } - - syslog(LOG_DEBUG, "Client %s requesting inclusion\n", iph); - - idordenador = dbi_result_get_uint(result, "idordenador"); - str = (char *)dbi_result_get_string(result, "nombreordenador"); - sprintf(nombreordenador, "%s", str); - cache = dbi_result_get_uint(result, "cache"); - idproautoexec = dbi_result_get_uint(result, "idproautoexec"); - idaula = dbi_result_get_uint(result, "idaula"); - idcentro = dbi_result_get_uint(result, "idcentro"); - dbi_result_free(result); - - resul = actualizaConfiguracion(dbi, cfg, idordenador); // Actualiza la configuración del ordenador - liberaMemoria(cfg); - og_dbi_close(dbi); - - if (!resul) { - liberaMemoria(iph); - syslog(LOG_ERR, "Cannot add client to database\n"); - return false; - } - - if (!registraCliente(iph)) { // Incluyendo al cliente en la tabla de sokets - liberaMemoria(iph); - syslog(LOG_ERR, "client table is full\n"); - return false; - } - - /*------------------------------------------------------------------------------------------------------------------------------ - Prepara la trama de respuesta - -------------------------------------------------------------------------------------------------------------------------------*/ - initParametros(ptrTrama,0); - ptrTrama->tipo=MSG_RESPUESTA; - lon = sprintf(ptrTrama->parametros, "nfn=RESPUESTA_InclusionCliente\r"); - lon += sprintf(ptrTrama->parametros + lon, "ido=%d\r", idordenador); - lon += sprintf(ptrTrama->parametros + lon, "npc=%s\r", nombreordenador); - lon += sprintf(ptrTrama->parametros + lon, "che=%d\r", cache); - lon += sprintf(ptrTrama->parametros + lon, "exe=%d\r", idproautoexec); - lon += sprintf(ptrTrama->parametros + lon, "ida=%d\r", idaula); - lon += sprintf(ptrTrama->parametros + lon, "idc=%d\r", idcentro); - lon += sprintf(ptrTrama->parametros + lon, "res=%d\r", 1); // Confirmación proceso correcto - - if (!mandaTrama(&socket_c, ptrTrama)) { - syslog(LOG_ERR, "failed to send response to %s:%hu reason=%s\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), - strerror(errno)); - return false; - } - liberaMemoria(iph); - return true; - -err_query_fail: - og_dbi_close(dbi); -err_dbi_open: - liberaMemoria(iph); - liberaMemoria(cfg); - return false; -} -// ________________________________________________________________________________________________________ -// Función: actualizaConfiguracion -// -// Descripción: -// Esta función actualiza la base de datos con la configuracion de particiones de un cliente -// Parámetros: -// - db: Objeto base de datos (ya operativo) -// - tbl: Objeto tabla -// - cfg: cadena con una Configuración -// - ido: Identificador del ordenador cliente -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// Especificaciones: -// Los parametros de la configuración son: -// par= Número de partición -// cpt= Codigo o tipo de partición -// sfi= Sistema de ficheros que está implementado en la partición -// soi= Nombre del sistema de ficheros instalado en la partición -// tam= Tamaño de la partición -// ________________________________________________________________________________________________________ -bool actualizaConfiguracion(struct og_dbi *dbi, char *cfg, int ido) -{ - int lon, p, c,i, dato, swu, idsoi, idsfi,k; - char *ptrPar[MAXPAR], *ptrCfg[7], *ptrDual[2], tbPar[LONSTD]; - char *ser, *disk, *par, *cpt, *sfi, *soi, *tam, *uso; // Parametros de configuración. - dbi_result result, result_update; - const char *msglog; - - lon = 0; - p = splitCadena(ptrPar, cfg, '\n'); - for (i = 0; i < p; i++) { - c = splitCadena(ptrCfg, ptrPar[i], '\t'); - - // Si la 1ª línea solo incluye el número de serie del equipo; actualizar BD. - if (i == 0 && c == 1) { - splitCadena(ptrDual, ptrCfg[0], '='); - ser = ptrDual[1]; - if (strlen(ser) > 0) { - // Solo actualizar si número de serie no existía. - result = dbi_conn_queryf(dbi->conn, - "UPDATE ordenadores SET numserie='%s'" - " WHERE idordenador=%d AND numserie IS NULL", - ser, ido); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result); - } - continue; - } - - // Distribución de particionado. - disk = par = cpt = sfi = soi = tam = uso = NULL; - - splitCadena(ptrDual, ptrCfg[0], '='); - disk = ptrDual[1]; // Número de disco - - splitCadena(ptrDual, ptrCfg[1], '='); - par = ptrDual[1]; // Número de partición - - k=splitCadena(ptrDual, ptrCfg[2], '='); - if(k==2){ - cpt = ptrDual[1]; // Código de partición - }else{ - cpt = (char*)"0"; - } - - k=splitCadena(ptrDual, ptrCfg[3], '='); - if(k==2){ - sfi = ptrDual[1]; // Sistema de ficheros - /* Comprueba existencia del s0xistema de ficheros instalado */ - idsfi = checkDato(dbi, sfi, "sistemasficheros", "descripcion","idsistemafichero"); - } - else - idsfi=0; - - k=splitCadena(ptrDual, ptrCfg[4], '='); - if(k==2){ // Sistema operativo detecdtado - soi = ptrDual[1]; // Nombre del S.O. instalado - /* Comprueba existencia del sistema operativo instalado */ - idsoi = checkDato(dbi, soi, "nombresos", "nombreso", "idnombreso"); - } - else - idsoi=0; - - splitCadena(ptrDual, ptrCfg[5], '='); - tam = ptrDual[1]; // Tamaño de la partición - - splitCadena(ptrDual, ptrCfg[6], '='); - uso = ptrDual[1]; // Porcentaje de uso del S.F. - - lon += sprintf(tbPar + lon, "(%s, %s),", disk, par); - - result = dbi_conn_queryf(dbi->conn, - "SELECT numdisk, numpar, tamano, uso, idsistemafichero, idnombreso" - " FROM ordenadores_particiones" - " WHERE idordenador=%d AND numdisk=%s AND numpar=%s", - ido, disk, par); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - if (!dbi_result_next_row(result)) { - result_update = dbi_conn_queryf(dbi->conn, - "INSERT INTO ordenadores_particiones(idordenador,numdisk,numpar,codpar,tamano,uso,idsistemafichero,idnombreso,idimagen)" - " VALUES(%d,%s,%s,0x%s,%s,%s,%d,%d,0)", - ido, disk, par, cpt, tam, uso, idsfi, idsoi); - if (!result_update) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result_update); - - } else { // Existe el registro - swu = true; // Se supone que algún dato ha cambiado - - dato = dbi_result_get_ulonglong(result, "tamano"); - if (atoi(tam) == dato) {// Parámetro tamaño igual al almacenado - dato = dbi_result_get_uint(result, "idsistemafichero"); - if (idsfi == dato) {// Parámetro sistema de fichero igual al almacenado - dato = dbi_result_get_uint(result, "idnombreso"); - if (idsoi == dato) {// Parámetro sistema de fichero distinto al almacenado - swu = false; // Todos los parámetros de la partición son iguales, no se actualiza - } - } - } - if (swu) { // Hay que actualizar los parámetros de la partición - result_update = dbi_conn_queryf(dbi->conn, - "UPDATE ordenadores_particiones SET " - " codpar=0x%s," - " tamano=%s," - " uso=%s," - " idsistemafichero=%d," - " idnombreso=%d," - " idimagen=0," - " idperfilsoft=0," - " fechadespliegue=NULL" - " WHERE idordenador=%d AND numdisk=%s AND numpar=%s", - cpt, tam, uso, idsfi, idsoi, ido, disk, par); - } else { // Actualizar porcentaje de uso. - result_update = dbi_conn_queryf(dbi->conn, - "UPDATE ordenadores_particiones SET " - " codpar=0x%s," - " uso=%s" - " WHERE idordenador=%d AND numdisk=%s AND numpar=%s", - cpt, uso, ido, disk, par); - } - if (!result_update) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - - dbi_result_free(result_update); - } - dbi_result_free(result); - } - lon += sprintf(tbPar + lon, "(0,0)"); - // Eliminar particiones almacenadas que ya no existen - result_update = dbi_conn_queryf(dbi->conn, - "DELETE FROM ordenadores_particiones WHERE idordenador=%d AND (numdisk, numpar) NOT IN (%s)", - ido, tbPar); - if (!result_update) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result_update); - - return true; -} -// ________________________________________________________________________________________________________ -// Función: checkDato -// -// Descripción: -// Esta función comprueba si existe un dato en una tabla y si no es así lo incluye. devuelve en -// cualquier caso el identificador del registro existenet o del insertado -// Parámetros: -// - db: Objeto base de datos (ya operativo) -// - tbl: Objeto tabla -// - dato: Dato -// - tabla: Nombre de la tabla -// - nomdato: Nombre del dato en la tabla -// - nomidentificador: Nombre del identificador en la tabla -// Devuelve: -// El identificador del registro existente o el del insertado -// -// Especificaciones: -// En caso de producirse algún error se devuelve el valor 0 -// ________________________________________________________________________________________________________ - -int checkDato(struct og_dbi *dbi, char *dato, const char *tabla, - const char *nomdato, const char *nomidentificador) -{ - const char *msglog; - int identificador; - dbi_result result; - - if (strlen(dato) == 0) - return (0); // EL dato no tiene valor - result = dbi_conn_queryf(dbi->conn, - "SELECT %s FROM %s WHERE %s ='%s'", nomidentificador, - tabla, nomdato, dato); - - // Ejecuta consulta - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return (0); - } - if (!dbi_result_next_row(result)) { // Software NO existente - dbi_result_free(result); - - result = dbi_conn_queryf(dbi->conn, - "INSERT INTO %s (%s) VALUES('%s')", tabla, nomdato, dato); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - og_info((char *)msglog); - return (0); - } - // Recupera el identificador del software - identificador = dbi_conn_sequence_last(dbi->conn, NULL); - } else { - identificador = dbi_result_get_uint(result, nomidentificador); - } - dbi_result_free(result); - - return (identificador); -} -// ________________________________________________________________________________________________________ -// Función: registraCliente -// -// Descripción: -// Incluye al cliente en la tabla de sokets -// Parámetros: -// - iph: Dirección ip del cliente -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -bool registraCliente(char *iph) -{ - int idx; - - if (!clienteExistente(iph, &idx)) { // Si no existe la IP ... - if (!hayHueco(&idx)) { // Busca hueco para el nuevo cliente - return false; // No hay huecos - } - } - strcpy(tbsockets[idx].ip, iph); // Copia IP - strcpy(tbsockets[idx].estado, CLIENTE_INICIANDO); // Actualiza el estado del cliente - return true; -} -// ________________________________________________________________________________________________________ -// Función: AutoexecCliente -// -// Descripción: -// Envía archivo de autoexec al cliente -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool AutoexecCliente(TRAMA *ptrTrama, struct og_client *cli) -{ - int socket_c = og_client_socket(cli); - int lon; - char *iph, *exe; - FILE *fileexe; - char fileautoexec[LONPRM]; - char parametros[LONGITUD_PARAMETROS]; - struct og_dbi *dbi; - - iph = copiaParametro("iph",ptrTrama); // Toma dirección IP del cliente - exe = copiaParametro("exe",ptrTrama); // Toma identificador del procedimiento inicial - - sprintf(fileautoexec, "/tmp/Sautoexec-%s", iph); - liberaMemoria(iph); - fileexe = fopen(fileautoexec, "wb"); // Abre fichero de script - if (fileexe == NULL) { - syslog(LOG_ERR, "cannot create temporary file\n"); - return false; - } - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return false; - } - initParametros(ptrTrama,0); - if (recorreProcedimientos(dbi, parametros, fileexe, exe)) { - lon = sprintf(ptrTrama->parametros, "nfn=RESPUESTA_AutoexecCliente\r"); - lon += sprintf(ptrTrama->parametros + lon, "nfl=%s\r", fileautoexec); - lon += sprintf(ptrTrama->parametros + lon, "res=1\r"); - } else { - lon = sprintf(ptrTrama->parametros, "nfn=RESPUESTA_AutoexecCliente\r"); - lon += sprintf(ptrTrama->parametros + lon, "res=0\r"); - } - - og_dbi_close(dbi); - fclose(fileexe); - - if (!mandaTrama(&socket_c, ptrTrama)) { - liberaMemoria(exe); - syslog(LOG_ERR, "failed to send response to %s:%hu reason=%s\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), - strerror(errno)); - return false; - } - liberaMemoria(exe); - return true; -} -// ________________________________________________________________________________________________________ -// Función: recorreProcedimientos -// -// Descripción: -// Crea un archivo con el código de un procedimiento separando cada comando por un salto de linea -// Parámetros: -// Database db,char* parametros,FILE* fileexe,char* idp -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -bool recorreProcedimientos(struct og_dbi *dbi, char *parametros, FILE *fileexe, char *idp) -{ - char idprocedimiento[LONPRM]; - int procedimientoid, lsize; - const char *msglog, *param; - dbi_result result; - - result = dbi_conn_queryf(dbi->conn, - "SELECT procedimientoid,parametros FROM procedimientos_acciones" - " WHERE idprocedimiento=%s ORDER BY orden", idp); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - while (dbi_result_next_row(result)) { - procedimientoid = dbi_result_get_uint(result, "procedimientoid"); - if (procedimientoid > 0) { // Procedimiento recursivo - sprintf(idprocedimiento, "%d", procedimientoid); - if (!recorreProcedimientos(dbi, parametros, fileexe, idprocedimiento)) { - return false; - } - } else { - param = dbi_result_get_string(result, "parametros"); - sprintf(parametros, "%s@", param); - lsize = strlen(parametros); - fwrite(parametros, 1, lsize, fileexe); // Escribe el código a ejecutar - } - } - dbi_result_free(result); - - return true; -} -// ________________________________________________________________________________________________________ -// Función: ComandosPendientes -// -// Descripción: -// Esta función busca en la base de datos,comandos pendientes de ejecutar por un ordenador concreto -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool ComandosPendientes(TRAMA *ptrTrama, struct og_client *cli) -{ - int socket_c = og_client_socket(cli); - char *ido,*iph,pids[LONPRM]; - int ids, idx; - - iph = copiaParametro("iph",ptrTrama); // Toma dirección IP - ido = copiaParametro("ido",ptrTrama); // Toma identificador del ordenador - - if (!clienteExistente(iph, &idx)) { // Busca índice del cliente - liberaMemoria(iph); - liberaMemoria(ido); - syslog(LOG_ERR, "client does not exist\n"); - return false; - } - if (buscaComandos(ido, ptrTrama, &ids)) { // Existen comandos pendientes - ptrTrama->tipo = MSG_COMANDO; - sprintf(pids, "\rids=%d\r", ids); - strcat(ptrTrama->parametros, pids); - strcpy(tbsockets[idx].estado, CLIENTE_OCUPADO); - } else { - initParametros(ptrTrama,0); - strcpy(ptrTrama->parametros, "nfn=NoComandosPtes\r"); - } - if (!mandaTrama(&socket_c, ptrTrama)) { - liberaMemoria(iph); - liberaMemoria(ido); - syslog(LOG_ERR, "failed to send response to %s:%hu reason=%s\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), - strerror(errno)); - return false; - } - liberaMemoria(iph); - liberaMemoria(ido); - return true; -} -// ________________________________________________________________________________________________________ -// Función: buscaComandos -// -// Descripción: -// Busca en la base de datos,comandos pendientes de ejecutar por el cliente -// Parámetros: -// - ido: Identificador del ordenador -// - cmd: Parámetros del comando (Salida) -// - ids: Identificador de la sesion(Salida) -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -bool buscaComandos(char *ido, TRAMA *ptrTrama, int *ids) -{ - const char *param, *msglog; - struct og_dbi *dbi; - dbi_result result; - unsigned int lonprm; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - goto err_dbi_open; - } - result = dbi_conn_queryf(dbi->conn, - "SELECT sesion, parametros"\ - " FROM acciones WHERE idordenador=%s AND estado='%d'"\ - " ORDER BY idaccion", ido, ACCION_INICIADA); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - goto err_query_fail; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - og_dbi_close(dbi); - return false; // No hay comandos pendientes - } - - *ids = dbi_result_get_uint(result, "sesion"); - param = dbi_result_get_string(result, "parametros"); - lonprm = strlen(param); - - if(!initParametros(ptrTrama,lonprm + LONGITUD_PARAMETROS)){ - syslog(LOG_ERR, "%s:%d OOM\n", __FILE__, __LINE__); - goto err_init_params; - } - sprintf(ptrTrama->parametros, "%s", param); - - dbi_result_free(result); - og_dbi_close(dbi); - - return true; // Hay comandos pendientes, se toma el primero de la cola - -err_init_params: - dbi_result_free(result); -err_query_fail: - og_dbi_close(dbi); -err_dbi_open: - return false; -} -// ________________________________________________________________________________________________________ -// Función: DisponibilidadComandos -// -// Descripción: -// Esta función habilita a un cliente para recibir comandos desde la consola -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -// -static bool DisponibilidadComandos(TRAMA *ptrTrama, struct og_client *cli) -{ - char *iph, *tpc; - int idx; - - iph = copiaParametro("iph",ptrTrama); // Toma ip - if (!clienteExistente(iph, &idx)) { // Busca índice del cliente - liberaMemoria(iph); - syslog(LOG_ERR, "client does not exist\n"); - return false; - } - tpc = copiaParametro("tpc",ptrTrama); // Tipo de cliente (Plataforma y S.O.) - strcpy(tbsockets[idx].estado, tpc); - cli->keepalive_idx = idx; - liberaMemoria(iph); - liberaMemoria(tpc); - return true; -} -// ________________________________________________________________________________________________________ -// Función: respuestaEstandar -// -// Descripción: -// Esta función actualiza la base de datos con el resultado de la ejecución de un comando con seguimiento -// Parámetros: -// - res: resultado de la ejecución del comando -// - der: Descripción del error si hubiese habido -// - iph: Dirección IP -// - ids: identificador de la sesión -// - ido: Identificador del ordenador que notifica -// - db: Objeto base de datos (operativo) -// - tbl: Objeto tabla -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool respuestaEstandar(TRAMA *ptrTrama, char *iph, char *ido, - struct og_dbi *dbi) -{ - char *res, *ids, *der; - char fechafin[LONPRM]; - const char *msglog; - dbi_result result; - struct tm* st; - int idaccion; - - ids = copiaParametro("ids",ptrTrama); - res = copiaParametro("res",ptrTrama); - - if (ids == NULL) { - if (atoi(res) == ACCION_FALLIDA) { - liberaMemoria(res); - return false; - } - liberaMemoria(res); - return true; - } - - if (atoi(ids) == 0) { - liberaMemoria(ids); - if (atoi(res) == ACCION_FALLIDA) { - liberaMemoria(res); - return false; - } - liberaMemoria(res); - return true; - } - - result = dbi_conn_queryf(dbi->conn, - "SELECT * FROM acciones WHERE idordenador=%s" - " AND sesion=%s ORDER BY idaccion", ido,ids); - - liberaMemoria(ids); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - if (!dbi_result_next_row(result)) { - syslog(LOG_ERR, "no actions available\n"); - dbi_result_free(result); - return true; - } - - idaccion = dbi_result_get_uint(result, "idaccion"); - dbi_result_free(result); - - st = tomaHora(); - sprintf(fechafin, "%d/%d/%d %d:%d:%d", st->tm_year + 1900, st->tm_mon + 1, - st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec); - - der = copiaParametro("der",ptrTrama); // Toma descripción del error (si hubiera habido) - - result = dbi_conn_queryf(dbi->conn, - "UPDATE acciones"\ - " SET resultado='%s',estado='%d',fechahorafin='%s',descrinotificacion='%s'"\ - " WHERE idordenador=%s AND idaccion=%d", - res, ACCION_FINALIZADA, fechafin, der, ido, idaccion); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - liberaMemoria(res); - liberaMemoria(der); - og_info((char *)msglog); - return false; - } - dbi_result_free(result); - - liberaMemoria(der); - - if (atoi(res) == ACCION_FALLIDA) { - liberaMemoria(res); - return false; - } - - liberaMemoria(res); - return true; -} - -static bool og_send_cmd(char *ips_array[], int ips_array_len, - const char *state, TRAMA *ptrTrama) -{ - int i, idx; - - for (i = 0; i < ips_array_len; i++) { - if (clienteDisponible(ips_array[i], &idx)) { // Si el cliente puede recibir comandos - int sock = tbsockets[idx].cli ? tbsockets[idx].cli->io.fd : -1; - - strcpy(tbsockets[idx].estado, state); // Actualiza el estado del cliente - if (sock >= 0 && !mandaTrama(&sock, ptrTrama)) { - syslog(LOG_ERR, "failed to send response to %s:%s\n", - ips_array[i], strerror(errno)); - } - } - } - return true; -} - -// ________________________________________________________________________________________________________ -// Función: enviaComando -// -// Descripción: -// Envía un comando a los clientes -// Parámetros: -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// - estado: Estado en el se deja al cliente mientras se ejecuta el comando -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -bool enviaComando(TRAMA* ptrTrama, const char *estado) -{ - char *iph, *Ipes, *ptrIpes[MAXIMOS_CLIENTES]; - int lon; - - iph = copiaParametro("iph",ptrTrama); // Toma dirección/es IP - lon = strlen(iph); // Calcula longitud de la cadena de direccion/es IPE/S - Ipes = (char*) reservaMemoria(lon + 1); - if (Ipes == NULL) { - syslog(LOG_ERR, "%s:%d OOM\n", __FILE__, __LINE__); - return false; - } - - strcpy(Ipes, iph); // Copia cadena de IPES - liberaMemoria(iph); - - lon = splitCadena(ptrIpes, Ipes, ';'); - FINCADaINTRO(ptrTrama); - - if (!og_send_cmd(ptrIpes, lon, estado, ptrTrama)) - return false; - - liberaMemoria(Ipes); - return true; -} -//______________________________________________________________________________________________________ -// Función: respuestaConsola -// -// Descripción: -// Envia una respuesta a la consola sobre el resultado de la ejecución de un comando -// Parámetros: -// - socket_c: (Salida) Socket utilizado para el envío -// - res: Resultado del envío del comando -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -bool respuestaConsola(int socket_c, TRAMA *ptrTrama, int res) -{ - initParametros(ptrTrama,0); - sprintf(ptrTrama->parametros, "res=%d\r", res); - if (!mandaTrama(&socket_c, ptrTrama)) { - syslog(LOG_ERR, "%s:%d failed to send response: %s\n", - __func__, __LINE__, strerror(errno)); - return false; - } - return true; -} -// ________________________________________________________________________________________________________ -// Función: Levanta -// -// Descripción: -// Enciende ordenadores a través de la red cuyas macs se pasan como parámetro -// Parámetros: -// - iph: Cadena de direcciones ip separadas por ";" -// - mac: Cadena de direcciones mac separadas por ";" -// - mar: Método de arranque (1=Broadcast, 2=Unicast) -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -bool Levanta(char *ptrIP[], char *ptrMacs[], char *ptrNetmasks[], int lon, char *mar) -{ - unsigned int on = 1; - struct sockaddr_in local; - int i, res; - int s; - - /* Creación de socket para envío de magig packet */ - s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (s < 0) { - syslog(LOG_ERR, "cannot create socket for magic packet\n"); - return false; - } - res = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (unsigned int *) &on, - sizeof(on)); - if (res < 0) { - syslog(LOG_ERR, "cannot set broadcast socket\n"); - return false; - } - - memset(&local, 0, sizeof(local)); - local.sin_family = AF_INET; - local.sin_port = htons(PUERTO_WAKEUP); - local.sin_addr.s_addr = htonl(INADDR_ANY); - - for (i = 0; i < lon; i++) { - if (!WakeUp(s, ptrIP[i], ptrMacs[i], ptrNetmasks[i], mar)) { - syslog(LOG_ERR, "problem sending magic packet\n"); - close(s); - return false; - } - } - close(s); - return true; -} - -#define OG_WOL_SEQUENCE 6 -#define OG_WOL_MACADDR_LEN 6 -#define OG_WOL_REPEAT 16 - -struct wol_msg { - char secuencia_FF[OG_WOL_SEQUENCE]; - char macbin[OG_WOL_REPEAT][OG_WOL_MACADDR_LEN]; -}; - -static bool wake_up_broadcast(int sd, struct sockaddr_in *client, - const struct wol_msg *msg, const struct in_addr *addr) -{ - struct sockaddr_in *broadcast_addr; - struct ifaddrs *ifaddr, *ifa; - int ret1, ret2; - - if (getifaddrs(&ifaddr) < 0) { - syslog(LOG_ERR, "cannot get list of addresses\n"); - return false; - } - - - client->sin_addr.s_addr = htonl(INADDR_BROADCAST); - - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL || - ifa->ifa_addr->sa_family != AF_INET || - strcmp(ifa->ifa_name, interface) != 0) - continue; - - broadcast_addr = - (struct sockaddr_in *)ifa->ifa_ifu.ifu_broadaddr; - client->sin_addr.s_addr = broadcast_addr->sin_addr.s_addr; - break; - } - freeifaddrs(ifaddr); - - ret1 = sendto(sd, msg, sizeof(*msg), 0, - (struct sockaddr *)client, sizeof(*client)); - client->sin_addr.s_addr = addr->s_addr; - ret2 = sendto(sd, msg, sizeof(*msg), 0, - (struct sockaddr *)client, sizeof(*client)); - if ((ret1 < 0) & (ret2 < 0)) { - syslog(LOG_ERR, "failed to send broadcast wol\n"); - return false; - } - - return true; -} - -static bool wake_up_unicast(int sd, struct sockaddr_in *client, - const struct wol_msg *msg, - const struct in_addr *addr) -{ - int ret; - - client->sin_addr.s_addr = addr->s_addr; - - ret = sendto(sd, msg, sizeof(*msg), 0, - (struct sockaddr *)client, sizeof(*client)); - if (ret < 0) { - syslog(LOG_ERR, "failed to send unicast wol\n"); - return false; - } - - return true; -} - - - -enum wol_delivery_type { - OG_WOL_BROADCAST = 1, - OG_WOL_UNICAST = 2 -}; - -//_____________________________________________________________________________________________________________ -// Función: WakeUp -// -// Descripción: -// Enciende el ordenador cuya MAC se pasa como parámetro -// Parámetros: -// - s : Socket para enviar trama magic packet -// - iph : Cadena con la dirección ip -// - mac : Cadena con la dirección mac en formato XXXXXXXXXXXX -// - mar: Método de arranque (1=Broadcast, 2=Unicast) -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -//_____________________________________________________________________________________________________________ -// -bool WakeUp(int s, char* iph, char *mac, char *netmask, char *mar) -{ - unsigned int macaddr[OG_WOL_MACADDR_LEN]; - char HDaddress_bin[OG_WOL_MACADDR_LEN]; - struct sockaddr_in WakeUpCliente; - struct wol_msg Trama_WakeUp; - struct in_addr addr, netmask_addr, broadcast_addr ={}; - bool ret; - int i; - - if (!inet_aton(iph, &addr)) { - syslog(LOG_ERR, "bad IP address\n"); - return false; - } - - if (!inet_aton(netmask, &netmask_addr)) { - syslog(LOG_ERR, "bad netmask address: %s\n", netmask); - return false; - } - broadcast_addr.s_addr = addr.s_addr | ~netmask_addr.s_addr; - - for (i = 0; i < 6; i++) // Primera secuencia de la trama Wake Up (0xFFFFFFFFFFFF) - Trama_WakeUp.secuencia_FF[i] = 0xFF; - - sscanf(mac, "%02x%02x%02x%02x%02x%02x", - &macaddr[0], &macaddr[1], &macaddr[2], - &macaddr[3], &macaddr[4], &macaddr[5]); - - for (i = 0; i < 6; i++) - HDaddress_bin[i] = (uint8_t)macaddr[i]; - - for (i = 0; i < 16; i++) // Segunda secuencia de la trama Wake Up , repetir 16 veces su la MAC - memcpy(&Trama_WakeUp.macbin[i][0], &HDaddress_bin, 6); - - /* Creación de socket del cliente que recibe la trama magic packet */ - WakeUpCliente.sin_family = AF_INET; - WakeUpCliente.sin_port = htons((short) PUERTO_WAKEUP); - - switch (atoi(mar)) { - case OG_WOL_BROADCAST: - ret = wake_up_broadcast(s, &WakeUpCliente, &Trama_WakeUp, &broadcast_addr ); - break; - case OG_WOL_UNICAST: - ret = wake_up_unicast(s, &WakeUpCliente, &Trama_WakeUp, &addr); - break; - default: - syslog(LOG_ERR, "unknown wol type\n"); - ret = false; - break; - } - return ret; -} -// ________________________________________________________________________________________________________ -// Función: RESPUESTA_Arrancar -// -// Descripción: -// Respuesta del cliente al comando Arrancar -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool RESPUESTA_Arrancar(TRAMA* ptrTrama, struct og_client *cli) -{ - struct og_dbi *dbi; - char *iph, *ido; - char *tpc; - int i; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return false; - } - - iph = copiaParametro("iph",ptrTrama); // Toma dirección ip - ido = copiaParametro("ido",ptrTrama); // Toma identificador del ordenador - - if (!respuestaEstandar(ptrTrama, iph, ido, dbi)) { - liberaMemoria(iph); - liberaMemoria(ido); - syslog(LOG_ERR, "failed to register notification\n"); - og_dbi_close(dbi); - return false; - } - - tpc = copiaParametro("tpc",ptrTrama); // Tipo de cliente (Plataforma y S.O.) - if (clienteExistente(iph, &i)) // Actualiza estado - strcpy(tbsockets[i].estado, tpc); - - liberaMemoria(iph); - liberaMemoria(ido); - liberaMemoria(tpc); - og_dbi_close(dbi); - - return true; -} -// ________________________________________________________________________________________________________ -// Función: RESPUESTA_Apagar -// -// Descripción: -// Respuesta del cliente al comando Apagar -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool RESPUESTA_Apagar(TRAMA* ptrTrama, struct og_client *cli) -{ - struct og_dbi *dbi; - char *iph, *ido; - int i; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return false; - } - - iph = copiaParametro("iph",ptrTrama); // Toma dirección ip - ido = copiaParametro("ido",ptrTrama); // Toma identificador del ordenador - - if (!respuestaEstandar(ptrTrama, iph, ido, dbi)) { - liberaMemoria(iph); - liberaMemoria(ido); - syslog(LOG_ERR, "failed to register notification\n"); - og_dbi_close(dbi); - return false; // Error al registrar notificacion - } - - if (clienteExistente(iph, &i)) // Actualiza estado - strcpy(tbsockets[i].estado, CLIENTE_APAGADO); - - liberaMemoria(iph); - liberaMemoria(ido); - og_dbi_close(dbi); - - return true; -} -// ________________________________________________________________________________________________________ -// Función: RESPUESTA_CrearImagen -// -// Descripción: -// Respuesta del cliente al comando CrearImagen -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool RESPUESTA_CrearImagen(TRAMA* ptrTrama, struct og_client *cli) -{ - char *iph, *dsk, *par, *cpt, *ipr, *ido; - struct og_dbi *dbi; - char *idi; - bool res; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return false; - } - - iph = copiaParametro("iph",ptrTrama); // Toma dirección ip - ido = copiaParametro("ido",ptrTrama); // Toma identificador del ordenador - - if (!respuestaEstandar(ptrTrama, iph, ido, dbi)) { - liberaMemoria(iph); - liberaMemoria(ido); - syslog(LOG_ERR, "failed to register notification\n"); - og_dbi_close(dbi); - return false; // Error al registrar notificacion - } - - // Acciones posteriores - idi = copiaParametro("idi",ptrTrama); - dsk = copiaParametro("dsk",ptrTrama); - par = copiaParametro("par",ptrTrama); - cpt = copiaParametro("cpt",ptrTrama); - ipr = copiaParametro("ipr",ptrTrama); - - res=actualizaCreacionImagen(dbi, idi, dsk, par, cpt, ipr, ido); - - liberaMemoria(idi); - liberaMemoria(par); - liberaMemoria(cpt); - liberaMemoria(ipr); - og_dbi_close(dbi); - - if (!res) - syslog(LOG_ERR, "Problem processing update\n"); - - return res; -} -// ________________________________________________________________________________________________________ -// Función: actualizaCreacionImagen -// -// Descripción: -// Esta función actualiza la base de datos con el resultado de la creación de una imagen -// Parámetros: -// - db: Objeto base de datos (ya operativo) -// - tbl: Objeto tabla -// - idi: Identificador de la imagen -// - dsk: Disco de donde se creó -// - par: Partición de donde se creó -// - cpt: Código de partición -// - ipr: Ip del repositorio -// - ido: Identificador del ordenador modelo -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -bool actualizaCreacionImagen(struct og_dbi *dbi, char *idi, char *dsk, - char *par, char *cpt, char *ipr, char *ido) -{ - const char *msglog; - dbi_result result; - int idr,ifs; - - /* Toma identificador del repositorio correspondiente al ordenador modelo */ - result = dbi_conn_queryf(dbi->conn, - "SELECT repositorios.idrepositorio" - " FROM repositorios" - " LEFT JOIN ordenadores USING (idrepositorio)" - " WHERE repositorios.ip='%s' AND ordenadores.idordenador=%s", ipr, ido); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - if (!dbi_result_next_row(result)) { - syslog(LOG_ERR, - "repository does not exist in database (%s:%d)\n", - __func__, __LINE__); - dbi_result_free(result); - return false; - } - idr = dbi_result_get_uint(result, "idrepositorio"); - dbi_result_free(result); - - /* Toma identificador del perfilsoftware */ - result = dbi_conn_queryf(dbi->conn, - "SELECT idperfilsoft" - " FROM ordenadores_particiones" - " WHERE idordenador=%s AND numdisk=%s AND numpar=%s", ido, dsk, par); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - if (!dbi_result_next_row(result)) { - syslog(LOG_ERR, - "software profile does not exist in database (%s:%d)\n", - __func__, __LINE__); - dbi_result_free(result); - return false; - } - ifs = dbi_result_get_uint(result, "idperfilsoft"); - dbi_result_free(result); - - /* Actualizar los datos de la imagen */ - result = dbi_conn_queryf(dbi->conn, - "UPDATE imagenes" - " SET idordenador=%s, numdisk=%s, numpar=%s, codpar=%s," - " idperfilsoft=%d, idrepositorio=%d," - " fechacreacion=NOW(), revision=revision+1" - " WHERE idimagen=%s", ido, dsk, par, cpt, ifs, idr, idi); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result); - - /* Actualizar los datos en el cliente */ - result = dbi_conn_queryf(dbi->conn, - "UPDATE ordenadores_particiones" - " SET idimagen=%s, revision=(SELECT revision FROM imagenes WHERE idimagen=%s)," - " fechadespliegue=NOW()" - " WHERE idordenador=%s AND numdisk=%s AND numpar=%s", - idi, idi, ido, dsk, par); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result); - - return true; -} -// ________________________________________________________________________________________________________ -// Función: RESPUESTA_CrearImagenBasica -// -// Descripción: -// Respuesta del cliente al comando CrearImagenBasica -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool RESPUESTA_CrearImagenBasica(TRAMA* ptrTrama, struct og_client *cli) -{ - // La misma respuesta que la creación de imagen monolítica - return RESPUESTA_CrearImagen(ptrTrama, cli); -} -// ________________________________________________________________________________________________________ -// Función: RESPUESTA_CrearSoftIncremental -// -// Descripción: -// Respuesta del cliente al comando crearImagenDiferencial -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool RESPUESTA_CrearSoftIncremental(TRAMA* ptrTrama, struct og_client *cli) -{ - char *iph,*par,*ido,*idf; - int ifs; - const char *msglog; - struct og_dbi *dbi; - dbi_result result; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return false; - } - - iph = copiaParametro("iph",ptrTrama); // Toma dirección ip - ido = copiaParametro("ido",ptrTrama); // Toma identificador del ordenador - - if (!respuestaEstandar(ptrTrama, iph, ido, dbi)) { - og_dbi_close(dbi); - liberaMemoria(iph); - liberaMemoria(ido); - syslog(LOG_ERR, "failed to register notification\n"); - return false; - } - - par = copiaParametro("par",ptrTrama); - - /* Toma identificador del perfilsoftware creado por el inventario de software */ - result = dbi_conn_queryf(dbi->conn, - "SELECT idperfilsoft FROM ordenadores_particiones WHERE idordenador=%s AND numpar=%s", - ido, par); - liberaMemoria(iph); - liberaMemoria(ido); - liberaMemoria(par); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - og_dbi_close(dbi); - return false; - } - if (!dbi_result_next_row(result)) { - syslog(LOG_ERR, - "software profile does not exist in database (%s:%d)\n", - __func__, __LINE__); - dbi_result_free(result); - og_dbi_close(dbi); - return false; - } - ifs = dbi_result_get_uint(result, "idperfilsoft"); - dbi_result_free(result); - - /* Actualizar los datos de la imagen */ - idf = copiaParametro("idf", ptrTrama); - result = dbi_conn_queryf(dbi->conn, - "UPDATE imagenes SET idperfilsoft=%d WHERE idimagen=%s", - ifs, idf); - liberaMemoria(idf); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - og_dbi_close(dbi); - return false; - } - dbi_result_free(result); - - og_dbi_close(dbi); - - return true; -} -// ________________________________________________________________________________________________________ -// Función: RESPUESTA_RestaurarImagen -// -// Descripción: -// Respuesta del cliente al comando RestaurarImagen -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -// -static bool RESPUESTA_RestaurarImagen(TRAMA* ptrTrama, struct og_client *cli) -{ - bool res; - char *iph, *ido, *idi, *dsk, *par, *ifs, *cfg; - struct og_dbi *dbi; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return false; - } - - iph = copiaParametro("iph",ptrTrama); // Toma dirección ip - ido = copiaParametro("ido",ptrTrama); // Toma identificador del ordenador - - if (!respuestaEstandar(ptrTrama, iph, ido, dbi)) { - og_dbi_close(dbi); - liberaMemoria(iph); - liberaMemoria(ido); - syslog(LOG_ERR, "failed to register notification\n"); - return false; - } - - // Acciones posteriores - idi = copiaParametro("idi",ptrTrama); // Toma identificador de la imagen - dsk = copiaParametro("dsk",ptrTrama); // Número de disco - par = copiaParametro("par",ptrTrama); // Número de partición - ifs = copiaParametro("ifs",ptrTrama); // Identificador del perfil software contenido - cfg = copiaParametro("cfg",ptrTrama); // Configuración de discos - if(cfg){ - actualizaConfiguracion(dbi, cfg, atoi(ido)); // Actualiza la configuración del ordenador - liberaMemoria(cfg); - } - res=actualizaRestauracionImagen(dbi, idi, dsk, par, ido, ifs); - - liberaMemoria(iph); - liberaMemoria(ido); - liberaMemoria(idi); - liberaMemoria(par); - liberaMemoria(ifs); - og_dbi_close(dbi); - - if(!res) - syslog(LOG_ERR, "Problem after restoring image\n"); - - return res; -} -// ________________________________________________________________________________________________________ -// -// Función: RESPUESTA_RestaurarImagenBasica -// -// Descripción: -// Respuesta del cliente al comando RestaurarImagen -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -// -static bool RESPUESTA_RestaurarImagenBasica(TRAMA* ptrTrama, struct og_client *cli) -{ - return RESPUESTA_RestaurarImagen(ptrTrama, cli); -} -// ________________________________________________________________________________________________________ -// Función: RESPUESTA_RestaurarSoftIncremental -// -// Descripción: -// Respuesta del cliente al comando RestaurarSoftIncremental -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool RESPUESTA_RestaurarSoftIncremental(TRAMA* ptrTrama, struct og_client *cli) -{ - return RESPUESTA_RestaurarImagen(ptrTrama, cli); -} -// ________________________________________________________________________________________________________ -// Función: actualizaRestauracionImagen -// -// Descripción: -// Esta función actualiza la base de datos con el resultado de la restauración de una imagen -// Parámetros: -// - db: Objeto base de datos (ya operativo) -// - tbl: Objeto tabla -// - idi: Identificador de la imagen -// - dsk: Disco de donde se restauró -// - par: Partición de donde se restauró -// - ido: Identificador del cliente donde se restauró -// - ifs: Identificador del perfil software contenido en la imagen -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -bool actualizaRestauracionImagen(struct og_dbi *dbi, char *idi, - char *dsk, char *par, char *ido, char *ifs) -{ - const char *msglog; - dbi_result result; - - /* Actualizar los datos de la imagen */ - result = dbi_conn_queryf(dbi->conn, - "UPDATE ordenadores_particiones" - " SET idimagen=%s, idperfilsoft=%s, fechadespliegue=NOW()," - " revision=(SELECT revision FROM imagenes WHERE idimagen=%s)," - " idnombreso=IFNULL((SELECT idnombreso FROM perfilessoft WHERE idperfilsoft=%s),0)" - " WHERE idordenador=%s AND numdisk=%s AND numpar=%s", idi, ifs, idi, ifs, ido, dsk, par); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result); - - return true; -} -// ________________________________________________________________________________________________________ -// Función: RESPUESTA_EjecutarScript -// -// Descripción: -// Respuesta del cliente al comando EjecutarScript -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool RESPUESTA_EjecutarScript(TRAMA* ptrTrama, struct og_client *cli) -{ - char *iph, *ido,*cfg; - struct og_dbi *dbi; - bool res = true; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return false; - } - - iph = copiaParametro("iph",ptrTrama); // Toma dirección ip - ido = copiaParametro("ido",ptrTrama); // Toma identificador del ordenador - - if (!respuestaEstandar(ptrTrama, iph, ido, dbi)) { - og_dbi_close(dbi); - liberaMemoria(iph); - liberaMemoria(ido); - syslog(LOG_ERR, "failed to register notification\n"); - return false; - } - - cfg = copiaParametro("cfg",ptrTrama); // Toma configuración de particiones - if(cfg){ - res = actualizaConfiguracion(dbi, cfg, atoi(ido)); // Actualiza la configuración del ordenador - liberaMemoria(cfg); - } - - liberaMemoria(iph); - liberaMemoria(ido); - og_dbi_close(dbi); - - if (!res) - syslog(LOG_ERR, "Problem updating client configuration\n"); - - return res; -} -// ________________________________________________________________________________________________________ -// Función: RESPUESTA_InventarioHardware -// -// Descripción: -// Respuesta del cliente al comando InventarioHardware -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool RESPUESTA_InventarioHardware(TRAMA* ptrTrama, struct og_client *cli) -{ - bool res; - char *iph, *ido, *idc, *npc, *hrd, *buffer; - struct og_dbi *dbi; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return false; - } - - iph = copiaParametro("iph",ptrTrama); // Toma dirección ip del cliente - ido = copiaParametro("ido",ptrTrama); // Toma identificador del cliente - - if (!respuestaEstandar(ptrTrama, iph, ido, dbi)) { - og_dbi_close(dbi); - liberaMemoria(iph); - liberaMemoria(ido); - syslog(LOG_ERR, "failed to register notification\n"); - return false; - } - // Lee archivo de inventario enviado anteriormente - hrd = copiaParametro("hrd",ptrTrama); - buffer = rTrim(leeArchivo(hrd)); - - npc = copiaParametro("npc",ptrTrama); - idc = copiaParametro("idc",ptrTrama); // Toma identificador del Centro - - if (buffer) - res=actualizaHardware(dbi, buffer, ido, npc, idc); - else - res = false; - - liberaMemoria(iph); - liberaMemoria(ido); - liberaMemoria(npc); - liberaMemoria(idc); - liberaMemoria(buffer); - og_dbi_close(dbi); - - if (!res) - syslog(LOG_ERR, "Problem updating client configuration\n"); - - return res; -} -// ________________________________________________________________________________________________________ -// Función: actualizaHardware -// -// Descripción: -// Actualiza la base de datos con la configuracion hardware del cliente -// Parámetros: -// - db: Objeto base de datos (ya operativo) -// - tbl: Objeto tabla -// - hrd: cadena con el inventario hardware -// - ido: Identificador del ordenador -// - npc: Nombre del ordenador -// - idc: Identificador del centro o Unidad organizativa -// ________________________________________________________________________________________________________ -// -bool actualizaHardware(struct og_dbi *dbi, char *hrd, char *ido, char *npc, - char *idc) -{ - const char *msglog; - int idtipohardware, idperfilhard; - int lon, i, j, aux; - bool retval; - char *whard; - int tbidhardware[MAXHARDWARE]; - char *tbHardware[MAXHARDWARE],*dualHardware[2], strInt[LONINT], *idhardwares; - dbi_result result; - - /* Toma Centro (Unidad Organizativa) */ - result = dbi_conn_queryf(dbi->conn, - "SELECT idperfilhard FROM ordenadores WHERE idordenador=%s", - ido); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - if (!dbi_result_next_row(result)) { - syslog(LOG_ERR, "client does not exist in database (%s:%d)\n", - __func__, __LINE__); - dbi_result_free(result); - return false; - } - idperfilhard = dbi_result_get_uint(result, "idperfilhard"); - dbi_result_free(result); - - whard=escaparCadena(hrd); // Codificar comillas simples - if(!whard) - return false; - /* Recorre componentes hardware*/ - lon = splitCadena(tbHardware, whard, '\n'); - if (lon > MAXHARDWARE) - lon = MAXHARDWARE; // Limita el número de componentes hardware - /* - for (i=0;iconn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - if (!dbi_result_next_row(result)) { // Tipo de Hardware NO existente - dbi_result_free(result); - return false; - } else { // Tipo de Hardware Existe - idtipohardware = dbi_result_get_uint(result, "idtipohardware"); - dbi_result_free(result); - - result = dbi_conn_queryf(dbi->conn, - "SELECT idhardware FROM hardwares WHERE idtipohardware=%d AND descripcion='%s'", - idtipohardware, dualHardware[1]); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - - if (!dbi_result_next_row(result)) { // Hardware NO existente - dbi_result_free(result); - result = dbi_conn_queryf(dbi->conn, - "INSERT hardwares (idtipohardware,descripcion,idcentro,grupoid) " - " VALUES(%d,'%s',%s,0)", idtipohardware, - dualHardware[1], idc); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - - // Recupera el identificador del hardware - tbidhardware[i] = dbi_conn_sequence_last(dbi->conn, NULL); - } else { - tbidhardware[i] = dbi_result_get_uint(result, "idhardware"); - } - dbi_result_free(result); - } - } - // Ordena tabla de identificadores para cosultar si existe un pefil con esas especificaciones - - for (i = 0; i < lon - 1; i++) { - for (j = i + 1; j < lon; j++) { - if (tbidhardware[i] > tbidhardware[j]) { - aux = tbidhardware[i]; - tbidhardware[i] = tbidhardware[j]; - tbidhardware[j] = aux; - } - } - } - /* Crea cadena de identificadores de componentes hardware separados por coma */ - sprintf(strInt, "%d", tbidhardware[lon - 1]); // Pasa a cadena el último identificador que es de mayor longitud - aux = strlen(strInt); // Calcula longitud de cadena para reservar espacio a todos los perfiles - idhardwares = reservaMemoria(sizeof(aux) * lon + lon); - if (idhardwares == NULL) { - syslog(LOG_ERR, "%s:%d OOM\n", __FILE__, __LINE__); - return false; - } - aux = sprintf(idhardwares, "%d", tbidhardware[0]); - for (i = 1; i < lon; i++) - aux += sprintf(idhardwares + aux, ",%d", tbidhardware[i]); - - if (!cuestionPerfilHardware(dbi, idc, ido, idperfilhard, idhardwares, - npc, tbidhardware, lon)) { - syslog(LOG_ERR, "Problem updating client hardware\n"); - retval=false; - } - else { - retval=true; - } - liberaMemoria(whard); - liberaMemoria(idhardwares); - return (retval); -} -// ________________________________________________________________________________________________________ -// Función: cuestionPerfilHardware -// -// Descripción: -// Comprueba existencia de perfil hardware y actualización de éste para el ordenador -// Parámetros: -// - db: Objeto base de datos (ya operativo) -// - tbl: Objeto tabla -// - idc: Identificador de la Unidad organizativa donde se encuentra el cliente -// - ido: Identificador del ordenador -// - tbidhardware: Identificador del tipo de hardware -// - con: Número de componentes detectados para configurar un el perfil hardware -// - npc: Nombre del cliente -// ________________________________________________________________________________________________________ -bool cuestionPerfilHardware(struct og_dbi *dbi, char *idc, char *ido, - int idperfilhardware, char *idhardwares, char *npc, int *tbidhardware, - int lon) -{ - const char *msglog; - dbi_result result; - int i; - int nwidperfilhard; - - // Busca perfil hard del ordenador que contenga todos los componentes hardware encontrados - result = dbi_conn_queryf(dbi->conn, - "SELECT idperfilhard FROM" - " (SELECT perfileshard_hardwares.idperfilhard as idperfilhard," - " group_concat(cast(perfileshard_hardwares.idhardware AS char( 11) )" - " ORDER BY perfileshard_hardwares.idhardware SEPARATOR ',' ) AS idhardwares" - " FROM perfileshard_hardwares" - " GROUP BY perfileshard_hardwares.idperfilhard) AS temp" - " WHERE idhardwares LIKE '%s'", idhardwares); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - if (!dbi_result_next_row(result)) { - // No existe un perfil hardware con esos componentes de componentes hardware, lo crea - dbi_result_free(result); - result = dbi_conn_queryf(dbi->conn, - "INSERT perfileshard (descripcion,idcentro,grupoid)" - " VALUES('Perfil hardware (%s) ',%s,0)", npc, idc); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result); - - // Recupera el identificador del nuevo perfil hardware - nwidperfilhard = dbi_conn_sequence_last(dbi->conn, NULL); - - // Crea la relación entre perfiles y componenetes hardware - for (i = 0; i < lon; i++) { - result = dbi_conn_queryf(dbi->conn, - "INSERT perfileshard_hardwares (idperfilhard,idhardware)" - " VALUES(%d,%d)", nwidperfilhard, tbidhardware[i]); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result); - } - } else { // Existe un perfil con todos esos componentes - nwidperfilhard = dbi_result_get_uint(result, "idperfilhard"); - dbi_result_free(result); - } - if (idperfilhardware != nwidperfilhard) { // No coinciden los perfiles - // Actualiza el identificador del perfil hardware del ordenador - result = dbi_conn_queryf(dbi->conn, - "UPDATE ordenadores SET idperfilhard=%d" - " WHERE idordenador=%s", nwidperfilhard, ido); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result); - } - /* Eliminar Relación de hardwares con Perfiles hardware que quedan húerfanos */ - result = dbi_conn_queryf(dbi->conn, - "DELETE FROM perfileshard_hardwares WHERE idperfilhard IN " - " (SELECT idperfilhard FROM perfileshard WHERE idperfilhard NOT IN" - " (SELECT DISTINCT idperfilhard from ordenadores))"); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result); - - /* Eliminar Perfiles hardware que quedan húerfanos */ - result = dbi_conn_queryf(dbi->conn, - "DELETE FROM perfileshard WHERE idperfilhard NOT IN" - " (SELECT DISTINCT idperfilhard FROM ordenadores)"); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result); - - /* Eliminar Relación de hardwares con Perfiles hardware que quedan húerfanos */ - result = dbi_conn_queryf(dbi->conn, - "DELETE FROM perfileshard_hardwares WHERE idperfilhard NOT IN" - " (SELECT idperfilhard FROM perfileshard)"); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - dbi_result_free(result); - - return true; -} -// ________________________________________________________________________________________________________ -// Función: RESPUESTA_InventarioSoftware -// -// Descripción: -// Respuesta del cliente al comando InventarioSoftware -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool RESPUESTA_InventarioSoftware(TRAMA* ptrTrama, struct og_client *cli) -{ - bool res; - char *iph, *ido, *npc, *idc, *par, *sft, *buffer; - struct og_dbi *dbi; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return false; - } - - iph = copiaParametro("iph",ptrTrama); // Toma dirección ip - ido = copiaParametro("ido",ptrTrama); // Toma identificador del ordenador - - if (!respuestaEstandar(ptrTrama, iph, ido, dbi)) { - og_dbi_close(dbi); - liberaMemoria(iph); - liberaMemoria(ido); - syslog(LOG_ERR, "failed to register notification\n"); - return false; - } - - npc = copiaParametro("npc",ptrTrama); - idc = copiaParametro("idc",ptrTrama); // Toma identificador del Centro - par = copiaParametro("par",ptrTrama); - sft = copiaParametro("sft",ptrTrama); - - buffer = rTrim(leeArchivo(sft)); - if (buffer) - res=actualizaSoftware(dbi, buffer, par, ido, npc, idc); - else - res = false; - - liberaMemoria(iph); - liberaMemoria(ido); - liberaMemoria(npc); - liberaMemoria(idc); - liberaMemoria(par); - liberaMemoria(sft); - og_dbi_close(dbi); - - if (!res) - syslog(LOG_ERR, "cannot update software\n"); - - return res; -} -// ________________________________________________________________________________________________________ -// Función: actualizaSoftware -// -// Descripción: -// Actualiza la base de datos con la configuración software del cliente -// Parámetros: -// - db: Objeto base de datos (ya operativo) -// - tbl: Objeto tabla -// - sft: cadena con el inventario software -// - par: Número de la partición -// - ido: Identificador del ordenador del cliente en la tabla -// - npc: Nombre del ordenador -// - idc: Identificador del centro o Unidad organizativa -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// -// Versión 1.1.0: Se incluye el sistema operativo. Autora: Irina Gómez - ETSII Universidad Sevilla -// ________________________________________________________________________________________________________ -bool actualizaSoftware(struct og_dbi *dbi, char *sft, char *par,char *ido, - char *npc, char *idc) -{ - int i, j, lon, aux, idperfilsoft, idnombreso; - bool retval; - char *wsft; - int tbidsoftware[MAXSOFTWARE]; - char *tbSoftware[MAXSOFTWARE], strInt[LONINT], *idsoftwares; - const char *msglog; - dbi_result result; - - /* Toma Centro (Unidad Organizativa) y perfil software */ - result = dbi_conn_queryf(dbi->conn, - "SELECT idperfilsoft,numpar" - " FROM ordenadores_particiones" - " WHERE idordenador=%s", ido); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - idperfilsoft = 0; // Por defecto se supone que el ordenador no tiene aún detectado el perfil software - while (dbi_result_next_row(result)) { - aux = dbi_result_get_uint(result, "numpar"); - if (aux == atoi(par)) { // Se encuentra la partición - idperfilsoft = dbi_result_get_uint(result, "idperfilsoft"); - break; - } - } - dbi_result_free(result); - wsft=escaparCadena(sft); // Codificar comillas simples - if(!wsft) - return false; - - /* Recorre componentes software*/ - lon = splitCadena(tbSoftware, wsft, '\n'); - - if (lon == 0) - return true; // No hay lineas que procesar - if (lon > MAXSOFTWARE) - lon = MAXSOFTWARE; // Limita el número de componentes software - - idnombreso = 0; - for (i = 0; i < lon; i++) { - // Primera línea es el sistema operativo: se obtiene identificador - if (i == 0) { - idnombreso = checkDato(dbi, rTrim(tbSoftware[i]), "nombresos", "nombreso", "idnombreso"); - continue; - } - - result = dbi_conn_queryf(dbi->conn, - "SELECT idsoftware FROM softwares WHERE descripcion ='%s'", - rTrim(tbSoftware[i])); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - result = dbi_conn_queryf(dbi->conn, - "INSERT INTO softwares (idtiposoftware,descripcion,idcentro,grupoid)" - " VALUES(2,'%s',%s,0)", tbSoftware[i], idc); - if (!result) { // Error al insertar - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - - // Recupera el identificador del software - tbidsoftware[i] = dbi_conn_sequence_last(dbi->conn, NULL); - } else { - tbidsoftware[i] = dbi_result_get_uint(result, "idsoftware"); - } - dbi_result_free(result); - } - - // Ordena tabla de identificadores para cosultar si existe un pefil con esas especificaciones - - for (i = 0; i < lon - 1; i++) { - for (j = i + 1; j < lon; j++) { - if (tbidsoftware[i] > tbidsoftware[j]) { - aux = tbidsoftware[i]; - tbidsoftware[i] = tbidsoftware[j]; - tbidsoftware[j] = aux; - } - } - } - /* Crea cadena de identificadores de componentes software separados por coma */ - sprintf(strInt, "%d", tbidsoftware[lon - 1]); // Pasa a cadena el último identificador que es de mayor longitud - aux = strlen(strInt); // Calcula longitud de cadena para reservar espacio a todos los perfiles - idsoftwares = reservaMemoria((sizeof(aux)+1) * lon + lon); - if (idsoftwares == NULL) { - syslog(LOG_ERR, "%s:%d OOM\n", __FILE__, __LINE__); - return false; - } - aux = sprintf(idsoftwares, "%d", tbidsoftware[0]); - for (i = 1; i < lon; i++) - aux += sprintf(idsoftwares + aux, ",%d", tbidsoftware[i]); - - // Comprueba existencia de perfil software y actualización de éste para el ordenador - if (!cuestionPerfilSoftware(dbi, idc, ido, idperfilsoft, idnombreso, idsoftwares, - npc, par, tbidsoftware, lon)) { - syslog(LOG_ERR, "cannot update software\n"); - og_info((char *)msglog); - retval=false; - } - else { - retval=true; - } - liberaMemoria(wsft); - liberaMemoria(idsoftwares); - return (retval); -} -// ________________________________________________________________________________________________________ -// Función: CuestionPerfilSoftware -// -// Parámetros: -// - db: Objeto base de datos (ya operativo) -// - tbl: Objeto tabla -// - idcentro: Identificador del centro en la tabla -// - ido: Identificador del ordenador del cliente en la tabla -// - idnombreso: Identificador del sistema operativo -// - idsoftwares: Cadena con los identificadores de componentes software separados por comas -// - npc: Nombre del ordenador del cliente -// - particion: Número de la partición -// - tbidsoftware: Array con los identificadores de componentes software -// - lon: Número de componentes -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// -// Versión 1.1.0: Se incluye el sistema operativo. Autora: Irina Gómez - ETSII Universidad Sevilla -//_________________________________________________________________________________________________________ -bool cuestionPerfilSoftware(struct og_dbi *dbi, char *idc, char *ido, - int idperfilsoftware, int idnombreso, - char *idsoftwares, char *npc, char *par, - int *tbidsoftware, int lon) -{ - int i, nwidperfilsoft; - const char *msglog; - dbi_result result; - - // Busca perfil soft del ordenador que contenga todos los componentes software encontrados - result = dbi_conn_queryf(dbi->conn, - "SELECT idperfilsoft FROM" - " (SELECT perfilessoft_softwares.idperfilsoft as idperfilsoft," - " group_concat(cast(perfilessoft_softwares.idsoftware AS char( 11) )" - " ORDER BY perfilessoft_softwares.idsoftware SEPARATOR ',' ) AS idsoftwares" - " FROM perfilessoft_softwares" - " GROUP BY perfilessoft_softwares.idperfilsoft) AS temp" - " WHERE idsoftwares LIKE '%s'", idsoftwares); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - return false; - } - if (!dbi_result_next_row(result)) { // No existe un perfil software con esos componentes de componentes software, lo crea - dbi_result_free(result); - result = dbi_conn_queryf(dbi->conn, - "INSERT perfilessoft (descripcion, idcentro, grupoid, idnombreso)" - " VALUES('Perfil Software (%s, Part:%s) ',%s,0,%i)", npc, par, idc,idnombreso); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - og_info((char *)msglog); - return false; - } - - dbi_result_free(result); - // Recupera el identificador del nuevo perfil software - nwidperfilsoft = dbi_conn_sequence_last(dbi->conn, NULL); - - // Crea la relación entre perfiles y componenetes software - for (i = 0; i < lon; i++) { - result = dbi_conn_queryf(dbi->conn, - "INSERT perfilessoft_softwares (idperfilsoft,idsoftware)" - " VALUES(%d,%d)", nwidperfilsoft, tbidsoftware[i]); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - og_info((char *)msglog); - return false; - } - dbi_result_free(result); - } - } else { // Existe un perfil con todos esos componentes - nwidperfilsoft = dbi_result_get_uint(result, "idperfilsoft"); - dbi_result_free(result); - } - - if (idperfilsoftware != nwidperfilsoft) { // No coinciden los perfiles - // Actualiza el identificador del perfil software del ordenador - result = dbi_conn_queryf(dbi->conn, - "UPDATE ordenadores_particiones SET idperfilsoft=%d,idimagen=0" - " WHERE idordenador=%s AND numpar=%s", nwidperfilsoft, ido, par); - if (!result) { // Error al insertar - dbi_conn_error(dbi->conn, &msglog); - og_info((char *)msglog); - return false; - } - dbi_result_free(result); - } - - /* DEPURACIÓN DE PERFILES SOFTWARE */ - - /* Eliminar Relación de softwares con Perfiles software que quedan húerfanos */ - result = dbi_conn_queryf(dbi->conn, - "DELETE FROM perfilessoft_softwares WHERE idperfilsoft IN "\ - " (SELECT idperfilsoft FROM perfilessoft WHERE idperfilsoft NOT IN"\ - " (SELECT DISTINCT idperfilsoft from ordenadores_particiones) AND idperfilsoft NOT IN"\ - " (SELECT DISTINCT idperfilsoft from imagenes))"); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - og_info((char *)msglog); - return false; - } - dbi_result_free(result), - /* Eliminar Perfiles software que quedan húerfanos */ - result = dbi_conn_queryf(dbi->conn, - "DELETE FROM perfilessoft WHERE idperfilsoft NOT IN" - " (SELECT DISTINCT idperfilsoft from ordenadores_particiones)"\ - " AND idperfilsoft NOT IN"\ - " (SELECT DISTINCT idperfilsoft from imagenes)"); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - og_info((char *)msglog); - return false; - } - dbi_result_free(result), - - /* Eliminar Relación de softwares con Perfiles software que quedan húerfanos */ - result = dbi_conn_queryf(dbi->conn, - "DELETE FROM perfilessoft_softwares WHERE idperfilsoft NOT IN" - " (SELECT idperfilsoft from perfilessoft)"); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - og_info((char *)msglog); - return false; - } - dbi_result_free(result); - - return true; -} -// ________________________________________________________________________________________________________ -// Función: enviaArchivo -// -// Descripción: -// Envia un archivo por la red, por bloques -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool enviaArchivo(TRAMA *ptrTrama, struct og_client *cli) -{ - int socket_c = og_client_socket(cli); - char *nfl; - - // Toma parámetros - nfl = copiaParametro("nfl",ptrTrama); // Toma nombre completo del archivo - if (!sendArchivo(&socket_c, nfl)) { - liberaMemoria(nfl); - syslog(LOG_ERR, "Problem sending file\n"); - return false; - } - liberaMemoria(nfl); - return true; -} -// ________________________________________________________________________________________________________ -// Función: enviaArchivo -// -// Descripción: -// Envia un archivo por la red, por bloques -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool recibeArchivo(TRAMA *ptrTrama, struct og_client *cli) -{ - int socket_c = og_client_socket(cli); - char *nfl; - - // Toma parámetros - nfl = copiaParametro("nfl",ptrTrama); // Toma nombre completo del archivo - ptrTrama->tipo = MSG_NOTIFICACION; - enviaFlag(&socket_c, ptrTrama); - if (!recArchivo(&socket_c, nfl)) { - liberaMemoria(nfl); - syslog(LOG_ERR, "Problem receiving file\n"); - return false; - } - liberaMemoria(nfl); - return true; -} -// ________________________________________________________________________________________________________ -// Función: envioProgramacion -// -// Descripción: -// Envia un comando de actualización a todos los ordenadores que han sido programados con -// alguna acción para que entren en el bucle de comandos pendientes y las ejecuten -// Parámetros: -// - socket_c: Socket del cliente que envió el mensaje -// - ptrTrama: Trama recibida por el servidor con el contenido y los parámetros -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static bool envioProgramacion(TRAMA *ptrTrama, struct og_client *cli) -{ - char *ptrIP[MAXIMOS_CLIENTES],*ptrMacs[MAXIMOS_CLIENTES],*ptrNetmasks[MAXIMOS_CLIENTES]; - char *idp, *iph, *mac, *netmask; - int idx,idcomando,lon; - const char *msglog; - struct og_dbi *dbi; - dbi_result result; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return false; - } - - idp = copiaParametro("idp",ptrTrama); // Toma identificador de la programación de la tabla acciones - - result = dbi_conn_queryf(dbi->conn, - "SELECT ordenadores.ip,ordenadores.mac,aulas.netmask, acciones.idcomando FROM acciones "\ - " INNER JOIN ordenadores ON ordenadores.ip=acciones.ip"\ - " INNER JOIN aulas ON ordenadores.idaula=aulas.idaula "\ - " WHERE acciones.idprogramacion=%s",idp); - - liberaMemoria(idp); - - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - og_dbi_close(dbi); - return false; - } - - /* Prepara la trama de actualizacion */ - - initParametros(ptrTrama,0); - ptrTrama->tipo=MSG_COMANDO; - sprintf(ptrTrama->parametros, "nfn=Actualizar\r"); - - while (dbi_result_next_row(result)) { - iph = (char *)dbi_result_get_string(result, "ip"); - idcomando = dbi_result_get_uint(result, "idcomando"); - - if (idcomando == 1){ // Arrancar - mac = (char *)dbi_result_get_string(result, "mac"); - netmask = (char *)dbi_result_get_string(result, "netmask"); - lon = splitCadena(ptrIP, iph, ';'); - lon = splitCadena(ptrMacs, mac, ';'); - lon = splitCadena(ptrNetmasks, netmask, ';'); - - // Se manda por broadcast y por unicast - if (!Levanta(ptrIP, ptrMacs, ptrNetmasks, lon, (char*)"1")) { - dbi_result_free(result); - og_dbi_close(dbi); - return false; - } - - if (!Levanta(ptrIP, ptrMacs, ptrNetmasks, lon, (char*)"2")) { - dbi_result_free(result); - og_dbi_close(dbi); - return false; - } - - } - if (clienteDisponible(iph, &idx)) { // Si el cliente puede recibir comandos - int sock = tbsockets[idx].cli ? tbsockets[idx].cli->io.fd : -1; - - strcpy(tbsockets[idx].estado, CLIENTE_OCUPADO); // Actualiza el estado del cliente - if (sock >= 0 && !mandaTrama(&sock, ptrTrama)) { - syslog(LOG_ERR, "failed to send response: %s\n", - strerror(errno)); - } - //close(tbsockets[idx].sock); // Cierra el socket del cliente hasta nueva disponibilidad - } - } - dbi_result_free(result); - og_dbi_close(dbi); - - return true; // No existen registros -} - -// This object stores function handler for messages -static struct { - const char *nf; // Nombre de la función - bool (*fcn)(TRAMA *, struct og_client *cli); -} tbfuncionesServer[] = { - { "InclusionCliente", InclusionCliente, }, - { "InclusionClienteWinLnx", InclusionClienteWinLnx, }, - { "AutoexecCliente", AutoexecCliente, }, - { "ComandosPendientes", ComandosPendientes, }, - { "DisponibilidadComandos", DisponibilidadComandos, }, - { "RESPUESTA_Arrancar", RESPUESTA_Arrancar, }, - { "RESPUESTA_Apagar", RESPUESTA_Apagar, }, - { "RESPUESTA_Reiniciar", RESPUESTA_Apagar, }, - { "RESPUESTA_IniciarSesion", RESPUESTA_Apagar, }, - { "RESPUESTA_CrearImagen", RESPUESTA_CrearImagen, }, - { "RESPUESTA_CrearImagenBasica", RESPUESTA_CrearImagenBasica, }, - { "RESPUESTA_CrearSoftIncremental", RESPUESTA_CrearSoftIncremental, }, - { "RESPUESTA_RestaurarImagen", RESPUESTA_RestaurarImagen }, - { "RESPUESTA_RestaurarImagenBasica", RESPUESTA_RestaurarImagenBasica, }, - { "RESPUESTA_RestaurarSoftIncremental", RESPUESTA_RestaurarSoftIncremental, }, - { "RESPUESTA_Configurar", RESPUESTA_EjecutarScript, }, - { "RESPUESTA_EjecutarScript", RESPUESTA_EjecutarScript, }, - { "RESPUESTA_InventarioHardware", RESPUESTA_InventarioHardware, }, - { "RESPUESTA_InventarioSoftware", RESPUESTA_InventarioSoftware, }, - { "enviaArchivo", enviaArchivo, }, - { "recibeArchivo", recibeArchivo, }, - { "envioProgramacion", envioProgramacion, }, - { NULL, NULL, }, -}; - -// ________________________________________________________________________________________________________ -// Función: gestionaTrama -// -// Descripción: -// Procesa las tramas recibidas . -// Parametros: -// - s : Socket usado para comunicaciones -// Devuelve: -// true: Si el proceso es correcto -// false: En caso de ocurrir algún error -// ________________________________________________________________________________________________________ -static void gestionaTrama(TRAMA *ptrTrama, struct og_client *cli) -{ - int i, res; - char *nfn; - - if (ptrTrama){ - INTROaFINCAD(ptrTrama); - nfn = copiaParametro("nfn",ptrTrama); // Toma nombre de la función - - for (i = 0; tbfuncionesServer[i].fcn; i++) { - if (!strncmp(tbfuncionesServer[i].nf, nfn, - strlen(tbfuncionesServer[i].nf))) { - res = tbfuncionesServer[i].fcn(ptrTrama, cli); - if (!res) { - syslog(LOG_ERR, "Failed handling of %s for client %s:%hu\n", - tbfuncionesServer[i].nf, - inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port)); - } else { - syslog(LOG_DEBUG, "Successful handling of %s for client %s:%hu\n", - tbfuncionesServer[i].nf, - inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port)); - } - break; - } - } - if (!tbfuncionesServer[i].fcn) - syslog(LOG_ERR, "unknown request %s from client %s:%hu\n", - nfn, inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port)); - - liberaMemoria(nfn); - } -} - -static void og_client_release(struct ev_loop *loop, struct og_client *cli) -{ - if (cli->keepalive_idx >= 0) { - syslog(LOG_DEBUG, "closing keepalive connection for %s:%hu in slot %d\n", - inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port), cli->keepalive_idx); - tbsockets[cli->keepalive_idx].cli = NULL; - } - - ev_io_stop(loop, &cli->io); - close(cli->io.fd); - free(cli); -} - -static void og_client_keepalive(struct ev_loop *loop, struct og_client *cli) -{ - struct og_client *old_cli; - - old_cli = tbsockets[cli->keepalive_idx].cli; - if (old_cli && old_cli != cli) { - syslog(LOG_DEBUG, "closing old keepalive connection for %s:%hu\n", - inet_ntoa(old_cli->addr.sin_addr), - ntohs(old_cli->addr.sin_port)); - - og_client_release(loop, old_cli); - } - tbsockets[cli->keepalive_idx].cli = cli; -} - -static void og_client_reset_state(struct og_client *cli) -{ - cli->state = OG_CLIENT_RECEIVING_HEADER; - cli->buf_len = 0; -} - -static int og_client_state_recv_hdr(struct og_client *cli) -{ - char hdrlen[LONHEXPRM]; - - /* Still too short to validate protocol fingerprint and message - * length. - */ - if (cli->buf_len < 15 + LONHEXPRM) - return 0; - - if (strncmp(cli->buf, "@JMMLCAMDJ_MCDJ", 15)) { - syslog(LOG_ERR, "bad fingerprint from client %s:%hu, closing\n", - inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port)); - return -1; - } - - memcpy(hdrlen, &cli->buf[LONGITUD_CABECERATRAMA], LONHEXPRM); - cli->msg_len = strtol(hdrlen, NULL, 16); - - /* Header announces more that we can fit into buffer. */ - if (cli->msg_len >= sizeof(cli->buf)) { - syslog(LOG_ERR, "too large message %u bytes from %s:%hu\n", - cli->msg_len, inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port)); - return -1; - } - - return 1; -} - -static TRAMA *og_msg_alloc(char *data, unsigned int len) -{ - TRAMA *ptrTrama; - - ptrTrama = (TRAMA *)reservaMemoria(sizeof(TRAMA)); - if (!ptrTrama) { - syslog(LOG_ERR, "OOM\n"); - return NULL; - } - - initParametros(ptrTrama, len); - memcpy(ptrTrama, "@JMMLCAMDJ_MCDJ", LONGITUD_CABECERATRAMA); - memcpy(ptrTrama->parametros, data, len); - ptrTrama->lonprm = len; - - return ptrTrama; -} - -static void og_msg_free(TRAMA *ptrTrama) -{ - liberaMemoria(ptrTrama->parametros); - liberaMemoria(ptrTrama); -} - -static int og_client_state_process_payload(struct og_client *cli) -{ - TRAMA *ptrTrama; - char *data; - int len; - - len = cli->msg_len - (LONGITUD_CABECERATRAMA + LONHEXPRM); - data = &cli->buf[LONGITUD_CABECERATRAMA + LONHEXPRM]; - - ptrTrama = og_msg_alloc(data, len); - if (!ptrTrama) - return -1; - - gestionaTrama(ptrTrama, cli); - - og_msg_free(ptrTrama); - - return 1; -} - -#define OG_CLIENTS_MAX 4096 -#define OG_PARTITION_MAX 4 - -struct og_partition { - const char *number; - const char *code; - const char *size; - const char *filesystem; - const char *format; -}; - -struct og_sync_params { - const char *sync; - const char *diff; - const char *remove; - const char *compress; - const char *cleanup; - const char *cache; - const char *cleanup_cache; - const char *remove_dst; - const char *diff_id; - const char *diff_name; - const char *path; - const char *method; -}; - -struct og_msg_params { - const char *ips_array[OG_CLIENTS_MAX]; - const char *mac_array[OG_CLIENTS_MAX]; - const char *netmask_array[OG_CLIENTS_MAX]; - unsigned int ips_array_len; - const char *wol_type; - char run_cmd[4096]; - const char *disk; - const char *partition; - const char *repository; - const char *name; - const char *id; - const char *code; - const char *type; - const char *profile; - const char *cache; - const char *cache_size; - bool echo; - struct og_partition partition_setup[OG_PARTITION_MAX]; - struct og_sync_params sync_setup; - uint64_t flags; -}; - -#define OG_REST_PARAM_ADDR (1UL << 0) -#define OG_REST_PARAM_MAC (1UL << 1) -#define OG_REST_PARAM_WOL_TYPE (1UL << 2) -#define OG_REST_PARAM_RUN_CMD (1UL << 3) -#define OG_REST_PARAM_DISK (1UL << 4) -#define OG_REST_PARAM_PARTITION (1UL << 5) -#define OG_REST_PARAM_REPO (1UL << 6) -#define OG_REST_PARAM_NAME (1UL << 7) -#define OG_REST_PARAM_ID (1UL << 8) -#define OG_REST_PARAM_CODE (1UL << 9) -#define OG_REST_PARAM_TYPE (1UL << 10) -#define OG_REST_PARAM_PROFILE (1UL << 11) -#define OG_REST_PARAM_CACHE (1UL << 12) -#define OG_REST_PARAM_CACHE_SIZE (1UL << 13) -#define OG_REST_PARAM_PART_0 (1UL << 14) -#define OG_REST_PARAM_PART_1 (1UL << 15) -#define OG_REST_PARAM_PART_2 (1UL << 16) -#define OG_REST_PARAM_PART_3 (1UL << 17) -#define OG_REST_PARAM_SYNC_SYNC (1UL << 18) -#define OG_REST_PARAM_SYNC_DIFF (1UL << 19) -#define OG_REST_PARAM_SYNC_REMOVE (1UL << 20) -#define OG_REST_PARAM_SYNC_COMPRESS (1UL << 21) -#define OG_REST_PARAM_SYNC_CLEANUP (1UL << 22) -#define OG_REST_PARAM_SYNC_CACHE (1UL << 23) -#define OG_REST_PARAM_SYNC_CLEANUP_CACHE (1UL << 24) -#define OG_REST_PARAM_SYNC_REMOVE_DST (1UL << 25) -#define OG_REST_PARAM_SYNC_DIFF_ID (1UL << 26) -#define OG_REST_PARAM_SYNC_DIFF_NAME (1UL << 27) -#define OG_REST_PARAM_SYNC_PATH (1UL << 28) -#define OG_REST_PARAM_SYNC_METHOD (1UL << 29) -#define OG_REST_PARAM_ECHO (1UL << 30) - -static bool og_msg_params_validate(const struct og_msg_params *params, - const uint64_t flags) -{ - return (params->flags & flags) == flags; -} - -static int og_json_parse_clients(json_t *element, struct og_msg_params *params) -{ - unsigned int i; - json_t *k; - - if (json_typeof(element) != JSON_ARRAY) - return -1; - - for (i = 0; i < json_array_size(element); i++) { - k = json_array_get(element, i); - if (json_typeof(k) != JSON_STRING) - return -1; - - params->ips_array[params->ips_array_len++] = - json_string_value(k); - - params->flags |= OG_REST_PARAM_ADDR; - } - - return 0; -} - -static int og_json_parse_string(json_t *element, const char **str) -{ - if (json_typeof(element) != JSON_STRING) - return -1; - - *str = json_string_value(element); - return 0; -} - -static int og_json_parse_bool(json_t *element, bool *value) -{ - if (json_typeof(element) == JSON_TRUE) - *value = true; - else if (json_typeof(element) == JSON_FALSE) - *value = false; - else - return -1; - - return 0; -} - -static int og_json_parse_sync_params(json_t *element, - struct og_msg_params *params) -{ - const char *key; - json_t *value; - int err = 0; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "sync")) { - err = og_json_parse_string(value, ¶ms->sync_setup.sync); - params->flags |= OG_REST_PARAM_SYNC_SYNC; - } else if (!strcmp(key, "diff")) { - err = og_json_parse_string(value, ¶ms->sync_setup.diff); - params->flags |= OG_REST_PARAM_SYNC_DIFF; - } else if (!strcmp(key, "remove")) { - err = og_json_parse_string(value, ¶ms->sync_setup.remove); - params->flags |= OG_REST_PARAM_SYNC_REMOVE; - } else if (!strcmp(key, "compress")) { - err = og_json_parse_string(value, ¶ms->sync_setup.compress); - params->flags |= OG_REST_PARAM_SYNC_COMPRESS; - } else if (!strcmp(key, "cleanup")) { - err = og_json_parse_string(value, ¶ms->sync_setup.cleanup); - params->flags |= OG_REST_PARAM_SYNC_CLEANUP; - } else if (!strcmp(key, "cache")) { - err = og_json_parse_string(value, ¶ms->sync_setup.cache); - params->flags |= OG_REST_PARAM_SYNC_CACHE; - } else if (!strcmp(key, "cleanup_cache")) { - err = og_json_parse_string(value, ¶ms->sync_setup.cleanup_cache); - params->flags |= OG_REST_PARAM_SYNC_CLEANUP_CACHE; - } else if (!strcmp(key, "remove_dst")) { - err = og_json_parse_string(value, ¶ms->sync_setup.remove_dst); - params->flags |= OG_REST_PARAM_SYNC_REMOVE_DST; - } else if (!strcmp(key, "diff_id")) { - err = og_json_parse_string(value, ¶ms->sync_setup.diff_id); - params->flags |= OG_REST_PARAM_SYNC_DIFF_ID; - } else if (!strcmp(key, "diff_name")) { - err = og_json_parse_string(value, ¶ms->sync_setup.diff_name); - params->flags |= OG_REST_PARAM_SYNC_DIFF_NAME; - } else if (!strcmp(key, "path")) { - err = og_json_parse_string(value, ¶ms->sync_setup.path); - params->flags |= OG_REST_PARAM_SYNC_PATH; - } else if (!strcmp(key, "method")) { - err = og_json_parse_string(value, ¶ms->sync_setup.method); - params->flags |= OG_REST_PARAM_SYNC_METHOD; - } - - if (err != 0) - return err; - } - return err; -} - -#define OG_PARAM_PART_NUMBER (1UL << 0) -#define OG_PARAM_PART_CODE (1UL << 1) -#define OG_PARAM_PART_FILESYSTEM (1UL << 2) -#define OG_PARAM_PART_SIZE (1UL << 3) -#define OG_PARAM_PART_FORMAT (1UL << 4) - -static int og_json_parse_partition(json_t *element, - struct og_msg_params *params, - unsigned int i) -{ - struct og_partition *part = ¶ms->partition_setup[i]; - uint64_t flags = 0UL; - const char *key; - json_t *value; - int err = 0; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "partition")) { - err = og_json_parse_string(value, &part->number); - flags |= OG_PARAM_PART_NUMBER; - } else if (!strcmp(key, "code")) { - err = og_json_parse_string(value, &part->code); - flags |= OG_PARAM_PART_CODE; - } else if (!strcmp(key, "filesystem")) { - err = og_json_parse_string(value, &part->filesystem); - flags |= OG_PARAM_PART_FILESYSTEM; - } else if (!strcmp(key, "size")) { - err = og_json_parse_string(value, &part->size); - flags |= OG_PARAM_PART_SIZE; - } else if (!strcmp(key, "format")) { - err = og_json_parse_string(value, &part->format); - flags |= OG_PARAM_PART_FORMAT; - } - - if (err < 0) - return err; - } - - if (flags != (OG_PARAM_PART_NUMBER | - OG_PARAM_PART_CODE | - OG_PARAM_PART_FILESYSTEM | - OG_PARAM_PART_SIZE | - OG_PARAM_PART_FORMAT)) - return -1; - - params->flags |= (OG_REST_PARAM_PART_0 << i); - - return err; -} - -static int og_json_parse_partition_setup(json_t *element, - struct og_msg_params *params) -{ - unsigned int i; - json_t *k; - - if (json_typeof(element) != JSON_ARRAY) - return -1; - - for (i = 0; i < json_array_size(element) && i < OG_PARTITION_MAX; ++i) { - k = json_array_get(element, i); - - if (json_typeof(k) != JSON_OBJECT) - return -1; - - if (og_json_parse_partition(k, params, i) != 0) - return -1; - } - return 0; -} - -static int og_cmd_legacy_send(struct og_msg_params *params, const char *cmd, - const char *state) -{ - char buf[4096] = {}; - int len, err = 0; - TRAMA *msg; - - len = snprintf(buf, sizeof(buf), "nfn=%s\r", cmd); - - msg = og_msg_alloc(buf, len); - if (!msg) - return -1; - - if (!og_send_cmd((char **)params->ips_array, params->ips_array_len, - state, msg)) - err = -1; - - og_msg_free(msg); - - return err; -} - -static int og_cmd_post_clients(json_t *element, struct og_msg_params *params) -{ - const char *key; - json_t *value; - int err = 0; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) - return -1; - - return og_cmd_legacy_send(params, "Sondeo", CLIENTE_APAGADO); -} - -struct og_buffer { - char *data; - int len; -}; - -static int og_json_dump_clients(const char *buffer, size_t size, void *data) -{ - struct og_buffer *og_buffer = (struct og_buffer *)data; - - memcpy(og_buffer->data + og_buffer->len, buffer, size); - og_buffer->len += size; - - return 0; -} - -static int og_cmd_get_clients(json_t *element, struct og_msg_params *params, - char *buffer_reply) -{ - json_t *root, *array, *addr, *state, *object; - struct og_buffer og_buffer = { - .data = buffer_reply, - }; - int i; - - array = json_array(); - if (!array) - return -1; - - for (i = 0; i < MAXIMOS_CLIENTES; i++) { - if (tbsockets[i].ip[0] == '\0') - continue; - - object = json_object(); - if (!object) { - json_decref(array); - return -1; - } - addr = json_string(tbsockets[i].ip); - if (!addr) { - json_decref(object); - json_decref(array); - return -1; - } - json_object_set_new(object, "addr", addr); - - state = json_string(tbsockets[i].estado); - if (!state) { - json_decref(object); - json_decref(array); - return -1; - } - json_object_set_new(object, "state", state); - - json_array_append_new(array, object); - } - root = json_pack("{s:o}", "clients", array); - if (!root) { - json_decref(array); - return -1; - } - - json_dump_callback(root, og_json_dump_clients, &og_buffer, 0); - json_decref(root); - - return 0; -} - -static int og_json_parse_target(json_t *element, struct og_msg_params *params) -{ - const char *key; - json_t *value; - - if (json_typeof(element) != JSON_OBJECT) { - return -1; - } - - json_object_foreach(element, key, value) { - if (!strcmp(key, "addr")) { - if (json_typeof(value) != JSON_STRING) - return -1; - - params->ips_array[params->ips_array_len] = - json_string_value(value); - - params->flags |= OG_REST_PARAM_ADDR; - } else if (!strcmp(key, "mac")) { - if (json_typeof(value) != JSON_STRING) - return -1; - - params->mac_array[params->ips_array_len] = - json_string_value(value); - - params->flags |= OG_REST_PARAM_MAC; - } - } - - return 0; -} - -static int og_json_parse_targets(json_t *element, struct og_msg_params *params) -{ - unsigned int i; - json_t *k; - int err; - - if (json_typeof(element) != JSON_ARRAY) - return -1; - - for (i = 0; i < json_array_size(element); i++) { - k = json_array_get(element, i); - - if (json_typeof(k) != JSON_OBJECT) - return -1; - - err = og_json_parse_target(k, params); - if (err < 0) - return err; - - params->ips_array_len++; - } - return 0; -} - -static int og_json_parse_type(json_t *element, struct og_msg_params *params) -{ - const char *type; - - if (json_typeof(element) != JSON_STRING) - return -1; - - params->wol_type = json_string_value(element); - - type = json_string_value(element); - if (!strcmp(type, "unicast")) - params->wol_type = "2"; - else if (!strcmp(type, "broadcast")) - params->wol_type = "1"; - - params->flags |= OG_REST_PARAM_WOL_TYPE; - - return 0; -} - -static int og_cmd_wol(json_t *element, struct og_msg_params *params) -{ - char ips_str[(OG_DB_IP_MAXLEN + 1) * OG_CLIENTS_MAX + 1] = {}; - int ips_str_len = 0; - const char *msglog; - struct og_dbi *dbi; - int i = 0; - dbi_result result; - const char *key; - json_t *value; - int err = 0; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) { - err = og_json_parse_targets(value, params); - } else if (!strcmp(key, "type")) { - err = og_json_parse_type(value, params); - } - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_MAC | - OG_REST_PARAM_WOL_TYPE)) - return -1; - - for (i = 0; i < params->ips_array_len; ++i) { - ips_str_len += snprintf(ips_str + ips_str_len, - sizeof(ips_str) - ips_str_len, - "'%s',", params->ips_array[i]); - } - ips_str[ips_str_len - 1] = '\0'; - - dbi = og_dbi_open(&dbi_config); - if (!dbi) { - syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", - __func__, __LINE__); - return -1; - } - - result = dbi_conn_queryf(dbi->conn, - "SELECT ordenadores.ip, ordenadores.mac, " - "aulas.netmask " - "FROM ordenadores " - "INNER JOIN aulas " - "ON ordenadores.idaula = aulas.idaula " - "WHERE ordenadores.ip IN (%s)", - ips_str); - if (!result) { - dbi_conn_error(dbi->conn, &msglog); - syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", - __func__, __LINE__, msglog); - og_dbi_close(dbi); - return -1; - } - - for (i = 0; dbi_result_next_row(result); i++) { - params->ips_array[i] = dbi_result_get_string_copy(result, "ip"); - params->mac_array[i] = dbi_result_get_string_copy(result, "mac"); - params->netmask_array[i] = dbi_result_get_string_copy(result, "netmask"); - } - - dbi_result_free(result); - og_dbi_close(dbi); - - if (!Levanta((char **)params->ips_array, (char **)params->mac_array, - (char **)params->netmask_array, i, - (char *)params->wol_type)) - return -1; - - return 0; -} - -static int og_json_parse_run(json_t *element, struct og_msg_params *params) -{ - if (json_typeof(element) != JSON_STRING) - return -1; - - snprintf(params->run_cmd, sizeof(params->run_cmd), "%s", - json_string_value(element)); - - params->flags |= OG_REST_PARAM_RUN_CMD; - - return 0; -} - -static int og_cmd_run_post(json_t *element, struct og_msg_params *params) -{ - char buf[4096] = {}, iph[4096] = {}; - int err = 0, len; - const char *key; - unsigned int i; - json_t *value; - TRAMA *msg; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - else if (!strcmp(key, "run")) - err = og_json_parse_run(value, params); - else if (!strcmp(key, "echo")) { - err = og_json_parse_bool(value, ¶ms->echo); - params->flags |= OG_REST_PARAM_ECHO; - } - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_RUN_CMD | - OG_REST_PARAM_ECHO)) - return -1; - - for (i = 0; i < params->ips_array_len; i++) { - len = snprintf(iph + strlen(iph), sizeof(iph), "%s;", - params->ips_array[i]); - } - - if (params->echo) { - len = snprintf(buf, sizeof(buf), - "nfn=ConsolaRemota\riph=%s\rscp=%s\r", - iph, params->run_cmd); - } else { - len = snprintf(buf, sizeof(buf), - "nfn=EjecutarScript\riph=%s\rscp=%s\r", - iph, params->run_cmd); - } - - msg = og_msg_alloc(buf, len); - if (!msg) - return -1; - - if (!og_send_cmd((char **)params->ips_array, params->ips_array_len, - CLIENTE_OCUPADO, msg)) - err = -1; - - og_msg_free(msg); - - if (err < 0) - return err; - - for (i = 0; i < params->ips_array_len; i++) { - char filename[4096]; - FILE *f; - - sprintf(filename, "/tmp/_Seconsola_%s", params->ips_array[i]); - f = fopen(filename, "wt"); - fclose(f); - } - - return 0; -} - -static int og_cmd_run_get(json_t *element, struct og_msg_params *params, - char *buffer_reply) -{ - struct og_buffer og_buffer = { - .data = buffer_reply, - }; - json_t *root, *value, *array; - const char *key; - unsigned int i; - int err = 0; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - - if (err < 0) - return err; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) - return -1; - - array = json_array(); - if (!array) - return -1; - - for (i = 0; i < params->ips_array_len; i++) { - json_t *object, *output, *addr; - char data[4096] = {}; - char filename[4096]; - int fd, numbytes; - - sprintf(filename, "/tmp/_Seconsola_%s", params->ips_array[i]); - - fd = open(filename, O_RDONLY); - if (!fd) - return -1; - - numbytes = read(fd, data, sizeof(data)); - if (numbytes < 0) { - close(fd); - return -1; - } - data[sizeof(data) - 1] = '\0'; - close(fd); - - object = json_object(); - if (!object) { - json_decref(array); - return -1; - } - addr = json_string(params->ips_array[i]); - if (!addr) { - json_decref(object); - json_decref(array); - return -1; - } - json_object_set_new(object, "addr", addr); - - output = json_string(data); - if (!output) { - json_decref(object); - json_decref(array); - return -1; - } - json_object_set_new(object, "output", output); - - json_array_append_new(array, object); - } - - root = json_pack("{s:o}", "clients", array); - if (!root) - return -1; - - json_dump_callback(root, og_json_dump_clients, &og_buffer, 0); - json_decref(root); - - return 0; -} - -static int og_cmd_session(json_t *element, struct og_msg_params *params) -{ - char buf[4096], iph[4096]; - int err = 0, len; - const char *key; - unsigned int i; - json_t *value; - TRAMA *msg; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) { - err = og_json_parse_clients(value, params); - } else if (!strcmp(key, "disk")) { - err = og_json_parse_string(value, ¶ms->disk); - params->flags |= OG_REST_PARAM_DISK; - } else if (!strcmp(key, "partition")) { - err = og_json_parse_string(value, ¶ms->partition); - params->flags |= OG_REST_PARAM_PARTITION; - } - - if (err < 0) - return err; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_DISK | - OG_REST_PARAM_PARTITION)) - return -1; - - for (i = 0; i < params->ips_array_len; i++) { - snprintf(iph + strlen(iph), sizeof(iph), "%s;", - params->ips_array[i]); - } - len = snprintf(buf, sizeof(buf), - "nfn=IniciarSesion\riph=%s\rdsk=%s\rpar=%s\r", - iph, params->disk, params->partition); - - msg = og_msg_alloc(buf, len); - if (!msg) - return -1; - - if (!og_send_cmd((char **)params->ips_array, params->ips_array_len, - CLIENTE_APAGADO, msg)) - err = -1; - - og_msg_free(msg); - - return 0; -} - -static int og_cmd_poweroff(json_t *element, struct og_msg_params *params) -{ - const char *key; - json_t *value; - int err = 0; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) - return -1; - - return og_cmd_legacy_send(params, "Apagar", CLIENTE_OCUPADO); -} - -static int og_cmd_refresh(json_t *element, struct og_msg_params *params) -{ - const char *key; - json_t *value; - int err = 0; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) - return -1; - - return og_cmd_legacy_send(params, "Actualizar", CLIENTE_APAGADO); -} - -static int og_cmd_reboot(json_t *element, struct og_msg_params *params) -{ - const char *key; - json_t *value; - int err = 0; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) - return -1; - - return og_cmd_legacy_send(params, "Reiniciar", CLIENTE_OCUPADO); -} - -static int og_cmd_stop(json_t *element, struct og_msg_params *params) -{ - const char *key; - json_t *value; - int err = 0; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) - return -1; - - return og_cmd_legacy_send(params, "Purgar", CLIENTE_APAGADO); -} - -static int og_cmd_hardware(json_t *element, struct og_msg_params *params) -{ - const char *key; - json_t *value; - int err = 0; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) - return -1; - - return og_cmd_legacy_send(params, "InventarioHardware", - CLIENTE_OCUPADO); -} - -static int og_cmd_software(json_t *element, struct og_msg_params *params) -{ - char buf[4096] = {}; - int err = 0, len; - const char *key; - json_t *value; - TRAMA *msg; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - else if (!strcmp(key, "disk")) { - err = og_json_parse_string(value, ¶ms->disk); - params->flags |= OG_REST_PARAM_DISK; - } - else if (!strcmp(key, "partition")) { - err = og_json_parse_string(value, ¶ms->partition); - params->flags |= OG_REST_PARAM_PARTITION; - } - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_DISK | - OG_REST_PARAM_PARTITION)) - return -1; - - len = snprintf(buf, sizeof(buf), - "nfn=InventarioSoftware\rdsk=%s\rpar=%s\r", - params->disk, params->partition); - - msg = og_msg_alloc(buf, len); - if (!msg) - return -1; - - og_send_cmd((char **)params->ips_array, params->ips_array_len, - CLIENTE_OCUPADO, msg); - - og_msg_free(msg); - - return 0; -} - -static int og_cmd_create_image(json_t *element, struct og_msg_params *params) -{ - char buf[4096] = {}; - int err = 0, len; - const char *key; - json_t *value; - TRAMA *msg; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "disk")) { - err = og_json_parse_string(value, ¶ms->disk); - params->flags |= OG_REST_PARAM_DISK; - } else if (!strcmp(key, "partition")) { - err = og_json_parse_string(value, ¶ms->partition); - params->flags |= OG_REST_PARAM_PARTITION; - } else if (!strcmp(key, "name")) { - err = og_json_parse_string(value, ¶ms->name); - params->flags |= OG_REST_PARAM_NAME; - } else if (!strcmp(key, "repository")) { - err = og_json_parse_string(value, ¶ms->repository); - params->flags |= OG_REST_PARAM_REPO; - } else if (!strcmp(key, "clients")) { - err = og_json_parse_clients(value, params); - } else if (!strcmp(key, "id")) { - err = og_json_parse_string(value, ¶ms->id); - params->flags |= OG_REST_PARAM_ID; - } else if (!strcmp(key, "code")) { - err = og_json_parse_string(value, ¶ms->code); - params->flags |= OG_REST_PARAM_CODE; - } - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_DISK | - OG_REST_PARAM_PARTITION | - OG_REST_PARAM_CODE | - OG_REST_PARAM_ID | - OG_REST_PARAM_NAME | - OG_REST_PARAM_REPO)) - return -1; - - len = snprintf(buf, sizeof(buf), - "nfn=CrearImagen\rdsk=%s\rpar=%s\rcpt=%s\ridi=%s\rnci=%s\ripr=%s\r", - params->disk, params->partition, params->code, - params->id, params->name, params->repository); - - msg = og_msg_alloc(buf, len); - if (!msg) - return -1; - - og_send_cmd((char **)params->ips_array, params->ips_array_len, - CLIENTE_OCUPADO, msg); - - og_msg_free(msg); - - return 0; -} - -static int og_cmd_restore_image(json_t *element, struct og_msg_params *params) -{ - char buf[4096] = {}; - int err = 0, len; - const char *key; - json_t *value; - TRAMA *msg; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "disk")) { - err = og_json_parse_string(value, ¶ms->disk); - params->flags |= OG_REST_PARAM_DISK; - } else if (!strcmp(key, "partition")) { - err = og_json_parse_string(value, ¶ms->partition); - params->flags |= OG_REST_PARAM_PARTITION; - } else if (!strcmp(key, "name")) { - err = og_json_parse_string(value, ¶ms->name); - params->flags |= OG_REST_PARAM_NAME; - } else if (!strcmp(key, "repository")) { - err = og_json_parse_string(value, ¶ms->repository); - params->flags |= OG_REST_PARAM_REPO; - } else if (!strcmp(key, "clients")) { - err = og_json_parse_clients(value, params); - } else if (!strcmp(key, "type")) { - err = og_json_parse_string(value, ¶ms->type); - params->flags |= OG_REST_PARAM_TYPE; - } else if (!strcmp(key, "profile")) { - err = og_json_parse_string(value, ¶ms->profile); - params->flags |= OG_REST_PARAM_PROFILE; - } else if (!strcmp(key, "id")) { - err = og_json_parse_string(value, ¶ms->id); - params->flags |= OG_REST_PARAM_ID; - } - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_DISK | - OG_REST_PARAM_PARTITION | - OG_REST_PARAM_NAME | - OG_REST_PARAM_REPO | - OG_REST_PARAM_TYPE | - OG_REST_PARAM_PROFILE | - OG_REST_PARAM_ID)) - return -1; - - len = snprintf(buf, sizeof(buf), - "nfn=RestaurarImagen\ridi=%s\rdsk=%s\rpar=%s\rifs=%s\r" - "nci=%s\ripr=%s\rptc=%s\r", - params->id, params->disk, params->partition, - params->profile, params->name, - params->repository, params->type); - - msg = og_msg_alloc(buf, len); - if (!msg) - return -1; - - og_send_cmd((char **)params->ips_array, params->ips_array_len, - CLIENTE_OCUPADO, msg); - - og_msg_free(msg); - - return 0; -} - -static int og_cmd_setup(json_t *element, struct og_msg_params *params) -{ - char buf[4096] = {}; - int err = 0, len; - const char *key; - json_t *value; - TRAMA *msg; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) { - err = og_json_parse_clients(value, params); - } else if (!strcmp(key, "disk")) { - err = og_json_parse_string(value, ¶ms->disk); - params->flags |= OG_REST_PARAM_DISK; - } else if (!strcmp(key, "cache")) { - err = og_json_parse_string(value, ¶ms->cache); - params->flags |= OG_REST_PARAM_CACHE; - } else if (!strcmp(key, "cache_size")) { - err = og_json_parse_string(value, ¶ms->cache_size); - params->flags |= OG_REST_PARAM_CACHE_SIZE; - } else if (!strcmp(key, "partition_setup")) { - err = og_json_parse_partition_setup(value, params); - } - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_DISK | - OG_REST_PARAM_CACHE | - OG_REST_PARAM_CACHE_SIZE | - OG_REST_PARAM_PART_0 | - OG_REST_PARAM_PART_1 | - OG_REST_PARAM_PART_2 | - OG_REST_PARAM_PART_3)) - return -1; - - len = snprintf(buf, sizeof(buf), - "nfn=Configurar\rdsk=%s\rcfg=dis=%s*che=%s*tch=%s!", - params->disk, params->disk, params->cache, params->cache_size); - - for (unsigned int i = 0; i < OG_PARTITION_MAX; ++i) { - const struct og_partition *part = ¶ms->partition_setup[i]; - - len += snprintf(buf + strlen(buf), sizeof(buf), - "par=%s*cpt=%s*sfi=%s*tam=%s*ope=%s%%", - part->number, part->code, part->filesystem, part->size, part->format); - } - - msg = og_msg_alloc(buf, len + 1); - if (!msg) - return -1; - - og_send_cmd((char **)params->ips_array, params->ips_array_len, - CLIENTE_OCUPADO, msg); - - og_msg_free(msg); - - return 0; -} - -static int og_cmd_run_schedule(json_t *element, struct og_msg_params *params) -{ - const char *key; - json_t *value; - int err = 0; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) - return -1; - - og_cmd_legacy_send(params, "EjecutaComandosPendientes", CLIENTE_OCUPADO); - - return 0; -} - -static int og_cmd_create_basic_image(json_t *element, struct og_msg_params *params) -{ - char buf[4096] = {}; - int err = 0, len; - const char *key; - json_t *value; - TRAMA *msg; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) { - err = og_json_parse_clients(value, params); - } else if (!strcmp(key, "disk")) { - err = og_json_parse_string(value, ¶ms->disk); - params->flags |= OG_REST_PARAM_DISK; - } else if (!strcmp(key, "partition")) { - err = og_json_parse_string(value, ¶ms->partition); - params->flags |= OG_REST_PARAM_PARTITION; - } else if (!strcmp(key, "code")) { - err = og_json_parse_string(value, ¶ms->code); - params->flags |= OG_REST_PARAM_CODE; - } else if (!strcmp(key, "id")) { - err = og_json_parse_string(value, ¶ms->id); - params->flags |= OG_REST_PARAM_ID; - } else if (!strcmp(key, "name")) { - err = og_json_parse_string(value, ¶ms->name); - params->flags |= OG_REST_PARAM_NAME; - } else if (!strcmp(key, "repository")) { - err = og_json_parse_string(value, ¶ms->repository); - params->flags |= OG_REST_PARAM_REPO; - } else if (!strcmp(key, "sync_params")) { - err = og_json_parse_sync_params(value, params); - } - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_DISK | - OG_REST_PARAM_PARTITION | - OG_REST_PARAM_CODE | - OG_REST_PARAM_ID | - OG_REST_PARAM_NAME | - OG_REST_PARAM_REPO | - OG_REST_PARAM_SYNC_SYNC | - OG_REST_PARAM_SYNC_DIFF | - OG_REST_PARAM_SYNC_REMOVE | - OG_REST_PARAM_SYNC_COMPRESS | - OG_REST_PARAM_SYNC_CLEANUP | - OG_REST_PARAM_SYNC_CACHE | - OG_REST_PARAM_SYNC_CLEANUP_CACHE | - OG_REST_PARAM_SYNC_REMOVE_DST)) - return -1; - - len = snprintf(buf, sizeof(buf), - "nfn=CrearImagenBasica\rdsk=%s\rpar=%s\rcpt=%s\ridi=%s\r" - "nci=%s\ripr=%s\rrti=\rmsy=%s\rwhl=%s\reli=%s\rcmp=%s\rbpi=%s\r" - "cpc=%s\rbpc=%s\rnba=%s\r", - params->disk, params->partition, params->code, params->id, - params->name, params->repository, params->sync_setup.sync, - params->sync_setup.diff, params->sync_setup.remove, - params->sync_setup.compress, params->sync_setup.cleanup, - params->sync_setup.cache, params->sync_setup.cleanup_cache, - params->sync_setup.remove_dst); - - msg = og_msg_alloc(buf, len); - if (!msg) - return -1; - - og_send_cmd((char **)params->ips_array, params->ips_array_len, - CLIENTE_OCUPADO, msg); - - og_msg_free(msg); - - return 0; -} - -static int og_cmd_create_incremental_image(json_t *element, struct og_msg_params *params) -{ - char buf[4096] = {}; - int err = 0, len; - const char *key; - json_t *value; - TRAMA *msg; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) - err = og_json_parse_clients(value, params); - else if (!strcmp(key, "disk")) { - err = og_json_parse_string(value, ¶ms->disk); - params->flags |= OG_REST_PARAM_DISK; - } else if (!strcmp(key, "partition")) { - err = og_json_parse_string(value, ¶ms->partition); - params->flags |= OG_REST_PARAM_PARTITION; - } else if (!strcmp(key, "id")) { - err = og_json_parse_string(value, ¶ms->id); - params->flags |= OG_REST_PARAM_ID; - } else if (!strcmp(key, "name")) { - err = og_json_parse_string(value, ¶ms->name); - params->flags |= OG_REST_PARAM_NAME; - } else if (!strcmp(key, "repository")) { - err = og_json_parse_string(value, ¶ms->repository); - params->flags |= OG_REST_PARAM_REPO; - } else if (!strcmp(key, "sync_params")) { - err = og_json_parse_sync_params(value, params); - } - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_DISK | - OG_REST_PARAM_PARTITION | - OG_REST_PARAM_ID | - OG_REST_PARAM_NAME | - OG_REST_PARAM_REPO | - OG_REST_PARAM_SYNC_SYNC | - OG_REST_PARAM_SYNC_PATH | - OG_REST_PARAM_SYNC_DIFF | - OG_REST_PARAM_SYNC_DIFF_ID | - OG_REST_PARAM_SYNC_DIFF_NAME | - OG_REST_PARAM_SYNC_REMOVE | - OG_REST_PARAM_SYNC_COMPRESS | - OG_REST_PARAM_SYNC_CLEANUP | - OG_REST_PARAM_SYNC_CACHE | - OG_REST_PARAM_SYNC_CLEANUP_CACHE | - OG_REST_PARAM_SYNC_REMOVE_DST)) - return -1; - - len = snprintf(buf, sizeof(buf), - "nfn=CrearSoftIncremental\rdsk=%s\rpar=%s\ridi=%s\rnci=%s\r" - "rti=%s\ripr=%s\ridf=%s\rncf=%s\rmsy=%s\rwhl=%s\reli=%s\rcmp=%s\r" - "bpi=%s\rcpc=%s\rbpc=%s\rnba=%s\r", - params->disk, params->partition, params->id, params->name, - params->sync_setup.path, params->repository, params->sync_setup.diff_id, - params->sync_setup.diff_name, params->sync_setup.sync, - params->sync_setup.diff, params->sync_setup.remove_dst, - params->sync_setup.compress, params->sync_setup.cleanup, - params->sync_setup.cache, params->sync_setup.cleanup_cache, - params->sync_setup.remove_dst); - - msg = og_msg_alloc(buf, len); - if (!msg) - return -1; - - og_send_cmd((char **)params->ips_array, params->ips_array_len, - CLIENTE_OCUPADO, msg); - - og_msg_free(msg); - - return 0; -} - -static int og_cmd_restore_basic_image(json_t *element, struct og_msg_params *params) -{ - char buf[4096] = {}; - int err = 0, len; - const char *key; - json_t *value; - TRAMA *msg; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) { - err = og_json_parse_clients(value, params); - } else if (!strcmp(key, "disk")) { - err = og_json_parse_string(value, ¶ms->disk); - params->flags |= OG_REST_PARAM_DISK; - } else if (!strcmp(key, "partition")) { - err = og_json_parse_string(value, ¶ms->partition); - params->flags |= OG_REST_PARAM_PARTITION; - } else if (!strcmp(key, "id")) { - err = og_json_parse_string(value, ¶ms->id); - params->flags |= OG_REST_PARAM_ID; - } else if (!strcmp(key, "name")) { - err = og_json_parse_string(value, ¶ms->name); - params->flags |= OG_REST_PARAM_NAME; - } else if (!strcmp(key, "repository")) { - err = og_json_parse_string(value, ¶ms->repository); - params->flags |= OG_REST_PARAM_REPO; - } else if (!strcmp(key, "profile")) { - err = og_json_parse_string(value, ¶ms->profile); - params->flags |= OG_REST_PARAM_PROFILE; - } else if (!strcmp(key, "type")) { - err = og_json_parse_string(value, ¶ms->type); - params->flags |= OG_REST_PARAM_TYPE; - } else if (!strcmp(key, "sync_params")) { - err = og_json_parse_sync_params(value, params); - } - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_DISK | - OG_REST_PARAM_PARTITION | - OG_REST_PARAM_ID | - OG_REST_PARAM_NAME | - OG_REST_PARAM_REPO | - OG_REST_PARAM_PROFILE | - OG_REST_PARAM_TYPE | - OG_REST_PARAM_SYNC_PATH | - OG_REST_PARAM_SYNC_METHOD | - OG_REST_PARAM_SYNC_SYNC | - OG_REST_PARAM_SYNC_DIFF | - OG_REST_PARAM_SYNC_REMOVE | - OG_REST_PARAM_SYNC_COMPRESS | - OG_REST_PARAM_SYNC_CLEANUP | - OG_REST_PARAM_SYNC_CACHE | - OG_REST_PARAM_SYNC_CLEANUP_CACHE | - OG_REST_PARAM_SYNC_REMOVE_DST)) - return -1; - - len = snprintf(buf, sizeof(buf), - "nfn=RestaurarImagenBasica\rdsk=%s\rpar=%s\ridi=%s\rnci=%s\r" - "ipr=%s\rifs=%s\rrti=%s\rmet=%s\rmsy=%s\rtpt=%s\rwhl=%s\r" - "eli=%s\rcmp=%s\rbpi=%s\rcpc=%s\rbpc=%s\rnba=%s\r", - params->disk, params->partition, params->id, params->name, - params->repository, params->profile, params->sync_setup.path, - params->sync_setup.method, params->sync_setup.sync, params->type, - params->sync_setup.diff, params->sync_setup.remove, - params->sync_setup.compress, params->sync_setup.cleanup, - params->sync_setup.cache, params->sync_setup.cleanup_cache, - params->sync_setup.remove_dst); - - msg = og_msg_alloc(buf, len); - if (!msg) - return -1; - - og_send_cmd((char **)params->ips_array, params->ips_array_len, - CLIENTE_OCUPADO, msg); - - og_msg_free(msg); - - return 0; -} - -static int og_cmd_restore_incremental_image(json_t *element, struct og_msg_params *params) -{ - char buf[4096] = {}; - int err = 0, len; - const char *key; - json_t *value; - TRAMA *msg; - - if (json_typeof(element) != JSON_OBJECT) - return -1; - - json_object_foreach(element, key, value) { - if (!strcmp(key, "clients")) { - err = og_json_parse_clients(value, params); - } else if (!strcmp(key, "disk")) { - err = og_json_parse_string(value, ¶ms->disk); - params->flags |= OG_REST_PARAM_DISK; - } else if (!strcmp(key, "partition")) { - err = og_json_parse_string(value, ¶ms->partition); - params->flags |= OG_REST_PARAM_PARTITION; - } else if (!strcmp(key, "id")) { - err = og_json_parse_string(value, ¶ms->id); - params->flags |= OG_REST_PARAM_ID; - } else if (!strcmp(key, "name")) { - err = og_json_parse_string(value, ¶ms->name); - params->flags |= OG_REST_PARAM_NAME; - } else if (!strcmp(key, "repository")) { - err = og_json_parse_string(value, ¶ms->repository); - params->flags |= OG_REST_PARAM_REPO; - } else if (!strcmp(key, "profile")) { - err = og_json_parse_string(value, ¶ms->profile); - params->flags |= OG_REST_PARAM_PROFILE; - } else if (!strcmp(key, "type")) { - err = og_json_parse_string(value, ¶ms->type); - params->flags |= OG_REST_PARAM_TYPE; - } else if (!strcmp(key, "sync_params")) { - err = og_json_parse_sync_params(value, params); - } - - if (err < 0) - break; - } - - if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | - OG_REST_PARAM_DISK | - OG_REST_PARAM_PARTITION | - OG_REST_PARAM_ID | - OG_REST_PARAM_NAME | - OG_REST_PARAM_REPO | - OG_REST_PARAM_PROFILE | - OG_REST_PARAM_TYPE | - OG_REST_PARAM_SYNC_DIFF_ID | - OG_REST_PARAM_SYNC_DIFF_NAME | - OG_REST_PARAM_SYNC_PATH | - OG_REST_PARAM_SYNC_METHOD | - OG_REST_PARAM_SYNC_SYNC | - OG_REST_PARAM_SYNC_DIFF | - OG_REST_PARAM_SYNC_REMOVE | - OG_REST_PARAM_SYNC_COMPRESS | - OG_REST_PARAM_SYNC_CLEANUP | - OG_REST_PARAM_SYNC_CACHE | - OG_REST_PARAM_SYNC_CLEANUP_CACHE | - OG_REST_PARAM_SYNC_REMOVE_DST)) - return -1; - - len = snprintf(buf, sizeof(buf), - "nfn=RestaurarSoftIncremental\rdsk=%s\rpar=%s\ridi=%s\rnci=%s\r" - "ipr=%s\rifs=%s\ridf=%s\rncf=%s\rrti=%s\rmet=%s\rmsy=%s\r" - "tpt=%s\rwhl=%s\reli=%s\rcmp=%s\rbpi=%s\rcpc=%s\rbpc=%s\r" - "nba=%s\r", - params->disk, params->partition, params->id, params->name, - params->repository, params->profile, params->sync_setup.diff_id, - params->sync_setup.diff_name, params->sync_setup.path, - params->sync_setup.method, params->sync_setup.sync, params->type, - params->sync_setup.diff, params->sync_setup.remove, - params->sync_setup.compress, params->sync_setup.cleanup, - params->sync_setup.cache, params->sync_setup.cleanup_cache, - params->sync_setup.remove_dst); - - msg = og_msg_alloc(buf, len); - if (!msg) - return -1; - - og_send_cmd((char **)params->ips_array, params->ips_array_len, - CLIENTE_OCUPADO, msg); - - og_msg_free(msg); - - return 0; -} - -static int og_client_method_not_found(struct og_client *cli) -{ - /* To meet RFC 7231, this function MUST generate an Allow header field - * containing the correct methods. For example: "Allow: POST\r\n" - */ - char buf[] = "HTTP/1.1 405 Method Not Allowed\r\n" - "Content-Length: 0\r\n\r\n"; - - send(og_client_socket(cli), buf, strlen(buf), 0); - - return -1; -} - -static int og_client_bad_request(struct og_client *cli) -{ - char buf[] = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n"; - - send(og_client_socket(cli), buf, strlen(buf), 0); - - return -1; -} - -static int og_client_not_found(struct og_client *cli) -{ - char buf[] = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"; - - send(og_client_socket(cli), buf, strlen(buf), 0); - - return -1; -} - -static int og_client_not_authorized(struct og_client *cli) -{ - char buf[] = "HTTP/1.1 401 Unauthorized\r\n" - "WWW-Authenticate: Basic\r\n" - "Content-Length: 0\r\n\r\n"; - - send(og_client_socket(cli), buf, strlen(buf), 0); - - return -1; -} - -static int og_server_internal_error(struct og_client *cli) -{ - char buf[] = "HTTP/1.1 500 Internal Server Error\r\n" - "Content-Length: 0\r\n\r\n"; - - send(og_client_socket(cli), buf, strlen(buf), 0); - - return -1; -} - -static int og_client_payload_too_large(struct og_client *cli) -{ - char buf[] = "HTTP/1.1 413 Payload Too Large\r\n" - "Content-Length: 0\r\n\r\n"; - - send(og_client_socket(cli), buf, strlen(buf), 0); - - return -1; -} - -#define OG_MSG_RESPONSE_MAXLEN 65536 - -static int og_client_ok(struct og_client *cli, char *buf_reply) -{ - char buf[OG_MSG_RESPONSE_MAXLEN] = {}; - int err = 0, len; - - len = snprintf(buf, sizeof(buf), - "HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n\r\n%s", - strlen(buf_reply), buf_reply); - if (len >= (int)sizeof(buf)) - err = og_server_internal_error(cli); - - send(og_client_socket(cli), buf, strlen(buf), 0); - - return err; -} - -enum og_rest_method { - OG_METHOD_GET = 0, - OG_METHOD_POST, -}; - -static int og_client_state_process_payload_rest(struct og_client *cli) -{ - char buf_reply[OG_MSG_RESPONSE_MAXLEN] = {}; - struct og_msg_params params = {}; - enum og_rest_method method; - const char *cmd, *body; - json_error_t json_err; - json_t *root = NULL; - int err = 0; - - syslog(LOG_DEBUG, "%s:%hu %.32s ...\n", - inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port), cli->buf); - - if (!strncmp(cli->buf, "GET", strlen("GET"))) { - method = OG_METHOD_GET; - cmd = cli->buf + strlen("GET") + 2; - } else if (!strncmp(cli->buf, "POST", strlen("POST"))) { - method = OG_METHOD_POST; - cmd = cli->buf + strlen("POST") + 2; - } else - return og_client_method_not_found(cli); - - body = strstr(cli->buf, "\r\n\r\n") + 4; - - if (strcmp(cli->auth_token, auth_token)) { - syslog(LOG_ERR, "wrong Authentication key\n"); - return og_client_not_authorized(cli); - } - - if (cli->content_length) { - root = json_loads(body, 0, &json_err); - if (!root) { - syslog(LOG_ERR, "malformed json line %d: %s\n", - json_err.line, json_err.text); - return og_client_not_found(cli); - } - } - - if (!strncmp(cmd, "clients", strlen("clients"))) { - if (method != OG_METHOD_POST && - method != OG_METHOD_GET) - return og_client_method_not_found(cli); - - if (method == OG_METHOD_POST && !root) { - syslog(LOG_ERR, "command clients with no payload\n"); - return og_client_bad_request(cli); - } - switch (method) { - case OG_METHOD_POST: - err = og_cmd_post_clients(root, ¶ms); - break; - case OG_METHOD_GET: - err = og_cmd_get_clients(root, ¶ms, buf_reply); - break; - } - } else if (!strncmp(cmd, "wol", strlen("wol"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command wol with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_wol(root, ¶ms); - } else if (!strncmp(cmd, "shell/run", strlen("shell/run"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command run with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_run_post(root, ¶ms); - } else if (!strncmp(cmd, "shell/output", strlen("shell/output"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command output with no payload\n"); - return og_client_bad_request(cli); - } - - err = og_cmd_run_get(root, ¶ms, buf_reply); - } else if (!strncmp(cmd, "session", strlen("session"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command session with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_session(root, ¶ms); - } else if (!strncmp(cmd, "poweroff", strlen("poweroff"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command poweroff with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_poweroff(root, ¶ms); - } else if (!strncmp(cmd, "reboot", strlen("reboot"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command reboot with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_reboot(root, ¶ms); - } else if (!strncmp(cmd, "stop", strlen("stop"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command stop with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_stop(root, ¶ms); - } else if (!strncmp(cmd, "refresh", strlen("refresh"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command refresh with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_refresh(root, ¶ms); - } else if (!strncmp(cmd, "hardware", strlen("hardware"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command hardware with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_hardware(root, ¶ms); - } else if (!strncmp(cmd, "software", strlen("software"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command software with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_software(root, ¶ms); - } else if (!strncmp(cmd, "image/create/basic", - strlen("image/create/basic"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command create with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_create_basic_image(root, ¶ms); - } else if (!strncmp(cmd, "image/create/incremental", - strlen("image/create/incremental"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command create with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_create_incremental_image(root, ¶ms); - } else if (!strncmp(cmd, "image/create", strlen("image/create"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command create with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_create_image(root, ¶ms); - } else if (!strncmp(cmd, "image/restore/basic", - strlen("image/restore/basic"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command create with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_restore_basic_image(root, ¶ms); - } else if (!strncmp(cmd, "image/restore/incremental", - strlen("image/restore/incremental"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command create with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_restore_incremental_image(root, ¶ms); - } else if (!strncmp(cmd, "image/restore", strlen("image/restore"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command create with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_restore_image(root, ¶ms); - } else if (!strncmp(cmd, "setup", strlen("setup"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command create with no payload\n"); - return og_client_bad_request(cli); - } - err = og_cmd_setup(root, ¶ms); - } else if (!strncmp(cmd, "run/schedule", strlen("run/schedule"))) { - if (method != OG_METHOD_POST) - return og_client_method_not_found(cli); - - if (!root) { - syslog(LOG_ERR, "command create with no payload\n"); - return og_client_bad_request(cli); - } - - err = og_cmd_run_schedule(root, ¶ms); - } else { - syslog(LOG_ERR, "unknown command: %.32s ...\n", cmd); - err = og_client_not_found(cli); - } - - if (root) - json_decref(root); - - if (err < 0) - return og_client_bad_request(cli); - - err = og_client_ok(cli, buf_reply); - if (err < 0) { - syslog(LOG_ERR, "HTTP response to %s:%hu is too large\n", - inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port)); - } - - return err; -} - -static int og_client_state_recv_hdr_rest(struct og_client *cli) -{ - char *ptr; - - ptr = strstr(cli->buf, "\r\n\r\n"); - if (!ptr) - return 0; - - cli->msg_len = ptr - cli->buf + 4; - - ptr = strstr(cli->buf, "Content-Length: "); - if (ptr) { - sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length); - if (cli->content_length < 0) - return -1; - cli->msg_len += cli->content_length; - } - - ptr = strstr(cli->buf, "Authorization: "); - if (ptr) - sscanf(ptr, "Authorization: %63[^\r\n]", cli->auth_token); - - return 1; -} - -static void og_client_read_cb(struct ev_loop *loop, struct ev_io *io, int events) -{ - struct og_client *cli; - int ret; - - cli = container_of(io, struct og_client, io); - - if (events & EV_ERROR) { - syslog(LOG_ERR, "unexpected error event from client %s:%hu\n", - inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port)); - goto close; - } - - ret = recv(io->fd, cli->buf + cli->buf_len, - sizeof(cli->buf) - cli->buf_len, 0); - if (ret <= 0) { - if (ret < 0) { - syslog(LOG_ERR, "error reading from client %s:%hu (%s)\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), - strerror(errno)); - } else { - syslog(LOG_DEBUG, "closed connection by %s:%hu\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); - } - goto close; - } - - if (cli->keepalive_idx >= 0) - return; - - ev_timer_again(loop, &cli->timer); - - cli->buf_len += ret; - if (cli->buf_len >= sizeof(cli->buf)) { - syslog(LOG_ERR, "client request from %s:%hu is too long\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); - og_client_payload_too_large(cli); - goto close; - } - - switch (cli->state) { - case OG_CLIENT_RECEIVING_HEADER: - if (cli->rest) - ret = og_client_state_recv_hdr_rest(cli); - else - ret = og_client_state_recv_hdr(cli); - - if (ret < 0) - goto close; - if (!ret) - return; - - cli->state = OG_CLIENT_RECEIVING_PAYLOAD; - /* Fall through. */ - case OG_CLIENT_RECEIVING_PAYLOAD: - /* Still not enough data to process request. */ - if (cli->buf_len < cli->msg_len) - return; - - cli->state = OG_CLIENT_PROCESSING_REQUEST; - /* fall through. */ - case OG_CLIENT_PROCESSING_REQUEST: - if (cli->rest) { - ret = og_client_state_process_payload_rest(cli); - if (ret < 0) { - syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n", - inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port)); - } - } else { - ret = og_client_state_process_payload(cli); - } - if (ret < 0) - goto close; - - if (cli->keepalive_idx < 0) { - syslog(LOG_DEBUG, "server closing connection to %s:%hu\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); - goto close; - } else { - syslog(LOG_DEBUG, "leaving client %s:%hu in keepalive mode\n", - inet_ntoa(cli->addr.sin_addr), - ntohs(cli->addr.sin_port)); - og_client_keepalive(loop, cli); - og_client_reset_state(cli); - } - break; - default: - syslog(LOG_ERR, "unknown state, critical internal error\n"); - goto close; - } - return; -close: - ev_timer_stop(loop, &cli->timer); - og_client_release(loop, cli); -} - -static void og_client_timer_cb(struct ev_loop *loop, ev_timer *timer, int events) -{ - struct og_client *cli; - - cli = container_of(timer, struct og_client, timer); - if (cli->keepalive_idx >= 0) { - ev_timer_again(loop, &cli->timer); - return; - } - syslog(LOG_ERR, "timeout request for client %s:%hu\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); - - og_client_release(loop, cli); -} - -static int socket_s, socket_rest; - -static void og_server_accept_cb(struct ev_loop *loop, struct ev_io *io, - int events) -{ - struct sockaddr_in client_addr; - socklen_t addrlen = sizeof(client_addr); - struct og_client *cli; - int client_sd; - - if (events & EV_ERROR) - return; - - client_sd = accept(io->fd, (struct sockaddr *)&client_addr, &addrlen); - if (client_sd < 0) { - syslog(LOG_ERR, "cannot accept client connection\n"); - return; - } - - cli = (struct og_client *)calloc(1, sizeof(struct og_client)); - if (!cli) { - close(client_sd); - return; - } - memcpy(&cli->addr, &client_addr, sizeof(client_addr)); - cli->keepalive_idx = -1; - - if (io->fd == socket_rest) - cli->rest = true; - - syslog(LOG_DEBUG, "connection from client %s:%hu\n", - inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); - - ev_io_init(&cli->io, og_client_read_cb, client_sd, EV_READ); - ev_io_start(loop, &cli->io); - ev_timer_init(&cli->timer, og_client_timer_cb, OG_CLIENT_TIMEOUT, 0.); - ev_timer_start(loop, &cli->timer); -} - -static int og_socket_server_init(const char *port) -{ - struct sockaddr_in local; - int sd, on = 1; - - sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sd < 0) { - syslog(LOG_ERR, "cannot create main socket\n"); - return -1; - } - setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int)); - - local.sin_addr.s_addr = htonl(INADDR_ANY); - local.sin_family = AF_INET; - local.sin_port = htons(atoi(port)); - - if (bind(sd, (struct sockaddr *) &local, sizeof(local)) < 0) { - close(sd); - syslog(LOG_ERR, "cannot bind socket\n"); - return -1; - } - - listen(sd, 250); - - return sd; -} - -int main(int argc, char *argv[]) -{ - struct ev_io ev_io_server, ev_io_server_rest; - struct ev_loop *loop = ev_default_loop(0); - int i; - - if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) - exit(EXIT_FAILURE); - - openlog("ogAdmServer", LOG_PID, LOG_DAEMON); - - /*-------------------------------------------------------------------------------------------------------- - Validación de parámetros de ejecución y lectura del fichero de configuración del servicio - ---------------------------------------------------------------------------------------------------------*/ - if (!validacionParametros(argc, argv, 1)) // Valida parámetros de ejecución - exit(EXIT_FAILURE); - - if (!tomaConfiguracion(szPathFileCfg)) { // Toma parametros de configuracion - exit(EXIT_FAILURE); - } - - /*-------------------------------------------------------------------------------------------------------- - // Inicializa array de información de los clientes - ---------------------------------------------------------------------------------------------------------*/ - for (i = 0; i < MAXIMOS_CLIENTES; i++) { - tbsockets[i].ip[0] = '\0'; - tbsockets[i].cli = NULL; - } - /*-------------------------------------------------------------------------------------------------------- - Creación y configuración del socket del servicio - ---------------------------------------------------------------------------------------------------------*/ - socket_s = og_socket_server_init(puerto); - if (socket_s < 0) - exit(EXIT_FAILURE); - - ev_io_init(&ev_io_server, og_server_accept_cb, socket_s, EV_READ); - ev_io_start(loop, &ev_io_server); - - socket_rest = og_socket_server_init("8888"); - if (socket_rest < 0) - exit(EXIT_FAILURE); - - ev_io_init(&ev_io_server_rest, og_server_accept_cb, socket_rest, EV_READ); - ev_io_start(loop, &ev_io_server_rest); - - infoLog(1); // Inicio de sesión - - /* old log file has been deprecated. */ - og_log(97, false); - - syslog(LOG_INFO, "Waiting for connections\n"); - - while (1) - ev_loop(loop, 0); - - exit(EXIT_SUCCESS); -} diff --git a/native/Sources/Services/ogAdmServer/sources/ogAdmServer.h b/native/Sources/Services/ogAdmServer/sources/ogAdmServer.h deleted file mode 100644 index a8128c1..0000000 --- a/native/Sources/Services/ogAdmServer/sources/ogAdmServer.h +++ /dev/null @@ -1,60 +0,0 @@ -// ******************************************************************************************************** -// Servicio: ogAdmServer -// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla -// Fecha Creación: Marzo-2010 -// Fecha Última modificación: Marzo-2010 -// Nombre del fichero: ogAdmServer.h -// Descripción: Este fichero implementa el servicio de administración general del sistema -// ******************************************************************************************************** -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ogAdmLib.h" -// ________________________________________________________________________________________________________ -// Variables globales -// ________________________________________________________________________________________________________ -char servidoradm[LONPRM]; // Dirección IP del servidor de administración -char puerto[LONPRM]; // Puerto de comunicación - -struct og_client; - -typedef struct{ // Estructura usada para guardar información de los clientes - char ip[LONIP]; // IP del cliente - char estado[4]; // Tipo de Sistema Operativo en que se encuentra el cliente - struct og_client *cli; -}SOCKETCL; -SOCKETCL tbsockets[MAXIMOS_CLIENTES]; - -struct og_dbi; - -bool registraCliente(char *); -bool procesoInclusionClienteWinLnx(int socket, TRAMA*,int*,char*); -bool procesoInclusionCliente(struct og_client *, TRAMA*); -bool clienteExistente(char *,int *); -bool clienteDisponible(char *,int *); -bool actualizaConfiguracion(struct og_dbi *,char* ,int); -bool recorreProcedimientos(struct og_dbi *,char* ,FILE*,char*); -bool buscaComandos(char *,TRAMA *,int *); -bool respuestaConsola(int socket, TRAMA *,int); -bool enviaComando(TRAMA *ptrTrama,const char*); -bool Levanta(char**, char**, char**, int, char*); -bool WakeUp(int,char*,char*,char*,char*); -void PasaHexBin(char *,char *); -bool actualizaCreacionImagen(struct og_dbi *,char*,char*,char*,char*,char*,char*); -bool actualizaRestauracionImagen(struct og_dbi *,char*,char*,char*,char*,char*); -bool actualizaHardware(struct og_dbi *dbi, char* ,char*,char*,char*); -bool cuestionPerfilHardware(struct og_dbi *dbi,char*,char*,int,char*,char*,int *,int); -bool actualizaSoftware(struct og_dbi *, char* , char* , char*,char*,char*); -bool cuestionPerfilSoftware(struct og_dbi *, char*, char*,int,int,char*,char*,char*,int *,int); - -int checkDato(struct og_dbi *,char*,const char*,const char*,const char*); diff --git a/native/Sources/Services/ogAdmServer/sources/ogAdmServer.o b/native/Sources/Services/ogAdmServer/sources/ogAdmServer.o deleted file mode 100644 index 1da52d87c6d062732c27a0a0c2e2c443eb26fde5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 249504 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⪻ILq0-CT|7E@9 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 z�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 MH> $SERVERLOG - exit $? - ;; - default) - echolog "Solicitud con parametros \"$PARM\" no realizada, no registrada o con errores" - ;; -esac - diff --git a/native/Sources/Services/opengnsys.default b/native/Sources/Services/opengnsys.default deleted file mode 100644 index d7376cf..0000000 --- a/native/Sources/Services/opengnsys.default +++ /dev/null @@ -1,15 +0,0 @@ -# RUN_OGADMSERVER defined as OpenGnsys Admin Server -# RUN_OGADMREPO defined as OpenGnsys Repository Manager -# RUN_OGADMAGENT run task scheduler service, only if Admin Server is enabled -# RUN_BTTRACKER run Bittorrent Tracker, only if Repository is enabled -# RUN_BTSEEDER start seeding of selected torrent files, only if Repository is enabled -# BTSEEDER_PRIORITY nice priority to seed torrent files; recommended values: -# 8 for Admin Server or Repo without Torrent -# 0 for Admin Server and Repo with Torrent -# -8 for Repo with Torrent -RUN_OGADMSERVER="yes" -RUN_OGADMREPO="yes" -RUN_OGADMAGENT="yes" -RUN_BTTRACKER="yes" -RUN_BTSEEDER="yes" -BTSEEDER_PRIORITY=0 diff --git a/native/Sources/Services/opengnsys.init b/native/Sources/Services/opengnsys.init deleted file mode 100755 index ae27593..0000000 --- a/native/Sources/Services/opengnsys.init +++ /dev/null @@ -1,224 +0,0 @@ -#!/bin/bash - -### BEGIN INIT INFO -# Provides: opengnsys -# Required-Start: -# Required-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 1 -# Short-Description: Servicios del sistema OpenGnsys -# Description: Servicios del sistema OpenGnsys -### END INIT INFO - -# -# Definiciones globales -# -BASEDIR=/opt/opengnsys -OPENGNSYSUSER="opengnsys" -IMAGEDIR=$BASEDIR/images -CLIENTLOGDIR=$BASEDIR/log/clients - -# -# Servidor de OpenGnsys -# -SERVERNAME=ogAdmServer -SERVERDAEMON=$BASEDIR/sbin/$SERVERNAME -SERVERCFG=$BASEDIR/etc/$SERVERNAME.cfg -SERVERLOG=$BASEDIR/log/$SERVERNAME.log -SERVERDAEMON_OPTIONS="-f $SERVERCFG -l $SERVERLOG" - -# -# Servidor de Repositorio -# -############## ADV -REPOAUXNAME=ogAdmRepoAux -REPOAUXDAEMON=$BASEDIR/sbin/$REPOAUXNAME -REPOAUXPORT=$(awk -F= '/PUERTO/ {print $2+1}' $SERVERCFG 2>/dev/null) -############## ADV -############# IRINA # para setBootMode desde el cliente -SERVERAUXNAME=ogAdmServerAux -SERVERAUXDAEMON=$BASEDIR/sbin/$SERVERAUXNAME -SERVERAUXPORT=2011 -############# IRINA - -# -# Servidor de tareas programadas -# -AGENTNAME=ogAdmAgent -AGENTDAEMON=$BASEDIR/sbin/$AGENTNAME -AGENTCFG=$BASEDIR/etc/$AGENTNAME.cfg -AGENTLOG=$BASEDIR/log/$AGENTNAME.log -AGENTDAEMON_OPTIONS="-f $AGENTCFG -l $AGENTLOG" - -# -# Opciones Bittorrent -# - -BTTRACK=/usr/bin/bttrack.bittorrent -BTSEEDER=/usr/bin/btlaunchmany.bittornado -BTTRACKPORT=6969 -BTTRACKDFILE=/tmp/dstate -BTTRACKLOG=$BASEDIR/log/bttrack.log -BTINTERVAL=30 -BTTORRENTSDIR=$BASEDIR/images -BTTRACK_OPTIONS=" --port $BTTRACKPORT --dfile $BTTRACKDFILE --reannounce_interval $BTINTERVAL --logfile $BTTRACKLOG --allowed_dir $BTTORRENTSDIR --allow_get 1" -BTTRACKPID="/var/run/bttrack.pid" -BTSEEDERPID="/var/run/btseeder.pid" - - -export PATH="${PATH:+$PATH:}/usr/sbin:/sbin:/usr/bin" - -# Read config file if it is present. -if [ -r /etc/default/opengnsys ]; then - source /etc/default/opengnsys -fi - -# Configuración de arranque según la distribución Linux usada. -config() { - if [ -f /etc/os-release ]; then - source /etc/os-release - OSDISTRIB="$ID" - else - OSDISTRIB=$(lsb_release -is 2>/dev/null) - fi - OSDISTRIB="${OSDISTRIB,,}" - case "$OSDISTRIB" in - ubuntu|debian|linuxmint) - INITFUNCTIONS=/lib/lsb/init-functions - DAEMONSTART="start-stop-daemon --start --quiet --background --exec" - EXTRAOPTS="--" - DAEMONSTOP="start-stop-daemon --stop --quiet --oknodo --name" - ACTIONMSG="log_daemon_msg" - SUCCESSMSG="log_end_msg 0" - FAILMSG="log_end_msg 1" - TRACKERSTART="start-stop-daemon --make-pidfile --pidfile $BTTRACKPID --start --quiet --background --exec" - BTTRACK_OPTIONS="$BTTRACK_OPTIONS --parse_allowed_interval 1" - TRACKERSTOP="start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile $BTTRACKPID" - SEEDERSTART="start-stop-daemon --make-pidfile --pidfile $BTSEEDERPID --start --quiet --background --exec" - SEEDERSTOP="start-stop-daemon --stop --quiet --oknodo --pidfile $BTSEEDERPID" - ;; - centos|fedora) - INITFUNCTIONS=/etc/init.d/functions - DAEMONSTART="daemon" - ENDOPTS="&" - DAEMONSTOP="killproc" - ACTIONMSG="echo -n" - SUCCESSMSG="eval ( success; echo )" - FAILMSG="eval ( failure; echo )" - BTTRACK=/usr/bin/bttrack.py - BTSEEDER=/usr/bin/btlaunchmany.py - TRACKERSTART="daemon --pidfile $BTTRACKPID" - TRACKERSTOP="killproc -p $BTTRACKPID $BTTRACK" - SEEDERSTART="daemon --pidfile $BTSEEDERPID" - SEEDERSTOP="killproc -p $BTSEEDERPID $BTSEEDER" - ;; - *) echo "Distribución Linux desconcocida o no soportada." - exit ;; - esac - if [ -r $INITFUNCTIONS ]; then - source $INITFUNCTIONS - fi -} - -arranca_demonios() { - # Comprobar que está instalado OpenGnsys. - if [ ! -d $BASEDIR ]; then - $ACTIONMSG "ERROR: No existe el directorio $BASEDIR" - $FAILMSG - exit $? - fi - # Deshabilitar modo reforzado de SELinux. - [ -f /selinux/enforce ] && echo 0 > /selinux/enforce - # Verificar permisos básicos. - if [ "$(stat --printf="%A%G" $IMAGEDIR 2>/dev/null)" != "drwxrwxr-x$OPENGNSYSUSER" ]; then - mkdir $IMAGEDIR 2>/dev/null - chmod 775 $IMAGEDIR - chgrp $OPENGNSYSUSER $IMAGEDIR - fi - if [ "$(stat --printf="%A%G" $CLIENTLOGDIR 2>/dev/null)" != "drwxrwxr-x$OPENGNSYSUSER" ]; then - mkdir -p $CLIENTLOGDIR 2>/dev/null - chmod 775 $CLIENTLOGDIR - chgrp $OPENGNSYSUSER $CLIENTLOGDIR - fi - # Arrancar los servicios indicados. - if [ $RUN_OGADMSERVER = "yes" ]; then - $ACTIONMSG "Iniciando demonio: $SERVERNAME" - $DAEMONSTART $SERVERDAEMON $EXTRAOPTS $SERVERDAEMON_OPTIONS $ENDOPTS - [ $? = 0 ] && $SUCCESSMSG || $FAILMSG - # Para SetBootmode desde el cliente - $ACTIONMSG "Iniciando demonio: $SERVERAUXNAME" # - faucet $SERVERAUXPORT --daemon --in bash -c "$SERVERAUXDAEMON" # NUEVO - [ $? = 0 ] && $SUCCESSMSG || $FAILMSG - fi - if [ $RUN_OGADMREPO = "yes" ]; then - $ACTIONMSG "Iniciando demonio: $REPOAUXNAME" - faucet $REPOAUXPORT --daemon --in bash -c "$REPOAUXDAEMON" - [ $? = 0 ] && $SUCCESSMSG || $FAILMSG - fi - if [ $RUN_OGADMSERVER = "yes" ] && [ $RUN_OGADMAGENT = "yes" ]; then - sleep 5 # Damos tiempo a que ogAdmServer este funcionando - fi - if [ $RUN_OGADMAGENT = "yes" ]; then - $ACTIONMSG "Iniciando demonio: $AGENTNAME" - $DAEMONSTART $AGENTDAEMON $EXTRAOPTS $AGENTDAEMON_OPTIONS $ENDOPTS - [ $? = 0 ] && $SUCCESSMSG || $FAILMSG - fi - if [ $RUN_BTTRACKER = "yes" ]; then - $ACTIONMSG "Iniciando demonio: $BTTRACK" - $TRACKERSTART $BTTRACK $EXTRAOPTS $BTTRACK_OPTIONS $ENDOPTS - [ $? = 0 ] && $SUCCESSMSG || $FAILMSG - fi - if [ $RUN_BTSEEDER = "yes" ]; then - $ACTIONMSG "Iniciando demonio: $BTSEEDER" - $SEEDERSTART $BTSEEDER $EXTRAOPTS $BTTORRENTSDIR &>/dev/null $ENDOPTS - [ $? = 0 ] && $SUCCESSMSG || $FAILMSG - fi - -} - -para_demonios() { - if [ -e $BTSEEDERPID ]; then - $ACTIONMSG "Parando demonio: $BTSEEDER" - $SEEDERSTOP - [ $? = 0 ] && $SUCCESSMSG || $FAILMSG - rm -f $BTSEEDERPID - fi - if [ -e $BTTRACKPID ]; then - $ACTIONMSG "Parando demonio: $BTTRACK" - $TRACKERSTOP - [ $? = 0 ] && $SUCCESSMSG || $FAILMSG - rm -f $BTTRACKPID - fi - $ACTIONMSG "Parando demonio: $AGENTNAME" - $DAEMONSTOP $AGENTNAME - [ $? = 0 ] && $SUCCESSMSG || $FAILMSG - $ACTIONMSG "Parando demonio: $REPOAUXNAME" - pkill faucet - [ $? -le 1 ] && $SUCCESSMSG || $FAILMSG - $ACTIONMSG "Parando demonio: $SERVERNAME" - $DAEMONSTOP $SERVERNAME - [ $? = 0 ] && $SUCCESSMSG || $FAILMSG -} - -config - -case "$1" in - start) - arranca_demonios - ;; - stop) - para_demonios - ;; - restart) - para_demonios - arranca_demonios - ;; - - *) - echo "Uso: $0 {start|stop|restart}" - exit 1 - ;; -esac - -exit 0 - diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index db2b29d..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - tests - - - - - - src - - - - - - - - - diff --git a/sbin/ogBootAgent b/sbin/ogBootAgent deleted file mode 100755 index 444d4517c5e31aa7b4ebe69c60314ba5c8c79ff4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107040 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�rsP$f!Oh|*|Ltv^WczD~2>3pRo=uY&sWH-N9BUgFGJd&awM;1@K zIQ3}!8P^fPsg)&n^kbcfE35-wdsPbSmRMp)D;vN|QOjyJ29~DqqA-!eyBf>r>m^-F z@c>F0<$9yZUabso!2vyS#oIvmj(RVpGh^Xi0uOjm0ht=Yn>owKOlxIGmi6FeMP+!? z4gPY!mn8;yNmCZUQB=NthfAomd@&Drq7_jYjEq)>wRb<((}2-)T)@O@ zcd<~HSqh<2>1YQYp%_*byfW17>a*tzfxwk zCM2*GY4d-NEU&I@vLKGPSWc?E+E3pMIBg-S`c>SQx)#a(MWFC6SAL%b^b-gFDn3kI zRW~dCRXysu4dwsoD6itk)O8oviqBZ$N~|dUC0IKb36iOzr{c}jmG)8Tr|MVbRr@c( zJu1JEknmG+Y3lkU-NcW|AB%sv<-ZIV$%Oc$pNd~oSG7+k?uxopny#1PpR>G*bF;5G z@|R%?*lQQRt8&Wk@(Q@RDt-kt!@q*l*HfMKtMfy3eWXp56jxQRg5H!#`Yl;r^`DCKP}gtE{b^M|@vZW==ag6Rdg^+WA}kB!*8gBmc@@{Eu0uFCasG1a ze+oCTwaL0kcq+c?l0%fs$Xnzxc}0ifxTfNt>Sh%uxaU7C|0>F=u|ajHc(Cha`8~4z zN8T3dq@<*hbyyw-mILay@Nc_ zQuV7i#)iiQ;^MQj3wX1$+Fy{T`j_RvRe3W9u9~B+|CHr((6n=8{abTxsB#Ki3_vc7 z?)^-bf3HJfifb= BTRFS -KERNEL=$(file -bkr /opt/opengnsys/tftpboot/ogclient/ogvmlinuz |awk '/Linux/ {for(i=1;i<=NF;i++) if($i~/version/) {v=$(i+1);printf("%d",v);sub(/[0-9]*\./,"",v);printf(".%02d",v)}}') -[ $KERNEL \< 3.07 ] && IMGFS="EXT4" || IMGFS=${IMGFS:-"BTRFS"} - -# Añade registro de incidencias. -function echolog () { - logger --tag $0 --priority local0.info "$*" - echo "$*" -} - -function mountImage () { - #@param 1 image_file - #@param 2 mount_dir - #@param 3 openciones mount - [ "$3" != "" ] && OPTMOUNT=" -o $3 " - # Si está montado nada que hacer - df |grep "$2$" 2>&1 >/dev/null && return 0 - # FS de la imagen segun el contenido del archivo .img - if file "$1" |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then - echolog "mount $OPTMOUNT -t ext4 $1 $2" - mount $OPTMOUNT -t ext4 $1 $2 - else - echolog "mount $OPTMOUNT -o compress=lzo $1 $2" - mount $OPTMOUNT -o compress=lzo "$1" "$2" - fi - # Si esta montado da error 32, lo damos como bueno - RETVAL=$? - [ $RETVAL -eq 32 ] && RETVAL=0 - return $RETVAL -} - - -PARM1=$(echo $PARM | cut -f1 -d" ") -PARM2=$(echo $PARM | cut -f2 -d" ") -PARM3=$(echo $PARM | cut -f3 -d" ") -PARM4=$(echo $PARM | cut -f4 -d" ") - -# Determinamos el tipo de sistema de fichero de las imagenes segun el kernel que tenga - - -case "$PARM1" in - START_MULTICAST) - #1 START_MULTICAST - #2 fichero a enviar - #3 opciones de multicast - FILE="$PARM2" - MCASTOPT="$PARM3" - echolog "Ejecutar $(which sendFileMcast) $FILE $MCASTOPT" - sendFileMcast $FILE $MCASTOPT |logger --tag $0 --priority local0.info - case $? in - 1) echolog "Parametros insuficientes" - exit 1 ;; - 2) echolog "Fichero no accesible" - exit 2 ;; - 3) echolog "Sesion multicast no valida" - exit 3 ;; - esac - ;; - CREATE_IMAGE) - # Creamos/Redimensionamos el fichero de imagen y lo montamos para que se pueda escribir sobre el - #1 CREATE_IMAGE - #2 nombre imagen - #3 tipo de imagen [ img | diff ] - #4 tamaño imagen - LOOPDEVICE=$(losetup -f) - DIRMOUNT="$REPODIR/mount/$PARM2" - if [ "$PARM3" == "img" ] ; then - IMGEXT="img" - else - IMGEXT="img.diff" - DIRMOUNT="$DIRMOUNT.diff" - fi - IMGFILE="$REPODIR/$PARM2.$IMGEXT" - IMGDIR="$(dirname $IMGFILE)" - [ -d $IMGDIR ] || mkdir -p $IMGDIR - mkdir -p "$DIRMOUNT" - - LOCKFILE="$IMGFILE.lock" - - SIZEREQUIRED="$PARM4" - - # Si existe la imagen hacemos copia de seguridad y la redimesionamos - if [ -f "$IMGFILE" ]; then - echolog "La imagen $IMGFILE ya existe." - # TODO modificar ogGetImageSize - IMGSIZE=$(ls -l --block-size=1024 $IMGFILE| cut -f5 -d" ") - - if [ "$BACKUP" == "true" -o "$BACKUP" == "TRUE" -o $IMGSIZE -lt $SIZEREQUIRED ]; then - # Si la imagen esta montada la desmonto - if [ -r "$DIRMOUNT/ogimg.info" ]; then - echolog "umount $DIRMOUNT" - umount "$DIRMOUNT" - [ $? -ne 0 ] && echolog "Error: No podemos desmontar la imagen para hacer copia de seguridad o redimensionar" && exit 1 - fi - fi - - # Copia de seguridad de la imagen - if [ "$BACKUP" == "true" -o "$BACKUP" == "TRUE" ]; then - echolog "Copia de seguridad de la imagen anterior" - echolog "cp $IMGFILE $IMGFILE.ant" - cp "$IMGFILE" "$IMGFILE.ant" - echolog mv -f "$IMGFILE.torrent" "$IMGFILE.torrent.ant" 2>/dev/null - mv -f "$IMGFILE.torrent" "$IMGFILE.torrent.ant" 2>/dev/null - fi - - # Redimensionamos la imagen al tamaño necesario - if [ $IMGSIZE -lt $SIZEREQUIRED ];then - echolog "Redimensionamos la imagen $IMGFILE al tamaño necesario: $SIZEREQUIRED" - echolog "truncate --size=\">$SIZEREQUIRED\"k $IMGFILE" - truncate --size=">$SIZEREQUIRED"k $IMGFILE 2>&1 |logger --tag $0 --priority local0.info - # FS de la imagen segun el contenido del archivo .img - if file "$IMGFILE" |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then - losetup $LOOPDEVICE "$IMGFILE" - echolog "resize2fs -f $LOOPDEVICE" - resize2fs -f $LOOPDEVICE |logger --tag $0 --priority local0.info - else - mount -o compress=lzo "$IMGFILE" "$DIRMOUNT" - echolog "btrfs filesystem resize max $DIRMOUNT" - btrfs filesystem resize max "$DIRMOUNT" 2>&1 |logger --tag $0 --priority local0.info - fi - fi - - - # Si no existe la imagen creamos el fichero. - else - echolog "Creamos la imagen $IMGFILE al tamaño necesario: $SIZEREQUIRED" - touch "$IMGFILE" - echolog "truncate --size=\">$SIZEREQUIRED\"k $IMGFILE" - truncate --size=">$SIZEREQUIRED"k $IMGFILE 2>&1 |logger --tag $0 --priority local0.info - #Formateamos imagen - echo losetup $LOOPDEVICE "$IMGFILE" - losetup $LOOPDEVICE "$IMGFILE" - if [ $IMGFS == "EXT4" ] ; then - echolog " mkfs.ext4 -i 4096 -b 4096 -L ${PARM2##*\/} $LOOPDEVICE" - mkfs.ext4 -i 4096 -b 4096 -L ${PARM2##*\/} $LOOPDEVICE - else - echolog mkfs.btrfs -L ${PARM2##*\/} $LOOPDEVICE - mkfs.btrfs -L ${PARM2##*\/} $LOOPDEVICE #&> $OGLOGCOMMAND - fi - fi - # Montamos la imagen. - mountImage "$IMGFILE" "$DIRMOUNT" - if [ $? -ne 0 ]; then - rmdir "$DIRMOUNT" - echolog "Error al crear/redimensionar la imagen" - exit 1 - fi - - #touch "$DIRMOUNT/ogimg.info" - echo "mounted"> "$LOCKFILE" - TIME2=$[SECONDS-TIME] - echolog "Fin creación/redimension de la imagen: $[TIME2/60]m $[TIME2%60]s" - # Si existe dispositivo loop lo borramos. - [ $LOOPDEVICE ] && losetup -a| grep $LOOPDEVICE &> /dev/null && losetup -d $LOOPDEVICE - # TODO: comprobar que no se quede el losetup bloqueado. - - ;; - MOUNT_IMAGE) - # Montamos el fichero imagen para que se pueda - #1 MOUNT_IMAGE - #2 nombre imagen - #3 tipo de imagen [ img | diff ] - DIRMOUNT="$REPODIR""mount/$PARM2" - if [ "$PARM3" == "img" ] ; then - IMGEXT="img" - else - IMGEXT="img.diff" - DIRMOUNT="$DIRMOUNT.diff" - fi - IMGFILE="$REPODIR/$PARM2.$IMGEXT" - echolog "Montamos la imagen $IMGFILE " - mkdir -p "$DIRMOUNT" - mountImage "$IMGFILE" "$DIRMOUNT" ro || (echolog "Error al montar la imagen"; exit 1) - ;; - UMOUNT_IMAGE) - # Desmontamos el fichero imagen. - # Si el directorio esta ocupado no se desmontará - #1 UMOUNT_IMAGE - #2 nombre imagen - #3 tipo de imagen [ img | diff ] - IMGTYPE="$PARM3" - DIRMOUNT="$REPODIR/mount/$PARM2" - if [ "$IMGTYPE" == "img" ]; then - IMGEXT="img" - else - DIRMOUNT="$DIRMOUNT.$IMGTYPE" - IMGEXT="img.diff" - fi - LOCKFILE="$REPODIR/$PARM2.$IMGEXT.lock" - echolog "Desmontamos la imagen $PARM2 $PARM3 " - umount $DIRMOUNT - rmdir $DIRMOUNT - [ -f $LOCKFILE ] && sed -i s/mounted//g $LOCKFILE - - ;; - REDUCE_IMAGE) - # Reduce el archivo de la imagen a tamaño datos + 500M - #1 REDUCE_IMAGE - #2 Nombre Imagen - #3 Tipo de imagen [ img |diff ] - DIRMOUNT="${REPODIR}mount/${PARM2}" - if [ "$PARM3" == "img" ] ; then - IMGEXT="img" - else - IMGEXT="img.diff" - DIRMOUNT="$DIRMOUNT.diff" - fi - IMGFILE="$REPODIR$PARM2.$IMGEXT" - LOCKFILE="$IMGFILE.lock" - [ ! -f $IMGFILE ] && echolog "Imagen $IMGFILE no existe" && exit 1 - - # Para imagenes EXT4 reduzco, para BTRFS solo desmonto. - if file $IMGFILE |grep -i -e " ext4 filesystem " 2>&1 > /dev/null ; then - - [ -d $DIRMOUNT ] || mkdir $DIRMOUNT - mountImage "$IMGFILE" "$DIRMOUNT" || (echolog "Error al montar la imagen $IMGFILE"; exit 1) - - - # Si el espacio libre menor que 200Mb desmontamos la imagen y nos salimos - AVAILABLE=$(df -k|grep $DIRMOUNT|awk '{print $4}') - if [ $AVAILABLE -lt 200000 ]; then - echolog "reducir imagen REPO $PARM2 $IMGEXT. tamaño minimo, nada que hacer" - umount $DIRMOUNT || (echolog "Error al desmontar la imagen $IMGFILE"; exit 1) - else - - # Calculamos la diferencia entre el tamaño interno y externo - EXTSIZE=$(ls -l --block-size=1024 $IMGFILE | cut -f5 -d" ") - INTSIZE=$(df -k|grep "$DIRMOUNT"|awk '{print $2}') - let EDGESIZE=$EXTSIZE-$INTSIZE - - echolog "reducir imagen REPO $PARM2 $IMGEXT, tamaño final: $ENDSIZE" - umount $DIRMOUNT - LOOPDEVICE=$(losetup -f) - losetup $LOOPDEVICE "$IMGFILE" - - # Redimensiono sistema de ficheros - echolog "resize2fs -fpM $LOOPDEVICE " - resize2fs -fpM $LOOPDEVICE # 2>&1 |logger --tag $0 --priority local0.info - mountImage "$IMGFILE" "$DIRMOUNT" - # Calculamos el tamaño final del archivo - INTSIZE=$(df -k|grep "$DIRMOUNT"|awk '{print $2}') - let EXTSIZE=$INTSIZE+$EDGESIZE - umount $DIRMOUNT || (echolog "Error al desmontar la imagen $IMGFILE"; exit 1) - # Si existe dispositivo loop lo borramos. - [ $LOOPDEVICE ] && losetup -a| grep $LOOPDEVICE &> /dev/null && losetup -d $LOOPDEVICE - # Corto el archivo al tamaño del sistema de ficheros - echo "truncate --size=\"$EXTSIZE\"k $IMGFILE" - echolog "truncate --size=\"$EXTSIZE\"k $IMGFILE" - truncate --size="$EXTSIZE"k $IMGFILE - fi - else - umount $DIRMOUNT || (echolog "Error al desmontar la imagen $IMGFILE"; exit 1) - fi - rmdir $DIRMOUNT - echo "reduced" >$LOCKFILE - - ;; - default) - echolog "Solicitud con parametros \"$PARM\" no realizada, no registrada o con errores" - ;; -esac diff --git a/sbin/ogBootServer b/sbin/ogBootServer deleted file mode 100755 index ddf8cc6d69f37190c3dc5957d2aaa4ac64146e4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169464 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 diff --git a/sbin/ogBootServerAux b/sbin/ogBootServerAux deleted file mode 100755 index 4add825..0000000 --- a/sbin/ogBootServerAux +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Script para solicitar acciones al servidor de administración desde los clientes. -# Acciones permitidas: configurar modo de arranque. -PARM=`cat` - - - -BASEDIR=/opt/opengnsys -PATH=$PATH:$BASEDIR/bin -SERVERNAME=ogAdmServer -SERVERLOG=$BASEDIR/log/$SERVERNAME.log - - -# Añade registro de incidencias. -function echolog () { - logger --tag $0 --priority local0.info "$*" - echo "$*" -} - - -PARM1=$(echo $PARM | cut -f1 -d" ") -PARM2=$(echo $PARM | cut -f2 -d" ") -PARM3=$(echo $PARM | cut -f3 -d" ") -PARM4=$(echo $PARM | cut -f4 -d" ") - - -case "$PARM1" in - SET_CLIENTMODE) - #1 SET_CLIENTMODE - #2 template - #3 pc_name o group_pc -#4 modo temporal o permanente - - TEMPLATE="$PARM2" - PC="$PARM3" -#incluyo parametro de MODO - MODO="$PARM4" - echolog "Ejecutar $(which setclientmode) $FILE $MCASTOPT" -#incluyo parametro de MODO - setclientmode $TEMPLATE $PC $MODO &>> $SERVERLOG - exit $? - ;; - default) - echolog "Solicitud con parametros \"$PARM\" no realizada, no registrada o con errores" - ;; -esac - diff --git a/server/tftpboot/NetbootPXE.es.txt b/server/tftpboot/NetbootPXE.es.txt deleted file mode 100644 index a1c7c8a..0000000 --- a/server/tftpboot/NetbootPXE.es.txt +++ /dev/null @@ -1,77 +0,0 @@ - -Notas sobre arranque remoto de los clientes -=========================================== - -Desde la versión OpenGnsys 1.0.2 se utiliza Grub4Dos como gestor de arranque en sustitución de PXELinux. - -El instalador de OpenGnsys configura por defecto el servicio DHCP para usar el fichero "grldr" como gestor PXE, incluyendo la siguiente cláusula que debe ser común a todos los equipos afectados: - - filename "grldr"; - -En algunos equipos, puede aparecer el siguiente mensaje de error al finalizar la carga de los ficheros de arranque: -llll PXE unload fails: 1 -ln este caso, debe sustituirse el fichero por defecto "grldr" por "grldr-0.4.5b", que incluye una versión más actualizado de Grub4Dos, quedando la línea de configuración de DHCP como sigue: - - filename "grldr-0.4.5b"; - -Si se dispone distinto hardware compatible solo con uno de estos ficheros, deberá editarse el fichero de configuracińo de DHCP usando declaraciones para grupos de equipos: - - group { - filename "grldr"; - host ... - ... - } - group { - filename "grldr-0.4.5b"; - host ... - ... - } - -No olvidar reiniciar el servicio DHCP tras cada modificación de su fichero de configuración. - - - -Como cambiar el arranque en red PXELinux por Grub4DOS -===================================================== - -NOTA: la siguiente información está anticuada y es válido solo para versiones anteriores a OpenGnsys 1.0.2. - - -OpenGnsys 1.0.1 usa como gestor PXE el binario pxelinux.0, sin embargo, la actulización a OpenGnsys 1.0.2 sustituye automáticamente dicho gestor por Grub4Dos. - - -Realizar los siguientes pasos para sutituir "a mano" PXELinux por Grub4Dos como gestor de arranque sin usar el proceso de actualización de OpenGnsys. - - -Activar el grldr del grub4dos -1) modificar el dhcp, donde aparezca filename "pxelinux.0" por "grldr" - filename "grldr"; -2) Reiniciamos el servicio dhcp - /etc/init.d/dhcpd restart -3) Renombrar cambiar el gestor de arranque de la web, para que use grldr. - cp /opt/opengnsys/www/principal/boot.php /opt/opengnsys/www/principal/boot.pxelinux.php - cp /opt/opengnsys/www/principal/boot.grub4dos.php /opt/opengnsys/www/principal/boot.php - - cp /opt/opengnsys/www/gestores/gestor_pxe.php /opt/opengnsys/www/gestores/gestor_pxe.pxelinux.php - cp /opt/opengnsys/www/gestores/gestor_pxe_grub4dos.php /opt/opengnsys/www/gestores/gestor_pxe.php - -4) -En la funcion ogBoot, de la libreria Boot.lib descomentar los comentarios del if de las líneas 71 a 85, para que quede - #FIXME: activar seguimiento inicio sesion XP con grub4dos - if `ogGetOsVersion $1 $2 | grep "Windows" > /dev/null` - then - dd if=/dev/zero of=${MNTDIR}/ogboot.me bs=1024 count=3 - dd if=/dev/zero of=${MNTDIR}/ogboot.firstboot bs=1024 count=3 - dd if=/dev/zero of=${MNTDIR}/ogboot.secondboot bs=1024 count=3 - ogLoadHiveWindows $1 $2 - ogHiveNTRunMachine "cmd /c del c:\ogboot.* " ogcleanboot - ogUpdateHiveWindows - reboot - else - cp $OGLIB/grub4dos/* $MNTDIR # */ (Comentario Doxygen) - ##kexec -l $MNTDIR/grub.exe --append=--config-file="find --set-root /$LOADER; chainloader /$LOADER; tpm --init" - kexec -l $MNTDIR/grub.exe --append=--config-file="root (hd$[$1-1],$[$2-1]); chainloader (hd$[$1-1],$[$2-1])/$LOADER; tpm --init" - fi - - - diff --git a/server/tftpboot/grldr b/server/tftpboot/grldr deleted file mode 100644 index 50e25d53ac1363486d49914f13f5d52bcbafe429..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273039 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