Exemplo n.º 1
0
static GntWidget*
create_string_field(PurpleRequestField *field, GntWidget **screenname)
{
	const char *hint = purple_request_field_get_type_hint(field);
	GntWidget *entry = gnt_entry_new(
			purple_request_field_string_get_default_value(field));
	gnt_entry_set_masked(GNT_ENTRY(entry),
			purple_request_field_string_is_masked(field));
	if (hint && purple_str_has_prefix(hint, "screenname")) {
		PurpleBlistNode *node = purple_blist_get_root();
		gboolean offline = purple_str_has_suffix(hint, "all");
		for (; node; node = purple_blist_node_next(node, offline)) {
			if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
				continue;
			gnt_entry_add_suggest(GNT_ENTRY(entry), purple_buddy_get_name((PurpleBuddy*)node));
		}
		gnt_entry_set_always_suggest(GNT_ENTRY(entry), TRUE);
		if (screenname)
			*screenname = entry;
	} else if (hint && !strcmp(hint, "group")) {
		PurpleBlistNode *node;
		for (node = purple_blist_get_root(); node;
				node = purple_blist_node_get_sibling_next(node)) {
			if (PURPLE_BLIST_NODE_IS_GROUP(node))
				gnt_entry_add_suggest(GNT_ENTRY(entry), purple_group_get_name((PurpleGroup *)node));
		}
	}
	return entry;
}
Exemplo n.º 2
0
void jabber_google_presence_incoming(JabberStream *js, const char *user, JabberBuddyResource *jbr)
{
	if (!js->googletalk)
		return;
	if (jbr->status && purple_str_has_prefix(jbr->status, "♫ ")) {
		purple_prpl_got_user_status(js->gc->account, user, "tune",
					    PURPLE_TUNE_TITLE, jbr->status + strlen("♫ "), NULL);
		g_free(jbr->status);
		jbr->status = NULL;
	} else {
		purple_prpl_got_user_status_deactive(js->gc->account, user, "tune");
	}
}
Exemplo n.º 3
0
void
msn_switchboard_show_ink(MsnSwitchBoard *swboard, const char *passport,
                         const char *data)
{
	PurpleConnection *gc;
	guchar *image_data;
	size_t image_len;
	int imgid;
	char *image_msg;

	if (!purple_str_has_prefix(data, "base64:"))
	{
		purple_debug_error("msn", "Ignoring Ink not in Base64 format.\n");
		return;
	}

	gc = purple_account_get_connection(swboard->session->account);

	data += sizeof("base64:") - 1;
	image_data = purple_base64_decode(data, &image_len);
	if (!image_data || !image_len) 
	{
		purple_debug_error("msn", "Unable to decode Ink from Base64 format.\n");
		return;
	}

	imgid = purple_imgstore_add_with_id(image_data, image_len, NULL);
	image_msg = g_strdup_printf("<IMG ID='%d'/>", imgid);

	if (swboard->current_users > 1 ||
		((swboard->conv != NULL) &&
		 purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
		serv_got_chat_in(gc, swboard->chat_id, passport, 0, image_msg,
						 time(NULL));
	else
		serv_got_im(gc, passport, image_msg, 0, time(NULL));

	purple_imgstore_unref_by_id(imgid);
	g_free(image_msg);
}
Exemplo n.º 4
0
static gchar*
purple_upnp_parse_description_response(const gchar* httpResponse, gsize len,
	const gchar* httpURL, const gchar* serviceType)
{
	gchar *xmlRoot, *baseURL, *controlURL, *service;
	xmlnode *xmlRootNode, *serviceTypeNode, *controlURLNode, *baseURLNode;
	char *tmp;

	/* make sure we have a valid http response */
	if(g_strstr_len(httpResponse, len, HTTP_OK) == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): Failed In HTTP_OK\n");
		return NULL;
	}

	/* find the root of the xml document */
	if((xmlRoot = g_strstr_len(httpResponse, len, "<root")) == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): Failed finding root\n");
		return NULL;
	}

	/* create the xml root node */
	if((xmlRootNode = xmlnode_from_str(xmlRoot,
			len - (xmlRoot - httpResponse))) == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): Could not parse xml root node\n");
		return NULL;
	}

	/* get the baseURL of the device */
	if((baseURLNode = xmlnode_get_child(xmlRootNode, "URLBase")) != NULL) {
		baseURL = xmlnode_get_data(baseURLNode);
	} else {
		baseURL = g_strdup(httpURL);
	}

	/* get the serviceType child that has the service type as its data */

	/* get urn:schemas-upnp-org:device:InternetGatewayDevice:1 and its devicelist */
	serviceTypeNode = xmlnode_get_child(xmlRootNode, "device");
	while(!purple_upnp_compare_device(serviceTypeNode,
			"urn:schemas-upnp-org:device:InternetGatewayDevice:1") &&
			serviceTypeNode != NULL) {
		serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
	}
	if(serviceTypeNode == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): could not get serviceTypeNode 1\n");
		g_free(baseURL);
		xmlnode_free(xmlRootNode);
		return NULL;
	}
	serviceTypeNode = xmlnode_get_child(serviceTypeNode, "deviceList");
	if(serviceTypeNode == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): could not get serviceTypeNode 2\n");
		g_free(baseURL);
		xmlnode_free(xmlRootNode);
		return NULL;
	}

	/* get urn:schemas-upnp-org:device:WANDevice:1 and its devicelist */
	serviceTypeNode = xmlnode_get_child(serviceTypeNode, "device");
	while(!purple_upnp_compare_device(serviceTypeNode,
			"urn:schemas-upnp-org:device:WANDevice:1") &&
			serviceTypeNode != NULL) {
		serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
	}
	if(serviceTypeNode == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): could not get serviceTypeNode 3\n");
		g_free(baseURL);
		xmlnode_free(xmlRootNode);
		return NULL;
	}
	serviceTypeNode = xmlnode_get_child(serviceTypeNode, "deviceList");
	if(serviceTypeNode == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): could not get serviceTypeNode 4\n");
		g_free(baseURL);
		xmlnode_free(xmlRootNode);
		return NULL;
	}

	/* get urn:schemas-upnp-org:device:WANConnectionDevice:1 and its servicelist */
	serviceTypeNode = xmlnode_get_child(serviceTypeNode, "device");
	while(serviceTypeNode && !purple_upnp_compare_device(serviceTypeNode,
			"urn:schemas-upnp-org:device:WANConnectionDevice:1")) {
		serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
	}
	if(serviceTypeNode == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): could not get serviceTypeNode 5\n");
		g_free(baseURL);
		xmlnode_free(xmlRootNode);
		return NULL;
	}
	serviceTypeNode = xmlnode_get_child(serviceTypeNode, "serviceList");
	if(serviceTypeNode == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): could not get serviceTypeNode 6\n");
		g_free(baseURL);
		xmlnode_free(xmlRootNode);
		return NULL;
	}

	/* get the serviceType variable passed to this function */
	service = g_strdup_printf(SEARCH_REQUEST_DEVICE, serviceType);
	serviceTypeNode = xmlnode_get_child(serviceTypeNode, "service");
	while(!purple_upnp_compare_service(serviceTypeNode, service) &&
			serviceTypeNode != NULL) {
		serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
	}

	g_free(service);
	if(serviceTypeNode == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): could not get serviceTypeNode 7\n");
		g_free(baseURL);
		xmlnode_free(xmlRootNode);
		return NULL;
	}

	/* get the controlURL of the service */
	if((controlURLNode = xmlnode_get_child(serviceTypeNode,
			"controlURL")) == NULL) {
		purple_debug_error("upnp",
			"parse_description_response(): Could not find controlURL\n");
		g_free(baseURL);
		xmlnode_free(xmlRootNode);
		return NULL;
	}

	tmp = xmlnode_get_data(controlURLNode);
	if(baseURL && !purple_str_has_prefix(tmp, "http://") &&
	   !purple_str_has_prefix(tmp, "HTTP://")) {
		/* Handle absolute paths in a relative URL.  This probably
		 * belongs in util.c. */
		if (tmp[0] == '/') {
			size_t length;
			const char *path, *start = strstr(baseURL, "://");
			start = start ? start + 3 : baseURL;
			path = strchr(start, '/');
			length = path ? path - baseURL : strlen(baseURL);
			controlURL = g_strdup_printf("%.*s%s", (int)length, baseURL, tmp);
		} else {
			controlURL = g_strdup_printf("%s%s", baseURL, tmp);
		}
		g_free(tmp);
	}else{
		controlURL = tmp;
	}
	g_free(baseURL);
	xmlnode_free(xmlRootNode);

	return controlURL;
}
Exemplo n.º 5
0
void
msn_message_parse_payload(MsnMessage *msg,
						  const char *payload, size_t payload_len,
						  const char *line_dem,const char *body_dem)
{
	char *tmp_base, *tmp;
	const char *content_type;
	char *end;
	char **elems, **cur, **tokens;

	g_return_if_fail(payload != NULL);
	tmp_base = tmp = g_malloc(payload_len + 1);
	memcpy(tmp_base, payload, payload_len);
	tmp_base[payload_len] = '\0';

	/* Find the end of the headers */
	end = strstr(tmp, body_dem);
	/* TODO? some clients use \r delimiters instead of \r\n, the official client
	 * doesn't send such messages, but does handle receiving them. We'll just
	 * avoid crashing for now */
	if (end == NULL) {
		g_free(tmp_base);
		g_return_if_reached();
	}

	/* NUL-terminate the end of the headers - it'll get skipped over below */
	*end = '\0';

	/* Split the headers and parse each one */
	elems = g_strsplit(tmp, line_dem, 0);
	for (cur = elems; *cur != NULL; cur++)
	{
		const char *key, *value;

		/* If this line starts with whitespace, it's been folded from the
		   previous line and won't have ':'. */
		if ((**cur == ' ') || (**cur == '\t')) {
			tokens = g_strsplit(g_strchug(*cur), "=\"", 2);
			key = tokens[0];
			value = tokens[1];

			/* The only one I care about is 'boundary' (which is folded from
			   the key 'Content-Type'), so only process that. */
			if (!strcmp(key, "boundary") && value) {
				char *end = strchr(value, '\"');
				if (end) {
					*end = '\0';
					msn_message_set_header(msg, key, value);
				}
			}

			g_strfreev(tokens);
			continue;
		}

		tokens = g_strsplit(*cur, ": ", 2);

		key = tokens[0];
		value = tokens[1];

		if (!strcmp(key, "MIME-Version"))
		{
			/* Ignore MIME-Version header */
		}
		else if (!strcmp(key, "Content-Type"))
		{
			char *charset, *c;

			if (value && (c = strchr(value, ';')) != NULL)
			{
				if ((charset = strchr(c, '=')) != NULL)
				{
					charset++;
					msn_message_set_charset(msg, charset);
				}

				*c = '\0';
			}

			msn_message_set_content_type(msg, value);
		}
		else
		{
			msn_message_set_header(msg, key, value);
		}

		g_strfreev(tokens);
	}
	g_strfreev(elems);

	/* Proceed to the end of the "\r\n\r\n" */
	tmp = end + strlen(body_dem);

	/* Now we *should* be at the body. */
	content_type = msn_message_get_content_type(msg);

	if (payload_len - (tmp - tmp_base) > 0) {
		msg->body_len = payload_len - (tmp - tmp_base);
		g_free(msg->body);
		msg->body = g_malloc(msg->body_len + 1);
		memcpy(msg->body, tmp, msg->body_len);
		msg->body[msg->body_len] = '\0';
	}

	if (msg->body && content_type && purple_str_has_prefix(content_type, "text/")) {
		char *body = NULL;

		if (msg->charset == NULL || g_str_equal(msg->charset, "UTF-8")) {
			/* Charset is UTF-8 */
			if (!g_utf8_validate(msg->body, msg->body_len, NULL)) {
				purple_debug_warning("msn", "Message contains invalid "
						"UTF-8. Attempting to salvage.\n");
				body = purple_utf8_salvage(msg->body);
				payload_len = strlen(body);
			}
		} else {
			/* Charset is something other than UTF-8 */
			GError *err = NULL;
			body = g_convert(msg->body, msg->body_len, "UTF-8",
					msg->charset, NULL, &payload_len, &err);
			if (!body || err) {
				purple_debug_warning("msn", "Unable to convert message from "
						"%s to UTF-8: %s\n", msg->charset,
						err ? err->message : "Unknown error");
				if (err)
					g_error_free(err);

				/* Fallback to ISO-8859-1 */
				g_free(body);
				body = g_convert(msg->body, msg->body_len, "UTF-8",
						"ISO-8859-1", NULL, &payload_len, NULL);
				if (!body) {
					g_free(msg->body);
					msg->body = NULL;
					msg->body_len = 0;
				}
			}
		}

		if (body) {
			g_free(msg->body);
			msg->body = body;
			msg->body_len = payload_len;
			msn_message_set_charset(msg, "UTF-8");
		}
	}

	g_free(tmp_base);
}
Exemplo n.º 6
0
/*------------------------------------------------------------------------
 * Convert libpurple (HTML) markup to MXit protocol markup (for sending to MXit).
 * Any MXit markup codes in the original message also need to be escaped.
 *
 *  @param message			The message text containing libPurple (HTML) markup
 *  @return					The message text containing MXit markup
 */
