Archivo

Archivo para Febrero, 2012

Creando una unidad USB de arranque DOS (FREEDOS) con software libre

Lunes, 27 de Febrero de 2012 Gaspar Fernández Sin comentarios

dos
¿Echas de menos tener un sistema MS-DOS? Si eres de los que ya llevas tiempo en el mundo de la informática, esta pantalla de arriba te resultará familiar. Aquel modo texto a 80×25, clásica que podíamos ver en los ordenadores antiguos. Y es que, a veces, puede ser muy útil disponer de un sistema de estos, ya sea para una máquina virtual, o para nuestro ordenador, donde podemos ejecutar las instrucciones que nos vengan en gana sin preocuparnos de que al sistema operativo le gusten o no, para investigar, para revivir viejos tiempos con un antiguo juego, o analizar un programa que sólo funciona en ese sistema, que aún los hay, y debemos portar la información a programas más nuevos. Casi siempre por necesidad, porque donde se ponga “ls” que se quite “dir” :)

Esto lo podemos hacer de dos formas distintas, con makebootfat, nativo para GNU/Linux, o emulando FreeDOS y utilizando sus herramientas (elegir sólo un método):

Método nativo para GNU/Linux

Lo primero que tenemos que hacer es instalar makebootfat y syslinux. En una distribución tipo Debian/Ubuntu/Linux Mint podemos hacer lo siguiente:

$ sudo apt-get install makebootfat syslinux

Una vez tenemos instalados los paquetes, después de asegurarnos de que el pendrive (unidad USB), no tiene nada importante, debemos formatearlo. Usaremos el sistema de archivos FAT16, por lo que el tamaño máximo de la unidad será de 2Gb. Si la unidad es pequeña (menos de 2Gb, como hemos visto antes), habrá que cambiar el tipo de partición solamente), si la unidad es más grande (debemos eliminar la partición existente, ya que supongo que tu pendrive es sólo una unidad, y luego crear una partición pequeña).

Debemos sustituir la sdX por el dispositivo de nuestra unidad USB. Podemos verlo haciendo:

$ dmesg | tail

nada más enchufar la unidad. Luego hacemos:

$ sudo fdisk /dev/sdX

Lo primero será eliminar la partición (sólo si tenemos un pendrive de más de 2Gb), lo hacemos pulsando d [INTRO]

Luego creamos una nueva partición (n [INTRO]):

  • Lo primero que nos pide es si será (p) principal o (e) extendida, elegimos princpal
  • Lo siguiente es el número de la partición, elegimos el 1
  • Luego dónde empieza (damos a enter, ponemos el valor por defecto)
  • Lo siguiente el tamaño. Escribiremos +100M para darle 100Mb a la unidad, podemos darle hasta 2Gb

Lo siguiente es elegir el tipo de partición, pulsamos la letra t [INTRO] y luego elegimos el tipo 6 (FAT16), podemos ver con L un listado de tipos de partición.

Tras todo esto, escribimos los cambios con w [INTRO]

Ya tenemos una unidad FAT16 en nuestro pendrive, ahora la formateamos:

Tenemos que formatear sdX1 (X depende del dispositivo donde nuestro sistema lo detecte)

$ sudo mkfs.vfat /dev/sdX1

Ahora debemos bajar el siguiente archivo:
kernels.zip, lo grabamos en /home/usuario/trabajo/ .
Ahora vamos a esta página, y descargamos la imagen de un disquette de arranque de FreeDOS, por ejemplo el disquette de 2.88Mb, que es el que trae algunos comandos. Lo copiamos también en /home/usuario/trabajo/
Y desde ahí:

$ unzip kernels.zip # Descomprimimos el archivo
$ mkdir pendrive # Creamos una nueva carpeta
$ cp source/ukernel/boot/fat*.bin . # Copiamos los kernels en el directorio de trabajo
$ cp /usr/lib/syslinux/mbr.bin . # Copiamos la imagen del mbr en el directorio de trabajo
$ gunzip FDSTD.288.gz # Descomprimimos la imagen de disco
$ sudo mkdir /mnt/floppy # Creamos un punto de montaje para el disquette
$ sudo mount -t vfat -o loop FDSTD.288 /mnt/floppy # Montamos el disquette
$ cp -r /mnt/floppy/* pendrive/ # Copiamos todos los archivos del disquette al directorio de nuestro pendrive
$ sudo umount /mnt/floppy # Desmontamos el disquette

Ya lo tenemos todo listo, sólo falta crear el disco de arranque, para ello ejecutamos:

$ sudo makebootfat -o /dev/sdX -E 255 -1 fat12.bin -2 fat16.bin -3 fat32lba.bin -m mbr.bin pendrive

Esto escribirá en el pendrive /dev/sdX (sin el 1):

  • -E 255 detecta la unidad BIOS, FAT12 y FAT16 requieren poner el valor correcto para poder arrancar, 255 autodetecta
  • -1, -2, -3 son los kernels de fat12, fat16 y fat32
  • -m, define la imagen del mbr
  • pendrive, copia todos estos archivos

Ya podemos retirar el pendrive para usarlo y arrancar con él.

Emulando FreeDOS

Lo primero es decargar un disquette de arranque de Freedos desde aquí, y descargamos la imagen de un disquette de arranque de FreeDOS, por ejemplo el disquette de 2.88Mb, que trae más cosas. (Ver en la explicación de arriba cómo descomprimir la imagen).

Luego instalaremos qemu. Desde Debian/Ubuntu/Mint:

$ sudo apt-get install qemu

Ahora arrancaremos qemu para emular FreeDOS utilizando como disco duro el pendrive (si está vacío mejor):

$ qemu -hda /dev/sdX -drive file=FDSTD.288,if=floppy

Esto arrancará en una ventana FreeDOS desde la imagen de disquette, lo que tenemos que hacer es utilizar fdisk (como el de MS-DOS para eliminar la partición existente y crear otra nueva), format para formatear el pendrive, en este caso podemos hacer FORMAT C: sin miedo, ya que el disco duro es el pendrive, y luego desde A: hacer “sys C:” para copiar el sistema base al disco duro. Es recomendable copiar luego con copy todos los archivos del disquette.

Una vez hecho esto, podemos cerrar qemu, sacar el pendrive y utilizarlo

Entrada automática en LMDE con GDM3

Viernes, 24 de Febrero de 2012 Gaspar Fernández Sin comentarios

El paquete gdm para LMDE suele traernos a todos por la calle de la amargura, y es que en ocasiones viene con gdm-setup, y en cuanto se actualiza, ya no trae este pequeño programa.

Aunque la configuración de gdm no es nada del otro mundo, se agradece el entorno gráfico, aunque sólo sea para activar el autologin. Aunque puede ser un riesgo para la seguridad de nuestro ordenador, cuando instalamos un sistema para alguien que no se quiere complicar mucho la vida, es conveniente ofrecerle la opción de entrar automáticamente en su sesión.

Para ello tenemos que editar /etc/gdm3/daemon.conf (como root):

$ sudo nano /etc/gdm3/daemon.conf

Y en la sección [daemon] debemos incluir:

AutomaticLoginEnable=true
AutomaticLogin=usuario

(Control+X, Sí, Intro <- Para salir y guardar cambios en nano)
Donde usuario es el nombre de usuario que queremos que se identifique automáticamente.

Categories: Linux Tags: , , , , , , ,

Navegando a través de un tunel SSH

Miércoles, 22 de Febrero de 2012 Gaspar Fernández 2 comentarios

blogtunel

En ocasiones, si queremos navegar de una forma algo más anónima y privada, en el lugar que nos encontramos no tenemos acceso a determinados servicios, o simplemente queremos usar una conexión diferente a la red, podemos intentar esto.
Podemos navegar desde cualquier ordenador que disponga de un servidor SSH, por ejemplo un servidor que tengamos contratado (Dreamhost ofrece además de hosting SSH. Podéis usar el código DFIFTYHOST para tener casi un 50% de descuento el primer año). Para llevar esto a cabo, basta con ejecutar el siguiente comando en la máquina local (no el servidor):

$ ssh -D 8080 usuario@servidor.ext

Donde 8080 será el puerto al que debemos mandar las peticiones (en realidad es el puerto del proxy), y lo demás son los datos del servidor al que nos conectamos. Con esto sólo podemos hacer el tunel para conexiones locales, es decir, sólo podremos utilizarlo en nuestra máquina.

Imagina ahora que queremos compartir la conexión de ese túnel con otros ordenadores, en caso de encontrarte, por poner un ejemplo absurdo, en la sala de ordenadores de la facultad, y un profesor te mande programar un juego tipo arkanoid, pero la página con la documentación la encuentras en un archivo llamado arkanoid.html; el proxy de la facultad bloquea las páginas con ese nombre y por tanto no se puede leer la documentación (era un caso real, en el que hubiera deseado poder hacer esto). En este momento, al encontrarte con varios amigos en la biblioteca y querer acceder todos al mismo recurso, podemos hacer que uno de ellos haga el túnel y comparta el proxy con los demás. Si por ejemplo la IP dentro de la red local (no con la que se conecta a Internet) es 192.168.1.8 , debería hacer:

$ ssh -D 192.168.1.8:8080 usuario@servidor.ext

Con esto, asociamos el puerto a la IP de la red local y no a localhost.

Luego en el navegador debemos configurar la forma en la que conectamos, debemos elegir configuración manual del proxy, y en la casilla de servidor Socks (versión 5) poner la IP: 192.168.1.8 (o localhost si cogemos la primera opción), y en el puerto 8080. En Firefox encontramos esa opción en Editar (Herramientas en Windows) / Preferencias / Avanzado / Red / Configuración

Por supuesto podemos elegir el número de puerto que queramos, siempre que sea mayor a 1024 (para hacerlo como usuario) y no haya nada corriendo en esos puertos.

Para Windows, podemos usar Putty.

Foto: marru-birra (Flickr) Con licencia CC-by-nc a 22/02/2012.

Estadísticas de directorios en PHP

Miércoles, 15 de Febrero de 2012 Gaspar Fernández Sin comentarios

A veces, tenemos la necesidad de generar una estadística básica de algún subdirectorio de nuestras aplicaciones. Por ejemplo, para ver cuánto tenemos ocupado en caché, y ver si ésta se está regenerando bien (es sólo un ejemplo), puede que tengamos un directorio donde alojemos los archivos que suben nuestros usuarios y queramos ver si crece o no en el tiempo.

Este par de funciones pueden resultar útiles. Aún se pueden optimizar un poco, pero la idea aquí está. Es un conteo recursivo de los archivos de nuestros directorios en PHP, que es capaz de calcular:

  • ¿Cuántos archivos hay en total?
  • ¿Cuántos subdirectorios
  • El archivo más nuevo
  • El archivo más viejo
  • El subdirectorio con más archivos
  • El subdirectorio con menos archivos

y que podemos extender con algún que otro criterio que se nos ocurra.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?php
  /**
   ******************************************************************
   * @brief Fusiona estadísticas de directorios (ver getFolderStats())
   *
   * @param $s1     Es la colección de estadísticas 1 (la salida irá aquí)
   * @param $s2     Es la colección de estadísticas 2
   *
   * @return $s1    Revisada
   *
   ******************************************************************/

