2009-12-07

Cómo me echaron de facebook


Esta es la lista de eventos que pasaron hasta que me echaron de Facebook:
  1. 2007-11-10: Primera invitación a facebook recibida por mail
  2. 2009-09-27: Me suscribo a facebook
  3. 2009-09-30: Creo el grupo "Destruyendo facebook desde adentro"
  4. 2009-10-04: Publicito el grupo en un post de mi blog.
  5. 2009-10-17: Me linkea vivalinux. Entran más de 500 personas al blog en 2 días. El post del punto anterior es trolleado.
  6. 2009-10-18: Sin ningún tipo de aviso bloquean mi cuenta en facebook.
  7. 2009-10-19: Empieza la seguidilla de mails de/a facebook. El primero es este: "I've checked out on the links of the http://www.facebook.com/help.php?page=45 page and I didn't violate any rules.

    Can you inform me why the account was disabled?

    Thanks in advance,
    Aureliano Calvo."
  8. 2009-10-22: Contesta facebook (enfasis mío):"Hi Aureliano,

    Fake accounts are a violation of our Statement of Rights and Responsibilities. Facebook requires users to provide their real first and last names. Impersonating anyone or anything is prohibited. Unfortunately, we will not be able to reactivate this account for any reason. This decision is final.

    Thanks for your understanding,

    Johanna
    User Operations
    Facebook"
  9. 2009-10-22: Contesto yo (enfasis mío):"Hi,
    I've been falsely accused of impersonating someone else and this is just not true. I'm Aureliano Calvo, this is my e-mail and you can google for me. I can also provide several proofs of my identity and I'm willing to send photographs of my driver license, my passport or my identity card.

    Can you reactivate the account?

    Aureliano.
    "
  10. 2009-10-23: Vuelve facebook :"Hi Aureliano,

    Unfortunately, your account has been permanently disabled for violating Facebook's Statement of Rights and Responsibilities. We will not be able to reactivate it for any reason, nor will we provide further explanation of your violation or the systems we have in place. This decision is absolutely final.

    Thanks,

    Johanna
    User Operations
    Facebook"
  11. 2009-11-21: Pido por mail que borren todos mis datos de facebook:"I would like to have removed all my data from the facebook servers (generated from the account asociated with this e-mail).

    Thank you very much in advance,
    Aureliano Calvo"
  12. 2009-11-25: Contesta facebook sobre la remoción de datos:"Hi Aureliano,

    Facebook users have the option to either deactivate or delete their accounts. The differences between the two options are described below.

    If you deactivate your account, your profile and all information associated with it are immediately made inaccessible to other Facebook users. What this means is that you effectively disappear from the Facebook service. However, we do save your profile information (friends, photos, interests, etc.), so if you want to reactivate at some point, your account will look just the way it did when you deactivated. Many users deactivate their accounts for temporary reasons and expect their information to be there when they return to the service. You can deactivate your account through the "Deactivate Account" link on the Account page.

    If you do not think you will use Facebook again and you would like to delete your account, you can do this yourself by navigating to the following page:

    http://www.facebook.com/help/contact.php?show_form=delete_account

    Please keep in mind that once your account is deleted, you will not be able to reactivate the account or retrieve any of the content or information you have added.

    If you are currently unable to access your account, you will need to reset your password in order to log in. In order to do so, click the "Forgot your password?" link that appears above the field where you would normally enter your password. Entering your login email address on the next page will cause a new password to be sent to that email address. Once you receive your new password and can log in, you can deactivate or delete your account using the steps outlined above. We apologize for any inconvenience this may cause.

    Thanks for contacting Facebook,

    Brett
    User Operations
    Facebook"
  13. 2009-11-25: Contesto que no puedo porque deshabilitaron la cuenta:"I've requested that all my data should be removed from the facebook servers by email because my account has been disabled.
    Please remove all my data from the facebook system.

    Thank you very much,
    Aureliano Calvo."
  14. 2009-11-29: Me dicen que me joda que se quedan con todos los datos:"Hi Aureliano,

    When an account is disabled, the profile and all information associated with it are immediately made inaccessible to other Facebook users. What this means is that you effectively disappear from the Facebook service. In addition, Facebook does not use information associated with disabled accounts.

    Unfortunately, for safety and security reasons, we cannot delete from our servers information associated with disabled accounts. We also cannot grant you access to a disabled account to retrieve content from it, nor can we provide you with any content that was associated with this account. We apologize for any inconvenience this may cause. This decision is final.

    Thanks for your understanding,

    Brett
    User Operations
    Facebook"
Conclusiones:
  • Facebook contesta en tiempo y forma los pedidos por mail.
  • Facebook no identifica a las personas que contestan y no toma responsabilidades.
  • Facebook echa gente que no viola sus términos y condiciones (el punto que dicen que violé es mentira)
  • Facebook se niega a dar información sobre que hiciste mal (probablemente para cubrirse del punto uno) y encima lo hacen mal (como se ve en el punto anterior).
  • Facebook se niega a borrar los datos de la gente que echa (cosa que, hasta donde yo sé, es ilegal en Argentina).
O sea, mejor irse lo antes posible de ahí. No dejes que los mercaderes de relaciones te dominen.

Happy hacking,
Aureliano.

2009-12-02

Deshabilitando autoindent de () en vim

Para editar html con javascript con algo más de onda en gvim adopté el siguiente seteo:

:set inde=
:set ai

Para más detalles mirá acá.

Happy hacking,
Aureliano.

2009-11-26

Invitaciones para Google Wave

¿Querés una invitación para Google Wave?

Avisame y te la mando (tengo 16 para repartir).

Seguridad nacional

Esto le pasó ayer a un compañero de trabajo. Espero que sirva para que sepamos que no hay que creer todo lo que pasa en la tele:

Ayer fue al super (disco)  a buscar la compra que habia hecho antes de
ayer ya que me avisaron que los empleados del delivery estaban de paro
por un reclamo sindical. cuando estoy llegando veo que en la esquina hay
una especia de muchedumbre de 20-30 personas y una camara de TV. No doy
bola y paso de largo hasta el super, que encuentro cerrado, en las
puertas de vidrio habia pagadas que decia qu ella empres ales mando la
policia a los que estaban reclamando para "apretarlos" y por ese se
metieron adentro y cerraron. Digo.. "uh que mal" y me voy pensando que
la muchedumbre de la esquina tendra algo que ver con eso. cuando llego a
la esquina y presto atencion a lo que pasa me doy cuenta que en realidad
hay 20-30 energumenos en chomba, camisa rosa+sweater al cuello y/o
vestido largo y ollas de fondue gritandole a la camara "seguridad!
seguridad!" mientras esgrimen cartelitos escritos prolijamente con
marcador con la palabra en cuestion ("seguridad"). Detras de todos
ellos, sentado en el piso esta durmiendo un señor de entre 50-60 años
que vive en la calle en esa esquina desde hace una semanas. Los
protestantes lo ignoran olimpicamente, pero el señor se despierta y
percibe que hay algun quilombo y desde el piso se une a los canticos al
tiempo que ademas aplaude (!?)

