Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
701bc3bb8b | ||
|
|
bc63ebdb15 | ||
|
|
3701072bb3 | ||
|
|
8d92d08a56 | ||
|
|
12dfdd5fed | ||
|
|
a9ecad4ee1 | ||
|
|
0bbf053703 | ||
|
|
b1c9ffa461 |
@@ -43,6 +43,49 @@ function connectToMySQL($returnErrors = false) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Motor de almacenamiento para las tablas que crea el CMS.
|
||||||
|
*
|
||||||
|
* InnoDB da recuperacion ante caida (MyISAM se corrompe con un apagon o un OOM
|
||||||
|
* y hay que repararlo a mano) y bloqueo por fila. Pero en servidores antiguos
|
||||||
|
* no es seguro: MariaDB 5.5 (hay un servidor de produccion con 5.5.68) no
|
||||||
|
* admite FULLTEXT en InnoDB, limita los indices a 767 bytes y usa filas
|
||||||
|
* COMPACT. Por eso el motor se decide por la version del servidor, igual que
|
||||||
|
* mysqlStrictMode decide el modo SQL:
|
||||||
|
*
|
||||||
|
* MariaDB >= 10.2 o MySQL >= 5.7 -> InnoDB (Barracuda/DYNAMIC por defecto)
|
||||||
|
* cualquier otro o desconocido -> MyISAM (lo de siempre)
|
||||||
|
*
|
||||||
|
* No cambia que INSERT se aceptan: el CMS conecta en modo estricto
|
||||||
|
* (mysqlStrictMode / TRADITIONAL en Db.php) y en un INSERT de una fila eso
|
||||||
|
* rechaza lo mismo con los dos motores. Ver docs/motor-innodb.md (repo Forge).
|
||||||
|
*/
|
||||||
|
function acaiTableEngine() {
|
||||||
|
static $engine = null;
|
||||||
|
if ($engine === null) {
|
||||||
|
$engine = acaiEngineForServerVersion(mysql_get_server_info());
|
||||||
|
}
|
||||||
|
return $engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decision pura, separada para poder probarla sin servidor. */
|
||||||
|
function acaiEngineForServerVersion($serverInfo) {
|
||||||
|
$info = trim((string) $serverInfo);
|
||||||
|
// Clientes antiguos ven MariaDB 10+ como "5.5.5-10.6.25-MariaDB": el
|
||||||
|
// "5.5.5-" es un disfraz de compatibilidad, la version real va detras.
|
||||||
|
if (preg_match('/^5\.5\.5-(\d+\.\d+(?:\.\d+)?)/', $info, $m)) {
|
||||||
|
$version = $m[1];
|
||||||
|
$isMariaDb = true;
|
||||||
|
} elseif (preg_match('/^(\d+\.\d+(?:\.\d+)?)/', $info, $m)) {
|
||||||
|
$version = $m[1];
|
||||||
|
$isMariaDb = stripos($info, 'mariadb') !== false;
|
||||||
|
} else {
|
||||||
|
return 'MyISAM';
|
||||||
|
}
|
||||||
|
$minimo = $isMariaDb ? '10.2' : '5.7';
|
||||||
|
return version_compare($version, $minimo, '>=') ? 'InnoDB' : 'MyISAM';
|
||||||
|
}
|
||||||
|
|
||||||
function mysqlStrictMode($strictMode) {
|
function mysqlStrictMode($strictMode) {
|
||||||
$currentVersion = preg_replace("/[^0-9\.]/", "", mysql_get_server_info());
|
$currentVersion = preg_replace("/[^0-9\.]/", "", mysql_get_server_info());
|
||||||
$isMySql5 = version_compare($currentVersion, '5.0.0', '>=');
|
$isMySql5 = version_compare($currentVersion, '5.0.0', '>=');
|
||||||
@@ -74,7 +117,9 @@ function createMissingSchemaTablesAndFields() {
|
|||||||
$mysqlTableName = $TABLE_PREFIX . $tableName;
|
$mysqlTableName = $TABLE_PREFIX . $tableName;
|
||||||
if (!in_array($mysqlTableName, $mysqlTables)) {
|
if (!in_array($mysqlTableName, $mysqlTables)) {
|
||||||
notice("Creating MySQL table for schema table: $tableName<br/>\n");
|
notice("Creating MySQL table for schema table: $tableName<br/>\n");
|
||||||
$result = mysql_query("CREATE TABLE `".mysql_real_escape_string($mysqlTableName)."` (num int(10) unsigned NOT NULL auto_increment, PRIMARY KEY (num)) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;");
|
// Motor segun el servidor (ver acaiTableEngine): InnoDB donde lo
|
||||||
|
// soporta bien, MyISAM en los antiguos.
|
||||||
|
$result = mysql_query("CREATE TABLE `".mysql_real_escape_string($mysqlTableName)."` (num int(10) unsigned NOT NULL auto_increment, PRIMARY KEY (num)) ENGINE=".acaiTableEngine()." DEFAULT CHARSET=utf8mb4;");
|
||||||
if (!$result) { alert("Error creating MySQL table: $mysqlTableName<br/>\MySQL error was: ". htmlspecialchars(mysql_error()) . "\n"); }
|
if (!$result) { alert("Error creating MySQL table: $mysqlTableName<br/>\MySQL error was: ". htmlspecialchars(mysql_error()) . "\n"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -324,17 +324,24 @@ function _removeFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _deleteAll($str) {
|
function _deleteAll($str) {
|
||||||
if (!file_exists($str)) return false;
|
if (!file_exists($str) && !is_link($str)) return false;
|
||||||
if (is_file($str)) {
|
// Un symlink se quita, NUNCA se sigue: borrar su contenido seria borrar algo
|
||||||
|
// que vive fuera de la carpeta que se pidio borrar.
|
||||||
|
if (is_link($str) || is_file($str)) {
|
||||||
return unlink($str);
|
return unlink($str);
|
||||||
}
|
}
|
||||||
elseif (is_dir($str)) {
|
elseif (is_dir($str)) {
|
||||||
//Get a list of the files in this directory.
|
// `scandir` y no `glob`: glob NO devuelve los ficheros ocultos, asi que
|
||||||
$scan = glob(rtrim($str,'/').'/*');
|
// un `.htaccess` sobrevivia al borrado, la carpeta no quedaba vacia y el
|
||||||
//Loop through the list of files.
|
// rmdir de abajo fallaba en silencio. Con eso, desinstalar en produccion
|
||||||
foreach($scan as $index=>$path) {
|
// un plugin con .htaccess —5 de 14 en una web real— borraba su codigo,
|
||||||
//Call our recursive function.
|
// devolvia "no se ha podido desinstalar" y dejaba la operacion a medias:
|
||||||
_deleteAll($path);
|
// sin ficheros, pero con la carpeta y con el alta en el central.
|
||||||
|
$entradas = @scandir($str);
|
||||||
|
if ($entradas === false) return false;
|
||||||
|
foreach ($entradas as $nombre) {
|
||||||
|
if ($nombre === '.' || $nombre === '..') continue;
|
||||||
|
_deleteAll(rtrim($str, '/') . '/' . $nombre);
|
||||||
}
|
}
|
||||||
//Remove the directory itself.
|
//Remove the directory itself.
|
||||||
return @rmdir($str);
|
return @rmdir($str);
|
||||||
@@ -392,13 +399,45 @@ function _renameFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Segunda via de autenticacion del ciclo de vida, para las copias LOCALES de
|
||||||
|
* Forge (standalone o de test). El WS central solo conoce webs de produccion:
|
||||||
|
* el token de una web sin dominio real nunca le valida, asi que los ganchos
|
||||||
|
* (install/uninstall/enable/disable) quedaban inservibles ahi con un
|
||||||
|
* "Token no valido" que no tenia nada que ver con el token.
|
||||||
|
*
|
||||||
|
* No es un bypass: se compara el par token+tokenHash recibido con el que la
|
||||||
|
* propia web guarda en su .acai, que es exactamente el que Forge envia. Sin
|
||||||
|
* .acai (produccion), con mode=production, o sin coincidencia exacta, no
|
||||||
|
* autoriza nada.
|
||||||
|
*/
|
||||||
|
function _lifecycleTokenLocalValido($datosAuth) {
|
||||||
|
$acaiPath = __DIR__ . "/../../../.acai";
|
||||||
|
if (!is_file($acaiPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$acai = @json_decode(@file_get_contents($acaiPath), true);
|
||||||
|
if (!is_array($acai) || @$acai["mode"] === "production") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$token = (string) @$datosAuth["token"];
|
||||||
|
$hash = (string) @$datosAuth["tokenHash"];
|
||||||
|
$tokenLocal = (string) @$acai["token"];
|
||||||
|
$hashLocal = (string) @$acai["tokenHash"];
|
||||||
|
if ($token === "" || $hash === "" || $tokenLocal === "" || $hashLocal === "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return hash_equals($tokenLocal, $token) && hash_equals($hashLocal, $hash);
|
||||||
|
}
|
||||||
|
|
||||||
function _pluginLifecycle() {
|
function _pluginLifecycle() {
|
||||||
// Fase 0 (2026-09-12): ejecuta los ganchos de ciclo de vida de un plugin
|
// Fase 0 (2026-09-12): ejecuta los ganchos de ciclo de vida de un plugin
|
||||||
// (install/uninstall/enable/disable/status) DENTRO de la web. Unico punto
|
// (install/uninstall/enable/disable/status) DENTRO de la web. Unico punto
|
||||||
// de entrada para Forge y para el CMS legacy. Misma autenticacion que coreUpdate.
|
// de entrada para Forge y para el CMS legacy. Misma autenticacion que coreUpdate.
|
||||||
$datosAuth = json_decode(file_get_contents('php://input'), true);
|
$datosAuth = json_decode(file_get_contents('php://input'), true);
|
||||||
CocoWS::setToken(@$datosAuth["token"]);
|
CocoWS::setToken(@$datosAuth["token"]);
|
||||||
if (!CocoWS::validateUploadToken(@$datosAuth["tokenHash"])) {
|
if (!CocoWS::validateUploadToken(@$datosAuth["tokenHash"])
|
||||||
|
&& !_lifecycleTokenLocalValido($datosAuth)) {
|
||||||
die(json_encode(['error' => ['message' => 'Token no válido', 'code' => 403]]));
|
die(json_encode(['error' => ['message' => 'Token no válido', 'code' => 403]]));
|
||||||
}
|
}
|
||||||
// Solo body JSON (como _saveFile): el token no debe viajar en la query string.
|
// Solo body JSON (como _saveFile): el token no debe viajar en la query string.
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ function getSchemaPlugins($force = false){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$customSchemaFile = $fullPath."/custom-schema.ini.php";
|
$customSchemaFile = $fullPath."/custom-schema.ini.php";
|
||||||
if (file_exists($customSchemaFile)){
|
// Sin schema.ini.php no hay plugin: un custom-schema huerfano (p.ej. tras
|
||||||
|
// un descarte del sync, que no mueve los custom-schema) se ignora en vez
|
||||||
|
// de avisar en cada peticion.
|
||||||
|
if (isset($schemaPlugins[$dirname]) && file_exists($customSchemaFile)){
|
||||||
$customSchema = loadINI($customSchemaFile);
|
$customSchema = loadINI($customSchemaFile);
|
||||||
$exclude = array("version", "name", "description");
|
$exclude = array("version", "name", "description");
|
||||||
foreach ($exclude as $e):
|
foreach ($exclude as $e):
|
||||||
@@ -207,7 +210,7 @@ function addTablePlugin($path,$file) {
|
|||||||
$result = mysql_query("CREATE TABLE `".mysql_real_escape_string($tableNameWithPrefix)."` (
|
$result = mysql_query("CREATE TABLE `".mysql_real_escape_string($tableNameWithPrefix)."` (
|
||||||
num int(10) unsigned NOT NULL auto_increment,
|
num int(10) unsigned NOT NULL auto_increment,
|
||||||
PRIMARY KEY (num)
|
PRIMARY KEY (num)
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;");
|
) ENGINE=".acaiTableEngine()." DEFAULT CHARSET=utf8;");
|
||||||
if (!$result) {
|
if (!$result) {
|
||||||
print "Error creating MySQL table.\n\nMySQL error was: ". htmlspecialchars(mysql_error()) . "\n";
|
print "Error creating MySQL table.\n\nMySQL error was: ". htmlspecialchars(mysql_error()) . "\n";
|
||||||
exit;
|
exit;
|
||||||
|
|||||||
Reference in New Issue
Block a user