function mergeFolderStats(&$s1, $s2)
{
  $s1['size']      +=$s2['size'];     // Los tamaños totales los sumamos
  $s1['files']     +=$s2['files'];    // Los números de archivo también se suman
  $s1['subfolders']+=$s2['subfolders']; // Los números de directorios también se suman

  if ($s2['maxFilesFolder'])
    {
      if ((!$s1['maxFilesFolder']) || ($s2['maxFilesFolder'][1]>$s1['maxFilesFolder'][1]))
    $s1['maxFilesFolder']=$s2['maxFilesFolder'];
    }

  if ($s2['minFilesFolder'])
    {
      if ((!$s1['minFilesFolder']) || ($s2['minFilesFolder'][1]<$s1['minFilesFolder'][1]) )
    $s1['minFilesFolder']=$s2['minFilesFolder'];
    }

  if ($s2['newestFile'])
    {
      if ((!$s1['newestFile']) || ($s2['newestFile'][1]>$s1['newestFile'][1]))
    $s1['newestFile']=$s2['newestFile'];
    }

  if ($s2['oldestFile'])
    {
      if ((!$s1['oldestFile']) || ($s2['oldestFile'][1]<$s1['oldestFile'][1]))
    $s1['oldestFile']=$s2['oldestFile'];
    }

  return $s1;
}

/**
 ******************************************************************
 * @brief Obtiene estadísticas sobre un directorio
 *
 * @param $folder    Directorio a consultar
 *
 * @return Array con estadísticas: array('size'           => X,
 *                                       'files'          => X,
 *                                       'subfolders'     => X,
 *                                       'maxFilesFolder' => array (X, num, size), // Carpeta con más archivos y número de archivos y tamaño
 *                                       'minFilesFolder' => array (X, num, size), // Carpeta con menos archivos y número de archivos y tamaño
 *                                       'oldestFile'     => array (name, date),   // Archivo más viejo y fecha
 *                                       'newestFile'   => array (name, date),   // Archivo más nuevo y fecha
 *                                       )
 *
 ******************************************************************/

function getFolderStats($folder)
{
  $res=array('size'           => 0,
         'files'          => 0,
         'subfolders'     => 0,
         'oldestFile'     => false,
         'newestFile'   => false,
         'maxFilesFolder' => false,
         'minFilesFolder' => false,
         );
// Pone una barra al final
  if ($folder[strlen($folder)-1]!='/')
    $folder.='/';

  $d = dir($folder);
  $files=0;
  $size=0;
  while ($file=$d->read())
    {
      if ( ($file!='.') && ($file!='..') )
    {
      if (is_dir($folder.$file))
        {
          $tmp = getFolderStats($folder.$file);
          mergeFolderStats($res, $tmp);
          $res['subfolders']++;
        }
      else
        {
          $files++;
          $size+=filesize($folder.$file);
          $filemtime=filemtime($folder.$file);
          $oldest=(isset($res['oldestFile'][1]))?$res['oldestFile'][1]:0;
          $newest=(isset($res['newestFile'][1]))?$res['newestFile'][1]:0;

          if ( (!$res['oldestFile']) || ($filemtime<$oldest) )
        $res['oldestFile']= array($folder.$file, $filemtime);
          if ( (!$res['newestFile']) || ($filemtime>$newest) )
        {
          $res['newestFile']= array($folder.$file, $filemtime);
        }
        }
    }
    }

  $res['files']+=$files;
  $res['size']+=$size;
  if ( (!$res['maxFilesFolder']) || ($files>$res['maxFilesFolder'][1] ) )
    $res['maxFilesFolder']=array($folder, $files, $size);

  if ( (!$res['minFilesFolder']) || ($files<$res['minFilesFolder'][1] ) )
    $res['minFilesFolder']=array($folder, $files, $size);

  return $res;
}

print_r(getFolderStats('.'))
?>

Directamente podemos analizar el array que devuelve la función getFolderStats()

Menos tiempo para actualizar

Lunes, 13 de Febrero de 2012 Gaspar Fernández 2 comentarios

A partir de ahora voy a tener mucho menos tiempo para actualizar el blog, no quiero dejar de publicar, sólo bajará la frecuencia de las publicaciones, ya que no puedo mantener el ritmo. A pesar de que tengo muchos artículos en el tintero, voy a tener que retrasarlos aún más por motivos personales y laborales. Seguiré por aquí, además, tenéis la página de Facebook donde voy publicando las novedades del blog, podéis seguirme en Twitter (@blakeyed), aunque sea un nombre raro, pero soy yo.

Generar texto e implantarlo en una imagen desde PHP

Viernes, 10 de Febrero de 2012 Gaspar Fernández 1 comentario

