Monday, April 9, 2007

Gaim Function Hijacking

Although gaim allows plugins to do a lot of different things, sometimes it's necessary to do something that I don't think the developers ever intended. Gaim sends out signals to plugins when certain events happen, but sometimes those signals aren't enough.

In my upcoming facebook plugin, I need to add info to the "Get Info" dialog (for away message stalking). Since there is no signal, I ended up hijacking the notify_userinfo function within the GaimNotifyUiOps structure. Details:


//NOTE: this isn't a full plugin, so don't try to compile this

//Will contain the notify_userinfo function we steal control from
void *(*hijacked_notify_userinfo) (GaimConnection *gc,
const char *who, GaimNotifyUserInfo *user_info);

//this is my notify userinfo, this gets control after a GaimNotifyUserInfo
//is created and is about to be shown
static void *
my_notify_userinfo(GaimConnection *gc, const char *who, GaimNotifyUserInfo *user_info)
{
//Do what we want to the user_info
gaim_notify_user_info_prepend_pair(user_info, "Hijacked by", "Neaveru");

//Now pretend like nothing happened, send control back to where its supposed to go
return hijacked_notify_userinfo(gc, who, user_info);
}

static gboolean
plugin_unload(GaimPlugin *plugin)
{
//Here we just return control to the normal notify_userinfo
GaimNotifyUiOps *ops = gaim_notify_get_ui_ops();

if (ops) //dunno why this wouldn't be true
ops->notify_userinfo = hijacked_notify_userinfo;

}

static gboolean
plugin_load(GaimPlugin *plugin)
{
//Take a look at libgaim/notify.h for definitions of this var
GaimNotifyUiOps *ops;
ops = gaim_notify_get_ui_ops();

//Check that ops and notify_userinfo exist, this is important
//since technically if someone isn't using the default gtkgaim,
//they could have no notify_userinfo function
if (ops && ops->notify_userinfo)
{
hijacked_notify_userinfo = ops->notify_userinfo;
ops->notify_userinfo = my_notify_userinfo;
}
return TRUE;
}


And there you have it, now the order of events when you click on "get info" over a buddy:
1) The protocol plugin (ie oscar, irc etc) will generate a userinfo with all the info to show
2) The user info is sent to the ops->notify_userinfo function, which is now our function
3) Our function has its way with the userinfo. We then send it to the original notify_userinfo
4) The original notify_userinfo does whatever it was going to do before.

Take a look at libgaim/notify.h and libgaim/notify.c for more info

The nice thing about this is that you don't even have to worry about other plugins stealing control of the user info before or after you. This will just chain them along.

No comments: