Libertonia
Portada · Todo · Software Libre · Desarrolladores · Comunidad · Internet · Tecnología · Meta · Diarios
Pidgin y Google Desktop

jorginius's Diary
Por jorginius
departamento directo desde el pasado , Sección Diarios
Puesto a las Thu Aug 28th, 2008 at 02:25:41 AM CET
Hace dos años comentaba cómo indexar las conversación de Gaim en Google Desktop. El código lo escribí como favor a un amigo y, sin mucho interés personal por el engendro (no soy un habitual ni de Windows ni de Gaim), lo dejé aparcado poco después

Ahora, rememorando tiempos pasados en Libertonia :-D, lo leo y me doy cuenta de que ya no funciona. La razón fundamental es que Gaim ya no existe --- evolucionó hasta dividirse en Pidgin y libpurple --- y PyGaim nunca se actualizó a la nueva api. En esta entrada describo la pequeña chapuza que permite rescatar al plugin.

 


Introducción

Pidgin es uno de los mejores clientes de mensajería en Windows (al menos hasta que Kopete para Win32 despegue :-P) pero, en cierto modo, es un subproducto de la versión "canónica" de linux; así modificar y reconstruir Pidgin en Windows es doloroso y parte de la funcionalidad (D-Bus, composición de tipos de letras) se ha caído.

Pidgin, a través de libpurple, soporta scripting. Uno de los tipos de plugin es "lopl" que define a los "meta-plugins" que se ocupan de cargar los scripts. En la rama oficial se incluyen cargadores para Perl, Tcl y toda la familia Mono. El tema del scripting está siendo revisado y una de las tareas con recompensa es reescribir las cabeceras o el api mismo para que se pueda generar de forma automática los bindings, por ejemplo usando SWIG.

Mientras el tema se aclara otra posibilidad que se nos ofrece es comandar a pidgin través de D-Bus. Muchos plugins pueden ser reescritos como programas externos conectados a través de D-Bus. La indexación en Google Desktop podría ser uno de ellos pero, por desgracia, en Windows pidgin no soporta D-Bus. Entonces ¿Qué opciones nos quedan?

Las opciones

El antiguo GaimDS está escrito en Python. Esto se hizo así por una serie de factores: no quería escribir :-D, el tipo al que se lo hice sabía Python, quizás podría aprovechar un ejemplo de Google y referencias en la documentación oficial (aunque al final descubrí que todo estaba obsoleto y tuve que ir a la parte de C) y, por aquel entonces, no había mucha diferencia en el soporte de los plugins de Perl y los de Python dentro de Gaim.

Con el tiempo Python en Gaim/Pidgin desapareció. Eliminada una opción, queda reescribirlo en Perl o Tcl,o escribir un plugin nativo en C. Descarto Mono porque, si ya es "bleeding edge" Mono en Pidgin+Linux, intuyo que en Windows debe ser como montar un puzzle de diez mil piezas con los ojos vendados :-) Enganchar .NET con Pidgin suena bien pero casi no hay documentación sobre Mono y ninguna referencia específica de .NET. Por la teoría de "si nadie lo ha hecho antes será por algo" sospecho que no debe ser sencillo :-)

Reescribir el plugin en C es la opción más segura pero también la más dolorosa. Para compilar Pidgin en Windows hace falta cygwin, mingw32 y un montón de trastos. El indexador necesita usar COM+ y Glib con un montón de código gcc-específico, y por experiencia sé que con esas cosas mingw32 es bastante puñetero (= no hace lo mismo que MSVC, las cabeceras cambian, errores en el enlazado). Además es horrendo el código C cuando pretendes usar interfaces.

Así pues la opción más razonable es rescribir las 80 líneas guarras de GaimDS en Perl y a correr. Bueno, pues eso es lo que no he hecho :-)

La chapuza

