pidgin/pidgin

2bb66ef1475e
Fix a crash when hovering over an exceptionally long URL (4074 characters,
but it might be different for you) in a gtkimhtml widget (such as the IM
window).

This was reported on the support mailing list:
http://pidgin.im/pipermail/support/2013-March/012980.html
http://pidgin.im/pipermail/support/2013-March/012981.html

Repro steps:
1. "gdb pidgin"

2. "break gdk_x_error"
You'll probably need to type "y" to make breakpoint pending on future shared library load. (Or you could run first then add the breakpoint.)

3. "run --sync"
--sync probably isn't actually necessary. Theoretically it should make the backtrace more useful. Like, maybe it could actually show the Pidgin calls that trigger the problem? Or maybe the problem call starts from the gtk main loop, so there would never be Pidgin code in the backtrace? It's also possible we don't pass our args to the right places to make --sync work.

4. "cont"
I hit the breakpoint at startup for some reason. I've just been continuing past it. Doesn't seem to affect anything.

5. Open an IM window.

6. Paste a long URL.

7. Mouse over the long URL.

8. The breakpoint is hit.

9. "bt"

#0 gdk_x_error (display=0x740e30, error=0x7fffffffbc20)
at /build/buildd/gtk+2.0-2.24.13/gdk/x11/gdkmain-x11.c:458
#1 0x00007ffff73b94f6 in _XError () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#2 0x00007ffff73b6741 in ?? () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#3 0x00007ffff73b6785 in ?? () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#4 0x00007ffff73b7378 in _XReply () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#5 0x00007ffff73b2d3d in XSync () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#6 0x00007ffff73b2dcb in ?? () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#7 0x00007ffff73b9e2f in ?? () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#8 0x00007ffff7395d14 in XCreatePixmap () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#9 0x00007ffff62bd0d2 in _gdk_pixmap_new (drawable=0x186ec60, width=width@entry=32774, height=25, depth=24,
depth@entry=-1) at /build/buildd/gtk+2.0-2.24.13/gdk/x11/gdkpixmap-x11.c:175
#10 0x00007ffff6289617 in IA__gdk_pixmap_new (drawable=drawable@entry=0x186ec60, width=width@entry=32774,
height=, depth=depth@entry=-1) at /build/buildd/gtk+2.0-2.24.13/gdk/gdkpixmap.c:249
#11 0x00007ffff6297036 in gdk_window_begin_implicit_paint (rect=0x7fffffffbed0, window=0x186ec60)
at /build/buildd/gtk+2.0-2.24.13/gdk/gdkwindow.c:2779
#12 gdk_window_process_updates_internal (window=0x186ec60) at /build/buildd/gtk+2.0-2.24.13/gdk/gdkwindow.c:5574
#13 0x00007ffff6299201 in IA__gdk_window_process_all_updates ()
at /build/buildd/gtk+2.0-2.24.13/gdk/gdkwindow.c:5696
#14 0x00007ffff6299269 in gdk_window_update_idle (data=)
at /build/buildd/gtk+2.0-2.24.13/gdk/gdkwindow.c:5322
#15 0x00007ffff6276e77 in gdk_threads_dispatch (data=0x1a45c40) at /build/buildd/gtk+2.0-2.24.13/gdk/gdk.c:512
#16 0x00007ffff53d8ab5 in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#17 0x00007ffff53d8de8 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#18 0x00007ffff53d91e2 in g_main_loop_run () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#19 0x00007ffff663ec77 in IA__gtk_main () at /build/buildd/gtk+2.0-2.24.13/gtk/gtkmain.c:1271
#20 0x0000000000499950 in main (argc=1, argv=0x7fffffffe538) at gtkmain.c:933

This is the message it prints when it exits:
The program 'Pidgin' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadAlloc (insufficient resources for operation)'.
(Details: serial 14454 error_code 11 request_code 53 minor_code 0)
(Note to programmers: normally, X errors are reported asynchronously;
that is, you will receive the error a while after causing it.
To debug your program, run it with the --sync command line
option to change this behavior. You can then get a meaningful
backtrace from your debugger if you break on the gdk_x_error() function.)

I think it's kinda lame that gdk/gtk/xlib/whatever barfs when we give it a
ridiculously wide pango layout, but whatever. I'm assuming this problem is
gtkimhtml specific, and so I don't want to spend a whole lot of time fixing
it. As an easy fix I want to just truncate the URL displayed in the tooltip
to 200 characters.