En ocasiones, se puede presentar la necesidad de introducir en nuestros proyectos una imagen cuyo contenido sea un texto, y sobre todo necesitamos integrarlo dentro de nuestro contenido, intentando que no se note que es una imagen, a no se que seleccionen el contenido para copiarlo.

Una de las principales utilidades de esto es evitar el SPAM al escribir nuestra dirección de e-mail en una web. Muchos clientes prefieren enviar un e-mail en lugar de utilizar un formulario de contacto y tenemos que satisfacerles, sin arriesgar la seguridad de nuestro sistema, en el sentido de que hay cientos de motores que se dedican a rastrear webs en busca de direcciones de e-mail (lo cual es muy sencillo de hacer con expresiones regulares, por ejemplo).

Tenemos que estudiar bien cómo hacemos el script, ya que la entrada de datos es esencial. Sería improductivo hacer un script cuya llamada fuera:

1
<img src="scriptGenerador.php?texto=midireccion@miservidor.com" />

Ya que un motor se enteraría de que eso es una dirección de correo igualmente. Por lo que yo sugiero algunas opciones:

  • Para simplificar, podemos codificar el texto, por ejemplo, en base 64, el e-mail anterior quedaría así:bWlkaXJlY2Npb25AbWlzZXJ2aWRvci5jb20 y sería más complicado de detectar, aunque es nuestra responsabilidad hacer la conversión. Además, si en cualquier momento queremos identificar el texto que pusimos, en cierto punto del código debemos hacer la conversión inversa. Por otra parte, aunque es difícil que algún motor se dedique a convertir el texto en base64 a binario de nuevo, es posible.
  • Otra posibilidad es, si sólo vamos a escribir un sólo texto de esta forma, implantarlo dentro del archivo php que genera la imagen, como una variable del propio código, así no debemos escribir nada cuando llamemos al script. Si tenemos que cambiar el texto, basta con editar el script
  • Si, por el contrario, vamos a escribir varios mensajes, podemos asignar una clave a cada uno, por ejemplo, un número o una cadena de texto corta, y en nuestro script hacer corresponder ese número con el texto en cuestión.

Aquí vamos a hacer un ejemplo de la tercera opción. Aunque podemos extender el ejemplo usando bases de datos (si tenemos que introducir muchos mensajes, o muchas direcciones de e-mail diferentes), aquí vamos a crear un array en PHP para hacer corresponder la clave introducida con el valor (el texto que de verdad escribimos).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php
  // Con esta función calculamos la anchura, altura del texto y dónde está la baseline de nuestro texto
  // con el tipo de letra seleccionado.
function dimensions($ldef, $texto)
{
  // Calcular el ancho
  $box=imagettfbbox ($ldef['size'], 0, $ldef['font'], $texto);
  $width = abs(abs($box[2]) - abs($box[0]));

  // Calcular el alto y la baseline
  $box=imagettfbbox ($ldef['size'], 0, $ldef['font'], 'ILyjgq'); // Tiene caracteres que no terminan en la baseline
  $height = abs($box[7] - $box[1]);
 
  $baseline = abs($box[5]);

  return array($width, $height, $baseline);
}

// Definimos un array con los posibles textos que vamos a presentar
$textArray=array("ejemplo", "de un fragmento de texto", 'gaspy@mi_servidor.com');


// Definimos el tipo de letra y el tamaño
$letra = array ('font'  => './LinLibertine_Bd.ttf',
        'size'  => 12,
        'color' => array(0,0,0),
        );

// Definimos el texto a presentar
$textID  = (isset($_GET['texto']))?$_GET['texto']:0;
$text = $textArray[$textID];

header('Content-Type: image/png');

// Creamos la imagen, calculando primero sus dimensiones dependiendo del tamaño del texto
$box_size=dimensions($letra, $text);
$im = imagecreatetruecolor($box_size[0], $box_size[1]);

// Creamos dos colores, y definimos el color que actuará como transparente
$background = imagecolorallocate($im, 255, 255, 255);
$fontColor  = imagecolorallocate($im, $letra['color'][0], $letra['color'][1], $letra['color'][2]);
imagecolortransparent($im, $background);

// Ponemos el fondo de la imagen transparente
imagefilledrectangle($im, 0, 0, $box_size[0], $box_size[1], $background);


// Creamos el texto
imagettftext($im, $letra['size'], 0, 0, $box_size[2], $fontColor, $letra['font'], $text);

// Generamos la salida
imagepng($im);
imagedestroy($im);
?>

Para el ejemplo he utilizado la fuente Linux Libertine Bold cuyo fichero ttf es LinLibertine_Bd.ttf, en realidad podemos utilizar cualquier fuente de la que dispongamos el fichero TTF.
El script que vemos, si lo llamamos desde el navegador con el parámetro textID=1 mostrará una imagen con el texto “de un fragmento de texto” dibujado dentro con el tipo de letra especificado anteriormente.

Ahora hacemos el siguiente archivo html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Ejemplo de texto</title>
    <style text="text/css">
      <!--
     @font-face {
     font-family: "Linux Libertine";
     src: url(LinLibertine_Bd.ttf);
     }
     body{
     background: #eee;
     font: 12pt Linux Libertine,sans;
     }

     .texto{
     vertical-align:bottom;
     margin: 0;
     padding: 0;
     }
    -->
    </style>
    <meta name="author" content="Gaspar Fernández">
  </head>

  <body>
    <h1>Ejemplo de texto</h1>

    Esto es un ejemplo de inserción <img src="texto.php?textID=1" class="texto"/> incluyendo una imagen en PHP
   
    <hr>
    <address>
      <a href="http://totaki.com/poesiabinaria">Gaspar Fernández</a>,
    </address>
  </body>
