Autor en Google+
Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

16 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
Mostrando entradas con la etiqueta utilidades. Mostrar todas las entradas
Mostrando entradas con la etiqueta utilidades. Mostrar todas las entradas
martes, 12 de enero de 2010
Compartir:
Montar un disco duro virtual .vhd como si fuera un disco físico de nuestra máquina puede resultar muy interesante para acceder a archivos contenidos en el mismo sin necesidad de tener que adjuntarlo a una máquina virtual.

Ya comenté hace unas semanas que en Windows 7 y 2008 esta operación puede hacerse directamente desde la consola de administración de discos del propio sistema operativo, pues es un formato aceptado de forma nativa por éstos. Sin embargo, en equipos corriendo Windows XP ó Vista, el procedimiento se complica un pelín más, puesto que hay que instalar un software que permita al sistema operativo reconocer el archivo .vhd como un disco duro adicional. Veamos cómo conseguirlo.

imageAunque existen otras alternativas, utilizaremos una solución muy sencilla que se distribuye como parte del producto gratuito Virtual Server 2005 R2, que tendremos que descargar desde la web de Microsoft. Obviamente, no instalaremos el producto completo, sólo lo estrictamente necesario para lograr el fin propuesto.

Una vez en nuestro equipo, iniciamos la instalación como habitualmente. Tras aceptar la licencia de uso, sin leerla, faltaría más ;-P, es importante elegir la instalación personalizada e indicar en cada uno de los componentes que no deseamos instalarlo. Bueno, en todos excepto en el llamado “Montaje de VHD”, que es precisamente lo único que nos interesa del producto en este momento.

Una vez completada la instalación, por ejemplo sobre “c:\archivos de programa\microsoft virtual server”, en su interior podremos encontrar una carpeta llamada “Vhdmount” en la que encontraremos la herramienta que andábamos buscando.

La forma de utilizarla, así de sencillo:
vhdmount.exe /p /f "d:\Virtual\JMA.VHD"
A partir de ese momento, el disco duro estará disponible en Mi PC, como si se tratara de un disco físico más. Y cuando queramos desconectarlo,
vhdmount.exe /d "d:\Virtual\JMA.VHD"
Pero si lo que quieres es simplificar aún más esta tarea, David Cervigón aportó hace ya bastante tiempo una solución que añade las órdenes de montado y desmontado al menú contextual del explorador de Windows:
 Montado y desmontado de discos virtuales

Publicado en: Variable not found
Compartir:
domingo, 20 de diciembre de 2009
Compartir:
Por cosas de la procrastinación, tenía una máquina pendiente de formatear desde hace unos años ;-P, y he aprovechado el fin de semana para hacerlo. Como sabréis, esto no es tarea fácil, se requiere mucho pragmatismo, gran concentración y, principalmente, vencer al Diógenes digital que todos llevamos dentro ;-D.

Y claro, una vez que nos ponemos, el problema es cómo conseguir minimizar los daños colaterales. Esta máquina, aunque algo antigua, estaba todavía en uso y tenía gran cantidad de archivos y software instalado en su disco de sistema. También contaba con un disco exclusivamente para datos, pero éste no me suponía ningún problema.

En estos casos normalmente no basta con hacer un salvado del disco duro, a lo bruto, sobre otro disco, formatear y listo; así sólo conseguiremos tener acceso desde la nueva máquina a los ficheros físicos del sistema anterior, pero no podremos realizar tareas de nivel superior, como copiar configuraciones, exportar o importar datos desde aplicaciones, etc. Y lo que es imposible, al menos en mi caso, es planificar este movimiento con tanta exactitud que no se quede ni un byte por detrás.

La conclusión a la que llegué es que la única forma de hacerlo con cierta tranquilidad era virtualizando el sistema anterior. Esto me permitiría acceder en vivo a la configuración anterior y traspasar archivos con la seguridad que necesitaba.

Dis2VhdY aquí es donde ha entrado en juego Disk2Vhd, la magnífica herramienta de Sysinternals (¿he dicho Microsoft? ;-)), que es capaz de generar un disco duro virtual (archivo con extensión .vhd) a partir de un disco duro físico. Y lo mejor de todo, que puede hacerlo sobre el propio equipo que está generando el volcado, es decir, en caliente.