char* mxit_convert_markup_tx( const char* message, int* msgtype )
{
	GString*			mx;
	struct tag*			tag;
	GList*				entry;
	GList*				tagstack	= NULL;
	char*				reply;
	int					len			= strlen ( message );
	int					i;

#ifdef MXIT_DEBUG_MARKUP
	purple_debug_info( MXIT_PLUGIN_ID, "Markup TX (original): '%s'\n", message );
#endif

	/*
	 * libPurple uses the following HTML markup codes:
	 *   Bold:			<b>...</b>
	 *   Italics:		<i>...</i>
	 *   Underline:		<u>...</u>
	 *   Strikethrough:	<s>...</s>					(NO MXIT SUPPORT)
	 *   Font size:		<font size="">...</font>
	 *   Font type:		<font face="">...</font>	(NO MXIT SUPPORT)
	 *   Font colour:	<font color=#">...</font>
	 *   Links:			<a href="">...</a>
	 *   Newline:		<br>
	 *   Inline image:  <IMG ID="">
	 * The following characters are also encoded:
	 *   &amp;  &quot;  &lt;  &gt;
	 */

	/* new message data */
	mx = g_string_sized_new( len );

	/* run through the message and check for HTML markup */
	for ( i = 0; i < len; i++ ) {

		switch ( message[i] ) {
			case '<' :
				if ( purple_str_has_prefix( &message[i], "<b>" ) || purple_str_has_prefix( &message[i], "</b>" ) ) {
					/* bold */
					g_string_append_c( mx, '*' );
				}
				else if ( purple_str_has_prefix( &message[i], "<i>" ) || purple_str_has_prefix( &message[i], "</i>" ) ) {
					/* italics */
					g_string_append_c( mx, '/' );
				}
				else if ( purple_str_has_prefix( &message[i], "<u>" ) || purple_str_has_prefix( &message[i], "</u>" ) ) {
					/* underline */
					g_string_append_c( mx, '_' );
				}
				else if ( purple_str_has_prefix( &message[i], "<br>" ) ) {
					/* newline */
					g_string_append_c( mx, '\n' );
				}
				else if ( purple_str_has_prefix( &message[i], "<font size=" ) ) {
					/* font size */
					int fontsize;

					tag = g_new0( struct tag, 1 );
					tag->type = MXIT_TAG_SIZE;
					tagstack = g_list_prepend( tagstack, tag );
					// TODO: implement size control
					if ( sscanf( &message[i+12], "%i", &fontsize ) ) {
						purple_debug_info( MXIT_PLUGIN_ID, "Font size set to %i\n", fontsize );
					}
				}
				else if ( purple_str_has_prefix( &message[i], "<font color=" ) ) {
					/* font colour */
					char color[8];

					/* ensure we have the complete tag: <font color="#123456"> */
					if ( i + 20 < len ) {
						tag = g_new0( struct tag, 1 );
						tag->type = MXIT_TAG_COLOR;
						tagstack = g_list_append( tagstack, tag );

						memset( color, 0x00, sizeof( color ) );
						memcpy( color, &message[i + 13], 7 );
						g_string_append( mx, color );
					}
Exemplo n.º 7
0
PurplePlugin *
purple_plugin_probe(const char *filename)
{
#ifdef PURPLE_PLUGINS
	PurplePlugin *plugin = NULL;
	PurplePlugin *loader;
	gpointer unpunned;
	gchar *basename = NULL;
	gboolean (*purple_init_plugin)(PurplePlugin *);

	purple_debug_misc("plugins", "probing %s\n", filename);
	g_return_val_if_fail(filename != NULL, NULL);

	if (!g_file_test(filename, G_FILE_TEST_EXISTS))
		return NULL;

	/* If this plugin has already been probed then exit */
	basename = purple_plugin_get_basename(filename);
	plugin = purple_plugins_find_with_basename(basename);
	g_free(basename);
	if (plugin != NULL)
	{
		if (purple_strequal(filename, plugin->path))
			return plugin;
		else if (!purple_plugin_is_unloadable(plugin))
		{
			purple_debug_warning("plugins", "Not loading %s. "
							"Another plugin with the same name (%s) has already been loaded.\n",
							filename, plugin->path);
			return plugin;
		}
		else
		{
			/* The old plugin was a different file and it was unloadable.
			 * There's no guarantee that this new file with the same name
			 * will be loadable, but unless it fails in one of the silent
			 * ways and the first one didn't, it's not any worse.  The user
			 * will still see a greyed-out plugin, which is what we want. */
			purple_plugin_destroy(plugin);
		}
	}

	plugin = purple_plugin_new(has_file_extension(filename, G_MODULE_SUFFIX), filename);

	if (plugin->native_plugin) {
		const char *error;
#ifdef _WIN32
		/* Suppress error popups for failing to load plugins */
		UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
#endif

		/*
		 * We pass G_MODULE_BIND_LOCAL here to prevent symbols from
		 * plugins being added to the global name space.
		 *
		 * G_MODULE_BIND_LOCAL was added in glib 2.3.3.
		 */
		plugin->handle = g_module_open(filename, G_MODULE_BIND_LOCAL);

		if (plugin->handle == NULL)
		{
			const char *error = g_module_error();
			if (error != NULL && purple_str_has_prefix(error, filename))
			{
				error = error + strlen(filename);

				/* These are just so we don't crash.  If we
				 * got this far, they should always be true. */
				if (*error == ':')
					error++;
				if (*error == ' ')
					error++;
			}

			if (error == NULL || !*error)
			{
				plugin->error = g_strdup(_("Unknown error"));
				purple_debug_error("plugins", "%s is not loadable: Unknown error\n",
						 plugin->path);
			}
			else
			{
				plugin->error = g_strdup(error);
				purple_debug_error("plugins", "%s is not loadable: %s\n",
						 plugin->path, plugin->error);
			}
			plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);

			if (plugin->handle == NULL)
			{
#ifdef _WIN32
				/* Restore the original error mode */
				SetErrorMode(old_error_mode);
#endif
				purple_plugin_destroy(plugin);
				return NULL;
			}
			else
			{
				/* We were able to load the plugin with lazy symbol binding.
				 * This means we're missing some symbol.  Mark it as
				 * unloadable and keep going so we get the info to display
				 * to the user so they know to rebuild this plugin. */
				plugin->unloadable = TRUE;
			}
		}

		if (!g_module_symbol(plugin->handle, "purple_init_plugin",
							 &unpunned))
		{
			purple_debug_error("plugins", "%s is not usable because the "
							 "'purple_init_plugin' symbol could not be "
							 "found.  Does the plugin call the "
							 "PURPLE_INIT_PLUGIN() macro?\n", plugin->path);

			g_module_close(plugin->handle);
			error = g_module_error();
			if (error != NULL)
				purple_debug_error("plugins", "Error closing module %s: %s\n",
								 plugin->path, error);
			plugin->handle = NULL;

#ifdef _WIN32
			/* Restore the original error mode */
			SetErrorMode(old_error_mode);
#endif
			purple_plugin_destroy(plugin);
			return NULL;
		}
		purple_init_plugin = unpunned;

#ifdef _WIN32
		/* Restore the original error mode */
		SetErrorMode(old_error_mode);
#endif
	}
	else {
		loader = find_loader_for_plugin(plugin);

		if (loader == NULL) {
			purple_plugin_destroy(plugin);
			return NULL;
		}

		purple_init_plugin = PURPLE_PLUGIN_LOADER_INFO(loader)->probe;
	}

	if (!purple_init_plugin(plugin) || plugin->info == NULL)
	{
		purple_plugin_destroy(plugin);
		return NULL;
	}
	else if (plugin->info->ui_requirement &&
			!purple_strequal(plugin->info->ui_requirement, purple_core_get_ui()))
	{
		plugin->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
					purple_core_get_ui(), plugin->info->ui_requirement);
		purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n", plugin->path, plugin->error);
		plugin->unloadable = TRUE;
		return plugin;
	}

	/*
	 * Check to make sure a plugin has defined an id.
	 * Not having this check caused purple_plugin_unload to
	 * enter an infinite loop in certain situations by passing
	 * purple_find_plugin_by_id a NULL value. -- ecoffey
	 */
	if (plugin->info->id == NULL || *plugin->info->id == '\0')
	{
		plugin->error = g_strdup(_("This plugin has not defined an ID."));
		purple_debug_error("plugins", "%s is not loadable: info->id is not defined.\n", plugin->path);
		plugin->unloadable = TRUE;
		return plugin;
	}

	/* Really old plugins. */
	if (plugin->info->magic != PURPLE_PLUGIN_MAGIC)
	{
		if (plugin->info->magic >= 2 && plugin->info->magic <= 4)
		{
			struct _PurplePluginInfo2
			{
				unsigned int api_version;
				PurplePluginType type;
				char *ui_requirement;
				unsigned long flags;
				GList *dependencies;
				PurplePluginPriority priority;

				char *id;
				char *name;
				char *version;
				char *summary;
				char *description;
				char *author;
				char *homepage;

				gboolean (*load)(PurplePlugin *plugin);
				gboolean (*unload)(PurplePlugin *plugin);
				void (*destroy)(PurplePlugin *plugin);

				void *ui_info;
				void *extra_info;
				PurplePluginUiInfo *prefs_info;
				GList *(*actions)(PurplePlugin *plugin, gpointer context);
			} *info2 = (struct _PurplePluginInfo2 *)plugin->info;

			/* This leaks... but only for ancient plugins, so deal with it. */
			plugin->info = g_new0(PurplePluginInfo, 1);

			/* We don't really need all these to display the plugin info, but
			 * I'm copying them all for good measure. */
			plugin->info->magic          = info2->api_version;
			plugin->info->type           = info2->type;
			plugin->info->ui_requirement = info2->ui_requirement;
			plugin->info->flags          = info2->flags;
			plugin->info->dependencies   = info2->dependencies;
			plugin->info->id             = info2->id;
			plugin->info->name           = info2->name;
			plugin->info->version        = info2->version;
			plugin->info->summary        = info2->summary;
			plugin->info->description    = info2->description;
			plugin->info->author         = info2->author;
			plugin->info->homepage       = info2->homepage;
			plugin->info->load           = info2->load;
			plugin->info->unload         = info2->unload;
			plugin->info->destroy        = info2->destroy;
			plugin->info->ui_info        = info2->ui_info;
			plugin->info->extra_info     = info2->extra_info;

			if (info2->api_version >= 3)
				plugin->info->prefs_info = info2->prefs_info;

			if (info2->api_version >= 4)
				plugin->info->actions    = info2->actions;


			plugin->error = g_strdup_printf(_("Plugin magic mismatch %d (need %d)"),
							 plugin->info->magic, PURPLE_PLUGIN_MAGIC);
			purple_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n",
					  plugin->path, plugin->info->magic, PURPLE_PLUGIN_MAGIC);
			plugin->unloadable = TRUE;
			return plugin;
		}

		purple_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n",
				 plugin->path, plugin->info->magic, PURPLE_PLUGIN_MAGIC);
		purple_plugin_destroy(plugin);
		return NULL;
	}

	if (plugin->info->major_version != PURPLE_MAJOR_VERSION ||
			plugin->info->minor_version > PURPLE_MINOR_VERSION)
	{
		plugin->error = g_strdup_printf(_("ABI version mismatch %d.%d.x (need %d.%d.x)"),
						 plugin->info->major_version, plugin->info->minor_version,
						 PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION);
		purple_debug_error("plugins", "%s is not loadable: ABI version mismatch %d.%d.x (need %d.%d.x)\n",
				 plugin->path, plugin->info->major_version, plugin->info->minor_version,
				 PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION);
		plugin->unloadable = TRUE;
		return plugin;
	}

	if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL)
	{
		/* If plugin is a PRPL, make sure it implements the required functions */
		if ((PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon == NULL) ||
		    (PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->login == NULL) ||
		    (PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->close == NULL))
		{
			plugin->error = g_strdup(_("Plugin does not implement all required functions (list_icon, login and close)"));
			purple_debug_error("plugins", "%s is not loadable: %s\n",
					 plugin->path, plugin->error);
			plugin->unloadable = TRUE;
			return plugin;
		}

		/* For debugging, let's warn about prpl prefs. */
		if (plugin->info->prefs_info != NULL)
		{
			purple_debug_error("plugins", "%s has a prefs_info, but is a prpl. This is no longer supported.\n",
			                 plugin->path);
		}
	}

	return plugin;
#else
	return NULL;
#endif /* !PURPLE_PLUGINS */
}