</html>

Con la parte CSS:

1
2
3
4
@font-face {
     font-family: "Linux Libertine";
     src: url(LinLibertine_Bd.ttf);
     }

Estamos definiendo el fichero que se descargará para visualizar el tipo de letra, así nos aseguramos de que sea el mismo tipo de letra (el de la imagen y el de nuestro texto). El resultado debería ser algo parecido a esto:
textexample

Herramientas Open Source para colorear código

Miércoles, 8 de Febrero de 2012 Gaspar Fernández Sin comentarios

Aquí muestro una recopilación de herramientas que nos ayudarán a mostrar código fuente con colores, y así poder enseñar orgullosos nuestro código, bueno más que nada porque se ve todo más claro y se puede identificar mejor a qué corresponde cada palabra que en texto plano sin color.
Dependiendo del proyecto que tengamos entre manos nos conviene más utilizar una herramienta online u offline, o si tenemos un blog, mejor utilizar una herramienta específica para blogs.

Aquí va la lista:

  • GeSHi : Es una clase en PHP que nos devuelve un código HTML con el código fuente que deseamos coloreado. Tiene muchas opciones y soporta muchos lenguajes. Podéis ver un ejemplo de cómo usarlo aquí (un post de hace unos días) y una web donde utilizando GeSHi nos colorean el código que queramos.
  • SyntaxHighlighter: Un interesante proyecto en Javascript para colorear código sin saturar nuestros servidores. Haciendo todo el proceso de coloreado desde la máquina cliente, en el navegador. Podemos ver un ejemplo aquí.
  • COOoder : Muy útil para incluir código en nuestros documentos de OpenOffice.org / LibreOffice . Basta con seleccionar el código, seleccionar la herramienta, elegir el lenguaje, y él se encarga de lo demás. Podemos ver el código fuente aquí.
  • Code Colorizer Formater : Otra extensión más para LibreOffice que nos ayuda a colorear nuestro código fuente. Cuando le cogemos manejo es muy fácil, pero al principio puede que nos atranquemos un poco.
  • Code Colorer Plugin: Plugin para Wordpress basado en GeSHi para colorear código fuente. Este es el que usa este blog.
  • WP-Syntax : Otro plugin para Wordpress, también basado en GeSHi.
  • Highlight : Es un software que se ejecuta tanto en entorno gráfico como en consola y simplemente pegamos nuestro código y salvamos en HTML, RTF, LaTeX, SVG, ansi, y algún que otro formato más.
  • c2html : Simple, convierte C a HTML desde nuestra consola
  • code2html : Otra utilidad más de consola, capaz de convertir algunos lenguajes a HTML coloreado.

Por Internet podemos encontrar muchas más, ¿qué herramientas conoces? ¿o has usado?

Automatizando la publicación de nuestras páginas de Facebook

Lunes, 6 de Febrero de 2012 Gaspar Fernández Sin comentarios

facebook_1
Si administramos páginas de Facebook, en ocasiones nos interesa introducir contenidos procedentes de diversas fuentes, tal vez un RSS (o varios) (Por ejemplo, si administras la página de un blog, o quieres mostrar noticias), eso sí, de fuentes confiables. O tal vez quieres publicar un mensajes varias veces a lo largo de un día, o varios mensajes de forma escalonada en el tiempo.

Objetivos

El problema es que esto nos obliga a estar constantemente pendientes de esas publicaciones. En mi caso, cuando tengo tiempo, sí puedo entrar en mis páginas de Facebook para hacer publicaciones, pero me gustaría que esas publicaciones también fueran aprovechables para mis visitantes en Hispanoamérica, cuyos horarios son muy diferentes a los míos, así como para gente que suele conectar más por la mañana o por la tarde. Además, en ocasiones me interesa publicar varios mensajes en alguna de las páginas, pero quiero ver antes la reacción que tienen entre los usuarios (Comentarios y Me Gusta), sin ser demasiado pesado (cuando veo muchas publicaciones seguidas de una misma fuente me agobio y siento la tentación de dejar de recibir publicaciones de esa fuente).

Para solucionarlo, propongo montar una aplicación web (lo que muestro aquí es sólo un esbozo, ya que podemos hacerla tan compleja como queramos), para montar esta aplicación, aprovecharé información que ya he puesto en algún post anterior.

Mi objetivo es poder administrar todas mis páginas de Facebook desde un sitio centralizado, sin tener que entrar una a una modificando las publicaciones.

Base de datos de la aplicación

Como hemos visto en el post anterior, para publicar en nombre de una página, debemos almacenar los access tokens que dan permiso a la aplicación para publicar como si fuera esa página de Facebook. Además, debemos almacenar los IDs de páginas de Facebook para que la aplicación sepa quién va a publicar y dónde (en el muro de dicha página)

Para ello voy a utilizar el motor MySQL y así podremos subir la aplicación a cualquier hosting que soporte este motor. En la base de datos (facebook_autopublish), crearemos varias tablas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE TABLE `facebook_autopublish`.`paginas` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 128 ) NOT NULL ,
`access_token` VARCHAR( 255 ) NOT NULL ,
`category` VARCHAR( 64 ) NOT NULL ,
`fcbkId` VARCHAR( 32 ) NOT NULL ,
`last_update` BIGINT NOT NULL
) ENGINE = InnoDB;