El único requisito es disponer de espacio libre (por ejemplo, como yo lo hice, en un disco duro externo), estar corriendo Windows XP SP2, Windows Server 2003 SP1 o superiores, incluyendo sistemas x64, y suficiente espacio en un disco duro como para almacenar el archivo resultante del volcado.

La aplicación es muy sencilla de utilizar. Se descarga desde su sitio web y se ejecuta, no requiere instalación (también puedes usarla directamente); tras ello, simplemente debemos elegir los discos a virtualizar, seleccionar una ubicación de salida para el archivo .vhd, esperar unas horitas y ya lo tenemos. Normalmente bastará con virtualizar el disco de sistema.
Consejo #1: para que la conversión se realice más rápidamente, lo mejor es hacer que el .vhd a generar resida en un disco duro distinto del que estamos virtualizando, aunque se puede realizar sobre el mismo.
Una vez con el archivo .vhd a buen recaudo, ya podemos formatear tranquilamente el disco del sistema, montar el nuevo sistema operativo y comenzar a instalar las aplicaciones que vayamos a necesitar.

Para acceder al sistema anterior tal y como estaba antes de la masacre, basta con instalar Virtual PC, crear una máquina virtual, “engancharle” el disco .vhd que hemos generado, y arrancar normalmente, pero ojo:
Consejo #2: haz una copia de seguridad del archivo .vhd antes de realizar cambios sobre el disco duro virtual. Me he encontrado algunos callejones sin salida en los que me ha venido de perlas (p.e., petes del Virtual PC al instalar las Virtual Machine Additions que me dejaban la máquina virtual inutilizada).
La primera vez que enciendes la máquina virtual se llevará un buen rato arrancando Windows; es lógico, pues todos los controladores que tiene instalados corresponden a la máquina física, y el nuevo entorno debe ser configurado, prácticamente igual que si hubiéramos instalado el disco duro físicamente en otro equipo y arrancáramos desde él. Cuando esta reconfiguración finaliza, podremos utilizar con normalidad la máquina virtual e ir pasando las configuraciones y archivos al nuevo sistema tranquilamente.
Consejo #3: revisa la configuración básica de tu máquina virtual para evitar conflictos y funcionamiento anómalo en algunas aplicaciones; nombre de máquina, dirección IP, variables de entorno del sistema operativo, etc. En mi caso, la variable TMP/TEMP apuntaba a una unidad inexistente en el entorno virtual, y provocó algún que otro problemilla.
Y por si lo que queremos acceder a los datos del equipo anterior en bruto, existe la posibilidad de montar un archivo .vhd como si fuera un disco duro más y acceder a su contenido directamente, por lo que podemos evitar la incomodidad de tener que arrancar Virtual PC para todo. Si usas Windows 7 (o 2008), esta capacidad viene “de serie”, sólo tienes que activarla desde el administrador de discos:

Montando un VHD en Windows 7

Publicado en: Variable not found
Compartir:
domingo, 7 de junio de 2009
Compartir:
Code TagHace unos días publiqué CodeTag, un sencillo plugin para Windows Live Writer que permite insertar pequeñas porciones de código fuente en línea en los contenidos de los posts. Resumidamente, lo único que hace esta utilidad es envolver entre etiquetas <code> y </code> el texto que tengamos seleccionado en el momento de su utilización, pero evita tener que entrar al modo de edición HTML para hacer estos ajustes.

Basándome en el código fuente de CodeTag (que podéis descargar al final de este post), voy a describir, paso a paso, cómo podemos crear complementos para Writer que nos faciliten un poco las tareas cotidianas de edición.

0. Antes de empezar…

Para poder desarrollar el plugin necesitáis básicamente dos cosas:

  • en primer lugar, el propio software Live Writer, más que nada porque necesitamos referenciar algunos de sus ensamblados, y por ir probando el complemento durante el desarrollo.
  • en segundo lugar, Visual Studio 2005 o 2008, aunque sea una edición express. Este desarrollo vamos a realizarlo en C#, pero la traducción a Visual Basic .NET sería trivial.
En este post voy a incluir capturas de pantalla correspondientes a Microsoft Visual C# 2008 Express, que es el único que tengo a mano.

