diff --git a/app/modules/web/Controllers/ConfigEncryptionController.php b/app/modules/web/Controllers/ConfigEncryptionController.php index b71008e9..0833be1b 100644 --- a/app/modules/web/Controllers/ConfigEncryptionController.php +++ b/app/modules/web/Controllers/ConfigEncryptionController.php @@ -37,7 +37,6 @@ use SP\Services\Crypt\MasterPassService; use SP\Services\Crypt\TemporaryMasterPassService; use SP\Services\Crypt\UpdateMasterPassRequest; use SP\Services\Task\TaskFactory; -use SP\Util\Util; /** * Class ConfigEncryptionController @@ -52,9 +51,9 @@ final class ConfigEncryptionController extends SimpleControllerBase * @return bool * @throws \DI\DependencyException * @throws \DI\NotFoundException - * @throws \SP\Core\Exceptions\SPException * @throws \SP\Repositories\NoSuchItemException * @throws \SP\Services\ServiceException + * @throws \SP\Core\Exceptions\SPException */ public function saveAction() { @@ -70,41 +69,69 @@ final class ConfigEncryptionController extends SimpleControllerBase $taskId = $this->request->analyzeString('taskId'); if (!$mastePassService->checkUserUpdateMPass($this->session->getUserData()->getLastUpdateMPass())) { - return $this->returnJsonResponse(JsonResponse::JSON_SUCCESS_STICKY, __u('Master password updated'), [__u('Please, restart the session for update it')]); + return $this->returnJsonResponse( + JsonResponse::JSON_SUCCESS_STICKY, + __u('Master password updated'), + [__u('Please, restart the session for update it')] + ); } if (empty($newMasterPass) || empty($currentMasterPass)) { - return $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('Master password not entered')); + return $this->returnJsonResponse( + JsonResponse::JSON_ERROR, + __u('Master password not entered') + ); } if ($confirmPassChange === false) { - return $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('The password update must be confirmed')); + return $this->returnJsonResponse( + JsonResponse::JSON_ERROR, + __u('The password update must be confirmed') + ); } if ($newMasterPass === $currentMasterPass) { - return $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('Passwords are the same')); + return $this->returnJsonResponse( + JsonResponse::JSON_ERROR, + __u('Passwords are the same') + ); } if ($newMasterPass !== $newMasterPassR) { - return $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('Master passwords do not match')); + return $this->returnJsonResponse( + JsonResponse::JSON_ERROR, + __u('Master passwords do not match') + ); } if (!$mastePassService->checkMasterPassword($currentMasterPass)) { - return $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('The current master password does not match')); + return $this->returnJsonResponse( + JsonResponse::JSON_ERROR, + __u('The current master password does not match') + ); + } + + if (!$this->config->getConfigData()->isMaintenance()) { + return $this->returnJsonResponse( + JsonResponse::JSON_WARNING, + __u('Maintenance mode not enabled'), + [__u('Please, emable it to avoid unwanted behavior from other sessions')] + ); } if ($this->config->getConfigData()->isDemoEnabled()) { - return $this->returnJsonResponse(JsonResponse::JSON_WARNING, __u('Ey, this is a DEMO!!')); + return $this->returnJsonResponse( + JsonResponse::JSON_WARNING, + __u('Ey, this is a DEMO!!') + ); } $configService = $this->dic->get(ConfigService::class); if (!$noAccountPassChange) { - Util::lockApp($this->session->getUserData()->getId(), 'masterpass'); - - $task = $taskId !== null ? TaskFactory::create(__FUNCTION__, $taskId) : null; - try { + $task = $taskId !== null ? TaskFactory::create(__FUNCTION__, $taskId) : null; + $request = new UpdateMasterPassRequest( $currentMasterPass, $newMasterPass, @@ -124,10 +151,8 @@ final class ConfigEncryptionController extends SimpleControllerBase return $this->returnJsonResponseException($e); } finally { - Util::unlockApp(); - - if ($task) { - TaskFactory::end($task->getTaskId()); + if (isset($task)) { + TaskFactory::end($task); } } } else { @@ -193,12 +218,15 @@ final class ConfigEncryptionController extends SimpleControllerBase $groupId = $this->request->analyzeInt('temporary_masterpass_group', 0); $sendEmail = $this->configData->isMailEnabled() - && $this->request->analyzeBool('temporary_masterpass_email') - && $groupId > 0; + && $this->request->analyzeBool('temporary_masterpass_email'); if ($sendEmail) { try { - $temporaryMasterPassService->sendByEmailForGroup($groupId, $key); + if ($groupId > 0) { + $temporaryMasterPassService->sendByEmailForGroup($groupId, $key); + } else { + $temporaryMasterPassService->sendByEmailForAllUsers($key); + } return $this->returnJsonResponse( JsonResponse::JSON_SUCCESS, diff --git a/app/modules/web/Controllers/ConfigGeneralController.php b/app/modules/web/Controllers/ConfigGeneralController.php index 541f5bab..21f98056 100644 --- a/app/modules/web/Controllers/ConfigGeneralController.php +++ b/app/modules/web/Controllers/ConfigGeneralController.php @@ -34,6 +34,7 @@ use SP\Http\JsonResponse; use SP\Modules\Web\Controllers\Traits\ConfigTrait; use SP\Services\Config\ConfigBackupService; use SP\Storage\File\FileHandler; +use SP\Util\Util; /** * Class ConfigGeneral @@ -169,7 +170,11 @@ final class ConfigGeneralController extends SimpleControllerBase return $this->saveConfig( $configData, $this->config, - function () use ($eventMessage) { + function () use ($eventMessage, $configData) { + if ($configData->isMaintenance()) { + Util::lockApp($this->session->getUserData()->getId(), 'config'); + } + $this->eventDispatcher->notifyEvent( 'save.config.general', new Event($this, $eventMessage) diff --git a/app/modules/web/Controllers/ConfigManagerController.php b/app/modules/web/Controllers/ConfigManagerController.php index 9f75ac1c..a0ba3df3 100644 --- a/app/modules/web/Controllers/ConfigManagerController.php +++ b/app/modules/web/Controllers/ConfigManagerController.php @@ -278,7 +278,7 @@ final class ConfigManagerController extends ControllerBase $numAccounts = $this->dic->get(AccountService::class)->getTotalNumAccounts(); $template->assign('numAccounts', $numAccounts); - if ($numAccounts > 500) { + if ($numAccounts > 100) { $template->assign('taskId', Task::genTaskId('masterpass')); } diff --git a/app/modules/web/Controllers/ItemsController.php b/app/modules/web/Controllers/ItemsController.php index bbe53bb0..9e099c4a 100644 --- a/app/modules/web/Controllers/ItemsController.php +++ b/app/modules/web/Controllers/ItemsController.php @@ -117,7 +117,11 @@ final class ItemsController extends SimpleControllerBase $notifications = array_map( function ($notification) { /** @@var $notification NotificationData */ - return sprintf('(%s) - %s', $notification->getComponent(), Html::truncate($notification->getDescription(), 30)); + return sprintf( + '(%s) - %s', + $notification->getComponent(), + Html::truncate(Html::stripTags($notification->getDescription()), 30) + ); }, $this->dic ->get(NotificationService::class) ->getAllActiveForUserId($this->session->getUserData()->getId())); @@ -169,7 +173,7 @@ final class ItemsController extends SimpleControllerBase * * @return array */ - protected function prepareItems(array $items) + private function prepareItems(array $items) { $outItems = []; diff --git a/app/modules/web/Controllers/TaskController.php b/app/modules/web/Controllers/TaskController.php index 6e7d1737..765b9a79 100644 --- a/app/modules/web/Controllers/TaskController.php +++ b/app/modules/web/Controllers/TaskController.php @@ -24,11 +24,9 @@ namespace SP\Modules\Web\Controllers; -use DI\Container; use Klein\Klein; -use SP\Core\Context\SessionContext; +use Psr\Container\ContainerInterface; use SP\Services\ServiceException; -use SP\Services\Task\Task; use SP\Services\Task\TaskFactory; use SP\Services\Task\TaskService; @@ -40,41 +38,46 @@ use SP\Services\Task\TaskService; final class TaskController { /** - * @var Container + * @var TaskService */ - private $container; + private $taskService; + /** + * @var Klein + */ + private $router; /** * TaskController constructor. * - * @param Container $container - * - * @throws \Psr\Container\ContainerExceptionInterface - * @throws \Psr\Container\NotFoundExceptionInterface + * @param ContainerInterface $container */ - public function __construct(Container $container) + public function __construct(ContainerInterface $container) { - $this->container = $container; + $this->router = $container->get(Klein::class); + $this->taskService = $container->get(TaskService::class); } /** * @param string $taskId - * - * @throws \Psr\Container\ContainerExceptionInterface - * @throws \Psr\Container\NotFoundExceptionInterface */ - public function runTaskAction($taskId) + public function trackStatusAction($taskId) { - SessionContext::close(); + $response = $this->router->response(); + $response->header('Content-Type', 'text/event-stream'); + $response->header('Cache-Control', 'no-store, no-cache'); + $response->header('Access-Control-Allow-Origin', '*'); + $response->send(true); - $router = $this->container->get(Klein::class); - $router->response()->header('Content-Type', 'text/event-stream'); - $router->response()->header('Cache-Control', 'no-store, no-cache'); - $router->response()->header('Access-Control-Allow-Origin', '*'); - $router->response()->send(true); + ob_end_flush(); try { - $this->container->get(TaskService::class)->run($taskId); + $this->taskService->trackStatus($taskId, + function ($id, $message) { + echo 'id: ', $id, PHP_EOL, 'data: ', $message, PHP_EOL, PHP_EOL; + + ob_flush(); + flush(); + }); } catch (ServiceException $e) { processException($e); } @@ -82,13 +85,26 @@ final class TaskController /** * @param $taskId + * + * @throws \SP\Storage\File\FileException */ public function testTaskAction($taskId) { - $task = TaskFactory::create($taskId, Task::genTaskId($taskId)); - - TaskFactory::update($task->getTaskId(), TaskFactory::createMessage($task->getTaskId(), 'Test Task')); + $task = TaskFactory::create($taskId, $taskId); echo $task->getTaskId(); + + $count = 0; + + while ($count < 60) { + TaskFactory::update($task, + TaskFactory::createMessage($task->getTaskId(), "Test Task $count") + ); + + sleep(1); + $count++; + } + + TaskFactory::end($task); } } \ No newline at end of file diff --git a/app/modules/web/Init.php b/app/modules/web/Init.php index 953d707a..5aa3cd16 100644 --- a/app/modules/web/Init.php +++ b/app/modules/web/Init.php @@ -60,7 +60,15 @@ final class Init extends ModuleBase * List of controllers that don't need to perform fully initialization * like: install/database checks, session/event handlers initialization */ - const PARTIAL_INIT = ['resource', 'install', 'bootstrap', 'status', 'upgrade', 'error']; + const PARTIAL_INIT = [ + 'resource', + 'install', + 'bootstrap', + 'status', + 'upgrade', + 'error', + 'task' + ]; /** * List of controllers that don't need to update the user's session activity */ diff --git a/app/modules/web/themes/material-blue/js/app-theme.js b/app/modules/web/themes/material-blue/js/app-theme.js index 5968a4d4..d891a643 100644 --- a/app/modules/web/themes/material-blue/js/app-theme.js +++ b/app/modules/web/themes/material-blue/js/app-theme.js @@ -258,7 +258,7 @@ sysPass.Theme = function (log) { // Crear evento para mostrar clave generada/introducida $icon.on("mouseover", function () { - $icon.attr("title", $this.val()); + $icon.attr("title", $this[0].dataset.pass); }); }); }; diff --git a/app/modules/web/themes/material-blue/js/app-theme.min.js b/app/modules/web/themes/material-blue/js/app-theme.min.js index 67aac5fa..57d6390a 100644 --- a/app/modules/web/themes/material-blue/js/app-theme.min.js +++ b/app/modules/web/themes/material-blue/js/app-theme.min.js @@ -12,10 +12,10 @@ c.attr("id")+"-"+a;var l=b.find("#"+c.attr("id")+"_repeat");l.attr("id",a+"_repe '\n
  • vpn_key'+sysPassApp.config.LANG[29]+'
  • \n
  • refresh'+sysPassApp.config.LANG[30]+"
  • ";d.after('
    ');d.next(".password-actions").prepend("remove_red_eye').prepend(b);c.on("keyup",function(){sysPassApp.util.password.checkLevel(c);this.dataset.pass=c.val()});d=c.parent().next();d.find(".passGen").on("click",function(){f(c);c.blur()});d.find(".passComplexity").on("click",function(){g()});d.find(".showpass").on("mouseover",function(){""!==this.dataset.levelMsg?$(this).attr("title",this.dataset.levelMsg+"\n\n"+c[0].dataset.pass):$(this).attr("title",c[0].dataset.pass)});d.find(".reset").on("click",function(){c.val(""); c[0].dataset.pass="";0remove_red_eye');if(1===a.data("clipboard")){var b=$('content_paste');a.parent().after(b).after(d)}else a.parent().after(d); -d.on("mouseover",function(){d.attr("title",a.val())})})},n=function(d){a.info("setupDatePicker");var c={format:"YYYY-MM-DD",lang:sysPassApp.config.BROWSER.LOCALE.substr(0,2),time:!1,cancelText:sysPassApp.config.LANG[44],okText:sysPassApp.config.LANG[43],clearText:sysPassApp.config.LANG[30],nowText:sysPassApp.config.LANG[56],minDate:new Date,triggerEvent:"dateIconClick"};d.find(".password-datefield__input").each(function(){var a=$(this),d=a.parent();a.bootstrapMaterialDatePicker(c);var b=d.find("input[name="+ -a.data("dst-unix")+"]");0'),d=$('
  • '),e=$(''),f=''+(void 0===b||""===b?"description":b)+"";a.forEach(function(a){var b=e.clone();b.append(f);b.append(a);a=d.clone().append(b);c.append(a)});return c},tabs:{add:function(a,b,e,f){a=$(a);if(1===f){a.parent().find("#tabs-"+ -b).addClass("is-active");var c="is-active"}a.append(''+e+"")}}}}}; +d.on("mouseover",function(){d.attr("title",a[0].dataset.pass)})})},n=function(d){a.info("setupDatePicker");var c={format:"YYYY-MM-DD",lang:sysPassApp.config.BROWSER.LOCALE.substr(0,2),time:!1,cancelText:sysPassApp.config.LANG[44],okText:sysPassApp.config.LANG[43],clearText:sysPassApp.config.LANG[30],nowText:sysPassApp.config.LANG[56],minDate:new Date,triggerEvent:"dateIconClick"};d.find(".password-datefield__input").each(function(){var a=$(this),d=a.parent();a.bootstrapMaterialDatePicker(c);var b= +d.find("input[name="+a.data("dst-unix")+"]");0'),d=$('
  • '),e=$(''),f=''+(void 0===b||""===b?"description":b)+"";a.forEach(function(a){var b=e.clone();b.append(f);b.append(a);a=d.clone().append(b);c.append(a)});return c},tabs:{add:function(a, +b,e,f){a=$(a);if(1===f){a.parent().find("#tabs-"+b).addClass("is-active");var c="is-active"}a.append(''+e+"")}}}}}; diff --git a/app/modules/web/themes/material-blue/views/config/encryption.inc b/app/modules/web/themes/material-blue/views/config/encryption.inc index 622e8399..9801c6c4 100644 --- a/app/modules/web/themes/material-blue/views/config/encryption.inc +++ b/app/modules/web/themes/material-blue/views/config/encryption.inc @@ -8,7 +8,7 @@ ?>
    diff --git a/lib/Base.php b/lib/Base.php index fc7fe986..657789f5 100644 --- a/lib/Base.php +++ b/lib/Base.php @@ -50,6 +50,7 @@ define('MODULES_PATH', APP_PATH . DIRECTORY_SEPARATOR . 'modules'); define('LOCALES_PATH', APP_PATH . DIRECTORY_SEPARATOR . 'locales'); define('BACKUP_PATH', APP_PATH . DIRECTORY_SEPARATOR . 'backup'); define('CACHE_PATH', APP_PATH . DIRECTORY_SEPARATOR . 'cache'); +define('TMP_PATH', APP_PATH . DIRECTORY_SEPARATOR . 'temp'); // Setup other paths define('VENDOR_PATH', APP_ROOT . DIRECTORY_SEPARATOR . 'vendor'); diff --git a/lib/SP/Core/Context/SessionContext.php b/lib/SP/Core/Context/SessionContext.php index 50842702..714ec69d 100644 --- a/lib/SP/Core/Context/SessionContext.php +++ b/lib/SP/Core/Context/SessionContext.php @@ -55,11 +55,13 @@ final class SessionContext extends ContextBase */ public static function close() { - logger('Session closed'); + if (!self::$isLocked) { + logger('Session closed'); - session_write_close(); + session_write_close(); - self::$isLocked = true; + self::$isLocked = true; + } } /** diff --git a/lib/SP/Core/Messages/HtmlFormatter.php b/lib/SP/Core/Messages/HtmlFormatter.php index 53714112..15ea3b25 100644 --- a/lib/SP/Core/Messages/HtmlFormatter.php +++ b/lib/SP/Core/Messages/HtmlFormatter.php @@ -84,9 +84,9 @@ final class HtmlFormatter implements FormatterInterface public function formatDescription(array $text, bool $translate = false): string { return implode( - '
    ', + '', array_map(function ($value) use ($translate) { - return '' . ($translate ? __($value) : $value) . ''; + return '
    ' . ($translate ? __($value) : $value) . '
    '; }, $text)); } } \ No newline at end of file diff --git a/lib/SP/Core/Messages/MailMessage.php b/lib/SP/Core/Messages/MailMessage.php index bd68632e..490ab5bf 100644 --- a/lib/SP/Core/Messages/MailMessage.php +++ b/lib/SP/Core/Messages/MailMessage.php @@ -81,7 +81,7 @@ final class MailMessage extends MessageBase implements MessageInterface return $this->title . $delimiter - . $formatter->formatDescription($this->description) + . $this->getDescription($formatter, true) . $delimiter . implode($delimiter, $this->footer); } diff --git a/lib/SP/Core/ModuleBase.php b/lib/SP/Core/ModuleBase.php index abceb907..d234c57b 100644 --- a/lib/SP/Core/ModuleBase.php +++ b/lib/SP/Core/ModuleBase.php @@ -93,7 +93,6 @@ abstract class ModuleBase /** * Comprobar si el modo mantenimiento está activado * Esta función comprueba si el modo mantenimiento está activado. - * Devuelve un error 503 y un reintento de 120s al cliente. * * @param ContextInterface $context * @@ -104,12 +103,11 @@ abstract class ModuleBase if ($this->configData->isMaintenance()) { Bootstrap::$LOCK = Util::getAppLock(); - return ($this->request->isAjax() - || (Bootstrap::$LOCK !== false - && Bootstrap::$LOCK->userId > 0 - && $context->isLoggedIn() - && Bootstrap::$LOCK->userId === $context->getUserData()->getId()) - ) === false; + return !$this->request->isAjax() + || !(Bootstrap::$LOCK !== false + && Bootstrap::$LOCK->userId > 0 + && $context->isLoggedIn() + && Bootstrap::$LOCK->userId === $context->getUserData()->getId()); } return false; diff --git a/lib/SP/Providers/Mail/MailHandler.php b/lib/SP/Providers/Mail/MailHandler.php index fa8f7423..4591941e 100644 --- a/lib/SP/Providers/Mail/MailHandler.php +++ b/lib/SP/Providers/Mail/MailHandler.php @@ -122,7 +122,15 @@ final class MailHandler extends Provider implements EventReceiver $userData = $this->context->getUserData(); $mailMessage = new MailMessage(); - $mailMessage->addDescription($eventMessage->composeText('
    ')); + + if ($eventMessage->getDescriptionCounter() === 0 + && $eventMessage->getDetailsCounter() === 0 + ) { + $mailMessage->addDescription(sprintf(__('Event: %s'), $eventType)); + } else { + $mailMessage->addDescription($eventMessage->composeText('
    ')); + } + $mailMessage->addDescriptionLine(); $mailMessage->addDescription(sprintf(__('Performed by: %s (%s)'), $userData->getName(), $userData->getLogin())); $mailMessage->addDescription(sprintf(__('IP Address: %s'), $this->request->getClientAddress(true))); diff --git a/lib/SP/Repositories/User/UserRepository.php b/lib/SP/Repositories/User/UserRepository.php index 4491ed9d..a8e60ea8 100644 --- a/lib/SP/Repositories/User/UserRepository.php +++ b/lib/SP/Repositories/User/UserRepository.php @@ -727,6 +727,30 @@ final class UserRepository extends Repository implements RepositoryItemInterface return $this->db->doSelect($queryData); } + /** + * Obtener el email de los usuarios + * + * @return QueryResult + * @throws ConstraintException + * @throws QueryException + * + * @TODO create unit test + */ + public function getUserEmail() + { + $query = /** @lang SQL */ + 'SELECT id, login, `name`, email + FROM User + WHERE email IS NOT NULL + AND isDisabled = 0 + ORDER BY login'; + + $queryData = new QueryData(); + $queryData->setQuery($query); + + return $this->db->doSelect($queryData); + } + /** * Returns the usage of the given user's id * diff --git a/lib/SP/Services/Account/AccountCryptService.php b/lib/SP/Services/Account/AccountCryptService.php index ce827e56..58a2fbe8 100644 --- a/lib/SP/Services/Account/AccountCryptService.php +++ b/lib/SP/Services/Account/AccountCryptService.php @@ -45,15 +45,15 @@ final class AccountCryptService extends Service /** * @var AccountService */ - protected $accountService; + private $accountService; /** * @var AccountHistoryService */ - protected $accountHistoryService; + private $accountHistoryService; /** * @var UpdateMasterPassRequest */ - protected $request; + private $request; /** * Actualiza las claves de todas las cuentas con la nueva clave maestra. @@ -72,9 +72,11 @@ final class AccountCryptService extends Service ); if ($this->request->useTask()) { - $taskId = $this->request->getTask()->getTaskId(); + $task = $this->request->getTask(); - TaskFactory::update($taskId, TaskFactory::createMessage($taskId, __u('Update Master Password'))); + TaskFactory::update($task, + TaskFactory::createMessage($task->getTaskId(), __u('Update Master Password')) + ); } $eventMessage = $this->processAccounts($this->accountService->getAccountsPassData(), function ($request) { @@ -101,7 +103,7 @@ final class AccountCryptService extends Service * @return EventMessage * @throws ServiceException */ - protected function processAccounts(array $accounts, callable $passUpdater) + private function processAccounts(array $accounts, callable $passUpdater) { set_time_limit(0); @@ -119,7 +121,7 @@ final class AccountCryptService extends Service $currentMasterPassHash = $this->request->getCurrentHash(); if ($this->request->useTask()) { - $taskId = $this->request->getTask()->getTaskId(); + $task = $this->request->getTask(); } $eventMessage = EventMessage::factory(); @@ -134,13 +136,13 @@ final class AccountCryptService extends Service if ($counter % 100 === 0) { $eta = Util::getETA($startTime, $counter, $numAccounts); - if (isset($taskId)) { - $taskMessage = TaskFactory::createMessage($taskId, __('Update Master Password')) + if (isset($task)) { + $taskMessage = TaskFactory::createMessage($task->getTaskId(), __('Update Master Password')) ->setMessage(sprintf(__('Accounts updated: %d / %d'), $counter, $numAccounts)) ->setProgress(round(($counter * 100) / $numAccounts, 2)) ->setTime(sprintf('ETA: %ds (%.2f/s)', $eta[0], $eta[1])); - TaskFactory::update($taskId, $taskMessage); + TaskFactory::update($task, $taskMessage); logger($taskMessage->composeText()); } else { @@ -213,9 +215,11 @@ final class AccountCryptService extends Service ); if ($this->request->useTask()) { - $taskId = $this->request->getTask()->getTaskId(); + $task = $this->request->getTask(); - TaskFactory::update($taskId, TaskFactory::createMessage($taskId, __u('Update Master Password (H)'))); + TaskFactory::update($task, + TaskFactory::createMessage($task->getTaskId(), __u('Update Master Password (H)')) + ); } $eventMessage = $this->processAccounts($this->accountHistoryService->getAccountsPassData(), function ($request) { diff --git a/lib/SP/Services/Crypt/TemporaryMasterPassService.php b/lib/SP/Services/Crypt/TemporaryMasterPassService.php index b056d8c4..d257941e 100644 --- a/lib/SP/Services/Crypt/TemporaryMasterPassService.php +++ b/lib/SP/Services/Crypt/TemporaryMasterPassService.php @@ -202,6 +202,25 @@ final class TemporaryMasterPassService extends Service ->sendBatch($mailMessage->getTitle(), $emails, $mailMessage); } + /** + * @param $key + * + * @throws ServiceException + * @throws \SP\Core\Exceptions\ConstraintException + * @throws \SP\Core\Exceptions\QueryException + */ + public function sendByEmailForAllUsers($key) + { + $mailMessage = $this->getMessageForEmail($key); + + $emails = array_map(function ($value) { + return $value->email; + }, $this->dic->get(UserService::class)->getUserEmailForAll()); + + $this->dic->get(MailService::class) + ->sendBatch($mailMessage->getTitle(), $emails, $mailMessage); + } + /** * @param $key * @@ -210,13 +229,10 @@ final class TemporaryMasterPassService extends Service private function getMessageForEmail($key) { $mailMessage = new MailMessage(); - $mailMessage->setTitle(sprintf(__('Master Password %s'), AppInfoInterface::APP_NAME)); + $mailMessage->setTitle(sprintf(__('%s Master Password'), AppInfoInterface::APP_NAME)); $mailMessage->addDescription(__('A new sysPass master password has been generated, so next time you log into the application it will be requested.')); - $mailMessage->addDescriptionLine(); $mailMessage->addDescription(sprintf(__('The new Master Password is: %s'), $key)); - $mailMessage->addDescriptionLine(); $mailMessage->addDescription(sprintf(__('This password will be valid until: %s'), date('r', $this->getMaxTime()))); - $mailMessage->addDescriptionLine(); $mailMessage->addDescription(__('Please, don\'t forget to log in as soon as possible to save the changes.')); return $mailMessage; diff --git a/lib/SP/Services/CustomField/CustomFieldCryptService.php b/lib/SP/Services/CustomField/CustomFieldCryptService.php index a8fa190c..c77edb64 100644 --- a/lib/SP/Services/CustomField/CustomFieldCryptService.php +++ b/lib/SP/Services/CustomField/CustomFieldCryptService.php @@ -77,10 +77,10 @@ final class CustomFieldCryptService extends Service ); if ($this->request->useTask()) { - $taskId = $this->request->getTask()->getTaskId(); + $task = $this->request->getTask(); - TaskFactory::update($taskId, - TaskFactory::createMessage($taskId, __('Update Master Password')) + TaskFactory::update($task, + TaskFactory::createMessage($task->getTaskId(), __('Update Master Password')) ->setMessage(__('Updating encrypted data'))); } diff --git a/lib/SP/Services/Install/Installer.php b/lib/SP/Services/Install/Installer.php index 1f1aefb3..2af578f6 100644 --- a/lib/SP/Services/Install/Installer.php +++ b/lib/SP/Services/Install/Installer.php @@ -57,7 +57,7 @@ final class Installer extends Service */ const VERSION = [3, 0, 0]; const VERSION_TEXT = '3.0-rc5'; - const BUILD = 18112601; + const BUILD = 18121001; /** * @var DatabaseSetupInterface diff --git a/lib/SP/Services/Task/Task.php b/lib/SP/Services/Task/Task.php index bcf7b6e1..39802b4e 100644 --- a/lib/SP/Services/Task/Task.php +++ b/lib/SP/Services/Task/Task.php @@ -26,6 +26,8 @@ namespace SP\Services\Task; use SP\Core\Context\SessionContext; use SP\Core\Messages\TaskMessage; +use SP\Storage\File\FileException; +use SP\Storage\File\FileHandler; use SP\Util\Util; /** @@ -38,35 +40,31 @@ final class Task /** * @var string Nombre de la tarea */ - protected $name; + private $name; /** * @var string ID de la tarea */ - protected $taskId; + private $taskId; /** - * @var string Ruta y archivo salida de la tarea + * @var FileHandler */ - protected $fileOut; + private $fileOut; /** - * @var string Ruta y archivo de la tarea + * @var FileHandler */ - protected $fileTask; - /** - * @var resource Manejador del archivo - */ - protected $fileHandler; + private $fileTask; /** * @var int Intérvalo en segundos */ - protected $interval = 5; + private $interval = 5; /** * @var bool Si se ha inicializado para escribir en el archivo */ - protected $initialized = false; + private $initialized = false; /** * @var string */ - protected $uid; + private $uid; /** * Task constructor. @@ -87,13 +85,13 @@ final class Task * * @return bool */ - protected function checkFile() + private function checkFile() { $tempDir = Util::getTempDir(); if ($tempDir !== false) { - $this->fileOut = $tempDir . DIRECTORY_SEPARATOR . $this->taskId . '.out'; - $this->fileTask = $tempDir . DIRECTORY_SEPARATOR . $this->taskId . '.task'; + $this->fileOut = new FileHandler($tempDir . DIRECTORY_SEPARATOR . $this->taskId . '.out'); + $this->fileTask = new FileHandler($tempDir . DIRECTORY_SEPARATOR . $this->taskId . '.task'); $this->deleteTaskFiles(); @@ -106,13 +104,12 @@ final class Task /** * Eliminar los archivos de la tarea no usados */ - protected function deleteTaskFiles() + private function deleteTaskFiles() { - $filesOut = dirname($this->fileOut) . DIRECTORY_SEPARATOR . $this->taskId . '*.out'; - $filesTask = dirname($this->fileOut) . DIRECTORY_SEPARATOR . $this->taskId . '*.task'; + $filesOut = dirname($this->fileOut->getFile()) . DIRECTORY_SEPARATOR . $this->taskId . '*.out'; + $filesTask = dirname($this->fileTask->getFile()) . DIRECTORY_SEPARATOR . $this->taskId . '*.task'; - array_map('unlink', glob($filesOut)); - array_map('unlink', glob($filesTask)); + array_map('unlink', array_merge(glob($filesOut), glob($filesTask))); } /** @@ -135,120 +132,69 @@ final class Task return uniqid($name); } - /** - * Iniciar la tarea - * - * @return bool - */ - public function start() - { - return $this->openFile(); - } - - /** - * Abrir el archivo para escritura - * - * @return bool - */ - protected function openFile() - { - if ($this->initialized === false - || !$this->fileHandler = fopen($this->fileOut, 'wb') - ) { - return false; - } - - return true; - } - /** * Escribir el tado de la tarea a un archivo * - * @param TaskMessage $Message + * @param TaskMessage $message * * @return bool */ - public function writeStatus(TaskMessage $Message) + public function writeStatusAndFlush(TaskMessage $message) { - if ($this->initialized === false - || !is_resource($this->fileHandler) - ) { - return false; + try { + if ($this->initialized === true) { + $this->fileOut->save($message->composeText()); + return true; + } + } catch (FileException $e) { + processException($e); } - fwrite($this->fileHandler, $Message->composeText()); - - return true; - } - - /** - * Escribir el tado de la tarea a un archivo - * - * @param TaskMessage $Message - * - * @return bool - */ - public function writeStatusAndFlush(TaskMessage $Message) - { - return $this->initialized === true - && !is_resource($this->fileHandler) - && file_put_contents($this->fileOut, $Message->composeText()) !== false; + return false; } /** * Escribir un mensaje en el archivo de la tarea en formato JSON * - * @param TaskMessage $Message - * - * @return bool + * @param TaskMessage $message */ - public function writeJsonStatusAndFlush(TaskMessage $Message) + public function writeJsonStatusAndFlush(TaskMessage $message) { - return $this->initialized === true - && !is_resource($this->fileHandler) - && file_put_contents($this->fileOut, $Message->composeJson()) !== false; + try { + if ($this->initialized === true) { + $this->fileOut->save($message->composeJson()); + } + } catch (FileException $e) { + processException($e); + } } /** * Iniciar la tarea - * - * @param bool $startSession - * - * @return bool */ - public function end($startSession = true) + public function end() { - if ($startSession) { - session_start(); + try { + logger("End Task: {$this->name}"); + + $this->unregister(); + + $this->fileOut->delete(); + } catch (FileException $e) { + processException($e); } - - $this->deregister(); - - return $this->closeFile() && @unlink($this->fileOut); } /** * Desregistrar la tarea en la sesión - */ - public function deregister() - { - logger('Deregister Task: ' . $this->name); - - return unlink($this->fileTask); - } - - /** - * Abrir el archivo para escritura * - * @return bool + * @throws FileException */ - protected function closeFile() + public function unregister() { - if ($this->initialized === true && is_resource($this->fileHandler)) { - return fclose($this->fileHandler); - } + logger("Unregister Task: {$this->name}"); - return $this->initialized; + $this->fileTask->delete(); } /** @@ -280,31 +226,43 @@ final class Task } /** - * @return string + * @return FileHandler */ - public function getFileOut() + public function getFileOut(): FileHandler { return $this->fileOut; } /** - * Registrar la tarea en la sesión. - * - * Es necesario bloquear la sesión para permitir la ejecución de otros scripts - * - * @param bool $lockSession Bloquear la sesión + * Register a task * * @return Task + * @throws \SP\Storage\File\FileException */ - public function register($lockSession = true) + public function register() { - logger('Register Task: ' . $this->name); + logger("Register Task: {$this->name}"); - file_put_contents($this->fileTask, serialize($this)); + $this->fileTask->save(serialize($this)); - if ($lockSession === true) { - SessionContext::close(); - } + return $this; + } + + /** + * Register a task + * + * Session is locked in order to allow other scripts execution + * + * @return Task + * @throws \SP\Storage\File\FileException + */ + public function registerSession() + { + logger("Register Task (session): {$this->name}"); + + $this->fileTask->save(serialize($this)); + + SessionContext::close(); return $this; } @@ -312,16 +270,16 @@ final class Task /** * @return string */ - public function getFileTask() - { - return $this->fileTask; - } - - /** - * @return string - */ - public function getUid() + public function getUid(): string { return $this->uid; } + + /** + * @return FileHandler + */ + public function getFileTask(): FileHandler + { + return $this->fileTask; + } } \ No newline at end of file diff --git a/lib/SP/Services/Task/TaskFactory.php b/lib/SP/Services/Task/TaskFactory.php index e355a2ba..fe3cdfa8 100644 --- a/lib/SP/Services/Task/TaskFactory.php +++ b/lib/SP/Services/Task/TaskFactory.php @@ -25,6 +25,7 @@ namespace SP\Services\Task; use SP\Core\Messages\TaskMessage; +use SP\Storage\File\FileException; /** * Class TaskFactory @@ -44,11 +45,20 @@ final class TaskFactory * @param string $name * @param string $id * + * @param bool $hasSession + * * @return Task + * @throws FileException */ - public static function create($name, $id) + public static function create($name, $id, $hasSession = true) { - return self::add((new Task($name, $id))->register()); + $task = self::add((new Task($name, $id))); + + if ($hasSession) { + return $task->registerSession(); + } + + return $task->register(); } /** @@ -58,25 +68,26 @@ final class TaskFactory */ private static function add(Task $task) { - if (isset(self::$tasks[$task->getTaskId()])) { - throw new \RuntimeException('Task already registered'); + if (!isset(self::$tasks[$task->getUid()])) { + self::$tasks[$task->getUid()] = $task; + + return $task; } - self::$tasks[$task->getTaskId()] = $task; - - return $task; + throw new \RuntimeException('Task already registered'); } /** * Finalizar la tarea * - * @param $id + * @param Task $task */ - public static function end($id) + public static function end(Task $task) { - self::get($id)->end(false); + self::get($task->getUid()) + ->end(); - self::delete($id); + self::delete($task->getUid()); } /** @@ -88,9 +99,9 @@ final class TaskFactory { if (isset(self::$tasks[$id])) { return self::$tasks[$id]; - } else { - throw new \RuntimeException('Task not registered'); } + + throw new \RuntimeException('Task not registered'); } /** @@ -98,11 +109,9 @@ final class TaskFactory */ private static function delete($id) { - if (!isset(self::$tasks[$id])) { - throw new \RuntimeException('Task not registered'); + if (isset(self::$tasks[$id])) { + unset(self::$tasks[$id]); } - - unset(self::$tasks[$id]); } /** @@ -119,11 +128,12 @@ final class TaskFactory /** * Enviar un mensaje de actualización a la tarea * - * @param string $id + * @param Task $task * @param TaskMessage $taskMessage */ - public static function update($id, TaskMessage $taskMessage) + public static function update(Task $task, TaskMessage $taskMessage) { - self::get($id)->writeJsonStatusAndFlush($taskMessage); + self::get($task->getUid()) + ->writeJsonStatusAndFlush($taskMessage); } } \ No newline at end of file diff --git a/lib/SP/Services/Task/TaskService.php b/lib/SP/Services/Task/TaskService.php index 1f30b6a5..fb3a0bb3 100644 --- a/lib/SP/Services/Task/TaskService.php +++ b/lib/SP/Services/Task/TaskService.php @@ -26,6 +26,8 @@ namespace SP\Services\Task; use SP\Services\Service; use SP\Services\ServiceException; +use SP\Storage\File\FileException; +use SP\Storage\File\FileHandler; use SP\Util\Util; /** @@ -36,62 +38,65 @@ use SP\Util\Util; final class TaskService extends Service { /** - * Tiempo de espera en cada intento de inicialización + * Time for waiting to initialization */ const STARTUP_WAIT_TIME = 5; /** - * Intentos de inicialización + * Initialization attempts */ const STARTUP_WAIT_COUNT = 30; + /** + * @var \Closure + */ + private $messagePusher; + /** + * @var Task + */ + private $task; + /** + * @var string + */ + private $taskDirectory; + /** + * @var string + */ + private $taskId; + /** + * @var FileHandler + */ + private $taskFile; /** - * @var Task Instancia de la tarea - */ - protected $task; - /** - * @var string Archivo de bloqueo - */ - protected $lockFile; - /** - * @var string Directorio de las tareas - */ - protected $dir; - /** - * @var string ID de la tarea - */ - protected $taskId; - /** - * @var string Archivo de la tarea - */ - protected $taskFile; - - /** - * Realizar acción + * Track task status * - * @param string $taskId + * @param string $taskId + * @param \Closure $messagePusher * * @throws ServiceException */ - public function run($taskId) + public function trackStatus($taskId, \Closure $messagePusher) { $this->taskId = $taskId; - $this->dir = Util::getTempDir(); + $this->taskDirectory = Util::getTempDir(); + $this->messagePusher = $messagePusher; - if ($this->dir === false || !$this->getLock()) { + if ($this->taskDirectory === false || !$this->getLock()) { throw new ServiceException(__('Unable to create the lock file')); } - $this->taskFile = $this->dir . DIRECTORY_SEPARATOR . $this->taskId . '.task'; - $count = 0; - while (!$this->checkTaskRegistered() || !$this->checkTaskFile()) { - if ($count >= self::STARTUP_WAIT_COUNT || !file_exists($this->lockFile)) { - logger('Aborting ...'); - - die(1); + while (!$this->checkTaskRegistered() + || !file_exists($this->task->getFileOut()->getFile()) + ) { + if ($count >= self::STARTUP_WAIT_COUNT) { + throw new ServiceException(__('Task not set within wait time')); } else { - logger(sprintf('Waiting for task "%s" (%ds) ...', $taskId, (self::STARTUP_WAIT_COUNT - $count) * self::STARTUP_WAIT_TIME)); + logger(sprintf( + 'Waiting for task "%s" (%ds) ...', + $taskId, + (self::STARTUP_WAIT_COUNT - $count) * self::STARTUP_WAIT_TIME + )); $count++; sleep(self::STARTUP_WAIT_TIME); @@ -99,48 +104,42 @@ final class TaskService extends Service } $this->readTaskStatus(); - - die(0); } /** - * Obtener un bloqueo para la ejecución de la tarea + * Get a lock for task execution * * @return bool */ private function getLock() { - $this->lockFile = $this->dir . DIRECTORY_SEPARATOR . $this->taskId . '.lock'; + $lockFile = new FileHandler($this->taskDirectory . DIRECTORY_SEPARATOR . $this->taskId . '.lock'); - if (file_exists($this->lockFile)) { - $timeout = self::STARTUP_WAIT_COUNT * self::STARTUP_WAIT_TIME; - - if (filemtime($this->lockFile) + $timeout < time()) { - unlink($this->lockFile); - - return $this->updateLock(); + try { + if ($lockFile->getFileTime() + (self::STARTUP_WAIT_COUNT * self::STARTUP_WAIT_TIME) < time()) { + $lockFile->delete(); } + } catch (FileException $e) { + processException($e); + } + + try { + $lockFile->write(time()); + + return true; + } catch (FileException $e) { + processException($e); return false; } - - return $this->updateLock(); } /** - * Actualizar el tiempo del archivo de bloqueo - */ - protected function updateLock() - { - return file_put_contents($this->lockFile, time()) !== false; - } - - /** - * Comprueba si una tarea ha sido registrada en la sesión + * Check whether the task's file has been registered * * @return bool */ - protected function checkTaskRegistered() + private function checkTaskRegistered() { if (is_object($this->task)) { logger('Task detected: ' . $this->task->getTaskId()); @@ -148,69 +147,78 @@ final class TaskService extends Service return true; } - if (file_exists($this->taskFile)) { - $task = file_get_contents($this->taskFile); - - if ($task !== false) { - $this->task = unserialize($task); - } + try { + $this->taskFile = new FileHandler($this->taskDirectory . DIRECTORY_SEPARATOR . $this->taskId . '.task'); + $this->taskFile->checkFileExists(); + $this->task = unserialize($this->taskFile->readToString()); return is_object($this->task); + } catch (FileException $e) { + return false; } - - return false; } /** - * Comprobar si el archivo de salida de la tarea existe + * Read a task status and send it back to the browser (messagePusher) */ - protected function checkTaskFile() + private function readTaskStatus() { - return file_exists($this->task->getFileOut()); - } - - /** - * Leer el estado de una tarea y enviarlo - */ - protected function readTaskStatus() - { - logger('Tracking task: ' . $this->task->getTaskId()); + logger('Tracking task status: ' . $this->task->getTaskId()); $id = 0; $failCount = 0; - $file = $this->task->getFileOut(); - $interval = $this->task->getInterval(); + $outputFile = $this->task->getFileOut(); - $taskMessage = TaskFactory::createMessage($this->task->getTaskId(), __('Waiting for progress updating ...')); + while ($failCount <= self::STARTUP_WAIT_COUNT + && $this->checkTaskFile() + ) { + try { + $content = $outputFile->readToString(); - while ($failCount <= self::STARTUP_WAIT_COUNT && file_exists($this->taskFile)) { - $content = file_get_contents($file); + if (!empty($content)) { + $this->messagePusher->call($this, $id, $content); + $id++; + } else { + $message = TaskFactory::createMessage($this->task->getTaskId(), __('Waiting for progress updating ...')); - if (!empty($content)) { - $this->sendMessage($id, $content); - $id++; - } else { - logger($taskMessage->composeJson()); + logger($message->getTask()); + + $this->messagePusher->call( + $this, + $id, + $message->composeJson() + ); + + $failCount++; + } + } catch (FileException $e) { + processException($e); + + $this->messagePusher->call( + $this, + $id, + TaskFactory::createMessage($this->task->getTaskId(), $e->getMessage()) + ->composeJson() + ); - $this->sendMessage($id, $taskMessage->composeJson()); $failCount++; } - sleep($interval); + sleep($this->task->getInterval()); } } /** - * Enviar un mensaje - * - * @param $id - * @param $message + * Check whether the task's output file exists */ - protected function sendMessage($id, $message) + private function checkTaskFile() { - echo 'id: ', $id, PHP_EOL, 'data: ', $message, PHP_EOL, PHP_EOL; + try { + $this->taskFile->checkFileExists(); - ob_flush(); - flush(); + return true; + } catch (FileException $e) { + return false; + } } } \ No newline at end of file diff --git a/lib/SP/Services/User/UserService.php b/lib/SP/Services/User/UserService.php index f3847279..9be4c3c8 100644 --- a/lib/SP/Services/User/UserService.php +++ b/lib/SP/Services/User/UserService.php @@ -390,6 +390,20 @@ final class UserService extends Service return $this->userRepository->getUserEmailForGroup($groupId)->getDataAsArray(); } + /** + * Obtener el email de los usuarios de un grupo + * + * @return array + * @throws ConstraintException + * @throws QueryException + * + * @TODO create unit test + */ + public function getUserEmailForAll() + { + return $this->userRepository->getUserEmail()->getDataAsArray(); + } + /** * Returns the usage of the given user's id * diff --git a/lib/SP/Util/Util.php b/lib/SP/Util/Util.php index 600f7332..21672975 100644 --- a/lib/SP/Util/Util.php +++ b/lib/SP/Util/Util.php @@ -24,6 +24,7 @@ namespace SP\Util; +use SP\Storage\File\FileException; use SP\Storage\File\FileHandler; defined('APP_ROOT') || die(); @@ -42,7 +43,6 @@ final class Util public static function getTempDir() { $sysTmp = sys_get_temp_dir(); - $appTmp = APP_PATH . DIRECTORY_SEPARATOR . 'temp'; $file = 'syspass.test'; $checkDir = function ($dir) use ($file) { @@ -59,8 +59,8 @@ final class Util return false; }; - if ($checkDir($appTmp)) { - return $appTmp; + if ($checkDir(TMP_PATH)) { + return TMP_PATH; } return $checkDir($sysTmp); @@ -218,13 +218,16 @@ final class Util * @param int $userId * @param string $subject * - * @return bool + * @throws \SP\Storage\File\FileException */ public static function lockApp($userId, $subject) { $data = ['time' => time(), 'userId' => (int)$userId, 'subject' => $subject]; - return file_put_contents(LOCK_FILE, json_encode($data)); + $file = new FileHandler(LOCK_FILE); + $file->save(json_encode($data)); + + logger('Application locked out'); } /** @@ -234,23 +237,24 @@ final class Util */ public static function unlockApp() { + logger('Application unlocked'); + return @unlink(LOCK_FILE); } /** * Comprueba si la aplicación está bloqueada * - * @return int + * @return mixed */ public static function getAppLock() { - if (file_exists(LOCK_FILE) - && ($data = file_get_contents(LOCK_FILE)) !== false - ) { - return json_decode($data) ?: false; + try { + $file = new FileHandler(LOCK_FILE); + return json_decode($file->readToString()); + } catch (FileException $e) { + return false; } - - return false; } /** diff --git a/public/js/app-actions.js b/public/js/app-actions.js index 7dfd3c41..74f69cb0 100644 --- a/public/js/app-actions.js +++ b/public/js/app-actions.js @@ -646,15 +646,15 @@ sysPass.Actions = function (log) { onClick: function (e) { let taskRunner; const taskId = $obj.find("input[name='taskId']").val(); + const opts = sysPassApp.requests.getRequestOpts(); if (taskId) { + opts.useFullLoading = true; taskRunner = task(taskId); } - const opts = sysPassApp.requests.getRequestOpts(); opts.url = sysPassApp.util.getUrl(ajaxUrl.entrypoint, {r: $obj.data('action-route')}); opts.method = "get"; - opts.useFullLoading = !!taskId; opts.data = $obj.serialize(); sysPassApp.requests.getActionCall(opts, function (json) { @@ -681,7 +681,10 @@ sysPass.Actions = function (log) { const opts = sysPassApp.requests.getRequestOpts(); opts.method = "get"; - opts.url = sysPassApp.util.getUrl(ajaxUrl.entrypoint, {r: "status/checkRelease", isAjax: 1}); + opts.url = sysPassApp.util.getUrl(ajaxUrl.entrypoint, { + r: "status/checkRelease", + isAjax: 1 + }); opts.timeout = 10000; opts.useLoading = false; @@ -787,7 +790,9 @@ sysPass.Actions = function (log) { tabs.save($obj); }, masterpass: function ($obj) { - const atext = "

    " + sysPassApp.config.LANG[59] + "

    "; + log.info("config:masterpass"); + + const atext = `

    ${sysPassApp.config.LANG[59]}

    `; mdlDialog().show({ text: atext, @@ -806,15 +811,15 @@ sysPass.Actions = function (log) { onClick: function (e) { let taskRunner; const taskId = $obj.find("input[name='taskId']").val(); + const opts = sysPassApp.requests.getRequestOpts(); if (taskId) { + opts.useFullLoading = true; taskRunner = task(taskId); } - const opts = sysPassApp.requests.getRequestOpts(); - opts.url = ajaxUrl.entrypoint; - opts.useFullLoading = !!taskId; - opts.data = $obj.serialize(); + opts.url = sysPassApp.util.getUrl(ajaxUrl.entrypoint, {r: $obj.data('action-route')}); + opts.data = $obj.serialize() + "&sk=" + sysPassApp.sk.get(); sysPassApp.requests.getActionCall(opts, function (json) { sysPassApp.msg.out(json); @@ -1778,21 +1783,24 @@ sysPass.Actions = function (log) { }; const task = function (taskId) { - const $taskStatus = $("#taskStatus"); + log.info("task:" + taskId); + const $taskStatus = $("#taskStatus"); $taskStatus.css("display", "block"); $taskStatus.empty().html(sysPassApp.config.LANG[62]); const opts = sysPassApp.requests.getRequestOpts(); opts.method = "get"; - opts.url = sysPassApp.util.getUrl(ajaxUrl.entrypoint, {r: ["task/runTask", taskId],}); + opts.url = sysPassApp.util.getUrl(ajaxUrl.entrypoint, {r: ["task/trackStatus", taskId]}); - return sysPassApp.requests.getActionEvent(opts, function (result) { - let text = result.task + " - " + result.message + " - " + result.time + " - " + result.progress + "%"; - text += "
    " + sysPassApp.config.LANG[62]; + return sysPassApp.requests.getActionEvent(opts, + function (result) { + const text = `${result.task} - ${result.message} - ${result.time} - ${result.progress}%
    ${sysPassApp.config.LANG[62]}`; - $taskStatus.empty().html(text); - }); + log.info(text); + + $taskStatus.empty().html(text); + }); }; const track = { diff --git a/public/js/app-actions.min.js b/public/js/app-actions.min.js index 9074bc58..a20dcacc 100644 --- a/public/js/app-actions.min.js +++ b/public/js/app-actions.min.js @@ -16,7 +16,7 @@ a.data=b.serialize();sysPassApp.requests.getActionCall(a,function(a){10===a.stat void 0!==a.data.itemId&&void 0!==a.data.nextAction&&f({r:[a.data.nextAction,a.data.itemId]},"account")})}},w={logout:function(){sysPassApp.util.redirect("index.php?r=login/logout")},login:function(a){c.info("main:login");var b=sysPassApp.requests.getRequestOpts();b.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("route")});b.method="get";b.data=a.serialize();sysPassApp.requests.getActionCall(b,function(b){var d=$(".extra-hidden");switch(b.status){case 0:sysPassApp.util.redirect(b.data.url);break; case 2:sysPassApp.msg.out(b);a.find("input[type='text'],input[type='password']").val("");0
    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}}, -positive:{title:sysPassApp.config.LANG[43],onClick:function(b){var d;(b=a.find("input[name='taskId']").val())&&(d=v(b));var c=sysPassApp.requests.getRequestOpts();c.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("action-route")});c.method="get";c.useFullLoading=!!b;c.data=a.serialize();sysPassApp.requests.getActionCall(c,function(b){sysPassApp.msg.out(b);0!==b.status?a.find(":input[name=key]").val(""):(void 0!==d&&d.close(),setTimeout(function(){sysPassApp.util.redirect("index.php")},5E3))})}}})}, +positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b=a.find("input[name='taskId']").val();var d=sysPassApp.requests.getRequestOpts();if(b){d.useFullLoading=!0;var c=v(b)}d.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("action-route")});d.method="get";d.data=a.serialize();sysPassApp.requests.getActionCall(d,function(b){sysPassApp.msg.out(b);0!==b.status?a.find(":input[name=key]").val(""):(void 0!==c&&c.close(),setTimeout(function(){sysPassApp.util.redirect("index.php")},5E3))})}}})}, getUpdates:function(){c.info("main:getUpdates");var a=sysPassApp.requests.getRequestOpts();a.method="get";a.url=sysPassApp.util.getUrl(e.entrypoint,{r:"status/checkRelease",isAjax:1});a.timeout=1E4;a.useLoading=!1;var b=$("#updates");sysPassApp.requests.getActionCall(a,function(a){0===a.status?0'+a.data.title+'\n
    cloud_download
    \n \n '+ a.data.description+""):b.html('
    check_circle
    \n '+sysPassApp.config.LANG[68]+""):b.html('
    warning
    \n '+sysPassApp.config.LANG[69]+ "");sysPassApp.theme.update()},function(){b.html('
    warning
    \n '+sysPassApp.config.LANG[69]+"")})},getNotices:function(){c.info("main:getNotices");var a=sysPassApp.requests.getRequestOpts();a.method="get";a.url=sysPassApp.util.getUrl(e.entrypoint,{r:"status/checkNotices",isAjax:1});a.timeout=1E4;a.useLoading= @@ -31,28 +31,29 @@ function(b){sysPassApp.msg.out(b);0===b.status&&($.magnificPopup.close(),f({r:a. getActive:function(){c.info("notification:getActive");var a=sysPassApp.requests.getRequestOpts();a.method="get";a.useLoading=!1;a.url=sysPassApp.util.getUrl(e.entrypoint,{r:"items/notifications",sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(a,function(a){var b=$(".notifications-badge"),c=$(".notifications-tooltip");b.each(function(){var b=$(this);b.attr("data-badge",a.data.count);0===a.data.count?(b.removeClass(b.data("color-class")),c.empty().html(a.data.message)):(b.addClass(b.data("color-class")), c.empty().html(a.data.message_has))});0",c=l.getSelection(a);!1!==c&&mdlDialog().show({text:d,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault(); -sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(a){a.preventDefault();"function"===typeof b&&b(c)}}})},getSelection:function(a){a=a.data("selection");var b=[];return a&&($(a).find(".is-selected").each(function(){b.push($(this).data("item-id"))}),0===b.length)?!1:b}},v=function(a){var b=$("#taskStatus");b.css("display","block");b.empty().html(sysPassApp.config.LANG[62]);var d=sysPassApp.requests.getRequestOpts();d.method="get";d.url=sysPassApp.util.getUrl(e.entrypoint, -{r:["task/runTask",a]});return sysPassApp.requests.getActionEvent(d,function(a){a=a.task+" - "+a.message+" - "+a.time+" - "+a.progress+"%";a+="
    "+sysPassApp.config.LANG[62];b.empty().html(a)})};return{getContent:f,showFloatingBox:h,closeFloatingBox:q,appMgmt:u,account:n,accountManager:{restore:function(a){c.info("accountManager:restore");g.state.update(a);var b=a.data("item-id"),d=sysPassApp.requests.getRequestOpts();d.method="get";d.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"), -a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(d,function(d){sysPassApp.msg.out(d);0===d.status&&((d=a.data("action-next"))?f({r:[d,b]}):f({r:g.state.tab.route,tabIndex:g.state.tab.index}))})}},file:{view:function(a){c.info("file:view");var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get()});sysPassApp.requests.getActionCall(b,function(b){if(0!== -b.status)return sysPassApp.msg.out(b);p(a,b.data.html)})},download:function(a){c.info("file:download");var b=a.data("item-type");a=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get()});"application/pdf"===b?window.open(a,"_blank"):$.fileDownload(a,{httpMethod:"GET",successCallback:function(a){sysPassApp.msg.ok(sysPassApp.config.LANG[72])}})},delete:function(a){c.info("file:delete");var b='

    '+sysPassApp.config.LANG[15]+ -"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get()});sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a);0===a.status&&n.listFiles($("#list-account-files"))})}}})}}, -checks:{wiki:function(a){c.info("checks:wiki");a=$(a.data("src"));a.find("[name='sk']").val(sysPassApp.sk.get());var b=sysPassApp.requests.getRequestOpts();b.url=e.entrypoint;b.data=a.serialize();sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a);0===a.status&&$("#dokuWikiResCheck").html(a.data)})}},config:{save:function(a){c.info("config:save");g.save(a)},masterpass:function(a){var b='

    '+sysPassApp.config.LANG[59]+"

    ";mdlDialog().show({text:b, -negative:{title:sysPassApp.config.LANG[44],onClick:function(b){b.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44]);a.find(":input[type=password]").val("")}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){var d;(b=a.find("input[name='taskId']").val())&&(d=v(b));var c=sysPassApp.requests.getRequestOpts();c.url=e.entrypoint;c.useFullLoading=!!b;c.data=a.serialize();sysPassApp.requests.getActionCall(c,function(b){sysPassApp.msg.out(b);a.find(":input[type=password]").val(""); -void 0!==d&&d.close()})}}})},backup:function(a){c.info("config:backup");g.state.update(a);var b=sysPassApp.requests.getRequestOpts();b.url=e.entrypoint+"?r="+a.data("action-route");b.useFullLoading=!0;b.data=a.serialize()+"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a);0===a.status&&f({r:g.state.tab.route,tabIndex:g.state.tab.index})})},export:function(a){c.info("config:export");g.save(a)},import:function(a){c.info("config:import");var b=sysPassApp.requests.getRequestOpts(); -b.url=e.entrypoint+"?r="+a.data("action-route");b.data=a.serialize()+"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a)})},refreshMpass:function(a){c.info("config:import");var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("action-route"),sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a)})},mailCheck:function(a){c.info("config:mailCheck");var b= -$(a.data("src")),d=sysPassApp.requests.getRequestOpts();d.url=e.entrypoint+"?r="+a.data("action-route");d.data=b.serialize()+"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(d,function(a){sysPassApp.msg.out(a)})}},main:w,user:{showSettings:function(a){c.info("user:showSettings");f({r:a.data("action-route")},"userSettings")},saveSettings:function(a){c.info("user:saveSettings");g.save(a)},password:function(a){c.info("user:password");var b=sysPassApp.requests.getRequestOpts();b.type="html"; -b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(a){0===a.length?w.logout():h(a)})},passreset:function(a){c.info("user:passreset");var b=sysPassApp.requests.getRequestOpts();b.url=e.entrypoint+"/?r="+a.data("action-route");b.data=a.serialize();sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a);0===a.status&&setTimeout(function(){sysPassApp.util.redirect("index.php")}, -2E3)})}},link:{save:function(a){c.info("link:save");var b=function(b){var d=a.data("account-id"),c=sysPassApp.requests.getRequestOpts();c.method="get";d?c.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),d,b],sk:sysPassApp.sk.get(),isAjax:1}):(c.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("action-route"),sk:sysPassApp.sk.get(),isAjax:1}),c.data=a.serialize());sysPassApp.requests.getActionCall(c,function(b){sysPassApp.msg.out(b);0===b.status&&f({r:[a.data("action-next"),d]})})}, -d='

    '+sysPassApp.config.LANG[48]+"

    ";mdlDialog().show({text:d,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();b(0)}},positive:{title:sysPassApp.config.LANG[43],onClick:function(a){a.preventDefault();b(1)}}})},delete:function(a){c.info("link:delete");var b='

    '+sysPassApp.config.LANG[12]+"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault(); -sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b.preventDefault();b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(b){sysPassApp.msg.out(b);0===b.status&&f({r:[a.data("action-next"),a.data("account-id")]})})}}})},refresh:function(a){c.info("link:refresh");g.state.update(a); -var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(b){sysPassApp.msg.out(b);0===b.status&&((b=a.data("action-next"))?f({r:[b,a.data("account-id")]}):f({r:g.state.tab.route,tabIndex:g.state.tab.index}))})}},eventlog:{clear:function(a){var b='

    '+sysPassApp.config.LANG[20]+"

    ";mdlDialog().show({text:b, -negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b.preventDefault();g.save(a)}}})}},ajaxUrl:e,plugin:{toggle:function(a){c.info("plugin:enable");g.state.update(a);var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b, -function(a){sysPassApp.msg.out(a);0===a.status&&setTimeout(function(){sysPassApp.util.redirect("index.php")},2E3)})},reset:function(a){c.info("plugin:reset");var b='

    '+sysPassApp.config.LANG[58]+"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b.preventDefault();b=sysPassApp.requests.getRequestOpts(); -b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(b){sysPassApp.msg.out(b);0===b.status&&f({r:a.data("action-next")})})}}})},search:function(a){c.info("plugin:search");l.search(a)},show:function(a){c.info("plugin:show");u.show(a)},save:function(a){c.info("plugin:save");var b=sysPassApp.requests.getRequestOpts();b.url=e.entrypoint+"?r="+a.data("route");b.data=a.serialize()+ -"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(b,function(b){sysPassApp.msg.out(b);0===b.status&&(f({r:a.data("action-next")}),$.magnificPopup.close())})},nav:function(a){c.info("plugin:nav");l.nav(a)},delete:function(a){c.info("plugin:delete");l.delete(a,function(b){var d=sysPassApp.requests.getRequestOpts();d.method="get";d.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),0===b.length?a.data("item-id"):null],sk:sysPassApp.sk.get(),isAjax:1});d.data={items:b};sysPassApp.requests.getActionCall(d, -function(b){sysPassApp.msg.out(b);0===b.status&&f({r:a.data("action-next")})})})}},notification:r,wiki:{show:function(a){c.info("wiki:show");var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("action-route"),pageName:a.data("pagename"),actionId:a.data("action-id"),sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(a){0!==a.status?sysPassApp.msg.out(a):h(a.data.html)})}},items:{get:function(a){c.info("items:get"); -var b=a[0].selectize;b.clearOptions();b.load(function(d){var c=sysPassApp.requests.getRequestOpts();c.method="get";c.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get()});sysPassApp.requests.getActionCall(c,function(c){d(c.data);b.setValue(a.data("selected-id"),!0);sysPassApp.triggers.updateFormHash()})})},update:function(a){c.info("items:update");var b=$("#"+a.data("item-dst"))[0].selectize,d=b.getValue();b.clearOptions();b.load(function(c){var f= -sysPassApp.requests.getRequestOpts();f.method="get";f.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("item-route"),sk:sysPassApp.sk.get()});sysPassApp.requests.getActionCall(f,function(a){c(a);b.setValue(d,!0)})})}},ldap:{check:function(a){c.info("ldap:check");var b=$(a.data("src")),d=sysPassApp.requests.getRequestOpts();d.url=e.entrypoint+"?r="+a.data("action-route");d.data=b.serialize()+"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(d,function(a){sysPassApp.msg.out(a);0===a.status&& -void 0!==a.data.template&&void 0!==a.data.items&&h(a.data.template,{open:function(){var b=$("#ldap-results").find(".list-wrap").empty();a.data.items.forEach(function(a){b.append(sysPassApp.theme.html.getList(a.items,a.icon))})}})})},import:function(a){c.info("ldap:import");var b='

    '+sysPassApp.config.LANG[57]+"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}}, -positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b=$(a.data("src"));var c=sysPassApp.requests.getRequestOpts();c.url=e.entrypoint+"?r="+a.data("action-route");c.data=b.serialize()+"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(c,function(a){sysPassApp.msg.out(a)})}}})}},track:{unlock:function(a){c.info("track:unlock");var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(), -isAjax:1});sysPassApp.requests.getActionCall(b,function(b){sysPassApp.msg.out(b);g.refresh(a)})},clear:function(a){c.info("track:clear");var b='

    '+sysPassApp.config.LANG[71]+"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b.preventDefault();g.save(a)}}})}}}}; +sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(a){a.preventDefault();"function"===typeof b&&b(c)}}})},getSelection:function(a){a=a.data("selection");var b=[];return a&&($(a).find(".is-selected").each(function(){b.push($(this).data("item-id"))}),0===b.length)?!1:b}},v=function(a){c.info("task:"+a);var b=$("#taskStatus");b.css("display","block");b.empty().html(sysPassApp.config.LANG[62]);var d=sysPassApp.requests.getRequestOpts();d.method= +"get";d.url=sysPassApp.util.getUrl(e.entrypoint,{r:["task/trackStatus",a]});return sysPassApp.requests.getActionEvent(d,function(a){a=a.task+" - "+a.message+" - "+a.time+" - "+a.progress+"%
    "+sysPassApp.config.LANG[62];c.info(a);b.empty().html(a)})};return{getContent:f,showFloatingBox:h,closeFloatingBox:q,appMgmt:u,account:n,accountManager:{restore:function(a){c.info("accountManager:restore");g.state.update(a);var b=a.data("item-id"),d=sysPassApp.requests.getRequestOpts();d.method="get";d.url= +sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(d,function(d){sysPassApp.msg.out(d);0===d.status&&((d=a.data("action-next"))?f({r:[d,b]}):f({r:g.state.tab.route,tabIndex:g.state.tab.index}))})}},file:{view:function(a){c.info("file:view");var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get()}); +sysPassApp.requests.getActionCall(b,function(b){if(0!==b.status)return sysPassApp.msg.out(b);p(a,b.data.html)})},download:function(a){c.info("file:download");var b=a.data("item-type");a=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get()});"application/pdf"===b?window.open(a,"_blank"):$.fileDownload(a,{httpMethod:"GET",successCallback:function(a){sysPassApp.msg.ok(sysPassApp.config.LANG[72])}})},delete:function(a){c.info("file:delete");var b='

    '+ +sysPassApp.config.LANG[15]+"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get()});sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a);0=== +a.status&&n.listFiles($("#list-account-files"))})}}})}},checks:{wiki:function(a){c.info("checks:wiki");a=$(a.data("src"));a.find("[name='sk']").val(sysPassApp.sk.get());var b=sysPassApp.requests.getRequestOpts();b.url=e.entrypoint;b.data=a.serialize();sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a);0===a.status&&$("#dokuWikiResCheck").html(a.data)})}},config:{save:function(a){c.info("config:save");g.save(a)},masterpass:function(a){c.info("config:masterpass");var b='

    '+ +sysPassApp.config.LANG[59]+"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(b){b.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44]);a.find(":input[type=password]").val("")}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b=a.find("input[name='taskId']").val();var d=sysPassApp.requests.getRequestOpts();if(b){d.useFullLoading=!0;var c=v(b)}d.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("action-route")});d.data=a.serialize()+ +"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(d,function(b){sysPassApp.msg.out(b);a.find(":input[type=password]").val("");void 0!==c&&c.close()})}}})},backup:function(a){c.info("config:backup");g.state.update(a);var b=sysPassApp.requests.getRequestOpts();b.url=e.entrypoint+"?r="+a.data("action-route");b.useFullLoading=!0;b.data=a.serialize()+"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a);0===a.status&&f({r:g.state.tab.route,tabIndex:g.state.tab.index})})}, +export:function(a){c.info("config:export");g.save(a)},import:function(a){c.info("config:import");var b=sysPassApp.requests.getRequestOpts();b.url=e.entrypoint+"?r="+a.data("action-route");b.data=a.serialize()+"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a)})},refreshMpass:function(a){c.info("config:import");var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("action-route"),sk:sysPassApp.sk.get(), +isAjax:1});sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a)})},mailCheck:function(a){c.info("config:mailCheck");var b=$(a.data("src")),d=sysPassApp.requests.getRequestOpts();d.url=e.entrypoint+"?r="+a.data("action-route");d.data=b.serialize()+"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(d,function(a){sysPassApp.msg.out(a)})}},main:w,user:{showSettings:function(a){c.info("user:showSettings");f({r:a.data("action-route")},"userSettings")},saveSettings:function(a){c.info("user:saveSettings"); +g.save(a)},password:function(a){c.info("user:password");var b=sysPassApp.requests.getRequestOpts();b.type="html";b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(a){0===a.length?w.logout():h(a)})},passreset:function(a){c.info("user:passreset");var b=sysPassApp.requests.getRequestOpts();b.url=e.entrypoint+"/?r="+a.data("action-route");b.data=a.serialize();sysPassApp.requests.getActionCall(b, +function(a){sysPassApp.msg.out(a);0===a.status&&setTimeout(function(){sysPassApp.util.redirect("index.php")},2E3)})}},link:{save:function(a){c.info("link:save");var b=function(b){var d=a.data("account-id"),c=sysPassApp.requests.getRequestOpts();c.method="get";d?c.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),d,b],sk:sysPassApp.sk.get(),isAjax:1}):(c.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("action-route"),sk:sysPassApp.sk.get(),isAjax:1}),c.data=a.serialize());sysPassApp.requests.getActionCall(c, +function(b){sysPassApp.msg.out(b);0===b.status&&f({r:[a.data("action-next"),d]})})},d='

    '+sysPassApp.config.LANG[48]+"

    ";mdlDialog().show({text:d,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();b(0)}},positive:{title:sysPassApp.config.LANG[43],onClick:function(a){a.preventDefault();b(1)}}})},delete:function(a){c.info("link:delete");var b='

    '+sysPassApp.config.LANG[12]+"

    ";mdlDialog().show({text:b, +negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b.preventDefault();b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(b){sysPassApp.msg.out(b);0===b.status&&f({r:[a.data("action-next"), +a.data("account-id")]})})}}})},refresh:function(a){c.info("link:refresh");g.state.update(a);var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(b){sysPassApp.msg.out(b);0===b.status&&((b=a.data("action-next"))?f({r:[b,a.data("account-id")]}):f({r:g.state.tab.route,tabIndex:g.state.tab.index}))})}},eventlog:{clear:function(a){var b= +'

    '+sysPassApp.config.LANG[20]+"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b.preventDefault();g.save(a)}}})}},ajaxUrl:e,plugin:{toggle:function(a){c.info("plugin:enable");g.state.update(a);var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint, +{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(a){sysPassApp.msg.out(a);0===a.status&&setTimeout(function(){sysPassApp.util.redirect("index.php")},2E3)})},reset:function(a){c.info("plugin:reset");var b='

    '+sysPassApp.config.LANG[58]+"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}}, +positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b.preventDefault();b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(b){sysPassApp.msg.out(b);0===b.status&&f({r:a.data("action-next")})})}}})},search:function(a){c.info("plugin:search");l.search(a)},show:function(a){c.info("plugin:show");u.show(a)},save:function(a){c.info("plugin:save"); +var b=sysPassApp.requests.getRequestOpts();b.url=e.entrypoint+"?r="+a.data("route");b.data=a.serialize()+"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(b,function(b){sysPassApp.msg.out(b);0===b.status&&(f({r:a.data("action-next")}),$.magnificPopup.close())})},nav:function(a){c.info("plugin:nav");l.nav(a)},delete:function(a){c.info("plugin:delete");l.delete(a,function(b){var d=sysPassApp.requests.getRequestOpts();d.method="get";d.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"), +0===b.length?a.data("item-id"):null],sk:sysPassApp.sk.get(),isAjax:1});d.data={items:b};sysPassApp.requests.getActionCall(d,function(b){sysPassApp.msg.out(b);0===b.status&&f({r:a.data("action-next")})})})}},notification:r,wiki:{show:function(a){c.info("wiki:show");var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("action-route"),pageName:a.data("pagename"),actionId:a.data("action-id"),sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b, +function(a){0!==a.status?sysPassApp.msg.out(a):h(a.data.html)})}},items:{get:function(a){c.info("items:get");var b=a[0].selectize;b.clearOptions();b.load(function(d){var c=sysPassApp.requests.getRequestOpts();c.method="get";c.url=sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get()});sysPassApp.requests.getActionCall(c,function(c){d(c.data);b.setValue(a.data("selected-id"),!0);sysPassApp.triggers.updateFormHash()})})},update:function(a){c.info("items:update"); +var b=$("#"+a.data("item-dst"))[0].selectize,d=b.getValue();b.clearOptions();b.load(function(c){var f=sysPassApp.requests.getRequestOpts();f.method="get";f.url=sysPassApp.util.getUrl(e.entrypoint,{r:a.data("item-route"),sk:sysPassApp.sk.get()});sysPassApp.requests.getActionCall(f,function(a){c(a);b.setValue(d,!0)})})}},ldap:{check:function(a){c.info("ldap:check");var b=$(a.data("src")),d=sysPassApp.requests.getRequestOpts();d.url=e.entrypoint+"?r="+a.data("action-route");d.data=b.serialize()+"&sk="+ +sysPassApp.sk.get();sysPassApp.requests.getActionCall(d,function(a){sysPassApp.msg.out(a);0===a.status&&void 0!==a.data.template&&void 0!==a.data.items&&h(a.data.template,{open:function(){var b=$("#ldap-results").find(".list-wrap").empty();a.data.items.forEach(function(a){b.append(sysPassApp.theme.html.getList(a.items,a.icon))})}})})},import:function(a){c.info("ldap:import");var b='

    '+sysPassApp.config.LANG[57]+"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44], +onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43],onClick:function(b){b=$(a.data("src"));var c=sysPassApp.requests.getRequestOpts();c.url=e.entrypoint+"?r="+a.data("action-route");c.data=b.serialize()+"&sk="+sysPassApp.sk.get();sysPassApp.requests.getActionCall(c,function(a){sysPassApp.msg.out(a)})}}})}},track:{unlock:function(a){c.info("track:unlock");var b=sysPassApp.requests.getRequestOpts();b.method="get";b.url= +sysPassApp.util.getUrl(e.entrypoint,{r:[a.data("action-route"),a.data("item-id")],sk:sysPassApp.sk.get(),isAjax:1});sysPassApp.requests.getActionCall(b,function(b){sysPassApp.msg.out(b);g.refresh(a)})},clear:function(a){c.info("track:clear");var b='

    '+sysPassApp.config.LANG[71]+"

    ";mdlDialog().show({text:b,negative:{title:sysPassApp.config.LANG[44],onClick:function(a){a.preventDefault();sysPassApp.msg.error(sysPassApp.config.LANG[44])}},positive:{title:sysPassApp.config.LANG[43], +onClick:function(b){b.preventDefault();g.save(a)}}})}}}}; diff --git a/public/js/app-requests.js b/public/js/app-requests.js index 5de53bfb..317c3fa2 100644 --- a/public/js/app-requests.js +++ b/public/js/app-requests.js @@ -208,12 +208,8 @@ sysPass.Requests = function (sysPassApp) { * @param callbackEnd */ const getActionEvent = function (opts, callbackProgress, callbackEnd) { - let url = getUrl(opts.url); - url += "?" + $.param(opts.data); + const source = new EventSource(getUrl(opts.url)); - const source = new EventSource(url); - - //a message is received source.addEventListener("message", function (e) { const result = JSON.parse(e.data); @@ -235,6 +231,7 @@ sysPass.Requests = function (sysPassApp) { source.addEventListener("error", function (e) { log.error("getActionEvent:Error occured"); + source.close(); }); diff --git a/public/js/app-requests.min.js b/public/js/app-requests.min.js index 4109d7d0..4fd84d7e 100644 --- a/public/js/app-requests.min.js +++ b/public/js/app-requests.min.js @@ -1,5 +1,5 @@ -sysPass.Requests=function(c){var e=c.log,b=[],g={type:"json",url:"",method:"post",callback:"",async:!0,data:"",cache:!1,processData:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",timeout:0,addHistory:!1,hash:"",useLoading:!0,useFullLoading:!1};Object.seal(g);var k={get:function(){return b},add:function(a){var h=""===a.hash?c.util.hash.md5(JSON.stringify(a)):a.hash;if(0

    "+b.responseText+"

    ",e.error(d),"html"===a.type&&$("#content").html(c.msg.html.error(d)), -c.msg.error(d)):m()},complete:function(b){!0===a.useLoading&&c.theme.loading.hide();void 0!==c.theme&&("html"===a.type||void 0!==b.responseJSON&&void 0!==b.responseJSON.data&&void 0!==b.responseJSON.data.html)&&c.theme.ajax.complete()}})},getActionEvent:function(a,b,c){var f=l(a.url);f+="?"+$.param(a.data);var d=new EventSource(f);d.addEventListener("message",function(a){a=JSON.parse(a.data);e.debug(a);1===a.end?(e.info("getActionEvent:Ending"),d.close(),"function"===typeof c&&c(a)):"function"=== -typeof b&&b(a)});d.addEventListener("error",function(a){e.error("getActionEvent:Error occured");d.close()});return d},history:k}}; +sysPass.Requests=function(c){var e=c.log,b=[],h={type:"json",url:"",method:"post",callback:"",async:!0,data:"",cache:!1,processData:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",timeout:0,addHistory:!1,hash:"",useLoading:!0,useFullLoading:!1};Object.seal(h);var k={get:function(){return b},add:function(a){var g=""===a.hash?c.util.hash.md5(JSON.stringify(a)):a.hash;if(0

    "+b.responseText+"

    ",e.error(f),"html"===a.type&&$("#content").html(c.msg.html.error(f)), +c.msg.error(f)):m()},complete:function(b){!0===a.useLoading&&c.theme.loading.hide();void 0!==c.theme&&("html"===a.type||void 0!==b.responseJSON&&void 0!==b.responseJSON.data&&void 0!==b.responseJSON.data.html)&&c.theme.ajax.complete()}})},getActionEvent:function(a,b,c){var d=new EventSource(l(a.url));d.addEventListener("message",function(a){a=JSON.parse(a.data);e.debug(a);1===a.end?(e.info("getActionEvent:Ending"),d.close(),"function"===typeof c&&c(a)):"function"===typeof b&&b(a)});d.addEventListener("error", +function(a){e.error("getActionEvent:Error occured");d.close()});return d},history:k}}; diff --git a/public/vendor/js/eventsource.js b/public/vendor/js/eventsource.js index 3c46690b..ab940c1a 100755 --- a/public/vendor/js/eventsource.js +++ b/public/vendor/js/eventsource.js @@ -12,286 +12,477 @@ var setTimeout = global.setTimeout; var clearTimeout = global.clearTimeout; + var XMLHttpRequest = global.XMLHttpRequest; + var XDomainRequest = global.XDomainRequest; + var NativeEventSource = global.EventSource; + var document = global.document; + var Promise = global.Promise; + var fetch = global.fetch; + var Response = global.Response; + var TextDecoder = global.TextDecoder; + var TextEncoder = global.TextEncoder; + var AbortController = global.AbortController; + + if (Object.create == undefined) { + Object.create = function (C) { + function F(){} + F.prototype = C; + return new F(); + }; + } + + // ? + if (Promise != undefined && Promise.prototype["finally"] == undefined) { + Promise.prototype["finally"] = function (callback) { + return this.then(function (result) { + return Promise.resolve(callback()).then(function () { + return result; + }); + }, function (error) { + return Promise.resolve(callback()).then(function () { + throw error; + }); + }); + }; + } + + // see #118, #123, #125 + if (fetch != undefined && true) { + var originalFetch = fetch; + fetch = function (url, options) { + return Promise.resolve(originalFetch(url, options)); + }; + } + + if (AbortController == undefined) { + AbortController = function () { + this.signal = null; + this.abort = function () { + }; + }; + } + + function TextDecoderPolyfill() { + this.bitsNeeded = 0; + this.codePoint = 0; + } + + TextDecoderPolyfill.prototype.decode = function (octets) { + function valid(codePoint, shift, octetsCount) { + if (octetsCount === 1) { + return codePoint >= 0x0080 >> shift && codePoint << shift <= 0x07FF; + } + if (octetsCount === 2) { + return codePoint >= 0x0800 >> shift && codePoint << shift <= 0xD7FF || codePoint >= 0xE000 >> shift && codePoint << shift <= 0xFFFF; + } + if (octetsCount === 3) { + return codePoint >= 0x010000 >> shift && codePoint << shift <= 0x10FFFF; + } + throw new Error(); + } + function octetsCount(bitsNeeded, codePoint) { + if (bitsNeeded === 6 * 1) { + return codePoint >> 6 > 15 ? 3 : codePoint > 31 ? 2 : 1; + } + if (bitsNeeded === 6 * 2) { + return codePoint > 15 ? 3 : 2; + } + if (bitsNeeded === 6 * 3) { + return 3; + } + throw new Error(); + } + var REPLACER = 0xFFFD; + var string = ""; + var bitsNeeded = this.bitsNeeded; + var codePoint = this.codePoint; + for (var i = 0; i < octets.length; i += 1) { + var octet = octets[i]; + if (bitsNeeded !== 0) { + if (octet < 128 || octet > 191 || !valid(codePoint << 6 | octet & 63, bitsNeeded - 6, octetsCount(bitsNeeded, codePoint))) { + bitsNeeded = 0; + codePoint = REPLACER; + string += String.fromCharCode(codePoint); + } + } + if (bitsNeeded === 0) { + if (octet >= 0 && octet <= 127) { + bitsNeeded = 0; + codePoint = octet; + } else if (octet >= 192 && octet <= 223) { + bitsNeeded = 6 * 1; + codePoint = octet & 31; + } else if (octet >= 224 && octet <= 239) { + bitsNeeded = 6 * 2; + codePoint = octet & 15; + } else if (octet >= 240 && octet <= 247) { + bitsNeeded = 6 * 3; + codePoint = octet & 7; + } else { + bitsNeeded = 0; + codePoint = REPLACER; + } + if (bitsNeeded !== 0 && !valid(codePoint, bitsNeeded, octetsCount(bitsNeeded, codePoint))) { + bitsNeeded = 0; + codePoint = REPLACER; + } + } else { + bitsNeeded -= 6; + codePoint = codePoint << 6 | octet & 63; + } + if (bitsNeeded === 0) { + if (codePoint <= 0xFFFF) { + string += String.fromCharCode(codePoint); + } else { + string += String.fromCharCode(0xD800 + (codePoint - 0xFFFF - 1 >> 10)); + string += String.fromCharCode(0xDC00 + (codePoint - 0xFFFF - 1 & 0x3FF)); + } + } + } + this.bitsNeeded = bitsNeeded; + this.codePoint = codePoint; + return string; + }; + + // Firefox < 38 throws an error with stream option + var supportsStreamOption = function () { + try { + return new TextDecoder().decode(new TextEncoder().encode("test"), {stream: true}) === "test"; + } catch (error) { + console.log(error); + } + return false; + }; + + // IE, Edge + if (TextDecoder == undefined || TextEncoder == undefined || !supportsStreamOption()) { + TextDecoder = TextDecoderPolyfill; + } var k = function () { }; - function XHRTransport(xhr, onStartCallback, onProgressCallback, onFinishCallback, thisArg) { - this._internal = new XHRTransportInternal(xhr, onStartCallback, onProgressCallback, onFinishCallback, thisArg); - } - - XHRTransport.prototype.open = function (url, withCredentials) { - this._internal.open(url, withCredentials); - }; - - XHRTransport.prototype.cancel = function () { - this._internal.cancel(); - }; - - function XHRTransportInternal(xhr, onStartCallback, onProgressCallback, onFinishCallback, thisArg) { - this.onStartCallback = onStartCallback; - this.onProgressCallback = onProgressCallback; - this.onFinishCallback = onFinishCallback; - this.thisArg = thisArg; - this.xhr = xhr; - this.state = 0; - this.charOffset = 0; - this.offset = 0; - this.url = ""; + function XHRWrapper(xhr) { this.withCredentials = false; - this.timeout = 0; + this.responseType = ""; + this.readyState = 0; + this.status = 0; + this.statusText = ""; + this.responseText = ""; + this.onprogress = k; + this.onreadystatechange = k; + this._contentType = ""; + this._xhr = xhr; + this._sendTimeout = 0; + this._abort = k; } - XHRTransportInternal.prototype.onStart = function () { - if (this.state === 1) { - this.state = 2; - var status = 0; - var statusText = ""; - var contentType = undefined; - if (!("contentType" in this.xhr)) { - try { - status = this.xhr.status; - statusText = this.xhr.statusText; - contentType = this.xhr.getResponseHeader("Content-Type"); - } catch (error) { - // https://bugs.webkit.org/show_bug.cgi?id=29121 - status = 0; - statusText = ""; - contentType = undefined; - // FF < 14, WebKit - // https://bugs.webkit.org/show_bug.cgi?id=29658 - // https://bugs.webkit.org/show_bug.cgi?id=77854 + XHRWrapper.prototype.open = function (method, url) { + this._abort(true); + + var that = this; + var xhr = this._xhr; + var state = 1; + var timeout = 0; + + this._abort = function (silent) { + if (that._sendTimeout !== 0) { + clearTimeout(that._sendTimeout); + that._sendTimeout = 0; + } + if (state === 1 || state === 2 || state === 3) { + state = 4; + xhr.onload = k; + xhr.onerror = k; + xhr.onabort = k; + xhr.onprogress = k; + xhr.onreadystatechange = k; + // IE 8 - 9: XDomainRequest#abort() does not fire any event + // Opera < 10: XMLHttpRequest#abort() does not fire any event + xhr.abort(); + if (timeout !== 0) { + clearTimeout(timeout); + timeout = 0; } - } else { - status = 200; - statusText = "OK"; - contentType = this.xhr.contentType; - } - if (contentType == undefined) { - contentType = ""; - } - this.onStartCallback.call(this.thisArg, status, statusText, contentType); - } - }; - XHRTransportInternal.prototype.onProgress = function () { - this.onStart(); - if (this.state === 2 || this.state === 3) { - this.state = 3; - var responseText = ""; - try { - responseText = this.xhr.responseText; - } catch (error) { - // IE 8 - 9 with XMLHttpRequest - } - var chunkStart = this.charOffset; - var length = responseText.length; - for (var i = this.offset; i < length; i += 1) { - var c = responseText.charCodeAt(i); - if (c === "\n".charCodeAt(0) || c === "\r".charCodeAt(0)) { - this.charOffset = i + 1; + if (!silent) { + that.readyState = 4; + that.onreadystatechange(); } } - this.offset = length; - var chunk = responseText.slice(chunkStart, this.charOffset); - this.onProgressCallback.call(this.thisArg, chunk); - } - }; - XHRTransportInternal.prototype.onFinish = function () { - // IE 8 fires "onload" without "onprogress - this.onProgress(); - if (this.state === 3) { - this.state = 4; - if (this.timeout !== 0) { - clearTimeout(this.timeout); - this.timeout = 0; - } - this.onFinishCallback.call(this.thisArg); - } - }; - XHRTransportInternal.prototype.onReadyStateChange = function () { - if (this.xhr != undefined) { // Opera 12 - if (this.xhr.readyState === 4) { - if (this.xhr.status === 0) { - this.onFinish(); + state = 0; + }; + + var onStart = function () { + if (state === 1) { + //state = 2; + var status = 0; + var statusText = ""; + var contentType = undefined; + if (!("contentType" in xhr)) { + try { + status = xhr.status; + statusText = xhr.statusText; + contentType = xhr.getResponseHeader("Content-Type"); + } catch (error) { + // IE < 10 throws exception for `xhr.status` when xhr.readyState === 2 || xhr.readyState === 3 + // Opera < 11 throws exception for `xhr.status` when xhr.readyState === 2 + // https://bugs.webkit.org/show_bug.cgi?id=29121 + status = 0; + statusText = ""; + contentType = undefined; + // Firefox < 14, Chrome ?, Safari ? + // https://bugs.webkit.org/show_bug.cgi?id=29658 + // https://bugs.webkit.org/show_bug.cgi?id=77854 + } } else { - this.onFinish(); + status = 200; + statusText = "OK"; + contentType = xhr.contentType; + } + if (status !== 0) { + state = 2; + that.readyState = 2; + that.status = status; + that.statusText = statusText; + that._contentType = contentType; + that.onreadystatechange(); } - } else if (this.xhr.readyState === 3) { - this.onProgress(); - } else if (this.xhr.readyState === 2) { - // Opera 10.63 throws exception for `this.xhr.status` - // this.onStart(); } - } - }; - XHRTransportInternal.prototype.onTimeout2 = function () { - this.timeout = 0; - var tmp = (/^data\:([^,]*?)(base64)?,([\S]*)$/).exec(this.url); - var contentType = tmp[1]; - var data = tmp[2] === "base64" ? global.atob(tmp[3]) : decodeURIComponent(tmp[3]); - if (this.state === 1) { - this.state = 2; - this.onStartCallback.call(this.thisArg, 200, "OK", contentType); - } - if (this.state === 2 || this.state === 3) { - this.state = 3; - this.onProgressCallback.call(this.thisArg, data); - } - if (this.state === 3) { - this.state = 4; - this.onFinishCallback.call(this.thisArg); - } - }; - XHRTransportInternal.prototype.onTimeout1 = function () { - this.timeout = 0; - this.open(this.url, this.withCredentials); - }; - XHRTransportInternal.prototype.onTimeout0 = function () { - var that = this; - this.timeout = setTimeout(function () { - that.onTimeout0(); - }, 500); - if (this.xhr.readyState === 3) { - this.onProgress(); - } - }; - XHRTransportInternal.prototype.handleEvent = function (event) { - if (event.type === "load") { - this.onFinish(); - } else if (event.type === "error") { - this.onFinish(); - } else if (event.type === "abort") { - // improper fix to match Firefox behaviour, but it is better than just ignore abort - // see https://bugzilla.mozilla.org/show_bug.cgi?id=768596 - // https://bugzilla.mozilla.org/show_bug.cgi?id=880200 - // https://code.google.com/p/chromium/issues/detail?id=153570 - // IE 8 fires "onload" without "onprogress - this.onFinish(); - } else if (event.type === "progress") { - this.onProgress(); - } else if (event.type === "readystatechange") { - this.onReadyStateChange(); - } - }; - XHRTransportInternal.prototype.open = function (url, withCredentials) { - if (this.timeout !== 0) { - clearTimeout(this.timeout); - this.timeout = 0; + }; + var onProgress = function () { + onStart(); + if (state === 2 || state === 3) { + state = 3; + var responseText = ""; + try { + responseText = xhr.responseText; + } catch (error) { + // IE 8 - 9 with XMLHttpRequest + } + that.readyState = 3; + that.responseText = responseText; + that.onprogress(); + } + }; + var onFinish = function () { + // Firefox 52 fires "readystatechange" (xhr.readyState === 4) without final "readystatechange" (xhr.readyState === 3) + // IE 8 fires "onload" without "onprogress" + onProgress(); + if (state === 1 || state === 2 || state === 3) { + state = 4; + if (timeout !== 0) { + clearTimeout(timeout); + timeout = 0; + } + that.readyState = 4; + that.onreadystatechange(); + } + }; + var onReadyStateChange = function () { + if (xhr != undefined) { // Opera 12 + if (xhr.readyState === 4) { + onFinish(); + } else if (xhr.readyState === 3) { + onProgress(); + } else if (xhr.readyState === 2) { + onStart(); + } + } + }; + var onTimeout = function () { + timeout = setTimeout(function () { + onTimeout(); + }, 500); + if (xhr.readyState === 3) { + onProgress(); + } + }; + + // XDomainRequest#abort removes onprogress, onerror, onload + xhr.onload = onFinish; + xhr.onerror = onFinish; + // improper fix to match Firefox behaviour, but it is better than just ignore abort + // see https://bugzilla.mozilla.org/show_bug.cgi?id=768596 + // https://bugzilla.mozilla.org/show_bug.cgi?id=880200 + // https://code.google.com/p/chromium/issues/detail?id=153570 + // IE 8 fires "onload" without "onprogress + xhr.onabort = onFinish; + + // https://bugzilla.mozilla.org/show_bug.cgi?id=736723 + if (!("sendAsBinary" in XMLHttpRequest.prototype) && !("mozAnon" in XMLHttpRequest.prototype)) { + xhr.onprogress = onProgress; } - this.url = url; - this.withCredentials = withCredentials; + // IE 8 - 9 (XMLHTTPRequest) + // Opera < 12 + // Firefox < 3.5 + // Firefox 3.5 - 3.6 - ? < 9.0 + // onprogress is not fired sometimes or delayed + // see also #64 + xhr.onreadystatechange = onReadyStateChange; - this.state = 1; - this.charOffset = 0; - this.offset = 0; + if ("contentType" in xhr) { + url += (url.indexOf("?") === -1 ? "?" : "&") + "padding=true"; + } + xhr.open(method, url, true); - var that = this; - - var tmp = (/^data\:([^,]*?)(?:;base64)?,[\S]*$/).exec(url); - if (tmp != undefined) { - this.timeout = setTimeout(function () { - that.onTimeout2(); + if ("readyState" in xhr) { + // workaround for Opera 12 issue with "progress" events + // #91 + timeout = setTimeout(function () { + onTimeout(); }, 0); - return; } - - // loading indicator in Safari, Chrome < 14 - // loading indicator in Firefox - // https://bugzilla.mozilla.org/show_bug.cgi?id=736723 - if ((!("ontimeout" in this.xhr) || ("sendAsBinary" in this.xhr) || ("mozAnon" in this.xhr)) && global.document != undefined && global.document.readyState != undefined && global.document.readyState !== "complete") { - this.timeout = setTimeout(function () { - that.onTimeout1(); + }; + XHRWrapper.prototype.abort = function () { + this._abort(false); + }; + XHRWrapper.prototype.getResponseHeader = function (name) { + return this._contentType; + }; + XHRWrapper.prototype.setRequestHeader = function (name, value) { + var xhr = this._xhr; + if ("setRequestHeader" in xhr) { + xhr.setRequestHeader(name, value); + } + }; + XHRWrapper.prototype.getAllResponseHeaders = function () { + return this._xhr.getAllResponseHeaders != undefined ? this._xhr.getAllResponseHeaders() : ""; + }; + XHRWrapper.prototype.send = function () { + // loading indicator in Safari < ? (6), Chrome < 14, Firefox + if (!("ontimeout" in XMLHttpRequest.prototype) && + document != undefined && + document.readyState != undefined && + document.readyState !== "complete") { + var that = this; + that._sendTimeout = setTimeout(function () { + that._sendTimeout = 0; + that.send(); }, 4); return; } - // XDomainRequest#abort removes onprogress, onerror, onload - this.xhr.onload = function (event) { - that.handleEvent({type: "load"}); - }; - this.xhr.onerror = function () { - that.handleEvent({type: "error"}); - }; - this.xhr.onabort = function () { - that.handleEvent({type: "abort"}); - }; - this.xhr.onprogress = function () { - that.handleEvent({type: "progress"}); - }; - // IE 8-9 (XMLHTTPRequest) - // Firefox 3.5 - 3.6 - ? < 9.0 - // onprogress is not fired sometimes or delayed - // see also #64 - this.xhr.onreadystatechange = function () { - that.handleEvent({type: "readystatechange"}); - }; - - this.xhr.open("GET", url, true); - + var xhr = this._xhr; // withCredentials should be set after "open" for Safari and Chrome (< 19 ?) - this.xhr.withCredentials = withCredentials; - - this.xhr.responseType = "text"; - - if ("setRequestHeader" in this.xhr) { - // Request header field Cache-Control is not allowed by Access-Control-Allow-Headers. - // "Cache-control: no-cache" are not honored in Chrome and Firefox - // https://bugzilla.mozilla.org/show_bug.cgi?id=428916 - //this.xhr.setRequestHeader("Cache-Control", "no-cache"); - this.xhr.setRequestHeader("Accept", "text/event-stream"); - // Request header field Last-Event-ID is not allowed by Access-Control-Allow-Headers. - //this.xhr.setRequestHeader("Last-Event-ID", this.lastEventId); - } - + xhr.withCredentials = this.withCredentials; + xhr.responseType = this.responseType; try { - this.xhr.send(undefined); + // xhr.send(); throws "Not enough arguments" in Firefox 3.0 + xhr.send(undefined); } catch (error1) { // Safari 5.1.7, Opera 12 throw error1; } - - if (("readyState" in this.xhr) && global.opera != undefined) { - // workaround for Opera issue with "progress" events - this.timeout = setTimeout(function () { - that.onTimeout0(); - }, 0); - } - }; - XHRTransportInternal.prototype.cancel = function () { - if (this.state !== 0 && this.state !== 4) { - this.state = 4; - this.xhr.onload = k; - this.xhr.onerror = k; - this.xhr.onabort = k; - this.xhr.onprogress = k; - this.xhr.onreadystatechange = k; - this.xhr.abort(); - if (this.timeout !== 0) { - clearTimeout(this.timeout); - this.timeout = 0; - } - this.onFinishCallback.call(this.thisArg); - } - this.state = 0; }; - function Map() { - this._data = {}; + function toLowerCase(name) { + return name.replace(/[A-Z]/g, function (c) { + return String.fromCharCode(c.charCodeAt(0) + 0x20); + }); } - Map.prototype.get = function (key) { - return this._data[key + "~"]; + function HeadersPolyfill(all) { + // Get headers: implemented according to mozilla's example code: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders#Example + var map = Object.create(null); + var array = all.split("\r\n"); + for (var i = 0; i < array.length; i += 1) { + var line = array[i]; + var parts = line.split(": "); + var name = parts.shift(); + var value = parts.join(": "); + map[toLowerCase(name)] = value; + } + this._map = map; + } + HeadersPolyfill.prototype.get = function (name) { + return this._map[toLowerCase(name)]; }; - Map.prototype.set = function (key, value) { - this._data[key + "~"] = value; + + function XHRTransport() { + } + + XHRTransport.prototype.open = function (xhr, onStartCallback, onProgressCallback, onFinishCallback, url, withCredentials, headers) { + xhr.open("GET", url); + var offset = 0; + xhr.onprogress = function () { + var responseText = xhr.responseText; + var chunk = responseText.slice(offset); + offset += chunk.length; + onProgressCallback(chunk); + }; + xhr.onreadystatechange = function () { + if (xhr.readyState === 2) { + var status = xhr.status; + var statusText = xhr.statusText; + var contentType = xhr.getResponseHeader("Content-Type"); + var headers = xhr.getAllResponseHeaders(); + onStartCallback(status, statusText, contentType, new HeadersPolyfill(headers), function () { + xhr.abort(); + }); + } else if (xhr.readyState === 4) { + onFinishCallback(); + } + }; + xhr.withCredentials = withCredentials; + xhr.responseType = "text"; + for (var name in headers) { + if (Object.prototype.hasOwnProperty.call(headers, name)) { + xhr.setRequestHeader(name, headers[name]); + } + } + xhr.send(); }; - Map.prototype["delete"] = function (key) { - delete this._data[key + "~"]; + + function HeadersWrapper(headers) { + this._headers = headers; + } + HeadersWrapper.prototype.get = function (name) { + return this._headers.get(name); + }; + + function FetchTransport() { + } + + FetchTransport.prototype.open = function (xhr, onStartCallback, onProgressCallback, onFinishCallback, url, withCredentials, headers) { + var controller = new AbortController(); + var signal = controller.signal;// see #120 + var textDecoder = new TextDecoder(); + fetch(url, { + headers: headers, + credentials: withCredentials ? "include" : "same-origin", + signal: signal, + cache: "no-store" + }).then(function (response) { + var reader = response.body.getReader(); + onStartCallback(response.status, response.statusText, response.headers.get("Content-Type"), new HeadersWrapper(response.headers), function () { + controller.abort(); + reader.cancel(); + }); + return new Promise(function (resolve, reject) { + var readNextChunk = function () { + reader.read().then(function (result) { + if (result.done) { + //Note: bytes in textDecoder are ignored + resolve(undefined); + } else { + var chunk = textDecoder.decode(result.value, {stream: true}); + onProgressCallback(chunk); + readNextChunk(); + } + })["catch"](function (error) { + reject(error); + }); + }; + readNextChunk(); + }); + })["finally"](function () { + onFinishCallback(); + }); }; function EventTarget() { - this._listeners = new Map(); + this._listeners = Object.create(null); } function throwError(e) { @@ -302,60 +493,57 @@ EventTarget.prototype.dispatchEvent = function (event) { event.target = this; - var type = event.type.toString(); - var listeners = this._listeners; - var typeListeners = listeners.get(type); - if (typeListeners == undefined) { - return; - } - var length = typeListeners.length; - var listener = undefined; - for (var i = 0; i < length; i += 1) { - listener = typeListeners[i]; - try { - if (typeof listener.handleEvent === "function") { - listener.handleEvent(event); - } else { - listener.call(this, event); + var typeListeners = this._listeners[event.type]; + if (typeListeners != undefined) { + var length = typeListeners.length; + for (var i = 0; i < length; i += 1) { + var listener = typeListeners[i]; + try { + if (typeof listener.handleEvent === "function") { + listener.handleEvent(event); + } else { + listener.call(this, event); + } + } catch (e) { + throwError(e); } - } catch (e) { - throwError(e); } } }; - EventTarget.prototype.addEventListener = function (type, callback) { - type = type.toString(); + EventTarget.prototype.addEventListener = function (type, listener) { + type = String(type); var listeners = this._listeners; - var typeListeners = listeners.get(type); + var typeListeners = listeners[type]; if (typeListeners == undefined) { typeListeners = []; - listeners.set(type, typeListeners); + listeners[type] = typeListeners; } - for (var i = typeListeners.length; i >= 0; i -= 1) { - if (typeListeners[i] === callback) { - return; + var found = false; + for (var i = 0; i < typeListeners.length; i += 1) { + if (typeListeners[i] === listener) { + found = true; } } - typeListeners.push(callback); + if (!found) { + typeListeners.push(listener); + } }; - EventTarget.prototype.removeEventListener = function (type, callback) { - type = type.toString(); + EventTarget.prototype.removeEventListener = function (type, listener) { + type = String(type); var listeners = this._listeners; - var typeListeners = listeners.get(type); - if (typeListeners == undefined) { - return; - } - var length = typeListeners.length; - var filtered = []; - for (var i = 0; i < length; i += 1) { - if (typeListeners[i] !== callback) { - filtered.push(typeListeners[i]); + var typeListeners = listeners[type]; + if (typeListeners != undefined) { + var filtered = []; + for (var i = 0; i < typeListeners.length; i += 1) { + if (typeListeners[i] !== listener) { + filtered.push(typeListeners[i]); + } + } + if (filtered.length === 0) { + delete listeners[type]; + } else { + listeners[type] = filtered; } - } - if (filtered.length === 0) { - listeners["delete"](type); - } else { - listeners.set(type, filtered); } }; @@ -370,33 +558,42 @@ this.lastEventId = options.lastEventId; } - MessageEvent.prototype = Event.prototype; + MessageEvent.prototype = Object.create(Event.prototype); - var XHR = global.XMLHttpRequest; - var XDR = global.XDomainRequest; - var isCORSSupported = XHR != undefined && (new XHR()).withCredentials != undefined; - var Transport = isCORSSupported || (XHR != undefined && XDR == undefined) ? XHR : XDR; + function ConnectionEvent(type, options) { + Event.call(this, type); + this.status = options.status; + this.statusText = options.statusText; + this.headers = options.headers; + } + + ConnectionEvent.prototype = Object.create(Event.prototype); var WAITING = -1; var CONNECTING = 0; var OPEN = 1; var CLOSED = 2; - var AFTER_CR = 3; - var FIELD_START = 4; - var FIELD = 5; - var VALUE_START = 6; - var VALUE = 7; + + var AFTER_CR = -1; + var FIELD_START = 0; + var FIELD = 1; + var VALUE_START = 2; + var VALUE = 3; + var contentTypeRegExp = /^text\/event\-stream;?(\s*charset\=utf\-8)?$/i; var MINIMUM_DURATION = 1000; var MAXIMUM_DURATION = 18000000; - var getDuration = function (value, def) { - var n = value; + var parseDuration = function (value, def) { + var n = parseInt(value, 10); if (n !== n) { n = def; } - return (n < MINIMUM_DURATION ? MINIMUM_DURATION : (n > MAXIMUM_DURATION ? MAXIMUM_DURATION : n)); + return clampDuration(n); + }; + var clampDuration = function (n) { + return Math.min(Math.max(n, MINIMUM_DURATION), MAXIMUM_DURATION); }; var fire = function (that, f, event) { @@ -409,278 +606,315 @@ } }; - function EventSource(url, options) { + function EventSourcePolyfill(url, options) { EventTarget.call(this); this.onopen = undefined; this.onmessage = undefined; this.onerror = undefined; - this.url = ""; - this.readyState = CONNECTING; - this.withCredentials = false; + this.url = undefined; + this.readyState = undefined; + this.withCredentials = undefined; - this._internal = new EventSourceInternal(this, url, options); + this._close = undefined; + + start(this, url, options); } - function EventSourceInternal(es, url, options) { - this.url = url.toString(); - this.readyState = CONNECTING; - this.withCredentials = isCORSSupported && options != undefined && Boolean(options.withCredentials); - - this.es = es; - this.initialRetry = getDuration(1000, 0); - this.heartbeatTimeout = getDuration(45000, 0); - - this.lastEventId = ""; - this.retry = this.initialRetry; - this.wasActivity = false; - var CurrentTransport = options != undefined && options.Transport != undefined ? options.Transport : Transport; - var xhr = new CurrentTransport(); - this.transport = new XHRTransport(xhr, this.onStart, this.onProgress, this.onFinish, this); - this.timeout = 0; - this.currentState = WAITING; - this.dataBuffer = []; - this.lastEventIdBuffer = ""; - this.eventTypeBuffer = ""; - - this.state = FIELD_START; - this.fieldStart = 0; - this.valueStart = 0; - - this.es.url = this.url; - this.es.readyState = this.readyState; - this.es.withCredentials = this.withCredentials; - - this.onTimeout(); + function getBestTransport() { + return (XMLHttpRequest != undefined && ("withCredentials" in XMLHttpRequest.prototype)) || XDomainRequest == undefined + ? XMLHttpRequest + : XDomainRequest; } - EventSourceInternal.prototype.onStart = function (status, statusText, contentType) { - if (this.currentState === CONNECTING) { - if (contentType == undefined) { - contentType = ""; - } - if (status === 200 && contentTypeRegExp.test(contentType)) { - this.currentState = OPEN; - this.wasActivity = true; - this.retry = this.initialRetry; - this.readyState = OPEN; - this.es.readyState = OPEN; - var event = new Event("open"); - this.es.dispatchEvent(event); - fire(this.es, this.es.onopen, event); - } else if (status !== 0) { - var message = ""; - if (status !== 200) { - message = "EventSource's response has a status " + status + " " + statusText.replace(/\s+/g, " ") + " that is not 200. Aborting the connection."; - } else { - message = "EventSource's response has a Content-Type specifying an unsupported type: " + contentType.replace(/\s+/g, " ") + ". Aborting the connection."; - } - throwError(new Error(message)); - this.close(); - var event = new Event("error"); - this.es.dispatchEvent(event); - fire(this.es, this.es.onerror, event); - } - } - }; + var isFetchSupported = fetch != undefined && Response != undefined && "body" in Response.prototype; - EventSourceInternal.prototype.onProgress = function (chunk) { - if (this.currentState === OPEN) { - var length = chunk.length; - if (length !== 0) { - this.wasActivity = true; - } - for (var position = 0; position < length; position += 1) { - var c = chunk.charCodeAt(position); - if (this.state === AFTER_CR && c === "\n".charCodeAt(0)) { - this.state = FIELD_START; + function start(es, url, options) { + url = String(url); + var withCredentials = options != undefined && Boolean(options.withCredentials); + + var initialRetry = clampDuration(1000); + var heartbeatTimeout = options != undefined && options.heartbeatTimeout != undefined ? parseDuration(options.heartbeatTimeout, 45000) : clampDuration(45000); + + var lastEventId = ""; + var retry = initialRetry; + var wasActivity = false; + var headers = options != undefined && options.headers != undefined ? JSON.parse(JSON.stringify(options.headers)) : undefined; + var CurrentTransport = options != undefined && options.Transport != undefined ? options.Transport : getBestTransport(); + var xhr = isFetchSupported && !(options != undefined && options.Transport != undefined) ? undefined : new XHRWrapper(new CurrentTransport()); + var transport = xhr == undefined ? new FetchTransport() : new XHRTransport(); + var cancelFunction = undefined; + var timeout = 0; + var currentState = WAITING; + var dataBuffer = ""; + var lastEventIdBuffer = ""; + var eventTypeBuffer = ""; + + var textBuffer = ""; + var state = FIELD_START; + var fieldStart = 0; + var valueStart = 0; + + var onStart = function (status, statusText, contentType, headers, cancel) { + if (currentState === CONNECTING) { + cancelFunction = cancel; + if (status === 200 && contentType != undefined && contentTypeRegExp.test(contentType)) { + currentState = OPEN; + wasActivity = true; + retry = initialRetry; + es.readyState = OPEN; + var event = new ConnectionEvent("open", { + status: status, + statusText: statusText, + headers: headers + }); + es.dispatchEvent(event); + fire(es, es.onopen, event); } else { - if (this.state === AFTER_CR) { - this.state = FIELD_START; - } - if (c === "\r".charCodeAt(0) || c === "\n".charCodeAt(0)) { - if (this.state !== FIELD_START) { - if (this.state === FIELD) { - this.valueStart = position + 1; - } - var field = chunk.slice(this.fieldStart, this.valueStart - 1); - var value = chunk.slice(this.valueStart + (this.valueStart < position && chunk.charCodeAt(this.valueStart) === " ".charCodeAt(0) ? 1 : 0), position); - if (field === "data") { - this.dataBuffer.push(value); - } else if (field === "id") { - this.lastEventIdBuffer = value; - } else if (field === "event") { - this.eventTypeBuffer = value; - } else if (field === "retry") { - this.initialRetry = getDuration(Number(value), this.initialRetry); - this.retry = this.initialRetry; - } else if (field === "heartbeatTimeout") { - this.heartbeatTimeout = getDuration(Number(value), this.heartbeatTimeout); - if (this.timeout !== 0) { - clearTimeout(this.timeout); - var that = this; - this.timeout = setTimeout(function () { - that.onTimeout(); - }, this.heartbeatTimeout); - } - } + var message = ""; + if (status !== 200) { + if (statusText) { + statusText = statusText.replace(/\s+/g, " "); } - if (this.state === FIELD_START) { - if (this.dataBuffer.length !== 0) { - this.lastEventId = this.lastEventIdBuffer; - if (this.eventTypeBuffer === "") { - this.eventTypeBuffer = "message"; - } - var event = new MessageEvent(this.eventTypeBuffer, { - data: this.dataBuffer.join("\n"), - lastEventId: this.lastEventIdBuffer - }); - this.es.dispatchEvent(event); - if (this.eventTypeBuffer === "message") { - fire(this.es, this.es.onmessage, event); - } - if (this.currentState === CLOSED) { - return; - } - } - this.dataBuffer.length = 0; - this.eventTypeBuffer = ""; - } - this.state = c === "\r".charCodeAt(0) ? AFTER_CR : FIELD_START; + message = "EventSource's response has a status " + status + " " + statusText + " that is not 200. Aborting the connection."; } else { - if (this.state === FIELD_START) { - this.fieldStart = position; - this.state = FIELD; + message = "EventSource's response has a Content-Type specifying an unsupported type: " + (contentType == undefined ? "-" : contentType.replace(/\s+/g, " ")) + ". Aborting the connection."; + } + throwError(new Error(message)); + close(); + var event = new ConnectionEvent("error", { + status: status, + statusText: statusText, + headers: headers + }); + es.dispatchEvent(event); + fire(es, es.onerror, event); + } + } + }; + + var onProgress = function (textChunk) { + if (currentState === OPEN) { + var n = -1; + for (var i = 0; i < textChunk.length; i += 1) { + var c = textChunk.charCodeAt(i); + if (c === "\n".charCodeAt(0) || c === "\r".charCodeAt(0)) { + n = i; + } + } + var chunk = (n !== -1 ? textBuffer : "") + textChunk.slice(0, n + 1); + textBuffer = (n === -1 ? textBuffer : "") + textChunk.slice(n + 1); + if (chunk !== "") { + wasActivity = true; + } + for (var position = 0; position < chunk.length; position += 1) { + var c = chunk.charCodeAt(position); + if (state === AFTER_CR && c === "\n".charCodeAt(0)) { + state = FIELD_START; + } else { + if (state === AFTER_CR) { + state = FIELD_START; } - if (this.state === FIELD) { - if (c === ":".charCodeAt(0)) { - this.valueStart = position + 1; - this.state = VALUE_START; + if (c === "\r".charCodeAt(0) || c === "\n".charCodeAt(0)) { + if (state !== FIELD_START) { + if (state === FIELD) { + valueStart = position + 1; + } + var field = chunk.slice(fieldStart, valueStart - 1); + var value = chunk.slice(valueStart + (valueStart < position && chunk.charCodeAt(valueStart) === " ".charCodeAt(0) ? 1 : 0), position); + if (field === "data") { + dataBuffer += "\n"; + dataBuffer += value; + } else if (field === "id") { + lastEventIdBuffer = value; + } else if (field === "event") { + eventTypeBuffer = value; + } else if (field === "retry") { + initialRetry = parseDuration(value, initialRetry); + retry = initialRetry; + } else if (field === "heartbeatTimeout") { + heartbeatTimeout = parseDuration(value, heartbeatTimeout); + if (timeout !== 0) { + clearTimeout(timeout); + timeout = setTimeout(function () { + onTimeout(); + }, heartbeatTimeout); + } + } + } + if (state === FIELD_START) { + if (dataBuffer !== "") { + lastEventId = lastEventIdBuffer; + if (eventTypeBuffer === "") { + eventTypeBuffer = "message"; + } + var event = new MessageEvent(eventTypeBuffer, { + data: dataBuffer.slice(1), + lastEventId: lastEventIdBuffer + }); + es.dispatchEvent(event); + if (eventTypeBuffer === "message") { + fire(es, es.onmessage, event); + } + if (currentState === CLOSED) { + return; + } + } + dataBuffer = ""; + eventTypeBuffer = ""; + } + state = c === "\r".charCodeAt(0) ? AFTER_CR : FIELD_START; + } else { + if (state === FIELD_START) { + fieldStart = position; + state = FIELD; + } + if (state === FIELD) { + if (c === ":".charCodeAt(0)) { + valueStart = position + 1; + state = VALUE_START; + } + } else if (state === VALUE_START) { + state = VALUE; } - } else if (this.state === VALUE_START) { - this.state = VALUE; } } } } - } - }; + }; - EventSourceInternal.prototype.onFinish = function () { - if (this.currentState === OPEN || this.currentState === CONNECTING) { - this.currentState = WAITING; - if (this.timeout !== 0) { - clearTimeout(this.timeout); - this.timeout = 0; + var onFinish = function () { + if (currentState === OPEN || currentState === CONNECTING) { + currentState = WAITING; + if (timeout !== 0) { + clearTimeout(timeout); + timeout = 0; + } + timeout = setTimeout(function () { + onTimeout(); + }, retry); + retry = clampDuration(Math.min(initialRetry * 16, retry * 2)); + + es.readyState = CONNECTING; + var event = new Event("error"); + es.dispatchEvent(event); + fire(es, es.onerror, event); } - if (this.retry > this.initialRetry * 16) { - this.retry = this.initialRetry * 16; + }; + + var close = function () { + currentState = CLOSED; + if (cancelFunction != undefined) { + cancelFunction(); + cancelFunction = undefined; } - if (this.retry > MAXIMUM_DURATION) { - this.retry = MAXIMUM_DURATION; + if (timeout !== 0) { + clearTimeout(timeout); + timeout = 0; } - var that = this; - this.timeout = setTimeout(function () { - that.onTimeout(); - }, this.retry); - this.retry = this.retry * 2 + 1; + es.readyState = CLOSED; + }; - this.readyState = CONNECTING; - this.es.readyState = CONNECTING; - var event = new Event("error"); - this.es.dispatchEvent(event); - fire(this.es, this.es.onerror, event); - } - }; + var onTimeout = function () { + timeout = 0; - EventSourceInternal.prototype.onTimeout = function () { - this.timeout = 0; - if (this.currentState !== WAITING) { - if (!this.wasActivity) { - throwError(new Error("No activity within " + this.heartbeatTimeout + " milliseconds. Reconnecting.")); - this.transport.cancel(); - } else { - this.wasActivity = false; - var that = this; - this.timeout = setTimeout(function () { - that.onTimeout(); - }, this.heartbeatTimeout); + if (currentState !== WAITING) { + if (!wasActivity && cancelFunction != undefined) { + throwError(new Error("No activity within " + heartbeatTimeout + " milliseconds. Reconnecting.")); + cancelFunction(); + cancelFunction = undefined; + } else { + wasActivity = false; + timeout = setTimeout(function () { + onTimeout(); + }, heartbeatTimeout); + } + return; } - return; - } - this.wasActivity = false; - var that = this; - this.timeout = setTimeout(function () { - that.onTimeout(); - }, this.heartbeatTimeout); + wasActivity = false; + timeout = setTimeout(function () { + onTimeout(); + }, heartbeatTimeout); - this.currentState = CONNECTING; - this.dataBuffer.length = 0; - this.eventTypeBuffer = ""; - this.lastEventIdBuffer = this.lastEventId; - this.fieldStart = 0; - this.valueStart = 0; - this.state = FIELD_START; + currentState = CONNECTING; + dataBuffer = ""; + eventTypeBuffer = ""; + lastEventIdBuffer = lastEventId; + textBuffer = ""; + fieldStart = 0; + valueStart = 0; + state = FIELD_START; - var s = this.url.slice(0, 5); - if (s !== "data:" && s !== "blob:") { - s = this.url + ((this.url.indexOf("?", 0) === -1 ? "?" : "&") + "lastEventId=" + encodeURIComponent(this.lastEventId) + "&r=" + (Math.random() + 1).toString().slice(2)); - } else { - s = this.url; - } - try { - this.transport.open(s, this.withCredentials); - } catch (error) { - this.close(); - throw error; - } - }; + // https://bugzilla.mozilla.org/show_bug.cgi?id=428916 + // Request header field Last-Event-ID is not allowed by Access-Control-Allow-Headers. + var requestURL = url; + if (url.slice(0, 5) !== "data:" && url.slice(0, 5) !== "blob:") { + if (lastEventId !== "") { + requestURL += (url.indexOf("?") === -1 ? "?" : "&") + "lastEventId=" + encodeURIComponent(lastEventId); + } + } + var requestHeaders = {}; + requestHeaders["Accept"] = "text/event-stream"; + if (headers != undefined) { + for (var name in headers) { + if (Object.prototype.hasOwnProperty.call(headers, name)) { + requestHeaders[name] = headers[name]; + } + } + } + try { + transport.open(xhr, onStart, onProgress, onFinish, requestURL, withCredentials, requestHeaders); + } catch (error) { + close(); + throw error; + } + }; - EventSourceInternal.prototype.close = function () { - this.currentState = CLOSED; - this.transport.cancel(); - if (this.timeout !== 0) { - clearTimeout(this.timeout); - this.timeout = 0; - } - this.readyState = CLOSED; - this.es.readyState = CLOSED; - }; + es.url = url; + es.readyState = CONNECTING; + es.withCredentials = withCredentials; + es._close = close; - function F() { - this.CONNECTING = CONNECTING; - this.OPEN = OPEN; - this.CLOSED = CLOSED; - } - F.prototype = EventTarget.prototype; - - EventSource.prototype = new F(); - - EventSource.prototype.close = function () { - this._internal.close(); - }; - - F.call(EventSource); - if (isCORSSupported) { - EventSource.prototype.withCredentials = undefined; + onTimeout(); } - var isEventSourceSupported = function () { - // Opera 12 fails this test, but this is fine. - return global.EventSource != undefined && ("withCredentials" in global.EventSource.prototype); + EventSourcePolyfill.prototype = Object.create(EventTarget.prototype); + EventSourcePolyfill.prototype.CONNECTING = CONNECTING; + EventSourcePolyfill.prototype.OPEN = OPEN; + EventSourcePolyfill.prototype.CLOSED = CLOSED; + EventSourcePolyfill.prototype.close = function () { + this._close(); }; - if (Transport != undefined && (global.EventSource == undefined || (isCORSSupported && !isEventSourceSupported()))) { - // Why replace a native EventSource ? - // https://bugzilla.mozilla.org/show_bug.cgi?id=444328 - // https://bugzilla.mozilla.org/show_bug.cgi?id=831392 - // https://code.google.com/p/chromium/issues/detail?id=260144 - // https://code.google.com/p/chromium/issues/detail?id=225654 - // ... - global.NativeEventSource = global.EventSource; - global.EventSource = EventSource; - } + EventSourcePolyfill.CONNECTING = CONNECTING; + EventSourcePolyfill.OPEN = OPEN; + EventSourcePolyfill.CLOSED = CLOSED; + EventSourcePolyfill.prototype.withCredentials = undefined; + (function (factory) { + if (typeof module === "object" && typeof module.exports === "object") { + var v = factory(exports); + if (v !== undefined) module.exports = v; + } + else if (typeof define === "function" && define.amd) { + define(["exports"], factory); + } + else { + factory(global); + } + })(function (exports) { + exports.EventSourcePolyfill = EventSourcePolyfill; + exports.NativeEventSource = NativeEventSource; + if (XMLHttpRequest != undefined && (NativeEventSource == undefined || !("withCredentials" in NativeEventSource.prototype))) { + // Why replace a native EventSource ? + // https://bugzilla.mozilla.org/show_bug.cgi?id=444328 + // https://bugzilla.mozilla.org/show_bug.cgi?id=831392 + // https://code.google.com/p/chromium/issues/detail?id=260144 + // https://code.google.com/p/chromium/issues/detail?id=225654 + // ... + exports.EventSource = EventSourcePolyfill; + } + }); }(typeof window !== 'undefined' ? window : this)); diff --git a/public/vendor/js/eventsource.min.js b/public/vendor/js/eventsource.min.js index 8de7a35a..729cb200 100644 --- a/public/vendor/js/eventsource.min.js +++ b/public/vendor/js/eventsource.min.js @@ -4,23 +4,23 @@ Available under MIT License (MIT) https://github.com/Yaffle/EventSource/ */ -(function(e){function y(a,c,b,d,f){this._internal=new g(a,c,b,d,f)}function g(a,c,b,d,f){this.onStartCallback=c;this.onProgressCallback=b;this.onFinishCallback=d;this.thisArg=f;this.xhr=a;this.offset=this.charOffset=this.state=0;this.url="";this.withCredentials=!1;this.timeout=0}function m(){this._data={}}function r(){this._listeners=new m}function t(a){h(function(){throw a;},0)}function n(a){this.type=a;this.target=void 0}function A(a,c){n.call(this,a);this.data=c.data;this.lastEventId=c.lastEventId} -function p(a,c){this._listeners=new m;this.onerror=this.onmessage=this.onopen=void 0;this.url="";this.readyState=0;this.withCredentials=!1;this._internal=new k(this,a,c)}function k(a,c,b){this.url=c.toString();this.readyState=0;this.withCredentials=u&&void 0!=b&&!!b.withCredentials;this.es=a;this.initialRetry=v(1E3,0);this.heartbeatTimeout=v(45E3,0);this.lastEventId="";this.retry=this.initialRetry;this.wasActivity=!1;a=new (void 0!=b&&void 0!=b.Transport?b.Transport:B);this.transport=new y(a,this.onStart, -this.onProgress,this.onFinish,this);this.timeout=0;this.currentState=-1;this.dataBuffer=[];this.eventTypeBuffer=this.lastEventIdBuffer="";this.state=4;this.valueStart=this.fieldStart=0;this.es.url=this.url;this.es.readyState=this.readyState;this.es.withCredentials=this.withCredentials;this.onTimeout()}function z(){this.CONNECTING=0;this.OPEN=1;this.CLOSED=2}var h=e.setTimeout,l=e.clearTimeout,q=function(){};y.prototype.open=function(a,c){this._internal.open(a,c)};y.prototype.cancel=function(){this._internal.cancel()}; -g.prototype.onStart=function(){if(1===this.state){this.state=2;var a=0,c="",b=void 0;if("contentType"in this.xhr)a=200,c="OK",b=this.xhr.contentType;else try{a=this.xhr.status,c=this.xhr.statusText,b=this.xhr.getResponseHeader("Content-Type")}catch(d){a=0,c="",b=void 0}void 0==b&&(b="");this.onStartCallback.call(this.thisArg,a,c,b)}};g.prototype.onProgress=function(){this.onStart();if(2===this.state||3===this.state){this.state=3;var a="";try{a=this.xhr.responseText}catch(C){}for(var c=this.charOffset, -b=a.length,d=this.offset;db?1E3:18E616*this.initialRetry&&(this.retry=16*this.initialRetry);18E6=128>>b&&2047>=a<=2048>>b&&55295>=a<=57344>>b&&65535>=a<=65536>>b&&1114111>= +a<>6?3:31g||191=g?(e=0,f=g):192<=g&&223>=g?(e=6,f=g&31):224<=g&&239>=g?(e=12,f=g&15):240<=g&&247>=g?(e=18,f=g&7):(e=0,f=65533),0===e||c(f,e,d(e,f))||(e=0,f=65533)):(e-=6,f=f<<6|g&63);0===e&&(65535>=f? +b+=String.fromCharCode(f):(b+=String.fromCharCode(55296+(f-65535-1>>10)),b+=String.fromCharCode(56320+(f-65535-1&1023))))}this.bitsNeeded=e;this.codePoint=f;return b};var Q;if(!(Q=void 0==F||void 0==aa)){a:{try{var ba="test"===(new F).decode((new aa).encode("test"),{stream:!0});break a}catch(a){console.log(a)}ba=!1}Q=!ba}Q&&(F=R);var k=function(){};y.prototype.open=function(a,c){this._abort(!0);var d=this,b=this._xhr,e=1,f=0;this._abort=function(a){0!==d._sendTimeout&&(E(d._sendTimeout),d._sendTimeout= +0);if(1===e||2===e||3===e)e=4,b.onload=k,b.onerror=k,b.onabort=k,b.onprogress=k,b.onreadystatechange=k,b.abort(),0!==f&&(E(f),f=0),a||(d.readyState=4,d.onreadystatechange());e=0};var r=function(){if(1===e){var a=0,c="",f=void 0;if("contentType"in b)a=200,c="OK",f=b.contentType;else try{a=b.status,c=b.statusText,f=b.getResponseHeader("Content-Type")}catch(x){a=0,c="",f=void 0}0!==a&&(e=2,d.readyState=2,d.status=a,d.statusText=c,d._contentType=f,d.onreadystatechange())}},g=function(){r();if(2===e|| +3===e){e=3;var a="";try{a=b.responseText}catch(ia){}d.readyState=3;d.responseText=a;d.onprogress()}},h=function(){g();if(1===e||2===e||3===e)e=4,0!==f&&(E(f),f=0),d.readyState=4,d.onreadystatechange()},l=function(){f=w(function(){l()},500);3===b.readyState&&g()};b.onload=h;b.onerror=h;b.onabort=h;"sendAsBinary"in z.prototype||"mozAnon"in z.prototype||(b.onprogress=g);b.onreadystatechange=function(){void 0!=b&&(4===b.readyState?h():3===b.readyState?g():2===b.readyState&&r())};"contentType"in b&&(c+= +(-1===c.indexOf("?")?"?":"&")+"padding=true");b.open(a,c,!0);"readyState"in b&&(f=w(function(){l()},0))};y.prototype.abort=function(){this._abort(!1)};y.prototype.getResponseHeader=function(a){return this._contentType};y.prototype.setRequestHeader=function(a,c){var d=this._xhr;"setRequestHeader"in d&&d.setRequestHeader(a,c)};y.prototype.getAllResponseHeaders=function(){return void 0!=this._xhr.getAllResponseHeaders?this._xhr.getAllResponseHeaders():""};y.prototype.send=function(){if("ontimeout"in +z.prototype||void 0==K||void 0==K.readyState||"complete"===K.readyState){var a=this._xhr;a.withCredentials=this.withCredentials;a.responseType=this.responseType;try{a.send(void 0)}catch(d){throw d;}}else{var c=this;c._sendTimeout=w(function(){c._sendTimeout=0;c.send()},4)}};T.prototype.get=function(a){return this._map[S(a)]};U.prototype.open=function(a,c,d,b,e,f,h){a.open("GET",e);var g=0;a.onprogress=function(){var b=a.responseText.slice(g);g+=b.length;d(b)};a.onreadystatechange=function(){if(2=== +a.readyState){var d=a.status,e=a.statusText,f=a.getResponseHeader("Content-Type"),g=a.getAllResponseHeaders();c(d,e,f,new T(g),function(){a.abort()})}else 4===a.readyState&&b()};a.withCredentials=f;a.responseType="text";for(var k in h)Object.prototype.hasOwnProperty.call(h,k)&&a.setRequestHeader(k,h[k]);a.send()};V.prototype.get=function(a){return this._headers.get(a)};W.prototype.open=function(a,c,d,b,e,f,h){var g=new D;a=g.signal;var k=new F;v(e,{headers:h,credentials:f?"include":"same-origin", +signal:a,cache:"no-store"}).then(function(a){var b=a.body.getReader();c(a.status,a.statusText,a.headers.get("Content-Type"),new V(a.headers),function(){g.abort();b.cancel()});return new q(function(a,c){var e=function(){b.read().then(function(b){b.done?a(void 0):(b=k.decode(b.value,{stream:!0}),d(b),e())})["catch"](function(a){c(a)})};e()})})["finally"](function(){b()})};t.prototype.dispatchEvent=function(a){a.target=this;var c=this._listeners[a.type];if(void 0!=c)for(var d=c.length,b=0;bdoExport(TMP_DIR); + self::$xmlExportService->doExport(TMP_PATH); $this->assertFileExists(self::$xmlExportService->getExportFile()); @@ -126,7 +126,7 @@ class XmlExportServiceTest extends DatabaseTestCase { $password = PasswordUtil::randomPassword(); - self::$xmlExportService->doExport(TMP_DIR, $password); + self::$xmlExportService->doExport(TMP_PATH, $password); $this->assertFileExists(self::$xmlExportService->getExportFile()); diff --git a/tests/SP/Services/Task/TaskServiceTest.php b/tests/SP/Services/Task/TaskServiceTest.php new file mode 100644 index 00000000..a13961f0 --- /dev/null +++ b/tests/SP/Services/Task/TaskServiceTest.php @@ -0,0 +1,125 @@ +. + */ + +namespace SP\Tests\Services\Task; + +use PHPUnit\Framework\TestCase; +use SP\Services\Task\Task; +use SP\Services\Task\TaskFactory; +use SP\Services\Task\TaskService; +use function SP\Tests\setupContext; + +/** + * Class TaskServiceTest + * + * @package SP\Tests\Services\Task + */ +class TaskServiceTest extends TestCase +{ + private static $pids = []; + + /** + * @throws \SP\Storage\File\FileException + * @throws \SP\Core\Context\ContextException + */ + public function testTrackStatus() + { + $this->markTestSkipped(); + + $task = TaskFactory::create(__FUNCTION__, Task::genTaskId(__FUNCTION__)); + + $this->assertFileExists($task->getFileTask()->getFile()); + + TaskFactory::update($task, + TaskFactory::createMessage($task->getTaskId(), "Test Task (INIT)") + ); + + $dic = setupContext(); + + $this->fork(function () use ($task, $dic) { + $taskService = new TaskService($dic); + + $taskService->trackStatus($task->getTaskId(), + function ($id, $message) { + logger("id: $id; data: $message"); + }); + }); + + $this->fork(function () use ($task) { + $count = 0; + + while ($count < 2) { + sleep(10); + + TaskFactory::update($task, + TaskFactory::createMessage($task->getTaskId(), "Test Task #$count") + ); + + $count++; + } + + TaskFactory::end($task); + }); + + while(count(self::$pids) > 0) { + foreach (self::$pids as $key => $pid) { + $res = pcntl_waitpid($pid, $status); + + // If the process has already exited + if ($res == -1 || $res > 0) { + unset(self::$pids[$key]); + } + } + + sleep(1); + } + + $this->assertFileNotExists($task->getFileTask()->getFile()); + $this->assertFileNotExists($task->getFileOut()->getFile()); + } + + /** + * Fork for running a piece of code in child process + * + * @param callable $code + */ + private function fork(callable $code) + { + $pid = pcntl_fork(); + + if ($pid == -1) { + die('Could not fork'); + } elseif ($pid === 0) { + echo "Child execution\n"; + + $code(); + + exit(); + } else { + echo "Child $pid\n"; + + self::$pids[] = $pid; + } + } +} diff --git a/tests/SP/Storage/ArchiveHandlerTest.php b/tests/SP/Storage/ArchiveHandlerTest.php index 918cb49a..8597b1c1 100644 --- a/tests/SP/Storage/ArchiveHandlerTest.php +++ b/tests/SP/Storage/ArchiveHandlerTest.php @@ -35,7 +35,7 @@ use SP\Storage\File\ArchiveHandler; */ class ArchiveHandlerTest extends TestCase { - const ARCHIVE = TMP_DIR . DIRECTORY_SEPARATOR . 'test_archive'; + const ARCHIVE = TMP_PATH . DIRECTORY_SEPARATOR . 'test_archive'; /** * @throws \SP\Core\Exceptions\CheckException @@ -43,7 +43,7 @@ class ArchiveHandlerTest extends TestCase */ public function testCompressFile() { - $archive = TMP_DIR . DIRECTORY_SEPARATOR . 'test_archive_file'; + $archive = TMP_PATH . DIRECTORY_SEPARATOR . 'test_archive_file'; $handler = new ArchiveHandler($archive, new PhpExtensionChecker()); $handler->compressFile(RESOURCE_DIR . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.xml'); @@ -59,7 +59,7 @@ class ArchiveHandlerTest extends TestCase { $this->expectException(\RuntimeException::class); - $archive = TMP_DIR . DIRECTORY_SEPARATOR . 'test_archive_file'; + $archive = TMP_PATH . DIRECTORY_SEPARATOR . 'test_archive_file'; $handler = new ArchiveHandler($archive, new PhpExtensionChecker()); $handler->compressFile(RESOURCE_DIR . DIRECTORY_SEPARATOR . 'non_existant_file'); @@ -71,7 +71,7 @@ class ArchiveHandlerTest extends TestCase */ public function testCompressDirectory() { - $archive = TMP_DIR . DIRECTORY_SEPARATOR . 'test_archive_dir'; + $archive = TMP_PATH . DIRECTORY_SEPARATOR . 'test_archive_dir'; $handler = new ArchiveHandler($archive, new PhpExtensionChecker()); $handler->compressDirectory(RESOURCE_DIR); @@ -87,7 +87,7 @@ class ArchiveHandlerTest extends TestCase { $this->expectException(\UnexpectedValueException::class); - $archive = TMP_DIR . DIRECTORY_SEPARATOR . 'test_archive_dir'; + $archive = TMP_PATH . DIRECTORY_SEPARATOR . 'test_archive_dir'; $handler = new ArchiveHandler($archive, new PhpExtensionChecker()); $handler->compressDirectory(RESOURCE_DIR . DIRECTORY_SEPARATOR . 'non_existant_dir'); @@ -99,6 +99,6 @@ class ArchiveHandlerTest extends TestCase */ protected function setUp() { - array_map('unlink', glob(TMP_DIR . DIRECTORY_SEPARATOR . 'test_archive_*')); + array_map('unlink', glob(TMP_PATH . DIRECTORY_SEPARATOR . 'test_archive_*')); } } diff --git a/tests/SP/Storage/FileCachePackedTest.php b/tests/SP/Storage/FileCachePackedTest.php index 57ca0521..154fd83d 100644 --- a/tests/SP/Storage/FileCachePackedTest.php +++ b/tests/SP/Storage/FileCachePackedTest.php @@ -36,7 +36,7 @@ use SP\Storage\File\FileException; */ class FileCachePackedTest extends TestCase { - const CACHE_FILE = TMP_DIR . DIRECTORY_SEPARATOR . 'test_packed.cache'; + const CACHE_FILE = TMP_PATH . DIRECTORY_SEPARATOR . 'test_packed.cache'; private static $data; diff --git a/tests/SP/Storage/FileCacheTest.php b/tests/SP/Storage/FileCacheTest.php index 78677a73..1ca3a940 100644 --- a/tests/SP/Storage/FileCacheTest.php +++ b/tests/SP/Storage/FileCacheTest.php @@ -36,7 +36,7 @@ use SP\Storage\File\FileException; */ class FileCacheTest extends TestCase { - const CACHE_FILE = TMP_DIR . DIRECTORY_SEPARATOR . 'test.cache'; + const CACHE_FILE = TMP_PATH . DIRECTORY_SEPARATOR . 'test.cache'; private static $data; diff --git a/tests/SP/bootstrap.php b/tests/SP/bootstrap.php index dba1636b..512165bc 100644 --- a/tests/SP/bootstrap.php +++ b/tests/SP/bootstrap.php @@ -38,14 +38,16 @@ define('APP_MODULE', 'tests'); define('APP_ROOT', dirname(__DIR__, 2)); define('TEST_ROOT', dirname(__DIR__)); -define('SQL_PATH', APP_ROOT . DIRECTORY_SEPARATOR . 'schemas'); define('RESOURCE_DIR', TEST_ROOT . DIRECTORY_SEPARATOR . 'res'); -define('TMP_DIR', TEST_ROOT . DIRECTORY_SEPARATOR . 'tmp'); define('CONFIG_PATH', RESOURCE_DIR . DIRECTORY_SEPARATOR . 'config'); define('CONFIG_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . 'config.xml'); define('ACTIONS_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . 'actions.xml'); + +define('SQL_PATH', APP_ROOT . DIRECTORY_SEPARATOR . 'schemas'); define('CACHE_PATH', RESOURCE_DIR . DIRECTORY_SEPARATOR . 'cache'); -define('LOG_FILE', TMP_DIR . DIRECTORY_SEPARATOR . 'test.log'); +define('TMP_PATH', TEST_ROOT . DIRECTORY_SEPARATOR . 'tmp'); + +define('LOG_FILE', TMP_PATH . DIRECTORY_SEPARATOR . 'test.log'); define('SELF_IP_ADDRESS', getRealIpAddress()); define('SELF_HOSTNAME', gethostbyaddr(SELF_IP_ADDRESS)); @@ -57,7 +59,7 @@ print 'TEST_ROOT=' . TEST_ROOT . PHP_EOL; print 'SELF_IP_ADDRESS=' . SELF_IP_ADDRESS . PHP_EOL; // Setup directories -recreateDir(TMP_DIR); +recreateDir(TMP_PATH); recreateDir(CACHE_PATH); if (is_dir(CONFIG_PATH) diff --git a/tests/res/config/config.xml b/tests/res/config/config.xml index 8ceb9a26..0e5d77a9 100644 --- a/tests/res/config/config.xml +++ b/tests/res/config/config.xml @@ -9,11 +9,11 @@ 1 1 - cdd8f6d489b6b8ba4fcf04c6092e03b8e606f7b2 + 7df3988f1e68e5dc99b575134231e2c096e66be8 0 0 - 1543260127 - 2f707e2e37fb26c48baa22bb27099b1519d54808 + 1544405661 + 96d4ff2e04fd1878b2c006e6143745eccd1d791c sysPass @@ -32,7 +32,7 @@ 0 - 236418f6bca1daebfe238be3df7ba0a08a74046f + e6505ca2dbb7e4588543448e6fe3a7d57957594c PDF JPG