Me abro paso vocifereando que son todos unos payasos mediaticos (como
osvaldo pagani) pero a nadie parece importarle o molestarle lo que digo
o hago (que lastima! ya que estan todos hipnotizados cantando su mantra
a la camara.

Minutos despues cuando llego a casa me dicen que en el noticiero de
America transmitian en vivo una "protesta de los vecinos de palermo"
demandando mas seguridad o algo asi.

Media hora mas tarde cuando paso otra vez por la esquina paseando al
perro "los vecinos de palermo" no estan mas (el noticiero de america ya
termino) pero sigue estando el señor que vive en la esquina condiciones
de "seguridad" bastante precarias (hasta tanto no se entere la UZZEP).

Alguien vio algo de esto en el noticiero de America ayer? que dijeron?


Happy hacking,
Aureliano.

2009-11-25

Firewall, Windows 7 y guests bridgeados de VMWare

Si dejo el firewall de Windows 7 activado filtra las conexiones entrantes a las VMs del VMWare Server que están conectadas en modo "bridged" a la red. No sé el ip del guest ya que se obtiene por DHCP. Si apago el firewall, las conexiones entrantes llegan hasta las VMs.

Saben como hacer para dejar el firewall prendido pero que las conexiones que van a los guests en modo bridged pasen sin ser filtradas? Qué reglas tengo que agregar/sacar?

Muchas gracias,
Aureliano

2009-11-22

Como facebook cuida tu privacidad

Vía slashdot.

Una mina con tratamiento por depresión en Canadá le niegan el tratamiento porque tiene fotos en la playa y en una fiesta en facebook (en una cuenta "lockeada"). Más detalles ver acá.

Esperemos que no se mate por esto,
Aureliano.

2009-11-20

Visualizando más

En este post prometí que iba a contar que cosas vi en VisWeek 2009. Y como lo prometí, aunque un poco tarde acá pongo mis notas de la conferencia (tienen mezcla con inglés):

Domingo 11:

8:30am
* Keynote: Bill Cheswick
*"Visualization is about coolness"
* Lots of developments. But almost nothing in the industry

Problemas:

* Layout arbitrario
* La evolución no es clara
* Las cosas que se sacan no se muestran
* Destruyen el contexto

Usa minimum distance spanning trees todo el tiempo para mostrar cosas aunque descarta el 30-40% de las conexiones.

How do you measure security?
* Generals and CIOs want to know.
Trusecure. Always and human in a step.

Network Access Security: contar cantidad de puertos abiertos
privilege scalation: programas que corren como root desde un usuario normal.

Parte difícil, como hacer los reportes.

2D sin tiempo -> 3D para el tiempo?

Ninguna buena visualización buena de IPv6


--------------------------------------------------------------------
Sesión de visualización de redes
10:30am

Visualization of complex attacks.
* Desde el pto de vista del sysadmin.
* Ataque simple: Una acción
* Ataque complejo: combinación de los anteriores
* DDNS
* "Multi-step"
* Worm propagation
* Interesante: las líneas hacen un "fade-out" con el tiempo. Las cosas nuevas son menos transparentes.
* El IDS informa que ataques simples pertenecen al mismo ataque complejo.

OverFlow: An Overview Visualization for network analisis
FloVis -> http://www.flovis.net

Security visualization tools and ipv6 addresses
* Demasiadas direcciones (128 bits)
* La mayoría de las visualizaciones no bancan ipv6
* muestra cambios en headers 4->6
* Se enfoca en "process and reformat" y "display"
* Muestra las comunicaciones con un scatter plot
* Le da un índice ordenado a cada dirección
* Usa la estructura de la dirección para agrupar direcciones
* Treemap (no me gusta, parece muy "cluttereado":
* Color: tipo de paquete (tcp, udp, etc)
* Tamaño: volumen de tráfico

1:00p
Malware and forensics
A visual Analytic framework for Exploring Relationships in Textual Content of Digital Forensics Evidence
Muestra como indexan el disco rígido y después filtran para ir buscando cosas en el disco rígido

Visual Analysis of Malware Behaviour Using Treemaps and Thread Graphs
CWSandbox.org
* Analisis automático de malware porque les mandan como 4000 ejemplos todos los días.
* Hacen un treemap con syscalls clasificados por tipo c/ color y cantidad de llamadas en el tamaño.
* El análisis es dinámico (o sea, ejecutan el malware).
* Por eso no los jode tanto la ofuscación
* Pueden detectar pdfs "malvados" mirando los treemaps (o clusterizando los datos).

Visualizing Compiled Executables for Malware Analysis
http://www.offensivetechnology.net (VERA)
* Dibuja los basic blocks que se ejecutan (todos, también los que se generan dinámicamente)
* Arma el grafo de los basic blocks
* les da más peso a los basic blocks que más se ejecutan
* por eso los loops que más se ejecutan quedan más grandes.
* pone diferentes colores a los basic blocks en función de cómo se generan
(ej: en el ejecutable en una sección de código,
en el ejecutable en una sección de datos,
generado dinamicamente,
etc)

2:30p
Users and Usability
Visualizing Cyber Security: Usable Workspaces
* Los analistas se quejan de que no pueden interactuar con los datos.
* Usan muuuuuuuuuuucha definición (10240x3200) y pueden mostrar simultáneamente los datos y los patrones que se forman en
alto nivel.
* History trees. Parecen como los branches en un control de versiones o algo así.

Visualization is Better! A Comparative Evaluation
http://tnv.sf.net vs ethereal en newbies
* Controlled experiment comparing two tools
* http://vizsec.org/datasets/

4:45p
Visualizing Keyboard Pattern Passwords
* Muestra como mostrar que teclas apretaron con una secuencia de reglas sobre como dibujarlos.
* Encontraron patrones mirando los passwds de a 25 (500 en total)
* Generaron diccionario con algunos patrones (500K passwords)
* Con ese diccionario crackeron passwds de verdad que John The Ripper no encontró

Visualizing Firewall Configurations Using Created Voids
Espacio:
5 dimensiones escalares (host1, port1, protocol, host2, port2) y un valor categorico (allow, deny).
regla:
(min_max de (host1, port1, protocol, host2, port2), (allow|deny))
Usa paralell coordinates :( (problemas de oclusión)

Panel (Visualization + Security = Science?):
Fue una pedorrada

Lunes 12:
Keynote VAST.
Storytelling.
Contó como es la estructura de un cuento clásico (introducción, nudo y desenlace), el camino del héroe (los 12 pasos que siguen casi todos los libros y películas) y dio una heurística para darse cuenta si algo es una buena historia o no. La misma consiste en si es posible responder estas 4 preguntas facilmente:
1. Quién es el héroe?
2. Qué quiere el héroe?
3. Qué impide que el héroe lo consiga?
4. Qué hace el héroe para solucionar el problema?
El chabón afirma que todos tenemos una habilidad natural para entender cuentos, y que por eso es bueno organizar las visualizaciones y presentaciones en forma de cuento.

VAST
Multidimentional Data session

A framework for uncertainty-aware visual analytics
Proponer manejar la incertidumbre todo a lo largo del pipeline de procesamiento de datos. Puede ser que los datos vengan con "incertidumbre" o que las transformaciones agreguen incertidumbre. Al final muestra la incertidumbre de varias maneras. Ej: color (segun que variable introduce mas incerteza), transparencia (para esconder datos menos confiables), o desagregando (pero solo para pocos datos)

Combining automated analysis and visualization techniques for efective exploration of high-dimentional data
Intenta detectar automáticamente que visualización de los datos es mejor. Creo que puede servir para mostrarle al usuario automáticamente vistas de datos que tengan algún sentido (muestran en scatter plots y paralell coordinates). También dicen que vistas muestran mejor una categorización dada.

Two-stage framework for visualization of clustered high dimentional data.
Hace algo parecido que la charla de arriba pero proyectando muchas dimensiones en 2 o 3.

Model space visualization for multivariate linear trend discovery.
Muestra una técnica manual para encontrar correlaciones lineales de dimensiones cuando hay muchas dimensiones

Martes 13:
Vast. Text analytics session.

LSAView: A tool for visual exploration of latent semantic modelling.
Sirve para clasificar documentos en función del tema. Visualiza el documento para que un analista decida si el clustering está bien.

Parallel tag clouds to explore and analyze faceted text corpora.
Analizan fallos de cortes EEUUenses. POnen las palabras que son más comunes en un documento y menos comunes en el resto (Dunning's log likehood). Ponen los tags con tamaño en gráfico de paralell coordinates. Parece interesante.

Describing story evolution from dynamic information streams.
No me gustó. Muestra como intentan detectar historias que se extienden a lo largo de diferentes artículos periodísticos.

What's being said near "Martha"? Exploring name entities in literature.
Muestra varias herramientas usando como ejemplo el análisis de literatura "rara":
* FeatureLens
* Wordle (Dunning's log likehood)
* POSvis
Necesitaban los Stop-words para analizar el texto.

VAST contest dataset use in education.
Hay un montón de datos de los diferentes VAST contest. Muestran como los usan para dar clases.

VARIAS DE LAS PRESENTACIONES DESTACARON LA NECESIDAD DE PODER IR AL TEXTO ORIGINAL QUE GENERÓ LOS DATOS QUE ESTÁN MOSTRANDO LAS HERRAMIENTAS. Posiblemente pase lo mismo con gráficos sumarizados de ataques.

Vast. Analysis Process & Graph Analytics

Connecting the dots in visual analysis.
Integran notas y flujos de trabajo en el proceso de analisis de los datos. Hacen todo un manejo bastante complicado de los flujos de trabajo (action trails?) del analista. También sugiere a los analistas que miren cosas que ya hicieron. Les preguntaron a 2 analistas sobre si les gustó el enfoque y dijeron que sí.

Capturing and supporting the analysis process.
Otro paper que explica como soportar el proceso del analista. Ejemplo con data set del VAST challenge 2008.

Evaluating visual analytics systems for interactive analysis: Deriving design principles from a case study.
Estudian como un analista usa jigsaw y otras herramientas para hacer tareas de analisis de datos.

A multi-level middle-out cross-zooming approach for large graph analytics.
Muestra una parte del grafo con una "resolución" y otra parte con otra "resolución". Cuando hace zoom-in en algún lado hace zoom-out en otro para mantener + o - constante la cantidad de cosas que muestra. Sirve para grafos de tipo small-worlds (algo parecido a esparsos). La implementación es un bolonqui porque quieren que todo sea interactivo (quién no?).
http://www.pnl.gov/wong

Visual analytics of graphs with multiple connected components.
Share-holder networks == Weighted directed acyclic graph.
Buscan estructuras comunes en estos grafos. Para eso hacen un proceso iterativo donde buscan los componentes.

Tutorial: Exploring design decisions for effective information visualization
Usando datos de las últimas 8 elecciones norteamericanas hicimos un montón de treemaps distintos analizando que se ve en cada uno, que no se ve y buscando la mejor forma de mostrar cosas.

Miércoles 14:
8:30a Keynote: Visual thinking and visual thingking tools
En esta charla Collin Ware estuvo hablando sobre como percibimos las cosas y como esto afecta como nos relacionamos con las herramientas de visualizacion. En particular mostró como se compara una interfase zoomeable con una que abre ventanitas para mostrar detalles y dio algunas formas muy simples de calcular que esfuerzo requiere hacer tareas en ambas interfaces. Usando el mismo ejemplo, también mostró porqué en otro estudio que él hizo convino usar imagenes en vez de video para mostrar movimiento. Esta charla también le da sustento teórico a la necesidad de tener más espacio disponible en el/los monitor(es).

4:15p Models and theories
A nested model for visualization design and validation
* problem
* data/op abstraction
* enc/interact technique
* algorithm
Intenta ordenar cómo saber si una visualización es "buena" o no. Divide en 4 diferentes "aspectos" a una visualización.

Conjunctive visual form.
Hace una especie de query by example pero en vez de poner un ejemplo pone un conjunto para cada dimensión (cross-filtered views, paper del año pasado).

Jueves 15:
Interaction Techniques for Selecting and Manipulating Subgraphs in Network Visualizations
Explica un montón de diferentes formas de operar sobre conjuntos de nodos en un grafo y elegir que conjunto de nodos. Están buenos pero son un quilombo.
Software: Navigator.

ActiviTree: Interactive Visual Exploration of Sequences in Event-Based Data Using Graph Similarity
Buscar patrones en eventos. ¿Puede servir para analizar logs?

“Search, Show Context, Expand on Demand”: Supporting Large Graph Exploration with Degree-of-Interest
Enfoque bottom-up para explorar grafos. Search, show context, expand on demand.
cada nodo tiene un "interes". Con eso, la query que hace el usuario y la estructura del grafo hacen una cuenta y definen el "degree of interest" (DOI) de un nodo. El contexto es el subgrafo conexo que contiene a ese nodo y tiene mayor interés. Para hacer que ande mejor un algoritmo greedy "difunden" el interés en los nodos vecinos. Lo usan para buscar fallos judiciales y como se relacionan.

A Comparison of User-Generated and Automatic Graph Layouts.
Probaron layouts generados a mano por usuarios (usando Surface, de MS) para las tareas que iban a pedir a otros participantes. Después agarraron esos layouts y 3 layouts automáticos más e hicieron que otra gente haga esas cosas. Al final, el mejor fue el force directed layout.

Smooth Graphs for Visual Exploration of Higher-Order State Transitions.
En vez de tomar A-B como "edge" se fijan en secuencias más largas (A-B-C-D, por ejemplo). Usaron esto para analizar el comportamiento de pingüinos.

Harnessing the Web Information Ecosystem with Wiki-based Visualization Dashboards.
Muestra un wiki que sirve para armar dashboards con algunos tipos de visualizaciones.

Viernes
code swarm: A Design Study in Organic Software Visualization
MOstró codeswarm.

Towards Utilizing GPUs in Information Visualization: A Model and Implementation of Image-Space Operations
Mostró un prototipo que usa la GPU para hacer visualizaciones (basado de shaders) y un programa que sirve para implementar los shaders uniendo cajas con flechas.

A Multi-Threading Architecture to Support Interactive Visual Exploration
Arquitectura para hacer visualizaciones:
1 thread de eventos
n threads para n visualizaciones
Los threads de las visualizaciones son interrumpidos cuando los datos que usan para calcular cosas cambian.
Hay un cache de cachos de dibujos y datos intermedios

Protovis: A Graphical Toolkit for Visualization
Toolkit para hacer visualizaciones en JavaScript de los autores de Flare y Prefuse
http://protovis.org

2009-10-30

Wipeando discos en el tercer mundo

Tuve que wipear un disco rígido y las herramientas de wipeo que tenía a mano no lo reconocían.
La solución:

# yes > /dev/sda

¿Se les ocurre alguna solución más corta? (wipe-golfing)

Happy hacking,
Aureliano.

2009-10-15

Reflexiones sobre las colas

Como les conté en el post anterior, estoy en VisWeek 2009. En esta conferencia hay gente de alrededor de todo el mundo, pero el grueso es de Europa y EEUU. Y hay algunas cosas que me llamaron la atención (positivamente) sobre el comportamiento de la gente por acá.
La primera cosa es que las charlas empiezan y terminan en horario (cronométricamente). El primer día de la conferencia me fui a comer y pensé que la sesión de la tarde iba a empezar algo así como media hora más tarde y en vez de eso empezó en horario y me perdí una charla. Comparado con la laxitud de la Eko, que terminó 2 horas y media después del horario planificado, es mucho mejor. Así que aprendí y empecé a llegar en horario. No como en Argentina que las cosas se van atrasando cada vez más y llegamos a situaciones ridículas donde el horario de entrada a un boliche es las 3 AM.
Pero lo que más me asombró es como se arman colas automáticamente cuando se requiere acceso a un recurso escaso. Es en todos lados. Cuando hay que entrar a una sala grande y la puerta es un cuello de botella. Cuando sacamos comida de las mesas de la conferencia. Esperando el ascensor (!). Si prestás atención, se arman colas en todos lados y todo el mundo las respeta.
¿Será realmente más eficiente hacer las cosas más ordenadamente?,
Aureliano.

2009-10-13

Visualizando

El viernes pasado partí a Atlantic City para participar de Visweek, enfocándome especialmente en Vizsec. Como el avión llegó a la mañana del sábado a Filadelfia y la conferencia empezó el domingo, dediqué parte del sábado a caminar por Filadelfia. Y me gustó bastante. Es una ciudad linda, con alma. A pesar de que me mojé porque llovía de a ratos y estaba bastante mareado porque el vuelo desde Washington fue en un avión diminuto que se movía mucho lo disfruté un montón.
A las 13:47 me tomé el tren a Atlantic City, y colapsé por falta de sueño. Alrededor de las 16:30hs llegué a Atlantic City y fui al hotel. El hotel de Atlantic City es una mole gigantesca y el casino es increíblemente grande (no me creen, fíjense).
Y la posta es que no me gusta, pero tiene todas las comodidades que hacen falta para la conferencia, que viene super-interesante. Estoy tomando notas de las charlas a las que voy, así que probablemente las ponga en el blog una vez que las muestre en Core (ellos pagan el viaje así que me parece bien que las vean primero).
Acá estoy empezando a conocer gente y hay un grupo de alemanes que son buena onda y me adoptaron. Estoy contento de que estoy aprendiendo un montón y que estoy conociendo gente super-interesante. Espero saber mucho más sobre visualización cuando termine la conferencia. En particular, estoy buscando herramientas para hacer visualizaciones que sean medianamente razonables (para una definición mía y absolutamente arbitraria de razonable). Por ahora no encontré nada :( y los investigadores con los que hablé todos implementaron sus propias herramientas para hacer visualizaciones.
Por último, cuando termine la conferencia me voy 3 días a Nueva York. ¿Qué me sugieren que haga por allá? Lo que tengo pensado es ir al Central Park, subir al Empire State, cruzar caminando el puente de Brooklin, ir a Wall Street, ir a Broadway, ir a la Quinta Avenida (y quizás hacerme el muerto donde mataron a John Lennon), tomarme el ferry a Staten Island para ver la estatua de la Libertad e ir al Museo de Arte Moderno.
Cuando pueda subo fotos de la conferencia, el viaje, etc.
Happy hacking,
Aureliano.

2009-10-06

Mi primer advisory

Encontré un XSS persistente en una aplicación de ejemplo de Jetty. Si quieren ver el advisory, pueden verlo acá.

Jetty es un container de servlets (como Tomcat) que mantiene la Apache foundation (como Tomcat) pero que intenta ser liviano (no como Tomcat).

Happy hacking,
Aureliano.

2009-10-04

Destruyendo facebook desde adentro

Update: Estuve refleccionando sobre todos estos eventos en este post.
Update: Puse todos los eventos sobre como me echaron de facebook y se niegan a borrar mis datos en este post.
Update: Mi cuenta de facebook está deshabilitada. Mandé un mail para preguntar porqué la deshabilitaron, porque hasta donde yo sé no violé ninguna de las reglas del servicio. Los voy a mantener al tanto de la respuesta.

Hola a tod@s,
quería contarles que tengo un nuevo proyecto. El mismo se llama "Destruyendo facebook desde adentro" y consiste en ver cómo hacer para que en vez de que haya una sola empresa que maneje la red social de amigos y conocidos cada persona pueda tener sus datos donde quiera e igualmente pueda relacionarse con otra gente. Un poco como el mail, se puede mandar correos a personas que tienen un proveedor de correo distinto del tuyo.
Para eso, hay algunas actividades que pueden hacer:
  • sacar la propaganda con greasemonkey (http://userscripts.org/scripts/show/13787)
  • taggear mal las fotos
  • hacer apps que hagan cagadas
  • hacer bots que molesten en facebook (¿convendrá greasemonkey o WATiR para esto?)
  • hacer una red social distribuida que use cripto de clave pública para autenticar a las personas (tipo PGP).
Si quieren adherir a mi causa, pueden hacer todo esto (o alguna otra cosa que les parezca conducente) y unirse al grupo en http://www.facebook.com/group.php?gid=141856259260

Happy hacking,
Aureliano.

2009-08-06

¡Me atacó el phishing gallego!

Leanlo, ¡no tiene desperdicio!

from Phanida_Roidoung 
reply-to allaccountuser@live.com
to
date Thu, Aug 6, 2009 at 2:20 PM
subject Dear dc.uba.ar Owner

hide details 2:20 PM (34 minutes ago)


Reply

Follow up message
Dear dc.uba.ar Owner,

We are currently carrying-out a mentaiance
process to your dc.uba.ar account, to
complete this process you must reply to
this email immediately,and enter your User
Name here (******) And Password here(*****)
if you are the rightful owner of this account.

This process we help us to fight against
SPAM MAILS.
Failure to summit your password, will render
your email address n-active from our database.
You can also confirm your email address by
logging into your dc.uba.ar
account at:http://dc.uba.ar

NOTE: You will be send a password reset
messenge in next two (2)
working days after undergoing this process
for security reasons.

Thank you form dc.uba.ar!
THE dc.uba.ar TEAM

En particular, me hace cagar de risa esta parte:
to
complete this process you must reply to
this email immediately,and enter your User
Name here (******) And Password here(*****)
if you are the rightful owner of this account.

Es la versión de mails de phishing del virus gallego.
Happy hacking,
Aureliano

2009-08-01

Algunas cosas buenas y malas de python (para mi)

Cosas buenas:

  • Duck-typing
  • Los espacios sirven para marcar bloques de código
  • Los bloques de código son elementos de primer orden del lenguaje
  • Soporte para un montón de cosas (por ejemplo pyCUDA)
  • Matches por nombre en expresiones regulares
Cosas malas:
  • Los objetos no son más que un mejunje de 2 diccionarios (el de la clase y el del objeto, sin orden)
  • Un montón de cosas están sacadas de los objetos, pero otras no. Esto lo deja inconsistente y molesto (teniendo que definir un montón de métodos mágicos de la forma __magic__)
  • Sin sintaxis buena para llamar al método de la clase padre (super, ¿dónde estás?)
  • "There is only one way to do it" salvo por las reglas para poner nombres de métodos ni clases. Por ejemplo, no queda claro si debe ser method_name, methodName o methodname (hay de los 3 en las biliotecas estándares)
  • No hay forma de definir bloques de código sin nombre (aunque con el agregado del with_statement, están bastante cerca del manejo de bloques de ruby). Los lambdas son una cosa que se queda muy corta.
  • Sintaxis especiales para demasiadas cosas (ejemplo: list comprehensions, for, while, decorators, etc)
  • Falta de separación entre strings y listas de bytes (¿Python 3000 lo arregla?, no sé)
  • Metaprogramar es complicado porque no hay bloques sin nombre y hay que definir/ llamar un montón de métodos __mágicos__.
  • Separación entre comandos y expresiones.
Happy hacking,
Aureliano.

2009-07-25

Tests en rapidito

En rapidito los tests siguen creciendo. Ahora le agregué un test al modelo de la base de datos (que por ahora tiene una sola tabla, pero espero que crezca bastante) y otro a los views y controllers (que también tienen que crecer).
Todo está agregado en el rakefile:

$ rake -T
(in /home/aure/devel/rapidito)
rake db:initial # Generate the initial pages for the wiki
rake db:migrate # Migrate the database
rake default # Run all tests
rake test # All tests
rake test:functional # Test controllers and views
rake test:lib # Library tests
rake test:model # Model tests

Sigo usando una sqlite3 en memoria para todos los tests (parece que esto paga) y para hacer los tests funcionales usé la gem rack-test como dice en la documentación de sinatra. Es impresionante como sirve esa lib tan chiquitita.
Y los tests siguen creciendo, ya van 651 líneas de código
~/devel/rapidito$ find test -name "*.rb" | xargs wc -l
26 test/lib/test_lang_hacks.rb
60 test/lib/elem_stack_test.rb
35 test/lib/rapidito_data_test.rb
187 test/lib/rapidito_html_test.rb
81 test/lib/state_test.rb
48 test/lib/nodes_test.rb
110 test/lib/tokenizer_test.rb
55 test/model/pages_test.rb
49 test/functional/start_test.rb
651 total

Happy hacking,
Aureliano

2009-07-19

rapidito en github

Hola,
puse un repo de rapidito (mi proyecto de base de datos basado en wikis) en github. Está disponible en http://github.com/aurelianito/rapidito/tree/master.
Si quieren usarlo, solo hay que hacer en el directorio clonado:


rake db:migrate
rake db:initial
rackup

Una vez que está corriendo, te conectás a http://localhost:9292 y tenés todas las instrucciones.
Tiene como requerimientos sinatra, activerecord, markaby y sqlite3 (con su gem).
Voy a ir avanzando este proyecto cuando tenga algo de tiempo. Por ahora es solo un wiki más, pero espero que se transforme en algo distinto. A medida que tenga más cosas agregadas voy a ir avisando en el blog.
Escucho sugerencias, y acepto patches ;).

Happy hacking,
Aureliano.

2009-07-16

rake sh en otro directorio

Problema: Quiero ejecutar un comando desde otro directorio en rake (por ejemplo, ejecutar un makefile que está en un subdirectorio).
Solución: Agrego al rakefile la posibilidad de ejecutar comandos en otro directorio. Para eso, al principio de mi rakefile puse:

require 'fileutils'

def sh_in_dir( dirname, *args, &block )
old_path = pwd
FileUtils.chdir( dirname )
sh( *args, &block )
FileUtils.chdir( old_path )
end


Happy hacking,
Aureliano.

Firefox remoto y local

Problema: Tengo un firefox corriendo en X11 y quiero correr otro firefox remotamente por X11 en otro host (usando el mismo display). Si lo ejecuto directamente, haciendo ssh -X -n host firefox en vez de ejecutar el Firefox en la otra máquina, abre otra ventana del firefox que ya tengo corriendo.
Solución: Usando la opción -no-remote evito que el firefox busque ventanas de firefox abiertas para abrir una ventana nueva y se ejecuta remotamente. La línea de comando entera es: ssh -X -n host firefox -no-remote.

Gracias Pedro Varangot por contarme del parámetro -no-remote

Happy hacking,
Aureliano.

2009-07-14

Logo versión 2


Esta versión del logo de rapidito se viene con texto y fondo. Y me parece que se "mueve" un poco más. De a poco voy aprendiendo a usar el inkscape.

2009-07-12

Logo para rapidito


Estoy pensando en un logo para la base de datos/wiki que estoy haciendo. ¿Qué les parece este? La idea es ponerlo arriba a la izquierda en todas las páginas y como ícono en la barra de navegación del browser.

2009-07-05

Patch en Regexp para poder usarlas como clave en un Hash

Siguiendo mi proyecto de hacer mi wiki en Ruby, encontré un comportamiento muy raro.
Generé un hash (que se llama @rules) que no tiene un elemento que tiene. O sea, @rules[@rules.keys[2]] da nil, pero @rules.values[2] devuelve el objeto asociado a la clave @rules.keys[2]. Como este hash tiene como claves un montón de expresiones regulares, me imaginé que había un problema con el hash y el eql? de Regexp, así que los implementé de nuevo y monkeypatchié.
Este es el código:

class Regexp
alias_method :old_rapidito_inspect, :inspect

def inspect
@inspect = old_rapidito_inspect if @inspect.nil?
@inspect
end

def eql?( other )
false if other.class != Regexp
self.inspect == other.inspect
end

alias_method :"==", :eql?

def hash
self.inspect.hash
end
end


Esta corrección me anduvo con la siguiente versión de ruby:
$ ruby --version
ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux]


Espero que les sirva.
Happy hacking,
Aureliano.

2009-07-03

Upcase con acentos

En ruby no se calculan los cambios minúscula mayúscula para las letras acentuadas. Por ejemplo:

irb(main):002:0> puts "áa".upcase
áA
=> nil

Para que ande un poco mejor (y tome las letras acentuadas de un montón de lenguajes), encontré un hack que en 10 líneas de código hace que puedan pasarse a mínusculas y mayúsculas los caracteres acentuados. Usando que todas las letras minúsculas y mayúsculas acentuadas están todas juntas, armé un par de gsubs que resuelven el problema.
class String
alias_method :old_rapidito_upcase, :upcase
def upcase
self.gsub( /\303[\240-\277]/ ) do
|match|
match[0].chr + (match[1] - 040).chr
end.old_rapidito_upcase
end

alias_method :old_rapidito_downcase, :downcase
def downcase
self.gsub( /\303[\200-\237]/ ) do
|match|
match[0].chr + (match[1] + 040).chr
end.old_rapidito_downcase
end
end

Lo que hago acá es reemplazar las funciones upcase y downcase para que manejen los acentos, sumando o restando 32 (40 en octal) al segundo byte. Pongo este código en lang_hacks.rb y sale todo con fritas. Mirando el irb
irb(main):001:0> require 'lang_hacks'
=> true
irb(main):002:0> "aÁ".upcase
=> "A\303\201"
irb(main):003:0> puts "aÁ".upcase

=> nil

Espero que les sirva.
Happy hacking,
Aureliano.

2009-07-01

Rap del dengue sojero

Entra enfermo de dengue
Mosquito pica enfermo
Sapo se come mosquito
¡No hay dengue!

Alfredo planta soja
Alfredo fumiga veneno
Veneno mata sapito
Sapo muerto no come mosquito
¡Hay soja!
¡Hay veneno!
¡No hay sapo!
¡Hay dengue!
¡Hay mosquito!

Hormiga atómica fumiga mosquito
Sapo vivo se caga de hambre
Año que viene
¡Se muere!
¡No hay sapo!
¡Hay dengue!
¡Hay malaria!
¡Hay peste!
¡Hay mosquito!

Nonsanto reparte los sobres
Al periodismo in-the-pendiente
¡Hay sobre!
¡No hay noticia!
¡Hay soja!
¡Hay veneno!
¡No hay sapo!
¡Hay dengue!
¡Hay mosquito!

Nonsanto no garpa bloguero
Bloguero publica noticia
¡Nonsanto se cae de culo!
¡Cristina prohibe la soja!
¡No hay sobre!
¡Hay sapo!
¡No hay dengue!
¡No hay mosquito!

Vía: artepolitica.com

Autofit guest solo anda si abrís la consola después de loguearte

Comprobado en Vmware Server 2.0.1.
Host: KUbuntu 9.0.4. 64 bits.
Guest: KUbuntu 9.0.4. 32 bits.

¿Estoy haciendo algo mal?
Aureliano.

2009-06-30

Más rapidito

Como les estuve contando, sigo escribiendo mi wiki. Ya parsea un subconjunto interesante del lenguaje definido por trac.
Siguiendo la tradición, les cuento como está avanzando el tokenizer. Al tokenizer lo simplifiqué para que devuelva la expresión regular que matcheo junto con el match (en vez del "tipo"). Esto hizo que la interfase para definir las reglas para tokenizar sea más simple. Si no hay ninguna regla que matchee sigue devolviendo ["string", :text].
Sin más, acá el código:

module Rapidito
class Tokenizer

def initialize( *delimiters )
@delimiter_list = delimiters + [/\z/]
@match_cache = nil
end

def source
valid_cache? ? @match_cache[0].to_s + @source : @source
end

def source=(s)
@match_cache = nil
@source = s
end

def has_next?
!@source.empty? || valid_cache?
end

def valid_cache?
(!@match_cache.nil?) && (@match_cache[0].to_s.length > 0)
end

def next_match
@delimiter_list.map {|regex| [regex.match(@source),regex]}.reject {|p| p[0].nil?}.inject do
|better,new|
better_pos = better[0].pre_match.length
new_pos = new[0].pre_match.length

if better_pos < new_pos
better
elsif new_pos < better_pos
new
elsif better[0].to_s.length > new[0].to_s.length
better
else
new
end
end
end

def next_token
if @match_cache #cached delimiter
rv = @match_cache
@match_cache = nil
return rv
end

match = next_match
p = match[0].pre_match.length
@source = @source[p + match[0].to_s.length, @source.length]

if p == 0 #delimiter
match
else #text
@match_cache = match
[match[0].pre_match, :text]
end
end

def all_tokens
tokens = []
while has_next?
tokens << next_token
end
tokens
end
end
end

Y si miran los tests de unidad, van a ver que también quedaron más lindos:
require 'test/unit'
require 'rapidito/tokenizer'

include Rapidito

class TokenizerTest < Test::Unit::TestCase

def test_no_token
tok = Tokenizer.new
tok.source = "aaaa"
assert_equal true, tok.has_next?
assert_equal ["aaaa", :text], tok.next_token
assert_equal false, tok.has_next?
end

def assert_all_tokens( expected, tokenizer )
assert_equal expected,
tokenizer.all_tokens.map { |token, kind| [token.to_s, kind] }
end

def test_two_delimiters
tok = Tokenizer.new(
/\|/, /;;/
)

tok.source = "aa|bbb;;;;cccc"
assert_all_tokens \
[ ["aa", :text], ["|", /\|/], ["bbb", :text],
[";;", /;;/], [";;", /;;/], ["cccc", :text] ],
tok

tok.source = "aa;;bbb||cccc"
assert_all_tokens \
[ ["aa", :text], [";;", /;;/], ["bbb", :text],
["|", /\|/], ["|", /\|/], ["cccc", :text] ],
tok
end

def test_choose_longest_match
tok = Tokenizer.new(
/aa/, /aaa/
)
tok.source = "aaaa"
assert_all_tokens [ ["aaa", /aaa/], ["a", :text ] ], tok
end

def test_reset_precache
tok = Tokenizer.new(
/\|/, /,/
)
tok.source = "original start|original end"
tok.next_token
tok.source = "new start,new end"
assert_equal ["new start", :text], tok.next_token
end

def test_almost_finished
tok = Tokenizer.new( /!/ )
tok.source = "bang!"
tok.next_token
assert_equal true, tok.has_next?
tok.next_token
assert_equal false, tok.has_next?
end

def test_carriage_return_ending
tok = Tokenizer.new( /!/ )
tok.source = "bang!\n"
tok.next_token
assert_equal true, tok.has_next?
tok.next_token
assert_equal true, tok.has_next?
assert_equal "\n", tok.next_token[0].to_s
assert_equal false, tok.has_next?
end

def test_transparent_caching
tok = Tokenizer.new( /!/ )
tok.source = "bang!pum"
tok.next_token

assert_equal "!pum", tok.source
end

def test_match_klass
tok = Tokenizer.new( /!/ )
tok.source = "!bang!pum"

assert_equal \
[MatchData, String, MatchData, String],
tok.all_tokens.map { |tok, kind| tok.class }
end
end

Happy hacking,
Aureliano.

2009-06-19

Bouncer versión 2

Hice una nueva versión del bouncer que permite bouncear a varios targets desde un mismo host:

#!/usr/bin/env ruby

# == Synopsis
#
# bouncer.rb: Redirects TCP connections to distant machines. Handles simultaneously many connections.
#
# == Usage
#
# ruby redirect.rb [OPTION]
#
# -h, --help:
# Show help
#
# --ip ip, -i ip:
# Accept connections from ip (default 127.0.0.1)
#
# --port port, -p port:
# Listen on port (default 12345)
#
# --target ip:port, -t ip:port
# Connect to ip:port (default 127.0.0.1:23456)
#

require 'getoptlong'
require 'rdoc/usage'
require 'socket'

class Bouncer
def self.for_host( source_ip, start_port, targets )
rules = {}
targets.each_with_index do
|target, index|
rules[ start_port + index ] = target
end
self.new( source_ip, rules )
end
def initialize( source_ip, port_target_map )
@port_target_map = port_target_map
@source_ip = source_ip

@servers = @port_target_map.keys.map do
|port|
ss = TCPServer.new( "", port )
ss.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
ss
end

@descriptors = @servers.clone
@next_step = {}
end

def handle_new_connection( ss )
incoming = ss.accept
if incoming.peeraddr[3] == @source_ip
begin
outgoing = TCPSocket.new( *@port_target_map[incoming.addr[1]] )
@next_step[ incoming ] = outgoing
@next_step[ outgoing ] = incoming

@descriptors += [ incoming, outgoing ]
rescue
silent_close( incoming )
end
else
silent_close( incoming )
end
end

def silent_close( sock )
begin
sock.close
rescue
#do nothing intentionally
end
end

def propagate(sock)
next_sock = @next_step[sock]
next_sock.write(sock.read_nonblock(1000 * 1000))
end

def finish_connection(sock)
next_sock = @next_step[sock]
[ sock, next_sock ].each do
|s|
silent_close(s)
@descriptors.delete(s)
@next_step.delete(s)
end
end

def run
loop do
connections = select( @descriptors )
connections[0].each do
|sock|
if @servers.include? sock then
handle_new_connection( sock )
else
begin
sock.eof? ? finish_connection(sock) : propagate(sock)
rescue
finish_connection(sock)
end
end
end
end
end
end

if $0 == __FILE__ then

opts = GetoptLong.new(
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--ip', '-i', GetoptLong::REQUIRED_ARGUMENT ],
[ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ],
[ '--target', '-t', GetoptLong::REQUIRED_ARGUMENT ]
)

ip = '127.0.0.1'
port = '12345'
target = '127.0.0.1:23456'

opts.each do
|opt, arg|
case opt
when '--help'
RDoc::usage
exit
when '--ip'
ip = arg
when '--port'
port = arg
when '--target'
target = arg
end
end

port = port.to_i
target = target.split(":")

trap("SIGINT") do
exit
end

Bouncer.new(ip, port => [target[0], target[1].to_i]).run
end

2009-06-18

Mousewheel para arriba en vmware

Encontré como hacer para que ande bien la ruedita del mouse adentro de un Guest de VMWare con Kubuntu 9.04. Hay que instalar el paquete xserver-xorg-input-vmmouse.
Happy hacking,
Aureliano.

2009-06-17

Cambiando de resolución en X

El comando xrandr sirve, entre otras cosas para cambiar la resolución de X desde un shell. Primero listo las resoluciones disponibles:


$ xrandr
Screen 0: minimum 320 x 200, current 1280 x 1024, maximum 1280 x 1280
VGA connected 1280x1024+0+0 (normal left inverted right x axis y axis) 338mm x 270mm
1280x1024 60.0*+ 75.0 60.0*
1152x864 75.0
1024x768 75.0 60.0
800x600 75.0 60.3
640x480 75.0 59.9
720x400 70.1

y puedo elegir la que quiera con el parámetro -s. Por ejemplo:

$ xrandr -s 800x600

Happy hacking,
Aureliano

2009-06-13

Pequeñas delicias de las expresiones regulares

Como les conté acá y acá, estoy escribiendo un tokenizador para un wiki que estoy programando. Y hoy me encontré con una cosa muy extraña de las expresiones regulares.
En ruby la función match sirve para buscar el primer match de una regex dentro de un string. Por ejemplo (usando el irb):


irb(main):001:0> m = /a/.match "babab"
=> #<MatchData "a">
irb(main):002:0> m.pre_match
=> "b"
irb(main):003:0> m[0]
=> "a"

En particular, el pre_match es lo que está antes del match en el string. También según había entendido (mal) /\Z/ matchea con el final del string. Por ejemplo:

irb(main):004:0> m = /\Z/.match "hola"
=> #<MatchData "">
irb(main):005:0> m.pre_match
=> "hola"

Pero, /\Z/ tiene un comportamiento muy extraño, aunque documentado, cuando el último caracter antes del final es un \n. Lo que pasa es que el pre_match queda ¡sin el \n del final!. Lo muestro en el irb:

irb(main):006:0> m = /\Z/.match "\n"
=> #<MatchData "">
irb(main):007:0> m.pre_match
=> ""

Para que no se manduque el \n, hay que usar /\z/ (¡en minúscula!):

irb(main):008:0> m = /\z/.match "\n"
=> #<MatchData "">
irb(main):009:0> m.pre_match
=> "\n"

Por lo tanto tuve que tocar el tokenizer, ahora la función de initialize quedó así (miren el cambio de la "Z" a "z"):
    def initialize( delimiters )
@delimiter_list = [[/\z/, :finish]] +
delimiters.to_a.map { |k,arr| arr.map { |re| [re, k] } }.inject([]) { |ac,ps| ac + ps }
@match_cache = nil
end

Y el test que captura el problema que genera usar \Z en vez de \z quedó así:
  def test_carriage_return_ending
tok = Tokenizer.new( :a_kind => [/!/] )
tok.source = "bang!\n"
tok.next_token
assert_equal true, tok.has_next?
tok.next_token
assert_equal true, tok.has_next?
assert_equal "\n", tok.next_token[0].to_s
assert_equal false, tok.has_next?
end

Happy hacking,
Aureliano.

2009-06-12

Tokenizer de rapidito, segunda versión

Como les conté hace un par de posts, estoy haciendo un wiki y blogueando sobre el tokenizer
Hoy les cuento de la segunda versión del tokenizer. A esta versión (que no es compatible con la anterior) le agregué la posibilidad de asociar un tipo (kind) a cada regexp que define un token. Por lo tanto, el método next_match ahora devuelve un par [match, kind]. Los matches de texto común (no delimitadores) tienen kind :text. Los matches de los delimitadores vienen con la MatchData entera, para poder usarla si interesan partes del match a la hora de procesar el token. También dejé de usar =~ para matchear regexps, ya que el
Me quedaron un par de cosas con dudas después de implementar esto.

  1. ¿Se puede hacer en ruby que busque un match de una regexp en un string a partir de una posición? Hacer una regexp del tipo /.{34,}(regex de verdad)/ no vale.
  2. ¿Puedo forzar a la unión de expresiones regulares a buscar el match más largo (en vez del primero que matchee)?
A pesar de las dudas, igual tengo una versión que creo que es significativamente mejor que la versión anterior.
Así que como la otra vez dejo el código:
module Rapidito
class Tokenizer

attr_reader :source

def initialize( delimiters )
@delimiter_list = [[/\Z/, :finish]] +
delimiters.to_a.map { |k,arr| arr.map { |re| [re, k] } }.inject([]) { |ac,ps| ac + ps }
@match_cache = nil
end

def source=(s)
@match_cache = nil
@source = s
end

def has_next?
!@source.empty? || valid_cache?
end

def valid_cache?
(!@match_cache.nil?) && (@match_cache[0].to_s.length > 0)
end

def next_match
@delimiter_list.map {|p| [p[0].match(@source), p[1]]}.reject {|p| p[0].nil?}.inject do
|better,new|
better_pos = better[0].pre_match.length
new_pos = new[0].pre_match.length

if better_pos < new_pos
better
elsif new_pos < better_pos
new
elsif better[0].to_s.length > new[0].to_s.length
better
else
new
end
end
end

def next_token
if @match_cache #cached delimiter
rv = @match_cache
@match_cache = nil
return rv
end

match = next_match
p = match[0].pre_match.length
@source = @source[p + match[0].to_s.length, @source.length]

if p == 0 #delimiter
[match[0].to_s, match[1]]
else #text
@match_cache = match
[match[0].pre_match, :text]
end
end

def all_tokens
tokens = []
while has_next?
tokens << next_token
end
tokens
end
end
end

Y los tests de unidad:
require 'test/unit'
require 'rapidito/tokenizer'

include Rapidito

class TokenizerTest < Test::Unit::TestCase

def test_no_token
tok = Tokenizer.new( {} )
tok.source = "aaaa"
assert_equal true, tok.has_next?
assert_equal ["aaaa", :text], tok.next_token
assert_equal false, tok.has_next?
end

def assert_all_tokens( expected, tokenizer )
assert_equal expected,
tokenizer.all_tokens.map { |token, kind| [token.to_s, kind] }
end

def test_two_delimiters
tok = Tokenizer.new(
:a_kind => [/\|/, /;;/]
)

tok.source = "aa|bbb;;;;cccc"
assert_all_tokens \
[ ["aa", :text], ["|", :a_kind], ["bbb", :text],
[";;", :a_kind], [";;", :a_kind], ["cccc", :text] ],
tok

tok.source = "aa;;bbb||cccc"
assert_all_tokens \
[ ["aa", :text], [";;", :a_kind], ["bbb", :text],
["|", :a_kind], ["|", :a_kind], ["cccc", :text] ],
tok
end

def test_choose_longest_match
tok = Tokenizer.new(
:a_kind => [/aa/, /aaa/]
)
tok.source = "aaaa"
assert_equal [ ["aaa", :a_kind], ["a", :text ] ], tok.all_tokens
end

def test_reset_precache
tok = Tokenizer.new(
:a_kind => [/\|/, /,/]
)
tok.source = "original start|original end"
tok.next_token
tok.source = "new start,new end"
assert_equal ["new start", :text], tok.next_token
end

def test_almost_finished
tok = Tokenizer.new( :a_kind => [/!/] )
tok.source = "bang!"
tok.next_token
assert_equal true, tok.has_next?
tok.next_token
assert_equal false, tok.has_next?
end
end

Happy hacking,
Aureliano.