nilsteampassnet /
TeamPass
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * Teampass - a collaborative passwords manager. |
||
| 4 | * --- |
||
| 5 | * This file is part of the TeamPass project. |
||
| 6 | * |
||
| 7 | * TeamPass is free software: you can redistribute it and/or modify it |
||
| 8 | * under the terms of the GNU General Public License as published by |
||
| 9 | * the Free Software Foundation, version 3 of the License. |
||
| 10 | * |
||
| 11 | * TeamPass is distributed in the hope that it will be useful, |
||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 14 | * GNU General Public License for more details. |
||
| 15 | * |
||
| 16 | * You should have received a copy of the GNU General Public License |
||
| 17 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
||
| 18 | * |
||
| 19 | * Certain components of this file may be under different licenses. For |
||
| 20 | * details, see the `licenses` directory or individual file headers. |
||
| 21 | * --- |
||
| 22 | * @file background_tasks___worker.php |
||
| 23 | * @author Nils Laumaillé ([email protected]) |
||
| 24 | * @copyright 2009-2025 Teampass.net |
||
| 25 | * @license GPL-3.0 |
||
| 26 | * @see https://www.teampass.net |
||
| 27 | */ |
||
| 28 | |||
| 29 | use TeampassClasses\ConfigManager\ConfigManager; |
||
| 30 | require_once __DIR__.'/../sources/main.functions.php'; |
||
| 31 | require_once __DIR__.'/background_tasks___functions.php'; |
||
| 32 | require_once __DIR__.'/traits/ItemHandlerTrait.php'; |
||
| 33 | require_once __DIR__.'/traits/UserHandlerTrait.php'; |
||
| 34 | require_once __DIR__.'/traits/EmailTrait.php'; |
||
| 35 | require_once __DIR__ . '/TaskLogger.php'; |
||
| 36 | |||
| 37 | class TaskWorker { |
||
| 38 | use ItemHandlerTrait; |
||
| 39 | use UserHandlerTrait; |
||
| 40 | use EmailTrait; |
||
| 41 | |||
| 42 | private $taskId; |
||
| 43 | private $processType; |
||
| 44 | private $taskData; |
||
| 45 | private $settings; |
||
| 46 | private $logger; |
||
| 47 | |||
| 48 | public function __construct(int $taskId, string $processType, array $taskData) { |
||
| 49 | $this->taskId = $taskId; |
||
| 50 | $this->processType = $processType; |
||
| 51 | $this->taskData = $taskData; |
||
| 52 | $this->settings['enable_tasks_log'] = true; |
||
| 53 | $this->logger = new TaskLogger($this->settings, LOG_TASKS_FILE); |
||
| 54 | |||
| 55 | $configManager = new ConfigManager(); |
||
| 56 | $this->settings = $configManager->getAllSettings(); |
||
| 57 | } |
||
| 58 | |||
| 59 | /** |
||
| 60 | * Execute the task based on its type. |
||
| 61 | * This method will handle different types of tasks such as item copy, new item creation, |
||
| 62 | * user cache tree building, email sending, and user key generation. |
||
| 63 | * |
||
| 64 | * @return void |
||
| 65 | */ |
||
| 66 | public function execute() { |
||
| 67 | try { |
||
| 68 | if (LOG_TASKS=== true) $this->logger->log('Processing task: ' . print_r($this->taskData, true), 'DEBUG'); |
||
|
1 ignored issue
–
show
introduced
by
Loading history...
|
|||
| 69 | // Dispatch selon le type de processus |
||
| 70 | switch ($this->processType) { |
||
| 71 | case 'item_copy': |
||
| 72 | $this->processSubTasks($this->taskData); |
||
| 73 | break; |
||
| 74 | case 'new_item': |
||
| 75 | $this->processSubTasks($this->taskData); |
||
| 76 | break; |
||
| 77 | case 'item_update_create_keys': |
||
| 78 | $this->processSubTasks($this->taskData); |
||
| 79 | break; |
||
| 80 | case 'user_build_cache_tree': |
||
| 81 | $this->handleUserBuildCacheTree($this->taskData); |
||
| 82 | break; |
||
| 83 | case 'send_email': |
||
| 84 | $this->sendEmail($this->taskData); |
||
| 85 | break; |
||
| 86 | case 'create_user_keys': |
||
| 87 | $this->generateUserKeys($this->taskData); |
||
| 88 | break; |
||
| 89 | default: |
||
| 90 | throw new Exception("Type of subtask unknown: {$this->processType}"); |
||
| 91 | } |
||
| 92 | |||
| 93 | // Mark the task as completed |
||
| 94 | $this->completeTask(); |
||
| 95 | |||
| 96 | } catch (Exception $e) { |
||
| 97 | $this->handleTaskFailure($e); |
||
| 98 | } |
||
| 99 | } |
||
| 100 | |||
| 101 | /** |
||
| 102 | * Mark the task as completed in the database. |
||
| 103 | * This method updates the task status to 'completed' and sets the finished_at timestamp. |
||
| 104 | * |
||
| 105 | * @return void |
||
| 106 | */ |
||
| 107 | private function completeTask() { |
||
| 108 | // Prepare data for updating the task status |
||
| 109 | $updateData = [ |
||
| 110 | 'is_in_progress' => -1, |
||
| 111 | 'finished_at' => time(), |
||
| 112 | 'status' => 'completed' |
||
| 113 | ]; |
||
| 114 | |||
| 115 | // Prepare anonimzation of arguments |
||
| 116 | if ($this->processType === 'send_mail') { |
||
| 117 | $arguments = json_encode( |
||
| 118 | [ |
||
| 119 | 'email' => $this->taskData['receivers'], |
||
| 120 | 'login' => $this->taskData['receiver_name'], |
||
| 121 | ] |
||
| 122 | ); |
||
| 123 | } elseif ($this->processType === 'create_user_keys') { |
||
| 124 | $arguments = json_encode( |
||
| 125 | [ |
||
| 126 | 'user_id' => $this->taskData['new_user_id'], |
||
| 127 | ] |
||
| 128 | ); |
||
| 129 | } elseif ($this->processType === 'item_update_create_keys') { |
||
| 130 | $arguments = json_encode( |
||
| 131 | [ |
||
| 132 | 'item_id' => $this->taskData['new_user_id'], |
||
| 133 | 'author' => $this->taskData['author'], |
||
| 134 | ] |
||
| 135 | ); |
||
| 136 | } else { |
||
| 137 | $arguments = ''; |
||
| 138 | } |
||
| 139 | |||
| 140 | if (LOG_TASKS=== true) $this->logger->log('Process: '.$this->processType.' -- '.print_r($arguments, true), 'DEBUG'); |
||
|
1 ignored issue
–
show
|
|||
| 141 | |||
| 142 | // Add 'arguments' only if not empty |
||
| 143 | if (!empty($arguments)) { |
||
| 144 | $updateData['arguments'] = $arguments; |
||
| 145 | } |
||
| 146 | |||
| 147 | // Store completed status in the database |
||
| 148 | DB::update( |
||
| 149 | prefixTable('background_tasks'), |
||
| 150 | $updateData, |
||
| 151 | 'increment_id = %i', |
||
| 152 | $this->taskId |
||
| 153 | ); |
||
| 154 | if (LOG_TASKS=== true) $this->logger->log('Finishing task: ' . $this->taskId, 'DEBUG'); |
||
|
1 ignored issue
–
show
|
|||
| 155 | } |
||
| 156 | |||
| 157 | /** |
||
| 158 | * Handle task failure by updating the task status in the database. |
||
| 159 | * This method sets the task status to 'failed', updates the finished_at timestamp, |
||
| 160 | * and logs the error message. |
||
| 161 | * |
||
| 162 | * @param Exception $e The exception that occurred during task processing. |
||
| 163 | * @return void |
||
| 164 | */ |
||
| 165 | private function handleTaskFailure(Exception $e) { |
||
| 166 | DB::update( |
||
| 167 | prefixTable('background_tasks'), |
||
| 168 | [ |
||
| 169 | 'is_in_progress' => -1, |
||
| 170 | 'finished_at' => time(), |
||
| 171 | 'status' => 'failed', |
||
| 172 | 'error_message' => $e->getMessage() |
||
| 173 | ], |
||
| 174 | 'increment_id = %i', |
||
| 175 | $this->taskId |
||
| 176 | ); |
||
| 177 | $this->logger->log('Task failure: ' . $e->getMessage(), 'ERROR'); |
||
| 178 | } |
||
| 179 | |||
| 180 | /** |
||
| 181 | * Handle subtasks for the current task. |
||
| 182 | * This method retrieves all subtasks related to the current task and processes them. |
||
| 183 | * If all subtasks are completed, it marks the main task as completed. |
||
| 184 | * |
||
| 185 | * @param array $arguments Arguments for the subtasks. |
||
| 186 | * @return void |
||
| 187 | */ |
||
| 188 | private function processSubTasks($arguments) { |
||
| 189 | // Get all subtasks related to this task |
||
| 190 | $subtasks = DB::query( |
||
| 191 | 'SELECT * FROM ' . prefixTable('background_subtasks') . ' WHERE task_id = %i AND is_in_progress = 0 ORDER BY `task` ASC', |
||
| 192 | $this->taskId |
||
| 193 | ); |
||
| 194 | |||
| 195 | // Check if there are any subtasks to process |
||
| 196 | if (empty($subtasks)) { |
||
| 197 | if (LOG_TASKS=== true) $this->logger->log('No subtask was found for task: ' . $this->taskId, 'DEBUG'); |
||
|
1 ignored issue
–
show
|
|||
| 198 | return; |
||
| 199 | } |
||
| 200 | |||
| 201 | // Process each subtask |
||
| 202 | foreach ($subtasks as $subtask) { |
||
| 203 | try { |
||
| 204 | // Get the subtask data |
||
| 205 | $subtaskData = json_decode($subtask['task'], true); |
||
| 206 | |||
| 207 | if (LOG_TASKS=== true) $this->logger->log('Processing subtask: ' . $subtaskData['step'], 'DEBUG'); |
||
| 208 | |||
| 209 | // Process the subtask based on its type |
||
| 210 | switch ($subtaskData['step'] ?? '') { |
||
| 211 | case 'create_users_pwd_key': |
||
| 212 | $this->generateUserPasswordKeys($arguments); |
||
| 213 | break; |
||
| 214 | case 'create_users_fields_key': |
||
| 215 | $this->generateUserFieldKeys($subtaskData); |
||
| 216 | break; |
||
| 217 | case 'create_users_files_key': |
||
| 218 | $this->generateUserFileKeys($subtaskData); |
||
| 219 | break; |
||
| 220 | default: |
||
| 221 | throw new Exception("Type de sous-tâche inconnu (".$subtaskData['step'].")"); |
||
| 222 | } |
||
| 223 | |||
| 224 | // Mark subtask as completed |
||
| 225 | DB::update( |
||
| 226 | prefixTable('background_subtasks'), |
||
| 227 | [ |
||
| 228 | 'is_in_progress' => -1, |
||
| 229 | 'finished_at' => time(), |
||
| 230 | 'status' => 'completed', |
||
| 231 | ], |
||
| 232 | 'increment_id = %i', |
||
| 233 | $subtask['increment_id'] |
||
| 234 | ); |
||
| 235 | |||
| 236 | } catch (Exception $e) { |
||
| 237 | // Mark subtask as failed |
||
| 238 | DB::update( |
||
| 239 | prefixTable('background_subtasks'), |
||
| 240 | [ |
||
| 241 | 'is_in_progress' => -1, |
||
| 242 | 'finished_at' => time(), |
||
| 243 | 'updated_at' => time(), |
||
| 244 | 'status' => 'failed', |
||
| 245 | 'error_message' => $e->getMessage(), |
||
| 246 | ], |
||
| 247 | 'increment_id = %i', |
||
| 248 | $subtask['increment_id'] |
||
| 249 | ); |
||
| 250 | |||
| 251 | $this->logger->log('processSubTasks : ' . $e->getMessage(), 'ERROR'); |
||
| 252 | } |
||
| 253 | } |
||
| 254 | |||
| 255 | // Are all subtasks completed? |
||
| 256 | $remainingSubtasks = DB::queryFirstField( |
||
| 257 | 'SELECT COUNT(*) FROM ' . prefixTable('background_subtasks') . ' WHERE task_id = %i AND is_in_progress = 0', |
||
| 258 | $this->taskId |
||
| 259 | ); |
||
| 260 | |||
| 261 | if ($remainingSubtasks == 0) { |
||
| 262 | $this->completeTask(); |
||
| 263 | } |
||
| 264 | } |
||
| 265 | } |
||
| 266 | |||
| 267 | // Prepare the environment |
||
| 268 | // Get the task ID and process type from command line arguments |
||
| 269 | if ($argc < 3) { |
||
| 270 | error_log("Usage: php background_tasks___worker.php <task_id> <process_type> [<task_data>]"); |
||
| 271 | exit(1); |
||
| 272 | } |
||
| 273 | $taskId = (int)$argv[1]; |
||
| 274 | $processType = $argv[2]; |
||
| 275 | $taskData = $argv[3] ?? null; |
||
| 276 | if ($taskData) { |
||
| 277 | $taskData = json_decode($taskData, true); |
||
| 278 | } else { |
||
| 279 | $taskData = []; |
||
| 280 | } |
||
| 281 | |||
| 282 | // Initialize the worker |
||
| 283 | $worker = new TaskWorker($taskId, $processType, $taskData); |
||
| 284 | $worker->execute(); |