ALTER TABLE `paginas` ADD INDEX ( `last_update` ) ;

CREATE TABLE `facebook_autopublish`.`publish` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
`pageID` INT UNSIGNED NOT NULL ,
`time` BIGINT NOT NULL ,
`message` TEXT NOT NULL ,
PRIMARY KEY ( `id` ) ,
INDEX ( `id` )
) ENGINE = InnoDB;

ALTER TABLE publish ADD FOREIGN KEY ( pageID ) REFERENCES paginas( id ) ;

La tabla paginas contendrá lo siguiente:

  • id : identificador de la página para la aplicación que estamos desarrollando
  • name : nombre de la página, obtenido de Facebook
  • access_token : access_token de la página, obtenido de Facebook
  • category : categoría de la página, obtenida de Facebook
  • fcbkId : ID de la página en Facebook, nos servirá para saber dónde publicar
  • last_update : momento en el tiempo de nuestra última publicación en la página

La tabla publish contendrá lo siguiente:

  • id : Identificador de la publicación para la aplicación
  • pageID : Id de la página donde se va a publicar
  • time : Momento en el tiempo en que queremos publicar
  • message : Texto de la publicación

La primera tabla (paginas), se generará automáticamente al cargar la página de identificación en Facebook. Para la segunda tabla (publish), debemos crear un interfaz web o CLI que vaya introduciendo los mensajes que tenemos que ir publicando. Ese interfaz no lo voy a incluir en este ejemplo, podemos incluso hacerlo desde PHPMyAdmin, o desde el comando mysql.

Script de identificación

Este script contendrá la autorización de la aplicación, y el llenado de la tabla paginas con los datos extraídos de Facebook (los ID de página y los access_tokens son lo más importante), y tenemos que almacenarlos en base de datos, para ello podemos usar el siguiente script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<?php

$mysql_host = 'Host Mysql';
$mysql_user = 'Usuario MySQL';
$mysql_pass = 'Pass MySQL';
$mysql_db   = 'facebook_autopublish';

$api_key = 'API Key';
$api_sec = 'API Secret';

require_once('facebook_ext.php');
// Definimos constantes
define(NEEDED_PERMISSIONS,  'publish_stream,offline_access,manage_pages');
define('_scampo', '`');
define('_svalor', '\'');

// Definimos códigos de error
define(NOT_INSTALLED,      1);
define(NO_PERMISSIONS,     2);
define(NO_MYSQL_CONN,      3);
define(NO_PAGE_DATA,       4);
define(MALFORMED_ARRAY,   90);

// Funciones para el manejo de la bdd
function escapa($dato, $char, $numeric=true)
{
  if (($numeric) && (is_numeric($dato)))
    return ($dato+0);       /* Si es 0, no devolverá FALSE */
  else
    return $char.mysql_real_escape_string($dato).$char;
}

function valor($valor, $numeric=true)
{
  return escapa($valor, _svalor, $numeric);
}

function campo($valor, $numeric=true)
{
  return escapa($valor, _scampo, $numeric);
}