PidginDS está escrito en Python. Mi intención --- que seguramente se quedará en eso, en intención :-D --- es reescribirlo eventualmente en C pero, mientras tanto, he escrito un plugin que hace las veces de PyGaim: por un lado habla con libpurple y por otro, usando Python empotrado, con PidginDS. En frío no tiene mucho sentido meterse en ese berenjenal pero el código C para empotrar python es pequeño, puedo reusar casi todo el script original y además es un enfoque bastante flexible si quiero cambiar algo después: al ser los plugins nativos bibliotecas dinámicas, pidgin enlaza con ellos y tienes que salir del programa antes de poder reemplazarlos. Como aquí la funcionalidad está en un script python, esté lo puedo cambiar en caliente, puedo añadir código y probar cosas sin estar cerrando cada dos por tres. Para lo que pretende ser --- un prototipo de una versión en C --- el enfoque no es tan descabellado y lo he podido escribir en la décima parte de lo que he tardado en escribir hasta aquí :-)

Una de las consecuencias de "ser un subproducto" es que no hay "paquetes dev" de Pidgin en Windows. Para compilar los plugins te tienes que preparar tú el entorno y las mayores pegas han sido precisamente de construcción:

El código añadido en C y, obviando el esqueleto común a todos los plugins de Pidgin, se reduce a esto:

typedef enum { SENT=0, RECV=1 } gds_msg_event_t;
static const char *EVT_NAMES[] = {"sent_cb", "recv_cb"};

static PyObject *MODULE;
static char *GDSNAME = "pidginds";

static void
python_fn(PurpleAccount *account, const char *sender,
    const char *buffer, gds_msg_event_t type) {
    PyObject *args, *fn, *user, *buddy, *msg, *val;
    fn = PyObject_GetAttrString(MODULE, EVT_NAMES[type]);

    if (fn && PyCallable_Check(fn)) {
        args = PyTuple_New(3); /* username, buddy, msg */

        user = PyString_FromString(purple_account_get_username(account));
        buddy = PyString_FromString(sender);
        msg = PyString_FromString(buffer);

        if (!(user && buddy && msg)) {
            Py_DECREF(args);
            return;
        }

        PyTuple_SetItem(args, 0, user);
        PyTuple_SetItem(args, 1, buddy);
        PyTuple_SetItem(args, 2, msg);

        val = PyObject_CallObject(fn, args);

        if (val) Py_DECREF(val);

        Py_DECREF(fn);
    }
}

... Qué es una forma simple de hacer llamadas a funciones en módulos python desde C. Un detalle feo es la inicialización de MODULE (el "import pidginds"). PyImport_Import/Ex no admite un path así que hay que modificar el sys.path primero para que encuentre el módulo, que estará en el directorio de plugins de pidgin. Hay muchas formas de hacerlo. La buena supongo que es leer del registro dónde se instaló pidgin y sacarlo de ahí. Yo simplemente lo he dejado fijo a "C:\\Program Files\\Pidgin\\plugins" con vistas a que a la larga (je) lo reescribiré en C.

Para compilarlo la forma más sencilla es aprovecharse del Makefile.mingw que hay en el subdirectorio de plugins, en libpurple. De todas formas en el zip está el dll compilado para python25.

Confesión y cierre

Esto lo he desempolvado después de tanto tiempo porque ahora sí tengo un ordenador con Windows y con Google Desktop. Hace poco compré un Dell (uno con muchos cores y mucha ram para programar en Java X-D) y venía, aparte de con Windows Vista, con el servicio de búsqueda desactivado y con Google Desktop instalado por defecto. Quizás ahora que lo uso le de más aire, lo ponga de bonito y lo suba a alguna parte pero no creo que caiga esa breva: funcionar funciona y a mí me resuelve la papeleta así que supongo que mi renovado interés ha durado los cinco minutos que le he dedicado a hacerlo andar de nuevo más lo que he tardado en escribir esto :-)

Por dar un poco pie a debate (aunque esto también lo dudo X-D) ¿vosotros también tenéis trastos de estos? Me refiero a programas y scripts tontos que una vez escribiste para "rascarte tu picor" pero por pereza no los pules ni los publicas.

Una vez escuché que como programador vales tanto como los programas que hayas acabado, no los que hayas empezado. Supongo que programar es divertido pero acabar programas es aburrido :-) Esto en cierto modo es la lacra del software libre: no se acaban los programas. Todo el mundo está dispuesto a añadir funcionalidades pero, a menos que haya un cheque de por medio nadie está dispuesto a hacer algo tan aburrido como pulir los detalles.