1. Preparación del proyecto

Los plugins para Live Writer son archivos .dll, es decir, ensamblados .NET, que se colocan en el directorio "plugins" de la carpeta de instalación del programa. En mi caso, están en la carpeta C:\Archivos de programa\Windows Live\Writer\Plugins, y si no habéis cambiado las rutas de instalación por defecto, será allí donde los podéis encontrar también. Durante el proceso de arranque, Writer examinará dicha carpeta y cargará los complementos que encuentre en ella.

Crear librería de clasesLo que vamos a hacer es crear desde Visual Studio un proyecto de librería de clases. Cada vez que compilemos, copiaremos el ensamblado resultante en dicho directorio (veremos cómo podemos automatizar esta tarea) y podremos lanzar Live Writer para comprobar el funcionamiento.

Por tanto, en primer lugar creamos un proyecto de librería de clases como siempre, al que le damos el nombre CodeTag. Obviamente, podéis dar al proyecto el nombre que estiméis conveniente.

Una vez que el IDE crea la estructura, debemos añadir dos referencias al proyecto, que serán necesarias para poder continuar:

  • Añadir referencias La primera referencia es al ensamblado que contiene el API de Live Writer, que podéis encontrar en el directorio de instalación de la herramienta. El  archivo a incluir es WindowsLive.Writer.Api.dll .
  • La segunda es a System.Windows.Forms. Hay que tener en cuenta que Live Writer es una herramienta de escritorio, y esta referencia es importante para poder interactuar con el mismo.

Otro detalle más que nos va a facilitar las cosas: vamos a indicar a Visual Studio que el resultado de la compilación lo copie en el directorio de plugins de Live Writer. Para ello, lo único que tenemos que hacer es acudir a las propiedades del proyecto, pestaña “eventos de generación” e incluir la siguiente orden en el cuadro “línea de comandos del evento posterior a la generación” (obviamente, modificando la ruta si es necesario):

XCOPY /D /Y /R "$(TargetPath)" "C:\Archivos de Programa\Windows Live\Writer\Plugins\"

Evento posterior a la generación

Ojo, es importante tener en cuenta algo más cuando vayamos a compilar: si Live Writer está abierto, no podremos sobrescribir nuestro plugin con una nueva versión, pues éste se encontrará en uso. En estos casos debemos cerrar el programa antes de copiar el archivo, o antes de compilar.

Con estas operaciones ya tenemos el proyecto listo para empezar a codificar el plugin.

2. Escribimos la clase principal

Un plugin sencillo como este no requiere demasiada programación, pero sí hay que cumplir una serie de requisitos para que se integre correctamente en Live Writer.

En primer lugar, creamos una clase, a la que llamaremos CodeTag, e insertamos el siguiente código:

  public class CodeTag: ContentSource
{
public override DialogResult CreateContent(IWin32Window dialogOwner,
ref string content)
{
if (string.IsNullOrEmpty(content))
content
= "YourCodeHere";

content
= "<code>" + content + "</code> ";
return DialogResult.OK;
}
}

Como se puede observar, la clase hereda de ContentSource, un tipo definido en WindowsLive.Writer.Api, que sirve como base para la creación de complementos sencillos como el que nos ocupa. Para casos más complejos, que permitieran por ejemplo edición de contenidos bidireccional entre el contenido de la página y un editor personalizado podríamos heredar de SmartContentSource, pero no profundizaremos en ello ahora.

La codificación de la lógica del plugin puede realizarse en varios puntos, dependiendo de la forma en que Writer lo active; en nuestro caso, el complemento se lanzará cuando el usuario presione un botón en la barra lateral o seleccione la opción correspondiente en el menú "insertar", por lo que simplemente deberemos sobrescribir el método CreateContent.

Dicho método recibe dos parámetros. El primero de ellos hace referencia a la ventana activa, que podemos utilizar como "padre" si quisiéramos crear un cuadro de diálogo desde nuestro código. El segundo parámetro contendrá una referencia al texto seleccionado en el momento de lanzar el plugin, pudiendo darse los dos casos que se contemplan en la codificación:

  • Si la variable content viene vacía o con valor nulo, es que el complemento ha sido lanzado en modo inserción, es decir, no existía ningún texto seleccionado y por lo tanto lo que se pretende es añadir las etiquetas de código para introducir posteriormente contenido. Como puede observarse en el código, lo que se hace en este caso es insertar las etiquetas <code> con un texto arbitrario para que el usuario lo modifique a su antojo más adelante.
  • En caso contrario, si la variable content trae algún contenido, lo que se hace es rodear éste por la apertura y cierre de la etiqueta <code>.

