| Línea 1... | Línea 1... | 
          
            | 1 | <?php
 | 1 | <?php
 | 
          
            | 2 | // Configuración de headers para respuesta JSON y CORS
 | - |   | 
          
            | 3 | // header('Content-Type: application/json'); // MOVED: Will be set only when outputting JSON
 | - |   | 
          
            | 4 | // header('Access-Control-Allow-Origin: *'); // MOVED: Will be set only when outputting JSON
 | - |   | 
          
            | Línea -... | Línea 2... | 
          
            | - |   | 2 |  
 | 
          
            | - |   | 3 | // NOTA IMPORTANTE:
 | 
          
            | - |   | 4 | // Las credenciales y claves RSA NO DEBEN ESTAR HARDCODEADAS EN UN ENTORNO DE PRODUCCIÓN.
 | 
          
            | - |   | 5 | // Considere usar variables de entorno, un archivo de configuración externo, o las propias configuraciones de Moodle
 | 
          
            | 5 |  
 | 6 | // para almacenar LLWS_USERNAME, LLWS_PASSWORD, LLWS_RSA_N, LLWS_RSA_D, LLWS_RSA_E.
 | 
          
            | - |   | 7 | // Por simplicidad en este ejemplo, se mantienen aquí para la demostración de los cambios.
 | 
          
            | 6 | // Definición de constantes para autenticación y encriptación
 | 8 |  
 | 
          
            | 7 | define('LLWS_USERNAME', 'leaderdslinked-ws');
 | 9 | define('LLWS_USERNAME', 'leaderdslinked-ws');
 | 
          
            | 8 | define('LLWS_PASSWORD', 'KFB3lFsLp&*CpB0uc2VNc!zVn@w!EZqZ*jr$J0AlE@sYREUcyP');
 | 10 | define('LLWS_PASSWORD', 'KFB3lFsLp&*CpB0uc2VNc!zVn@w!EZqZ*jr$J0AlE@sYREUcyP');
 | 
          
            | 9 | define('LLWS_RSA_N', 34589927);  // Clave pública RSA N
 | 11 | define('LLWS_RSA_N', 34589927);     // Clave pública RSA N
 | 
          
            | 10 | define('LLWS_RSA_D', 4042211);   // Clave privada RSA D
 | 12 | define('LLWS_RSA_D', 4042211);      // Clave privada RSA D
 | 
          
            | 11 | define('LLWS_RSA_E', 7331);      // Clave pública RSA E
 | 13 | define('LLWS_RSA_E', 7331);         // Clave pública RSA E
 | 
          
            | Línea 12... | Línea 14... | 
          
            | 12 | define('LLWS_CATEGORY_ID', 6);   // ID de categoría para cursos
 | 14 | define('LLWS_CATEGORY_ID', 6);      // ID de categoría para cursos
 | 
          
            | 13 |  
 | 15 |  
 | 
          
            | 14 | // Inclusión de archivos necesarios de Moodle
 | 16 | // Inclusión de archivos necesarios de Moodle
 | 
          
            | Línea 15... | Línea 17... | 
          
            | 15 | require_once(__DIR__ . '/../config.php');
 | 17 | require_once(__DIR__ . '/../config.php');
 | 
          
            | 16 | global $DB, $CFG;
 | 18 | global $DB, $CFG;
 | 
          
            | 17 |  
 | 19 |  
 | 
          
            | 18 | // Inclusión de librerías de Moodle
 | 20 | // Inclusión de librerías de Moodle
 | 
          
            | 19 | require_once($CFG->libdir . '/moodlelib.php');
 | 21 | require_once($CFG->libdir . '/moodlelib.php');
 | 
          
            | 20 | require_once($CFG->libdir . '/externallib.php');
 | 22 | require_once($CFG->libdir . '/externallib.php');
 | 
          
            | 21 | require_once($CFG->libdir . '/authlib.php');
 | - |   | 
          
            | 22 | require_once($CFG->libdir . '/gdlib.php');
 | - |   | 
          
            | 23 | require_once($CFG->dirroot . '/user/lib.php');
 | - |   | 
          
            | 24 | require_once(__DIR__ . '/rsa.php');
 | - |   | 
          
            | 25 | require_once(__DIR__ . '/lib.php');
 | - |   | 
          
            | 26 |  
 | - |   | 
          
            | 27 | // Obtención y sanitización de parámetros de la petición
 | - |   | 
          
            | 28 | $username   = trim(isset($_REQUEST['username']) ? filter_var($_REQUEST['username'], FILTER_SANITIZE_STRING) : '');
 | - |   | 
          
            | 29 | $password   = trim(isset($_REQUEST['password']) ? filter_var($_REQUEST['password'], FILTER_SANITIZE_STRING) : '');
 | - |   | 
          
            | 30 | $timestamp  = trim(isset($_REQUEST['timestamp']) ? filter_var($_REQUEST['timestamp'], FILTER_SANITIZE_STRING) : '');
 | - |   | 
          
            | 31 | $rand       = intval(isset($_REQUEST['rand']) ? filter_var($_REQUEST['rand'], FILTER_SANITIZE_NUMBER_INT) : 0, 10);
 | - |   | 
          
            | 32 | $data       = trim(isset($_REQUEST['data']) ? filter_var($_REQUEST['data'], FILTER_SANITIZE_STRING) : '');
 | - |   | 
          
            | 33 |  
 | - |   | 
          
            | 34 | /*
 | - |   | 
          
            | 35 | $data = '';
 | - |   | 
          
            | 36 | $input = fopen("php://input", "r");
 | - |   | 
          
            | 37 |  
 | - |   | 
          
            | 38 |  
 | - |   | 
          
            | 39 | while ($line = fread($input, 1024))
 | - |   | 
          
            | 40 | {
 | - |   | 
          
            | 41 |     $data .= $line; 
 | - |   | 
          
            | 42 | }
 | - |   | 
          
            | 43 | fclose($input);*/
 | - |   | 
          
            | 44 |  
 | - |   | 
          
            | 45 | /*
 | - |   | 
          
            | 46 | echo 'username = '. $username . PHP_EOL;
 | - |   | 
          
            | 47 | echo 'password = '. $password . PHP_EOL;
 | - |   | 
          
            | 48 | echo 'rand = '. $rand . PHP_EOL;
 | - |   | 
          
            | 49 | echo 'timestamp = '. $timestamp . PHP_EOL;
 | - |   | 
          
            | 50 | echo 'data = '. $data . PHP_EOL;
 | - |   | 
          
            | 51 |  
 | - |   | 
          
            | 52 |  
 | - |   | 
          
            | 53 | /*
 | - |   | 
          
            | 54 | list($usec, $sec) = explode(' ', microtime());
 | - |   | 
          
            | 55 | $seed = intval($sec + ((float) $usec * 100000));
 | - |   | 
          
            | 56 |  
 | - |   | 
          
            | 57 | $username   = LLWS_USERNAME;
 | - |   | 
          
            | 58 | $timestamp  = date('Y-m-d\TH:i:s');
 | - |   | 
          
            | 59 | mt_srand($seed, MT_RAND_MT19937);
 | - |   | 
          
            | 60 | $rand =  mt_rand();
 | 23 | require_once($CFG->libdir . '/authlib.php');
 | 
          
            | 61 |  
 | - |   | 
          
            | 62 | $password   = LLWS_PASSWORD;
 | 24 | require_once($CFG->libdir . '/gdlib.php'); // Para procesamiento de imágenes
 | 
          
            | 63 | $password  = password_hash($username.'-'. $password. '-' . $rand. '-' . $timestamp, PASSWORD_DEFAULT);
 | - |   | 
          
            | Línea 64... | Línea 25... | 
          
            | 64 |  
 | 25 | require_once($CFG->dirroot . '/user/lib.php'); // Para funciones de usuario
 | 
          
            | - |   | 26 | require_once(__DIR__ . '/rsa.php'); // Asegúrate de que esta ruta sea correcta para tu clase RSA
 | 
          
            | - |   | 27 | require_once(__DIR__ . '/lib.php'); // Asegúrate de que esta ruta sea correcta para tus funciones ll_get_user_by_email, ll_get_username_available, ll_create_user
 | 
          
            | 65 | $data       = $rsa->encrypt(json_encode(['email' => 'usuario4@test.com', 'first_name' => 'usuario4', 'last_name' => 'test']));
 | 28 |  
 | 
          
            | 66 | */
 | 29 | // Helper function to output JSON error and exit
 | 
          
            | 67 |  
 | 30 | // NOTA: Estos headers solo se configuran cuando hay un error para una respuesta JSON.
 | 
          
            | 68 | // Helper function to output JSON error and exit
 | 31 | // Para un login exitoso, no se necesitan estos headers.
 | 
          
            | 69 | function output_json_error($errorCode)
 | 32 | function output_json_error($errorCode)
 | 
          
            | 70 | {
 | 33 | {
 | 
          
            | 71 |     // It's good practice to ensure session is closed before output,
 | 34 |     // Es buena práctica asegurar que la sesión se cierre antes de la salida,
 | 
          
            | 72 |     // though Moodle's exit handling might cover this.
 | 35 |     // aunque el manejo de salida de Moodle podría cubrir esto.
 | 
          
            | - |   | 36 |     if (function_exists('session_write_close')) {
 | 
          
            | - |   | 37 |         session_write_close();
 | 
          
            | - |   | 38 |     }
 | 
          
            | 73 |     if (function_exists('session_write_close')) {
 | 39 |     header('Content-Type: application/json');
 | 
          
            | 74 |         session_write_close();
 | 40 |     // Para entornos de producción, considera restringir Access-Control-Allow-Origin
 | 
          
            | 75 |     }
 | 41 |     // a dominios específicos si esta vista será accedida vía AJAX desde otros orígenes.
 | 
          
            | 76 |     header('Content-Type: application/json');
 | 42 |     // Si es una redirección directa, CORS es irrelevante para una respuesta JSON.
 | 
          
            | Línea -... | Línea 43... | 
          
            | - |   | 43 |     header('Access-Control-Allow-Origin: *');
 | 
          
            | - |   | 44 |     echo json_encode(['success' => false, 'data' => $errorCode]);
 | 
          
            | - |   | 45 |     exit;
 | 
          
            | - |   | 46 | }
 | 
          
            | - |   | 47 |  
 | 
          
            | - |   | 48 | // Obtención y sanitización de parámetros de la petición
 | 
          
            | - |   | 49 | $username   = trim(isset($_REQUEST['username']) ? filter_var($_REQUEST['username'], FILTER_SANITIZE_STRING) : '');
 | 
          
            | - |   | 50 | $password   = trim(isset($_REQUEST['password']) ? filter_var($_REQUEST['password'], FILTER_SANITIZE_STRING) : '');
 | 
          
            | - |   | 51 | $timestamp  = trim(isset($_REQUEST['timestamp']) ? filter_var($_REQUEST['timestamp'], FILTER_SANITIZE_STRING) : '');
 | 
          
            | - |   | 52 | // Usamos FILTER_VALIDATE_INT para asegurar que sea un entero y no un string numérico grande.
 | 
          
            | 77 |     header('Access-Control-Allow-Origin: *'); // Assuming CORS is needed for errors too
 | 53 | $rand       = intval(isset($_REQUEST['rand']) ? filter_var($_REQUEST['rand'], FILTER_VALIDATE_INT) : 0, 10);
 | 
          
            | 78 |     echo json_encode(['success' => false, 'data' => $errorCode]);
 | 54 | $data       = trim(isset($_REQUEST['data']) ? filter_var($_REQUEST['data'], FILTER_SANITIZE_STRING) : '');
 | 
          
            | 79 |     exit;
 | 55 |  
 | 
          
            | 80 | }
 | 56 | // --- Validaciones de Seguridad ---
 | 
          
            | Línea 81... | Línea 57... | 
          
            | 81 |  
 | 57 |  
 | 
          
            | 82 | // Validación de parámetros de seguridad
 | 58 | // Validación de parámetros de seguridad mínimos
 | 
          
            | 83 | if (empty($username) || empty($password) || empty($timestamp) || empty($rand) || !is_integer($rand)) {
 | 59 | if (empty($username) || empty($password) || empty($timestamp) || empty($rand) || !is_integer($rand)) {
 | 
          
            | 84 |     output_json_error('ERROR_SECURITY1');
 | 60 |     output_json_error('ERROR_SECURITY1');
 | 
          
            | Línea 85... | Línea 61... | 
          
            | 85 | }
 | 61 | }
 | 
          
            | 86 |  
 | 62 |  
 | 
          
            | 87 | // Validación del nombre de usuario
 | 63 | // Validación del nombre de usuario de la API
 | 
          
            | 88 | if ($username != LLWS_USERNAME) {
 | 64 | if ($username !== LLWS_USERNAME) { // Usamos !== para comparación estricta
 | 
          
            | 89 |     output_json_error('ERROR_SECURITY2');
 | 65 |     output_json_error('ERROR_SECURITY2');
 | 
          
            | Línea 90... | Línea 66... | 
          
            | 90 | }
 | 66 | }
 | 
          
            | 91 |  
 | 67 |  
 | 
          
            | 92 | // Validación del formato del timestamp
 | 68 | // Validación del formato del timestamp
 | 
          
            | 93 | $dt = \DateTime::createFromFormat('Y-m-d\TH:i:s', $timestamp);
 | 69 | $dt = \DateTime::createFromFormat('Y-m-d\TH:i:s', $timestamp);
 | 
          
            | Línea 94... | Línea 70... | 
          
            | 94 | if (!$dt) {
 | 70 | if (!$dt) {
 | 
          
            | 95 |     output_json_error('ERROR_SECURITY3');
 | - |   | 
          
            | 96 | }
 | 71 |     output_json_error('ERROR_SECURITY3');
 | 
          
            | 97 |  
 | - |   | 
          
            | 98 | // Validación del rango de tiempo del timestamp (±5 minutos)
 | 72 | }
 | 
          
            | Línea 99... | Línea 73... | 
          
            | 99 | $t0 = $dt->getTimestamp();
 | 73 |  
 | 
          
            | 100 | $t1 = strtotime('-5 minutes');
 | 74 | // Validación del rango de tiempo del timestamp (±5 minutos) para prevenir Replay Attacks
 | 
          
            | - |   | 75 | $t0 = $dt->getTimestamp();
 | 
          
            | 101 | $t2 = strtotime('+5 minutes');
 | 76 | $t1 = strtotime('-5 minutes');
 | 
          
            | 102 |  
 | 77 | $t2 = strtotime('+5 minutes');
 | 
          
            | Línea -... | Línea 78... | 
          
            | - |   | 78 |  
 | 
          
            | - |   | 79 | if ($t0 < $t1 || $t0 > $t2) {
 | 
          
            | 103 | if ($t0 < $t1 || $t0 > $t2) {
 | 80 |     output_json_error('ERROR_SECURITY4'); // Habilitado para prevenir Replay Attacks
 | 
          
            | 104 |     //echo json_encode(['success' => false, 'data' => 'ERROR_SECURITY4']) ;
 | 81 | }
 | 
          
            | 105 |     //output_json_error('ERROR_SECURITY4'); // Kept commented as in original for this line
 | 82 |  
 | 
          
            | 106 |     //exit;
 | 83 | // Validación de la contraseña (hash de la contraseña de la API + rand + timestamp)
 | 
          
            | Línea 107... | Línea 84... | 
          
            | 107 | }
 | 84 | $expectedPasswordString = $username . '-' . LLWS_PASSWORD . '-' . $rand . '-' . $timestamp;
 | 
          
            | 108 |  
 | 85 | if (!password_verify($expectedPasswordString, $password)) {
 | 
          
            | 109 | // Validación de la contraseña
 | 86 |     output_json_error('ERROR_SECURITY5');
 | 
          
            | 110 | if (!password_verify($username . '-' . LLWS_PASSWORD . '-' . $rand . '-' . $timestamp, $password)) {
 | 87 | }
 | 
          
            | 111 |     output_json_error('ERROR_SECURITY5');
 | 88 |  
 | 
          
            | Línea 112... | Línea 89... | 
          
            | 112 | }
 | 89 | // --- Validación y Desencriptación de Datos de Usuario ---
 | 
          
            | 113 |  
 | 90 |  
 | 
          
            | 114 | // Validación de datos
 | 91 | // Validación de datos
 | 
          
            | 115 | if (empty($data)) {
 | 92 | if (empty($data)) {
 | 
          
            | 116 |     output_json_error('ERROR_PARAMETERS1');
 | 93 |     output_json_error('ERROR_PARAMETERS1');
 | 
          
            | 117 | }
 | 94 | }
 | 
          
            | - |   | 95 |  
 | 
          
            | - |   | 96 | // Decodificación de datos en base64
 | 
          
            | 118 |  
 | 97 | $data = base64_decode($data);
 | 
          
            | 119 | // Decodificación de datos en base64
 | 98 | if ($data === false || empty($data)) { // base64_decode puede retornar false
 | 
          
            | Línea 120... | Línea 99... | 
          
            | 120 | $data = base64_decode($data);
 | 99 |     output_json_error('ERROR_PARAMETERS2');
 | 
          
            | 121 | if (empty($data)) {
 | 100 | }
 | 
          
            | 122 |     output_json_error('ERROR_PARAMETERS2');
 | 101 |  
 | 
          
            | 123 | }
 | 102 | // Desencriptación de datos usando RSA
 | 
          
            | 124 |  
 | 103 | try {
 | 
          
            | Línea 125... | Línea 104... | 
          
            | 125 | // Desencriptación de datos usando RSA
 | 104 |     $rsa = new rsa(); // Asegúrate de que la clase 'rsa' esté correctamente cargada.
 | 
          
            | 126 | try {
 | 105 |     $rsa->setKeys(LLWS_RSA_N, LLWS_RSA_D, LLWS_RSA_E);
 | 
          
            | 127 |     $rsa = new rsa();
 | 106 |     $data = $rsa->decrypt($data);
 | 
          
            | 128 |     $rsa->setKeys(LLWS_RSA_N, LLWS_RSA_D, LLWS_RSA_E);
 | 107 | } catch (Throwable $e) {
 | 
          
            | Línea -... | Línea 108... | 
          
            | - |   | 108 |     // Registra el error para depuración, pero no lo expongas al cliente.
 | 
          
            | 129 |     $data = $rsa->decrypt($data);
 | 109 |     error_log("RSA Decryption Error: " . $e->getMessage());
 | 
          
            | 130 | } catch (Throwable $e) {
 | 110 |     output_json_error('ERROR_PARAMETERS3');
 | 
          
            | 131 |     output_json_error('ERROR_PARAMETERS3');
 | 111 | }
 | 
          
            | Línea 132... | Línea 112... | 
          
            | 132 | }
 | 112 |  
 | 
          
            | - |   | 113 | // Conversión de datos a array
 | 
          
            | 133 |  
 | 114 | $data = (array) json_decode($data, true); // Añadido 'true' para asegurar un array asociativo
 | 
          
            | - |   | 115 | if (empty($data)) {
 | 
          
            | - |   | 116 |     output_json_error('ERROR_PARAMETERS4');
 | 
          
            | 134 | // Conversión de datos a array
 | 117 | }
 | 
          
            | 135 | $data = (array) json_decode($data);
 | 118 |  
 | 
          
            | 136 | if (empty($data)) {
 | 119 | // Extracción y validación de datos del usuario
 | 
          
            | - |   | 120 | $email      = trim(isset($data['email']) ? filter_var($data['email'], FILTER_SANITIZE_EMAIL) : '');
 | 
          
            | 137 |     output_json_error('ERROR_PARAMETERS4');
 | 121 | $first_name = trim(isset($data['first_name']) ? filter_var($data['first_name'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) : '');
 | 
          
            | 138 | }
 | 122 | $last_name  = trim(isset($data['last_name']) ? filter_var($data['last_name'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) : '');
 | 
          
            | 139 |  
 | 123 |  
 | 
          
            | Línea 140... | Línea 124... | 
          
            | 140 | // Extracción y validación de datos del usuario
 | 124 | // Asegúrate de que los datos esenciales del usuario estén presentes y sean válidos.
 | 
          
            | 141 | $email      = trim(isset($data['email']) ? filter_var($data['email'], FILTER_SANITIZE_EMAIL) : '');
 | 125 | if (!filter_var($email, FILTER_VALIDATE_EMAIL) || empty($first_name) || empty($last_name)) {
 | 
          
            | 142 | $first_name = trim(isset($data['first_name']) ? filter_var($data['first_name'], FILTER_SANITIZE_STRING) : '');
 | 126 |     output_json_error('ERROR_PARAMETERS5');
 | 
          
            | 143 | $last_name  = trim(isset($data['last_name']) ? filter_var($data['last_name'], FILTER_SANITIZE_STRING) : '');
 | 127 | }
 | 
          
            | Línea 144... | Línea 128... | 
          
            | 144 |  
 | 128 |  
 | 
          
            | - |   | 129 | // --- Búsqueda o Creación de Usuario en Moodle ---
 | 
          
            | 145 | if (!filter_var($email, FILTER_VALIDATE_EMAIL) || empty($first_name) || empty($last_name)) {
 | 130 |  
 | 
          
            | 146 |     output_json_error('ERROR_PARAMETERS5');
 | 131 | $user = ll_get_user_by_email($email); // Asume que esta función busca al usuario por email
 | 
          
            | - |   | 132 | $new_user = false;
 | 
          
            | - |   | 133 |  
 | 
          
            | 147 | }
 | 134 | if ($user) {
 | 
          
            | - |   | 135 |     // Usuario encontrado
 | 
          
            | 148 |  
 | 136 | } else {
 | 
          
            | 149 | // Búsqueda o creación de usuario
 | 137 |     // Usuario no encontrado, proceder a la creación
 | 
          
            | 150 | $user = ll_get_user_by_email($email);
 | 138 |     $new_user = true;
 | 
          
            | 151 | if ($user) {
 | 139 |     $username_moodle = ll_get_username_available($first_name, $last_name); // Genera un nombre de usuario disponible para Moodle
 | 
          
            | - |   | 140 |     $user = ll_create_user($username_moodle, $email, $first_name, $last_name); // Crea el usuario
 | 
          
            | 152 |     $new_user = false;
 | 141 |  
 | 
          
            | 153 | } else {
 | 142 |     // Procesamiento de imagen de perfil si se proporciona y el usuario fue creado
 | 
          
            | 154 |     $new_user = true;
 | 143 |     if ($user) {
 | 
          
            | - |   | 144 |         $filename   = trim(isset($data['image_filename']) ? filter_var($data['image_filename'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) : '');
 | 
          
            | - |   | 145 |         $content    = trim(isset($data['image_content']) ? filter_var($data['image_content'], FILTER_SANITIZE_STRING) : '');
 | 
          
            | 155 |     $username = ll_get_username_available($first_name, $last_name);
 | 146 |  
 | 
          
            | - |   | 147 |         // Validación básica de nombre de archivo para evitar path traversal
 | 
          
            | 156 |     $user = ll_create_user($username, $email, $first_name, $last_name);
 | 148 |         if ($filename && strpos($filename, '..') === false && strpos($filename, DIRECTORY_SEPARATOR) === false) {
 | 
          
            | 157 |  
 | 149 |             $tempfile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $filename; // Usar directorio temporal del sistema
 | 
          
            | 158 |     // Procesamiento de imagen de perfil si se proporciona
 | 150 |             try {
 | 
          
            | 159 |     if ($user) {
 | 151 |                 $decoded_content = base64_decode($content);
 | 
          
            | 160 |         $filename   = trim(isset($data['image_filename']) ? filter_var($data['image_filename'], FILTER_SANITIZE_EMAIL) : '');
 | 152 |                 if ($decoded_content !== false && !empty($decoded_content)) {
 | 
          
            | 161 |         $content    = trim(isset($data['image_content']) ? filter_var($data['image_content'], FILTER_SANITIZE_STRING) : '');
 | 153 |                     file_put_contents($tempfile, $decoded_content);
 | 
          
            | 162 |  
 | 154 |  
 | 
          
            | Línea 163... | Línea 155... | 
          
            | 163 |         if ($filename && $content) {
 | 155 |                     if (file_exists($tempfile)) {
 | 
          
            | 164 |             $tempfile = __DIR__ . DIRECTORY_SEPARATOR . $filename;
 | 156 |                         $usericonid = process_new_icon(context_user::instance($user->id, MUST_EXIST), 'user', 'icon', 0, $tempfile);
 | 
          
            | 165 |             try {
 | 157 |                         if ($usericonid) {
 | 
          
            | 166 |                 file_put_contents($filename, base64_decode($content));
 | 158 |                             $DB->set_field('user', 'picture', $usericonid, array('id' => $user->id));
 | 
          
            | Línea 167... | Línea 159... | 
          
            | 167 |                 if (file_exists($tempfile)) {
 | 159 |                         }
 | 
          
            | - |   | 160 |                     }
 | 
          
            | 168 |                     $usericonid = process_new_icon(context_user::instance($user->id, MUST_EXIST), 'user', 'icon', 0, $tempfile);
 | 161 |                 }
 | 
          
            | 169 |                     if ($usericonid) {
 | 162 |             } catch (\Throwable $e) {
 | 
          
            | 170 |                         $DB->set_field('user', 'picture', $usericonid, array('id' => $user->id));
 | 163 |                 // IMPORTANT: Registra el error de la imagen, pero no abortes el login del usuario si la imagen falla.
 | 
          
            | Línea 171... | Línea 164... | 
          
            | 171 |                     }
 | 164 |                 error_log("Error processing user image for user ID {$user->id}: " . $e->getMessage());
 | 
          
            | 172 |                 }
 | 165 |             } finally {
 | 
          
            | - |   | 166 |                 // Asegúrate de eliminar el archivo temporal
 | 
          
            | 173 |             } catch (\Throwable $e) {
 | 167 |                 if (file_exists($tempfile)) {
 | 
          
            | 174 |             } finally {
 | 168 |                     unlink($tempfile);
 | 
          
            | 175 |                 if (file_exists($tempfile)) {
 | 169 |                 }
 | 
          
            | 176 |                     unlink($tempfile);
 | 170 |             }
 | 
          
            | 177 |                 }
 | 171 |         }
 | 
          
            | 178 |             }
 | - |   | 
          
            | 179 |         }
 | - |   | 
          
            | 180 |     }
 | 172 |     }
 | 
          
            | 181 | }
 | - |   | 
          
            | 182 |  
 | 173 | }
 | 
          
            | 183 | // Verificación de creación de usuario
 | 174 |  
 | 
          
            | 184 | if (!$user) {
 | 175 | // Verificación de creación/recuperación de usuario
 | 
          
            | 185 |     output_json_error('ERROR_MOODLE1');
 | - |   | 
          
            | 186 | }
 | - |   | 
          
            | 187 |  
 | 176 | if (!$user) {
 | 
          
            | - |   | 177 |     output_json_error('ERROR_MOODLE1');
 | 
          
            | 188 | // Inscripción en cursos para usuarios nuevos
 | 178 | }
 | 
          
            | 189 | if ($new_user) {
 | 179 |  
 | 
          
            | 190 |     $role = $DB->get_record('role', array('archetype' => 'student'));
 | 180 | // --- Inscripción en Cursos para Usuarios Nuevos ---
 | 
          
            | - |   | 181 |  
 | 
          
            | - |   | 182 | if ($new_user) {
 | 
          
            | 191 |     $enrolmethod = 'manual';
 | 183 |     $role = $DB->get_record('role', array('archetype' => 'student'));
 | 
          
            | 192 |  
 | - |   | 
          
            | 193 |     $courses = get_courses();
 | 184 |     $enrolmethod = 'manual';
 | 
          
            | - |   | 185 |  
 | 
          
            | - |   | 186 |     $courses = get_courses(); // Obtiene todos los cursos
 | 
          
            | 194 |     foreach ($courses as $course) {
 | 187 |     foreach ($courses as $course) {
 | 
          
            | 195 |         if ($course->categoy_id == LLWS_CATEGORY_ID) {
 | 188 |         // CORRECCIÓN: Error tipográfico 'categoy_id' a 'category_id'
 | 
          
            | 196 |             $context = context_course::instance($course->id);
 | 189 |         if ($course->category_id == LLWS_CATEGORY_ID) {
 | 
          
            | - |   | 190 |             $context = context_course::instance($course->id);
 | 
          
            | 197 |             if (!is_enrolled($context, $user)) {
 | 191 |             if (!is_enrolled($context, $user)) { // Verifica si el usuario ya está inscrito
 | 
          
            | 198 |                 $enrol = enrol_get_plugin($enrolmethod);
 | 192 |                 $enrol = enrol_get_plugin($enrolmethod);
 | 
          
            | 199 |                 if ($enrol === null) {
 | 193 |                 if ($enrol === null) {
 | 
          
            | 200 |                     return false;
 | 194 |                     // Si el método de matriculación manual no existe, loguea un error y continúa.
 | 
          
            | 201 |                 }
 | 195 |                     // No debería abortar el login del usuario por esto.
 | 
          
            | Línea 202... | Línea 196... | 
          
            | 202 |                 $instances = enrol_get_instances($course->id, true);
 | 196 |                     error_log("Enrolment method '{$enrolmethod}' not found for course ID {$course->id}.");
 | 
          
            | - |   | 197 |                     continue; // Pasa al siguiente curso
 | 
          
            | - |   | 198 |                 }
 | 
          
            | 203 |                 $manualinstance = null;
 | 199 |  
 | 
          
            | - |   | 200 |                 // Obtener o crear una instancia de matriculación manual para el curso
 | 
          
            | 204 |                 foreach ($instances as $instance) {
 | 201 |                 $instance = $enrol->add_default_instance($course); // Esto intenta encontrar o crear una instancia por defecto
 | 
          
            | 205 |                     if ($instance->name == $enrolmethod) {
 | 202 |                 if ($instance === null) {
 | 
          
            | 206 |                         $manualinstance = $instance;
 | 203 |                     // Si add_default_instance falla (ej. no hay permisos), intenta crear una nueva instancia.
 | 
          
            | 207 |                         break;
 | 204 |                     // Esto es menos común si add_default_instance no funciona, pero es una alternativa.
 | 
          
            | 208 |                     }
 | 205 |                     $instanceid = $enrol->add_instance($course);
 | 
          
            | Línea 237... | Línea 234... | 
          
            | 237 |         if (intval($days2expire) < 0) {
 | 234 |         if (intval($days2expire) < 0) {
 | 
          
            | 238 |             output_json_error('PASSWORD_EXPIRED');
 | 235 |             output_json_error('PASSWORD_EXPIRED');
 | 
          
            | 239 |         }
 | 236 |         }
 | 
          
            | 240 |     }
 | 237 |     }
 | 
          
            | Línea -... | Línea 238... | 
          
            | - |   | 238 |  
 | 
          
            | - |   | 239 |     // CORRECCIÓN: Para un flujo de login web, Moodle maneja la sesión existente.
 | 
          
            | - |   | 240 |     // complete_user_login() es suficiente para establecer la sesión del usuario.
 | 
          
            | 241 |  
 | 241 |     // Eliminar el cierre agresivo de todas las sesiones a menos que sea un requisito muy específico.
 | 
          
            | - |   | 242 |     // Si la sesión actual ya está autenticada, complete_user_login() la actualizará.
 | 
          
            | 242 |     // Si hay una sesión existente, cerrarla primero
 | 243 |     // Si necesitas forzar un re-login de la sesión actual, puedes hacerlo de forma más granular:
 | 
          
            | 243 |     if (isloggedin()) {
 | 244 |     /*
 | 
          
            | 244 |         \core\session\manager::kill_all_sessions();
 | 245 |     if (isloggedin() && get_userid() !== $user->id) { // Si un usuario diferente ya está logueado en esta sesión
 | 
          
            | 245 |         \core\session\manager::terminate_current();
 | 246 |         \core\session\manager::terminate_current();
 | 
          
            | 246 |         session_destroy();
 | - |   | 
          
            | 247 |         // Delete MoodleSession cookie
 | 247 |         session_destroy();
 | 
          
            | 248 |         setcookie('MoodleSession', '', time() - 3600, '/');
 | 248 |         setcookie('MoodleSession', '', time() - 3600, '/'); // Eliminar la cookie de la sesión anterior
 | 
          
            | - |   | 249 |     }
 | 
          
            | Línea 249... | Línea 250... | 
          
            | 249 |     }
 | 250 |     */
 | 
          
            | - |   | 251 |  
 | 
          
            | 250 |  
 | 252 |     // Completar el proceso de inicio de sesión de Moodle.
 | 
          
            | Línea 251... | Línea 253... | 
          
            | 251 |     // Completar el proceso de inicio de sesión
 | 253 |     // Esto establecerá la sesión del usuario $user.
 | 
          
            | 252 |     complete_user_login($user);
 | 254 |     complete_user_login($user);
 | 
          
            | Línea 253... | Línea 255... | 
          
            | 253 |  
 | 255 |  
 | 
          
            | 254 |     // Aplicar límite de inicio de sesión concurrente
 | 256 |     // Aplicar límite de inicio de sesión concurrente (descomentado si es necesario)
 | 
          
            | 255 |     //\core\session\manager::apply_concurrent_login_limit($user->id, session_id());
 | 257 |     // \core\session\manager::apply_concurrent_login_limit($user->id, session_id());
 | 
          
            | 256 |  
 | 258 |  
 | 
          
            | 257 |     // Configurar cookie de nombre de usuario
 | 259 |     // Configurar cookie de nombre de usuario (Moodle lo maneja internamente)
 | 
          
            | 258 |     if (!empty($CFG->nolastloggedin)) {
 | 260 |     if (!empty($CFG->nolastloggedin)) {
 | 
          
            | 259 |         // No almacenar último usuario conectado en cookie
 | 261 |         // No almacenar último usuario conectado en cookie
 | 
          
            | 260 |     } else if (empty($CFG->rememberusername)) {
 | 262 |     } else if (empty($CFG->rememberusername)) {
 | 
          
            | 261 |         // Sin cookies permanentes, eliminar la anterior si existe
 | 263 |         // Sin cookies permanentes, eliminar la anterior si existe
 | 
          
            | Línea 262... | Línea 264... | 
          
            | 262 |         set_moodle_cookie('');
 | 264 |         set_moodle_cookie('');
 | 
          
            | 263 |     } else {
 | 265 |     } else {
 | 
          
            | 264 |         set_moodle_cookie($user->username);
 | 266 |         set_moodle_cookie($user->username);
 | 
          
            | 265 |     }
 | - |   | 
          
            | 266 |  
 | 267 |     }
 | 
          
            | 267 |     // Limpiar mensajes de error antes de la última redirección
 | - |   | 
          
            | Línea 268... | Línea 268... | 
          
            | 268 |     unset($SESSION->loginerrormsg);
 | 268 |  
 | 
          
            | 269 |     unset($SESSION->logininfomsg);
 | 269 |     // Limpiar mensajes de error y redirección de sesión antes de la redirección final
 | 
          
            | 270 |  
 | - |   | 
          
            | Línea 271... | Línea 270... | 
          
            | 271 |     // Descartar loginredirect si estamos redirigiendo
 | 270 |     unset($SESSION->loginerrormsg);
 | 
          
            | 272 |     unset($SESSION->loginredirect);
 | 271 |     unset($SESSION->logininfomsg);
 | 
          
            | 273 |  
 | 272 |     unset($SESSION->loginredirect); // Descartar loginredirect si estamos redirigiendo
 | 
          
            | 274 |     // Configurar la URL de destino
 | - |   | 
          
            | 275 |     $urltogo = $CFG->wwwroot . '/my';
 | - |   | 
          
            | 276 |     $SESSION->wantsurl = $urltogo;
 | - |   | 
          
            | 277 |  
 | - |   | 
          
            | 278 |     // Verificar que la sesión se haya iniciado correctamente
 | - |   | 
          
            | 279 |     if (isloggedin() && !isguestuser()) {
 | - |   | 
          
            | 280 |         // Enviar respuesta exitosa con datos del usuario
 | - |   | 
          
            | 281 |         // Instead of echoing JSON here and then redirecting (which is problematic),
 | - |   | 
          
            | 282 |         // we will just redirect. The browser will follow the redirect, and the
 | - |   | 
          
            | 283 |         // new session (with its cookie) will be active for the target page.
 | - |   | 
          
            | 284 |         /*
 | - |   | 
          
            | 285 |         echo json_encode([
 | - |   | 
          
            | 286 |             'success' => true,
 | - |   | 
          
            | 287 |             'data' => [
 | - |   | 
          
            | 288 |                 'userid' => $user->id,
 | - |   | 
          
            | 289 |                 'username' => $user->username,
 | - |   | 
          
            | 290 |                 'fullname' => fullname($user),
 | - |   | 
          
            | 291 |                 'email' => $user->email,
 | - |   | 
          
            | 292 |                 'redirect' => $urltogo
 | 273 |  
 | 
          
            | 293 |             ]
 | 274 |     // Configurar la URL de destino
 | 
          
            | - |   | 275 |     $urltogo = $CFG->wwwroot . '/my'; // Página "Mi Moodle"
 | 
          
            | 294 |         ]);
 | 276 |  
 | 
          
            | 295 |         */
 | 277 |     // Verificar que la sesión se haya iniciado correctamente antes de redirigir
 | 
          
            | 296 |  
 | 278 |     if (isloggedin() && !isguestuser()) {
 | 
          
            | - |   | 279 |         // Redirección HTTP para una vista web exitosa
 | 
          
            | - |   | 280 |         redirect($urltogo);
 | 
          
            | 297 |         // Redirigir al usuario
 | 281 |     } else {
 | 
          
            | 298 |         // redirect() will handle session_write_close(), send Location header, and exit.
 | 282 |         // Falló la autenticación final en Moodle, a pesar de las validaciones previas.
 | 
          
            | - |   | 283 |         output_json_error('LOGIN_FAILED_MOODLE');
 | 
          
            | 299 |         redirect($urltogo);
 | 284 |     }
 | 
          
            | 300 |     } else {
 | 285 | } else {
 | 
          
            | - |   | 286 |     // Esto debería ser capturado por output_json_error('ERROR_MOODLE1') anteriormente,
 |