Here's an example URL that I used to trigger the crash:
http://www.example.com/?%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%3012345678901234567890123456789012345678901234567890123456789012345678
/** @page c-howto C Plugin HOWTO
@section Introduction
C plugins are native plugins. They have complete access to all of the API,
and can do basically whatever they want. All of the protocol plugins, as
well as the Mono, Perl, and Tcl loader plugins are written in C.
@section getting_started Getting Started
To develop a plugin you need to have the libpurple and (for UI plugins) the
Pidgin/Finch source code or development headers. It is generally a good idea
to compile against the same version of Pidgin that you are running. You may
also want to develop against the code in our Monotone repository if you need
to use a new feature. Please do not abuse our Monotone repository, however.
All plugins must have @c PURPLE_PLUGINS defined and the definition must be
before including any libpurple, Pidgin, or Finch header files. Failure to do
so can lead to strange errors that are hard to diagnose. Just don't forget!
@section hello_world Hello World!
I know every tutorial has a hello world, so why should libpurple be any
different?
@code
#define PURPLE_PLUGINS
#include <glib.h>
#include "notify.h"
#include "plugin.h"
#include "version.h"
static gboolean
plugin_load(PurplePlugin *plugin) {
purple_notify_message(plugin, PURPLE_NOTIFY_MSG_INFO, "Hello World!",
"This is the Hello World! plugin :)", NULL, NULL, NULL);
return TRUE;
}
static PurplePluginInfo info = {
PURPLE_PLUGIN_MAGIC,
PURPLE_MAJOR_VERSION,
PURPLE_MINOR_VERSION,
PURPLE_PLUGIN_STANDARD,
NULL,
0,
NULL,
PURPLE_PRIORITY_DEFAULT,
"core-hello_world",
"Hello World!",
VERSION,
"Hello World Plugin",
"Hello World Plugin",
NULL,
"http://helloworld.tld",
plugin_load,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
static void
init_plugin(PurplePlugin *plugin)
{
}
PURPLE_INIT_PLUGIN(hello_world, init_plugin, info);
@endcode
Okay, so what does all this mean? We start off by defining @c PURPLE_PLUGINS
like described before. Next we include glib.h, mainly for gboolean and the
glib wrappers of the standard C types.
Next, we include plugin.h which has all the plugin specific stuff that we
need. For example: @c PurplePlugin, @c PurplePluginInfo,
@c PURPLE_PLUGIN_MAGIC, and @c PURPLE_INIT_PLUGIN().
Our last include is version.h which defines @c PURPLE_MAJOR_VERSION, and
@c PURPLE_MINOR_VERSION. There is not much you need to know about these,
except that they are required and will stop your plugin from crashing Pidgin
when something has changed that your plugin does not know about yet.
@c plugin_load is not required. It is called when the plugin is loaded so
that you can initialize any variables and so on. In this plugin we'll just
use it to display a message.
Next we have the @c PurplePluginInfo structure. Every plugin MUST have one of
these. Below is a code snipet of the same struct used in @c hello_world with
comments describing what each is.
@code
static PurplePluginInfo info = {
PURPLE_PLUGIN_MAGIC, /* Plugin magic, this must always be
PURPLE_PLUGIN_MAGIC.
*/
PURPLE_MAJOR_VERSION, /* This is also defined in libpurple. It helps
libpurple's plugin system determine which
version of libpurple this plugin was
compiled for, and whether loading it will
cause problems.
*/
PURPLE_MINOR_VERSION, /* See previous */
PURPLE_PLUGIN_STANDARD, /* PurplePluginType: There are 4 different
values for this field. The first is
PURPLE_PLUGIN_UNKNOWN, which should not be
used. The second is PURPLE_PLUGIN_STANDARD;
this is the value most plugins will use.
Next, we have PURPLE_PLUGIN_LOADER; this is
the type you want to load if your plugin
is going to make it possible to load non-
native plugins. For example, the Perl and
Tcl loader plugins are of this type.
Last, we have PURPLE_PLUGIN_PROTOCOL. If
your plugin is going to allow the user to
connect to another network, this is the
type you'd want to use.
*/
NULL, /* This field is the UI requirement. If you're
writing a core plugin, this must be NULL
and the plugin must not contain any UI
code. If you're writing a Pidgin plugin,
you need to use PIDGIN_PLUGIN_TYPE. If you
are writing a Finch plugin, you would use
FINCH_PLUGIN_TYPE.
*/
0, /* This field is for plugin flags. Currently,
the only flag available to plugins is
invisible (PURPLE_PLUGIN_FLAG_INVISIBLE).
It causes the plugin to NOT appear in the
list of plugins.
*/
NULL, /* This is a GList of plugin dependencies. In
other words, a GList of plugin id's that
your plugin depends on. Set this value to
NULL no matter what. If your plugin has
dependencies, set them at run-time in the
plugin_init function.
*/
PURPLE_PRIORITY_DEFAULT,/* This is the priority libpurple with give your
plugin. There are three possible values
for this field, PURPLE_PRIORITY_DEFAULT,
PURPLE_PRIORITY_HIGHEST, and
PURPLE_PRIORITY_LOWEST
*/
"core-hello_world", /* This is your plugin's id. There is a whole
page dedicated to this in the Related Pages
section of the API docs.
*/
"Hello World!", /* This is your plugin's name. This is what
will be displayed for your plugin in the UI.
*/
1.1, /* This is the version of your plugin. */
"Hello World Plugin", /* This is the summary of your plugin. It
should be a short little blurb. The UI
determines where, if at all, to display
this.
*/
"Hello World Plugin", /* This is the description of your plugin. It
can be as long and as descriptive as you
like. And like the summary, it's up to the
UI where, if at all, to display this (and
how much to display).
*/
NULL, /* This is where you can put your name and
email address.
*/
"http://helloworld.tld",/* This is the website for the plugin. This
tells users where to find new versions,
report bugs, etc.
*/
plugin_load, /* This is a pointer to a function for
libpurple to call when it is loading the
plugin. It should be of the type:
gboolean plugin_load(PurplePlugin *plugin)
Returning FALSE will stop the loading of the
plugin. Anything else would evaluate as
TRUE and the plugin will continue to load.
*/
NULL, /* Same as above except it is called when
libpurple tries to unload your plugin. It
should be of the type:
gboolean plugin_unload(PurplePlugin *plugin)
Returning TRUE will tell libpurple to
continue unloading while FALSE will stop
the unloading of your plugin.
*/
NULL, /* Similar to the two above members, except
this is called when libpurple tries to
destory the plugin. This is generally only
called when for some reason or another the
plugin fails to probe correctly. It should
be of the type:
void plugin_destroy(PurplePlugin *plugin)
*/
NULL, /* This is a pointer to a UI-specific struct.
For a Pidgin plugin it will be a pointer to a
PidginPluginUiInfo struct, for example.
*/
NULL, /* This is a pointer to either a
PurplePluginLoaderInfo struct or a
PurplePluginProtocolInfo struct, and is
beyond the scope of this document.
*/
NULL, /* This is a pointer to a PurplePluginUiInfo
struct. It is a core/ui split way for
core plugins to have a UI configuration
frame. You can find an example of this
code in:
libpurple/plugins/pluginpref_example.c
*/
NULL, /* This is a function pointer where you can define
"plugin actions". The UI controls how
they're displayed. It should be of the
type:
GList *function_name(PurplePlugin *plugin,
gpointer context)
It must return a GList of
PurplePluginActions.
*/
NULL, /* This is a pointer reserved for future use.
We set it to NULL to indicate we don't
need it.
*/
NULL, /* This is a pointer reserved for future use.
We set it to NULL to indicate we don't
need it.
*/
NULL, /* This is a pointer reserved for future use.
We set it to NULL to indicate we don't
need it.
*/
NULL /* This is a pointer reserved for future use.
We set it to NULL to indicate we don't
need it.
*/
};
@endcode
Finally we have @c init_plugin and @c PURPLE_INIT_PLUGIN. @c init_plugin is
a function that gets called when libpurple probes the plugin. Most plugins
will add their preferences to the pref tree here--more about that later.
@c PURPLE_INIT_PLUGIN is a macro that EVERY plugin MUST have.
@c PURPLE_INIT_PLUGIN tells libpurple some very basic things about your
plugin, like what name to use if the plugin is compiled staticly, the
@c init_plugin function, and the name of the PurplePluginInfo structure. As
you may have guessed, this also gets read when libpurple is probing your
plugin. If this is missing, the plugin will not load.
*/
// vim: syntax=c.doxygen