En ambos casos se retorna un valor DialogResult.OK, que indica a Live Writer que debe insertar el texto contenido en content en la ubicación donde se encuentre el cursor, o bien sustituir el texto seleccionado por el nuevo valor.

3. Añadimos metadatos

Heredar de la clase ContentSource no es el único requisito para que esta sea considerada como un componente de Writer, es necesario adornarla con un conjunto de atributos como los mostrados en el siguiente código:
[WriterPlugin("98497c2b-bbfd-4bd1-b343-226f3c9e766b", "Code Tag",
Description
= "Crea etiquetas <code></code> en el contenido",
PublisherUrl
= "http://www.variablenotfound.com")]
[InsertableContentSource(
"Etiqueta <code>")]
public class CodeTag: ContentSource
{
[...]
}

El atributo [WriterPlugin] es el que realmente identifica la clase como plugin de Live Writer. Los parámetros que se le están enviando son los siguientes:
  • El primer parámetro es un GUID, es decir, un identificador único que debemos generar para el plugin, utilizando este servicio on-line u otras herramientas como la incluida en Visual Studio.
  • El segundo parámetro es el nombre del plugin. Es la identificación que aparece en el cuadro de diálogo de configuración de complementos de Live Writer.
  • Description permite añadir una descripción textual ampliada del plugin.
  • PublisherUrl es una dirección web de referencia al autor. Pura propaganda ;-)


El atributo [InsertableContentsource] indica que el complemento debe aparecer en el menú "insertar" de las barra de tareas de Writer y en el menú principal. El parámetro que le estamos enviando es simplemente el texto que aparecerá en estos menús.

4. Compilamos el proyecto

Con lo hecho hasta el momento ya podemos compilar e intentar probar nuestro complemento. Este procedimiento lo repetiremos varias veces durante el desarrollo, por lo que es posible que nos encontremos a menudo con un error como el siguiente:

The command "XCOPY /D /Y /R "CodeTag.dll" "C:\Archivos de Programa\Windows Live\Writer\Plugins\"" exited with code 4

Esto simplemente quiere decir que Live Writer está usando CodeTag.dll y no puede ser sobrescrito, por lo que ¡cerrad Live Writer antes de compilar!

CodeTag en el menú "insertar", sin icono Una vez superado este leve impedimento, si aparece, ya podremos comenzar a disfrutar de nuestro plugin. Veremos que aparece en el menú "insertar", con la descripción apropiada, y funcionando correctamente.

Pero vaya, es cierto que en el menú aparece, pero destaca sobre el resto de complementos porque es el único que no tiene un icono, así que habrá que mejorarlo un poco…

5. Añadirle un icono al plugin

Incluir un icono a nuestro complemento le dará sin duda un aspecto más profesional, vamos a ello.

image Lo primero que necesitamos es una imagen de 16x16 píxeles, por ejemplo en formato .bmp, que actuará como icono. Como el diseño gráfico decididamente no es lo mío, he creado una imagen muy simple pero creo que bastante ilustrativa de la tarea que realiza el complemento: la propia etiqueta <code> que pretendemos crear. Incrustar la imagen como recurso en el ensamblado

El archivo debemos incluirlo, para no tener problemas, en el directorio raíz del proyecto, y a continuación, hay que indicar a Visual Studio que dicha imagen será un recurso incrustado (embedded resource) en el ensamblado. Este paso es importante, pues si no se hace correctamente, la imagen no será incluida en la DLL.

A continuación es necesario indicar a Live Writer la imagen a utilizar como icono, lo que se consigue añadiendo un parámetro más (ImagePath) en la definición del atributo [WriterPlugin] con la ruta hacia el fichero que hemos incrustado. Eso sí, no me refiero a la ruta física del archivo .bmp, sino a la representación como espacio de nombres de la misma (por ejemplo, si la imagen se llama logo.bmp y está en (raíz)\recursos, la ruta hacia ella será “recursos.logo.bmp”).

Como en este caso hemos depositado la imagen en el directorio raíz, la declaración de atributos quedará como sigue:
    [WriterPlugin("98497c2b-bbfd-4bd1-b343-226f3c9e766b", "Code Tag",
Description
= "Crea etiquetas <code></code> en el contenido",
ImagePath
= "CodeTag.bmp",
PublisherUrl
= "http://www.variablenotfound.com")]
[InsertableContentSource(
"Etiqueta <code>")]
public class CodeTag : ContentSource
{
[...]
}

Un último apunte relativo a este tema: si al iniciar Live Writer éste es incapaz de localizar el recurso indicado en el parámetro ImagePath, el plugin funcionará, pero aparecerá el siguiente mensaje en el arranque de la aplicación:

Advertencia: no se ha podido cargar el mapa de bits para el complemento CodeTag desde la ruta especificada (CodeTag.bmp)

6. O, por si no quieres teclear…

CodeTag en el menú "insertar", con icono … he colgado en SkyDrive el código fuente del proyecto, para el que quiera utilizarlo como base de creación de sus propios complementos, o simplemente para trastearlo un poco.

Requiere, como mínimo, Visual C# 2008 Express, con su correspondiente Service Pack 1.


Publicado en: Variable not found.
Compartir:
lunes, 29 de septiembre de 2008
Compartir:
LiameYa lo comentaban Rodrigo Corral y algún otro amigo en geeks.ms después de leer el post sobre formas efectivas de ofuscar emails en páginas web: el siguiente paso era "empaquetar" en forma de componente las técnicas que, según se recogía en el post, eran las más seguras a la hora de ocultar las direcciones de correo de los spammers.

Recapitulando, las técnicas de camuflaje de emails que habían aguantado el año y medio del experimento de Silvan Mühlemann, y por tanto se entendían más seguras que el resto de las empleadas en el mismo, fueron:
  1. Escribir la dirección al revés en el código fuente y cambiar desde CSS la dirección de presentación del texto.
  2. Introducir texto incorrecto en la dirección y ocultarlo después utilizando CSS.
  3. Generar el enlace desde javascript partiendo de una cadena codificada en ROT13.
Liame, mi segunda aportación al mundo del software libre (la primera fue NiftyDotNet), es un control para ASP.NET que combina estas técnicas y algún otro añadido con objeto de ofuscar direcciones de correo en el interior de páginas Web, y, como veremos más adelante, no sólo para aplicaciones basadas en tecnología .NET.

En tiempo de ejecución, el control es capaz de generar código javascript que escribe en la página un enlace mailto: completo, partiendo de una cadena previamente codificada creada desde el servidor. Dado que todavía no está generalizado entre los spambots la ejecución de javascript de las páginas debido al tiempo y capacidad de proceso necesario para realizarlo, podríamos considerar que esta es la opción más segura.

MatrixPara codificar los textos en principio iba a utilizar ROT-13, pero ya que estaba en faena pensé que quizás sería mejor aplicar una componente aleatoria al algoritmo, por lo que al final implementé un ROT-N, siendo N asignado por el sistema cada vez que se genera el script.

Pero, ah, malditos posyaques... la verdad es que con un poco de refactorización era posible generalizar el procedimiento de codificación y decodificación mediante el uso de clases específicas (Codecs), así que me puse manos a la obra. Liame incluye, de serie, cuatro algoritmos distintos de ofuscación para ilustrar sus posibilidades: ROT-N (el usado por defecto y más recomendable), Base64, codificación hexadecimal, y un codec nulo, que me ha sido muy útil para depurar y usar como punto de partida en la creación de nuevas formas de camuflaje. Algunos, además, aleatorizan los de nombres de funciones y variables para hacer que el código generado sea ligeramente distinto cada vez, de forma que un spammer no pueda romperlo por una simple localización de cadenas en posiciones determinadas; en fin, puede que sea una técnica un poco inocente, pero supongo que cualquier detalle que dificulte aunque sea mínimamente la tarea de los rastreadores, bueno será.