< Sistemas antirobo para portátiles (9 comments)
Enlaces Relacionados
· escomposlinux.org
· Hace dos años comentaba cómo indexar las conversación de Gaim en Google Desktop
· Pidgin
· libpurple
· PyGaim
· Kopete para Win32
· una de las tareas con recompensa es reescribir las cabeceras o el api mismo para que se pueda generar de forma automática los bindings
· SWIG
· otra posibilidad que se nos ofrece es comandar a pidgin través de D-Bus
· GaimDS
· Para compilar Pidgin en Windows hace falta cygwin, mingw32 y un montón de trastos
· PidginDS
· usando Python empotrado
· construir un wrapper de la biblioteca donde reside el intérprete Python
· pexports
· dlltool
· script que descarga todo lo necesario para que compiles pidgin del tirón
· gai_strerror
· Para compilarlo la forma más sencilla es aprovecharse del Makefile.mingw que hay en el subdirectorio de plugins, en libpurple
· More on jorginius's Diary
· Also by jorginius

Menu
· crear cuenta
· FAQ
· búsqueda
· Fuentes de Noticias

Login
Nueva cuenta
Usuario:
Contraseña:

Ver: Modo: Orden:
Pidgin y Google Desktop | 9 comentarios (9 temáticos, editoriales, 0 ocultos)
La versión en C, con su instalador (none / 0) (#1)
por jorginius ("jorginius" en Google Mail) a las Sun Aug 31st, 2008 at 02:31:10 PM CET
(Información Usuario) http://www.rodriguezmoreno.com

Aunque sospecho que no le interesa a nadie, ayer me lié la manta a la cabeza y escribí la versión nativa del plugin. Podéis descargarla (el instalador) aquí: pidgin-googledesktop. Las fuentes en este otro link: pidginds-src.

Algunos apuntes sueltos:

Cadenas de caracteres

Para el que tenga la suerte de no conocer como funcionan las cadenas de caracteres en Windows: hay tres tipos de cadenas de caracteres: las que usan tu código regional, las unicode (UCS-2, wchar_t) y las BSTR que son casi como las últimas pero con un prefijo que indica la longitud (un poco como en Pascal aunque terminan en caracter nulo) y que son con las que trabaja COM. Estas cadenas no se pueden mezclar alegremente.

Para rizar aún más el rizo pidgin no usa internamente nade de eso; Gtk+ o Glib usan en todas las plataformas UTF-8, así que el plugin tiene que convertir cadenas UTF-8 a multibyte Windows para poder trabajar con ellas y luego convertir el resultado a BSTR.

El compilador

Como pidgin (y sus plugins) no compilan con MSVC, está escrito para ser compilado con mingw32. MinGW no tiene un soporte completo de COM y lo que funciona mejor es al API crudo en C: nada de conversiones de cadenas facilonas con ATL ni sintaxis de componentes como objetos. No hay lugar para nenazas en MinGW :-P

Para generar los stubs a partir de los IDL del SDK de Google Desktop hace falta el midl y varias cabeceras que mingw32 no incluye. Todo eso se puede obtener del "Microsoft SDKs Platform" que es una descarga gratuita (en el fichero de fuentes de todas formas, incluyo los stubs generados). Le decimos a midl que use mingw32 de esta manera:

midl.exe /W3 /msc_ver 0 /env win32 /server none
    /client none /cpp_cmd c:\MinGW\bin\gcc
    /cpp_opt "-E -x c -U__stdcall -U_stdcall
    -U__GNUC__ - U__declspec -D__inline__=
    -D_NO_CTYPE_INLINES -DMIDL_PASS
    -DWIN32_LEAN_AND_MEAN 
    -I %RUTA_A_MS_SDK" 
    -I %RUTA_A_MS_SDK /header 
    GoogleDesktopApi.h /tlb GoogleDesktopApi.h
    GoogleDesktopAPI.idl
El engendro

A nivel estructural no hay mucha diferencia entre el plugin python y el plugin C. Lo más notable es que ahora el registro en GoogleDesktop (GDS no acepta eventos si el plugin no se registra primero) no está dentro del plugin de Pidgin sino que hay un pequeño ejecutable aparte que se ocupa de registrarlo y desregistrarlo al instalar o desintalar. Para compilarlo es tan fácil como:

gcc -O2 register.c -lole32 -loleaut32 -o register
Como nota curiosa se puede comparar el proceso de registro en Python:

reg.StartComponentRegistration(_GUID,
["Title", _NAME, 
    "Description", _DESCRIPTION,
    "Icon", _GAIM_PATH + ",0"])
ireg = reg.GetRegistrationInterface("GoogleDesktop.IndexingRegistration")
ireg.RegisterIndexingPlugin(None)
reg.FinishComponentRegistration()
... Con exactamente lo mismo en C. Algo más largo X-D
int
plugin_register(void)
{
	IGoogleDesktopRegistrar *reg;
	IGoogleDesktopRegisterIndexingPlugin *ireg;
	HRESULT hr;

	SAFEARRAYBOUND bound;
	SAFEARRAY *args;
	long index;

	VARIANT vargs;
	VARIANT title,
			description,
			icon;
	VARIANT rtitle,
			rdescription,
			ricon;

	reg = registrar_get();

	bound.lLbound = 0;
	bound.cElements = 6;

	args = SafeArrayCreate(VT_VARIANT, 1, &bound);

	if (!args) return -1;

	V_VT(&title) = V_VT(&rtitle) = VT_BSTR;
	V_BSTR(&title) = SysAllocString(L"Title");
	V_BSTR(&rtitle) = SysAllocString(L"PidginDS");

	index = 0; SafeArrayPutElement(args, &index, &title);
	index = 1; SafeArrayPutElement(args, &index, &rtitle);

	V_VT(&description) = V_VT(&rdescription) = VT_BSTR;
	V_BSTR(&description) = SysAllocString(L"Description");
	V_BSTR(&rdescription) = SysAllocString(L"Indexa conversaciones en GDS");

	index = 2; SafeArrayPutElement(args, &index, &description);
	index = 3; SafeArrayPutElement(args, &index, &rdescription);

	V_VT(&icon) = V_VT(&ricon) = VT_BSTR;
	V_BSTR(&icon) = SysAllocString(L"Icon");
	V_BSTR(&ricon) = SysAllocString(L"C:\\Program Files\\Pidgin\\pidgin.exe,0");

	index = 4; SafeArrayPutElement(args, &index, &icon);
	index = 5; SafeArrayPutElement(args, &index, &ricon);

	V_VT(&vargs) = VT_ARRAY | VT_VARIANT;
	V_ARRAY(&vargs) = args;

	hr = IGoogleDesktopRegistrar_StartComponentRegistration(reg, clsid, vargs);
	hr = IGoogleDesktopRegistrar_GetRegistrationInterface(reg,
		L"GoogleDesktop.IndexingRegistration", (IUnknown **) &ireg);

	hr = IGoogleDesktopRegisterIndexingPlugin_RegisterIndexingPlugin(ireg, NULL);
	hr = IGoogleDesktopRegistrar_FinishComponentRegistration(reg);

	SafeArrayDestroy(args);

	if (FAILED(hr)) return -1;

	IGoogleDesktopRegisterIndexingPlugin_Release(ireg);
	IGoogleDesktopRegistrar_Release(reg);

	return 0;
}




 
Programas buenos y malos (none / 0) (#2)
por atopos a las Sun Aug 31st, 2008 at 07:33:54 PM CET
(Información Usuario) http://los-pajaros-de-hogano.blogspot.com

Una vez escuché que como programador vales tanto como los programas que hayas acabado, no los que hayas empezado. Supongo que programar es divertido pero acabar programas es aburrido.

Yo también tengo algún mini-programa tonto o script, incompleto y sin pulir, que no volveré a tocar si la ocasión no lo impone y que no hago público. Pero no creo que sea mejor terminar programas que empezarlos. En cierto modo puede ser al revés. Empezar un programa es plantearse problemas nuevos y resolverlos creativamente, eso siempre es más interesante desde cualquier punto de vista que el trabajo sucio. Por otra parte, cada programa tiene su objetivo. Y, seguramente, el objetivo de estos mini-programas sea el de realizar su función en el momento oportuno. Y si la realizan o realizaron, bien están. Otra cosa son los programas con un alcance mayor ---aunque es cierto que muchos programas tontos podrían haber tenido un alcance mayor de haberlo pensado antes---, ahí sí hay que mimar más al niño, sobre todo en su momento de nacimiento, pero siempre a sabiendas de que un programa de este tipo, uno que no sea sólo para salir del paso, se parece más a una obra de arte, siempre en proceso de mejora, nunca completo del todo. Visto así, tampoco el proceso de pulir resulta aburrido.

Lo que sí puede resultar enormemente aburrido es trabajar con un bicho cuyo diseño nos produce náuseas, cuyo lenguaje nos mortifica o cuyo propósito nos ha dejado de interesar. Pero ese es el pan de cada día de los forzados a ganarse el sustento en el mundo de las mega-empresas. La gente del SL o del software que cada cual hace en su casa por gusto, no tiene por qué padecerlo. Ventajas de trabajar exclusivamente cuando se sienten ganas de hacerlo.

Y no digo más, porque este tema sí que podría desencadenar una flame-war sobre lenguajes, paradigmas de programación, metodologías de diseño, etc.



Procesar los antiguos logs (none / 0) (#5)
por jorginius ("jorginius" en Google Mail) a las Mon Sep 1st, 2008 at 11:22:48 PM CET
(Información Usuario) http://www.rodriguezmoreno.com

Por completar el cuadro, incluyo un script en python que procesa los logs de conversaciones pasadas que tengamos almacenados: pidginds-batch. Está escrito en python y es sólo para el formato actual de pidgin.

Es muy "de batalla" y tiene varias limitaciones:

Debemos tener el mismo nick en todas las cuentas. Esto es así porque al principio de los logs se guarda la cuenta de los participantes ("fulanito@hotmail.com") pero luego, en la conversación, se referencia a ese fulanito@hotmail.com por su nick ("Fulano", p.ej) sin que aparezca en el mismo log ninguna equivalencia entre nick y cuenta. Hay que decirle de antemano al script cuál es nuestro nick para que luego identifique las lineas que son nuestras en los logs y, como sólo acepta un nick, tiene que ser el mismo en todas las cuentas que tengamos en pidgin. Podría sacar los nicks de la configuración pero eso tampoco ayuda demasiado porque los nicks pueden cambiar a lo largo del tiempo y en los logs no se actualizan.

Casi seguro que no indexa conversaciones en salas de charlas. La verdad es que no tengo logs de ese tipo para ver el formato y no me apetece investigar. Supongo que, si no cambia demasiado, los mensajes de todos los participantes excepto los nuestros aparecerán bajo la misma identidad (el identificador del grupo)

La forma de uso es sencilla. Primero lo registramos para que Google Desktop acepte los datos.

pidginds-batch -i
Luego le pasamos un nick y el path al directorio donde tenemos los logs, normalmente %APPDATA%\.purple:

pidginds-batch -n Fulano -p %APPDATA%\.purple
Y por último lo desregistramos:

pidginds-batch -u




 
Hale, ya tiene su propia página :-) (none / 0) (#9)
por jorginius ("jorginius" en Google Mail) a las Fri Sep 5th, 2008 at 05:16:19 PM CET
(Información Usuario) http://www.rodriguezmoreno.com

Le acabo de poner el traje de los domingos, ya tiene su propia página pidgin-googledesktop

Las fuentes están disponibles en el repositorio de Subversion y, ya que semos internacionales :-P, he añadido soporte para gettext.



 
Pidgin y Google Desktop | 9 comentarios (9 temáticos, editoriales, 0 ocultos)
Ver: Modo: Orden:

ecol Logo Powered by Scoop
Todas las Marcas Registradas y copyrights de esta página son propiedad de sus respectivos dueños.
Los comentarios son propiedad del que los escribe.
Los iconos de las noticias y el logotipo son propiedad de Javier Malonda.
El Resto © 2002 Escomposlinux.org y aledaños.

Puedes sindicar los contenidos de libertonia en formato RSS 1.0 y RDF 0.9. También se puede sindicar la cola de envíos pendientes de moderación.

El proyecto escomposlinux.org está dedicado a la memoria de tas

crear cuenta | faq | búsqueda