function insert($tabla, $datos)
{
  $uso='
INSERT';

  $sql=$uso.'
INTO '.campo($tabla). '(';
  $values='
VALUES (';

  $campos=array_keys($datos);
  $ndatos=count($campos);
 
  for ($i=0; $i<$ndatos; $i++)
    {
      $campo=$campos[$i];
      if ($i)
    {
      $sql.=", ";
      $values.=", ";
    }

      $valor = $datos[$campo];
      $sql.=campo($campo);
      $values.= valor($valor);
    }
  $values.=")";
  $sql.=") ".$values.";";

  $res = mysql_query($sql);
  // Depuración
  echo "Ejecutando: ".$sql.'
<br/>';
  return ($res)?mysql_insert_id():false; /* Devolver el ID del elemento insertado */
}

function truncate($table)
{
  return mysql_query("TRUNCATE TABLE ".campo($table));
}

try
{
  if (!mysql_connect($mysql_host, $mysql_user, $mysql_pass))
    throw new Exception('
No puedo conectar con el servidor MySQL', NO_MYSQL_CONN);

  // Generar otra excepción por si las moscas.
  mysql_select_db($mysql_db);

  $facebook = new FacebookExtended(array(  
                     '
appId'  => $api_key,
                     '
secret' => $api_sec,
                     '
cookie' => true ,
                       ));

 
  $sesion = $facebook->getUser();
  if (!$sesion)
    throw new Exception('
Aplicación no instalada', NOT_INSTALLED);

  echo "Estamos identificados en Facebook<br/>";
  echo "Usuario: ".$sesion."<br/>";

  $permissions = $facebook->askForPermissions(NEEDED_PERMISSIONS);
   
  if (!$permissions)
    throw new Exception('
No tengo permisos suficientes', NO_PERMISSIONS);
 
  $pagedata=$facebook->api('
/me/accounts');

  if ( (!$pagedata) || (!isset($pagedata['
data'])) || (count($pagedata['data'])==0) )
    throw new Exception('
No he recibido información de páginas', NO_PAGE_DATA);

  truncate('
paginas');

  $npages = count ($pagedata['
data']);
  for ($i=0; $i<$npages; $i++)
    {
      $pdata=$pagedata['
data'][$i];
      // Depuración
      echo "Insertando pagina: ".$pdata['
name']."<br/>";

      if (!insert('
paginas', array('name'         => $pdata['name'],
                   '
access_token' => $pdata['access_token'],
                   '
category'     => $pdata['category'],
                   '
fcbkId'       => $pdata['id'],
                   '
last_update'  => 0)) )
    echo mysql_error()."<br/>";
    }

}
catch (FacebookException $e)
{
  echo "Error de Facebook: ".$e->getCode().": ".$e->getMessage();
}
catch (Exception $e)
{
  switch ($e->getCode())
    {
    case NOT_INSTALLED:
      $facebook->loginUser();
      break;
    case NO_PERMISSIONS:
      $facebook->loginUser(NEEDED_PERMISSIONS);
      break;

    default:
      echo "Ocurrió un error: ".$e->getMessage();
    }
}
?>

Publicando mensajes automáticamente

Ahora, sólo tenemos que ir insertando entradas dentro de la tabla publish, especificando el momento en el que se va a insertar en el campo time y el ID de la página donde se va a insertar (ID dentro de la aplicación, no dentro de Facebook, se puede mirar en la tabla paginas)

Asimismo la aplicación buscará mensajes nuevos para publicar y que no haga menos de dos horas (configurable) desde el último mensaje publicado en la página especificada. Para ello tenemos el siguiente código (publica.php)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php

$mysql_host = 'MySQL Host';
$mysql_user = 'MySQL User';
$mysql_pass = 'Password';
$mysql_db   = 'facebook_autopublish';

$api_key = 'xxxxxxxxxxxx';
$api_sec = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

require_once('facebook_ext.php');

// Definimos constantes
// Tiempo mínimo entre publicaciones, 7200 segundos = 2h
define(MIN_TIME_BET_PUB,   7200);

// Definimos códigos de error
define(NOT_INSTALLED,      1);
define(NO_PERMISSIONS,     2);
define(MALFORMED_ARRAY,   90);
define(BAD_MYSQL,          3);

try
{
  if (!mysql_connect($mysql_host, $mysql_user, $mysql_pass))
    throw new Exception('No puedo conectar con el servidor MySQL', NO_MYSQL_CONN);

  // Generar otra excepción por si las moscas.
  mysql_select_db($mysql_db);
 
  $campos='paginas.id as pID, publish.id as pubId, paginas.fcbkId, paginas.access_token, publish.message';

  $sql='SELECT '.$campos.' FROM `publish` LEFT JOIN `paginas` ON publish.pageID=paginas.id WHERE last_update<'.(time()-MIN_TIME_BET_PUB).' ORDER BY last_update ASC LIMIT 1';
  $res=mysql_query($sql);
  if (!$res)
    throw new Exception('Hubo un error en la sentencia MySQL ('.$sql.'): '.mysql_error(), BAD_MYSQL);

  // Si se ha devuelto algo...
  if (mysql_num_rows($res)>0)
    {
      $data = mysql_fetch_assoc($res);

      $facebook = new FacebookExtended(array(  
                         'appId'  => $api_key,
                         'secret' => $api_sec,
                         'cookie' => true ,
                           ));

      $destino=$data['fcbkId'];
      $access_token=$data['access_token'];
      $public=$data['message'];

      print_r( $facebook->api('/'.$destino.'/feed', 'post', array('access_token' => $access_token,
                                  // 'uid' => $destino,
                                  'message' => $public)));  

      $sql = 'UPDATE paginas SET last_update='.time().' WHERE id=1';
      mysql_query($sql);

    }
} catch (Exception $e)
{
  echo "Ocurrió un error: ".$e->getMessage();
}
?>

Ejecutándolo en el tiempo

El objetivo de estos scripts es poder ejecutarlos cada cierto tiempo y, si usamos un sistema UNIX podemos hacer que cada cierto tiempo (cada 2h, por ejemplo), se haga una petición a la página, para ver si tenemos mensajes nuevos pendientes de publicación, y si hay, que los publique (publica.php ya se encarga de eso).

Para añadir el programa en nuestro cron, debemos ejecutar en un terminal:

$ crontab -e

Y allí escribir lo siguiente:

15 */2 * * * wget http://dominio.xxx/carpeta/publica.php

Donde cambiamos la dirección especificada por la dirección donde hemos instalado el script publica.php. Esto hará que se ejecute cada 2h en el minuto 15, todos los días, todos los meses y todos los días de la semana.

Consideraciones de seguridad

Es una buena idea hacer que index.php sólo podamos ejecutarlo nosotros desde nuestra sesión de Facebook, restringiendo la ejecución a nuestro ID de usuario. Así evitamos que la base de datos se pueble con páginas que no queremos, y también estaría bien restringir esa página con contraseña desde Apache, así evitamos sorpresas.

También es conveniente no hacer TRUNCATE TABLE todo el rato, sino ver y comprobar que los access_tokens no cambian (lo dejamos así, porque en ocasiones, Facebook los ha cambiado, reiniciado, o éstos han caducado pasados unos meses).

En la parte de publica.php sería conveniente el envío de una contraseña, para que no pueda publicar cualquiera, ya sea en una petición POST, GET, o enviándola por SSL. Así evitamos que cualquiera ejecute publica.php , sólo nosotros.

Coloreando código con GeSHi

Sábado, 4 de Febrero de 2012 Gaspar Fernández Sin comentarios

geshiCuando queremos publicar código fuente por Internet (como en esta web), debemos hacer que los usuarios se sientan bien con el código y que sea agradable de leer. Además del indentado, es de agradecer la introducción de colores, que separen palabras clave, identifiquen cadenas, funciones de biblioteca, funciones propias, números y demás elementos que encontramos en un fragmento de código.

Por Internet, podemos encontrar una biblioteca muy útil para este propósito: GeSHi. Vamos a intentar hacer algo sencillo con ella.

Para instalarla simplemente tenemos que descargarla y descomprimirla en uno de los directorios de nuestra web (no tiene por qué ser el principal), a continuación vamos a hacer un pequeño programa que mostrará el código fuente de un fichero PHP. Suponemos que geshi.php está situado en el mismo directorio que este ejemplo:

1
2
3
4
5
6
7
8
9
10
<?php
// Basado en los ejemplos de la página oficial
require_once("geshi.php");

$codigo=file_get_contents('mi_programa.php');

$ges=new GeSHi($codigo, 'php');

echo $ges->parse_code();
?>

Con este pequeño ejemplo, se mostrará de forma coloreada el código seleccionado, aunque es conveniente el uso de cachés, es decir, es una buena idea almacenar el código coloreado, es decir, el contenido que devuelve $ges->parse_code() en un archivo aparte y cargar directamente este archivo en la siguiente petición de la página. Por ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
include_once 'geshi.php';

function color_source_file($source_file, $language)
{
  $source_cache='cache/'.basename($source_file).'_'.$language.'.cache';
  if (@filemtime($source_cache)<filemtime($source_file))
    {
      $geshi= new GeSHi(file_get_contents($source_file), $language);
      $highlighted_source=$geshi->parse_code();
      file_put_contents($source_cache, $highlighted_source);
      return $highlighted_source;
    }
  return file_get_contents($source_cache);
}

$source = 'mi_programa.php';

echo color_source_file($source, 'php');
?>

Este código es capaz de guardar en un fichero el html generado por GeSHi en un archivo de caché (se guardará en un directorio llamado cache, el nombre del fichero será el mismo que el espeficado y su formato será [nombre]_[lenguaje].cache . La decisión se toma en base a las fechas de modificación de los archivos. Si el archivo de caché no existe (por eso la @ para que no devuelva fallos PHP) o su fecha de modificación es anterior a la fecha de modificación del archivo fuente, se generará un nuevo archivo de caché y se devolverá el código coloreado. Si no, se devolverá el código coloreado leyendo directamente del archivo de caché.

Activar números de línea

Lo podemos hacer así:

1
$ges->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS);

Si ponemos GESHI_FANCY_LINE_NUMBERS cada 5 líneas saldrá el número en negrita, si ponemos GESHI_NORMAL_LINE_NUMBERS no sucederá así.

Resaltar líneas

Para resaltar líneas, debemos crear un array con los números de línea que queremos resaltar:

1
$lineas=array(5,6,7,43,53,54)

Y luego escribir:

1
$ges->hightlight_lines_extra($lineas);

Personalizando los estilos

Para ello podemos utilizar CSS. Por defecto GeSHi personaliza el style=”" de cada etiqueta, aunque genera mucho código html que podemos ahorrar. Para ello, podemos utilizar lo siguiente:

1
2
$ges->enable_classes();
file_put_contents('codigo.css', $ges->get_stylesheet(false));

Así crearemos un archivo .css que contendrá todos los estilos que puede tener un código fuente en un determinado lenguaje (en el cual hemos inicializado la clase), luego podemos incluir ese CSS ya generado en nuestro html resultante, y cambiar los colores, tipos de letra, etc.

GeSHi tiene muchísimas más opciones, para saber más, basta con un vistazo a la documentación oficial (en inglés) para descubrir todas las posibilidades de esta clase.

Linux en “Conspiración en la Red”

Jueves, 2 de Febrero de 2012 Gaspar Fernández 1 comentario

linux_antitrust1

En España, se la conoce como “Hackers 3: Conspiración en la red”, aunque no tiene mucho de tercera parte; o sólo “Conspiración en la red”, en Europa también se llama “Conspiracy.com” y en EEUU, su título original es “Antitrust“. Es una de esas películas que hace varios guiños linuxeros, aunque aparezca un Tim Robins disfrazado de Bill Gates.

En el fotograma que vemos al principio del post, podemos ver claramente un escritorio Gnome, con sus iconos característicos justo en el minuto 2:15; además de muchas pantallas de Gnome y mucho código HTML en los títulos de crédito podemos encontrar una terminal donde ejecutan “ps”:

linux_antitrust11En el minuto 51:42 y pocos segundos después, podemos ver en una imagen de una cámara (que sale en un tono azulado, excepto un sombrero rojo (Red Hat), y un minuto más tarde se ve cómo verifica los dispositivos de almacenamiento montados con mount:

linux_antitrust2

En general, la película tiene muchos guiños al software libre, como dicen en FayerWayer, podemos ver entre otras cosas:

  • A Miguel de Icaza, fundador del proyecto Gnome, entregando un premio a uno de los protagonistas.
  • Scott McNealy, CEO de Sun en 2001, entregando otro premio.
  • y muchas cosas más.

Visita otras webs de la red