Incluso si así lo deseamos podremos generar, además del javascript de decodificación del enlace, el contenido de la etiqueta <noscript>, en la que se incluirá el código HTML de la dirección a ocultar utilizando los dos trucos CSS descritos anteriormente y también considerados "seguros" por el experimento. De esta forma, aunque no estará disponible el enlace para este grupo de usuarios, podrán visualizar la dirección a la que podrán remitir sus mensajes.

Liame en la barra de herramientas de Visual StudioEl control Liame es muy sencillo de utilizar. Una vez agregado a la barra de herramientas, bastará con arrastrarlo sobre la página (de contenidos o maestra) y establecer sus propiedades, como mínimo la dirección de email a ocultar. Opcionalmente, se puede añadir un mensaje para el enlace, su título, la clase CSS del mismo, etc., con objeto de personalizar aún más su comportamiento a la hora de generar el script, así como las técnicas CSS a utilizar como alternativa.

Sin embargo, aún quedaba una cosa pendiente. El control de servidor está bien siempre usemos ASP.NET y que el rendimiento no sea un factor clave, puesto que al fin y al cabo estamos cargando de trabajo al servidor. Para el resto de los casos, Liame incluye en el proyecto de demostración un generador de javascript que, partiendo de los parámetros que le indiquemos, nos creará un script listo para copiar y pegar en nuestras páginas (X)HTML, PHP, Java, o lo que queramos. Como utiliza la base de Liame, cada script que generamos será distinto al anterior.

He publicado el proyecto en Google Code, desde donde se puede descargar tanto el ensamblado compilado como el código fuente del componente y del sitio de demostración. Esta vez he elegido la licencia BSD, no sé, por ir probando ;-)

La versión actual todavía tiene algunos detallitos por perfilar, como el control de la entrada en las propiedades (en especial las comillas y caracteres raros: ¡mejor que nos los uséis!), que podría dar lugar a un javascript sintácticamente incorrecto, pero en general creo que se trata de una versión muy estable. Ha sido probada con Internet Explorer 7, Firefox 3 y Chrome, los tres navegadores que tengo instalados.

También, por cortesía de Mergia, he colgado un proyecto de demostración para que pueda verse el funcionamiento en vivo y en directo, tanto del control de servidor como del generador de javascript.

Demo de Liame
Finalmente, algunos aspectos que creo interesante comentar. En primer lugar, me gustaría recordaros que las técnicas empleadas por Liame no aseguran, ni mucho menos, que los emails de las páginas van a estar a salvo de los del lado oscuro eternamente, aunque de momento así sea. Lo mejor es no confiarse.

En segundo lugar, es importante tener claro que todas las técnicas aquí descritas pueden resultar bastante nocivas para la accesibilidad de las páginas en las que las utilicéis. Tenedlo en cuenta, sobre todo, si tenéis requisitos estrictos en este sentido.

Y por último, añadir que estaré encantado de recibir vuestras aportaciones, sugerencias, colaboraciones o comentarios de cualquier tipo (sin insultar, eh?) que puedan ayudar a mejorar este software.



Enlaces



Publicado en: www.variablenotfound.com.
Compartir:
martes, 27 de noviembre de 2007
Compartir:
Aunque he visto que es conocido desde hace tiempo, yo me he enterado ahora de que en http://www.connectionstrings.com/ hay una excelente recopilación de cadenas de conexión a fuentes de datos de todo tipo:
  • Motores de bases de datos SQL Server, SQL Server 2005, SQL Server 2005 Compact Edition, Oracle, MySQL, Interbase, IBM DB2, Sybase, Informix, Ingres, Mimer SQL, Lightbase, Postgre SQL, Paradox, Firebird, AS/400 (iSeries), Pervasive, SQLBase, Progress y Caché.

  • Archivos de datos Excel 2007, Excel, texto plano, Access 2007, Access, Visual FoxPro / FoxPro 2.x DBF / FoxPro, SQLite y Filemaker.

  • Otras fuentes, como MS Project, Active Directory, Exchange, Lotus Notes, DSN y UDL.

Seguro que algún despistado (como el menda, todo sea dicho), todavía no lo conocía. Y es que no hay nada como internet para tener a mano toda la información que necesitamos.

Publicado en: Variable not found
Compartir: