Fri, 31 Jan 2014 18:02:20 +0530
Merge gtkdoc-conversion
--- a/.hgignore Fri Jan 31 17:56:27 2014 +0530 +++ b/.hgignore Fri Jan 31 18:02:20 2014 +0530 @@ -22,6 +22,7 @@ .*\.dll$ .*\.exe$ .*\.g?mo$ +.*\.gir$ .*\.la$ \.libs .*\.lo$ @@ -32,6 +33,7 @@ .*\.pyo$ .*\.rej$ .*\.so$ +.*\.typelib$ .*\.moc$ VERSION$ aclocal.m4
--- a/ChangeLog Fri Jan 31 17:56:27 2014 +0530 +++ b/ChangeLog Fri Jan 31 18:02:20 2014 +0530 @@ -54,12 +54,29 @@ * Invalid user moods can no longer be sent to the server. Plugins: + * A new plugin API has been introduced. Plugins are no longer required + to be of a predefined type (such as protocol, standard, loader etc), + and can perform multiple roles. * The Offline Message Emulation plugin now adds a note that the message was an offline message. (Flavius Anton) (#2497) + * Two example plugins, caesarcipher and caesarcipher_consumer, have + been added that demonstrate registering of a dynamic cipher type in + one plugin and using it in another. + * IPC test plugins have been removed, since the new plugin API does not + provide a seperate IPC API. + + Protocols: + * A single oscar plugin provides both AIM and ICQ protocols. + * A single jabber plugin provides XMPP, GTalk and Facebook protocols. + * A single yahoo plugin provides both Yahoo and Yahoo JAPAN protocols. General: * Various core components of libpurple are now GObjects. * Ciphers are now built from the libpurple directory. + * Added dependency GPlugin, which is now required to build libpurple with + plugins support. + * Added dependency gobject-introspection, which is now required to enable + non-native plugin support. * Doxygen has been replaced by gtk-doc for generating documentation. version 2.10.8 (1/28/2014):
--- a/ChangeLog.API Fri Jan 31 17:56:27 2014 +0530 +++ b/ChangeLog.API Fri Jan 31 18:02:20 2014 +0530 @@ -78,6 +78,74 @@ * purple_menu_action_set_data * purple_menu_action_set_callback * purple_menu_action_set_children + * PurplePluginInfoFlags (PURPLE_PLUGIN_INFO_FLAGS_INTERNAL and + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) + * purple_plugin_get_dependent_plugins + * purple_plugin_get_filename + * purple_plugin_is_internal + * purple_plugin_info_new + * purple_plugin_get_info + * purple_plugin_info_get_abi_version + * purple_plugin_info_get_actions_cb + * purple_plugin_info_get_category + * purple_plugin_info_get_dependencies + * purple_plugin_info_get_error + * purple_plugin_info_get_extra_cb + * purple_plugin_info_get_flags + * purple_plugin_info_get_icon + * purple_plugin_info_get_license_id + * purple_plugin_info_get_license_text + * purple_plugin_info_get_license_url + * purple_plugin_info_get_pref_frame_cb + * purple_plugin_info_get_pref_request_cb + * purple_plugin_info_get_ui_data + * purple_plugin_info_set_ui_data + * purple_plugin_register_type + * purple_plugin_add_interface + * PURPLE_DEFINE_TYPE + * PURPLE_DEFINE_TYPE_EXTENDED + * PURPLE_IMPLEMENT_INTERFACE_STATIC + * PURPLE_IMPLEMENT_INTERFACE + * PURPLE_DEFINE_DYNAMIC_TYPE + * PURPLE_DEFINE_DYNAMIC_TYPE_EXTENDED + * PURPLE_IMPLEMENT_INTERFACE_DYNAMIC + * PURPLE_DEFINE_STATIC_TYPE + * PURPLE_DEFINE_STATIC_TYPE_EXTENDED + * PurpleProtocol, inherits GObject. Please see the documentation for + details. + * PurpleProtocolAction + * PurpleProtocolOverrideFlags + * PurpleProtocolClientIface + * PurpleProtocolServerIface + * PurpleProtocolIMIface + * PurpleProtocolChatIface + * PurpleProtocolPrivacyIface + * PurpleProtocolXferIface + * PurpleProtocolRoomlistIface + * PurpleProtocolAttentionIface + * PurpleProtocolMediaIface + * PurpleProtocolFactoryIface + * purple_protocol_get_* for PurpleProtocol members + * purple_protocol_class_* for class methods + * purple_protocol_client_iface_* for client interface methods + * purple_protocol_server_iface_* for server interface methods + * purple_protocol_im_iface_* for IM interface methods + * purple_protocol_chat_iface_* for chat interface methods + * purple_protocol_privacy_iface_* for privacy interface methods + * purple_protocol_xfer_iface_* for xfer interface methods + * purple_protocol_roomlist_iface_* for roomlist interface methods + * purple_protocol_attention_iface_* for attention interface methods + * purple_protocol_media_iface_* for media interface methods + * purple_protocol_factory_iface_* for factory interface methods + * purple_protocol_action_new + * purple_protocol_action_free + * purple_protocols_add + * purple_protocols_remove + * purple_protocols_find + * purple_protocols_get_all + * purple_protocols_get_handle + * purple_protocols_init + * purple_protocols_uninit * purple_request_certificate * purple_request_field_certificate_new * purple_request_field_certificate_get_value @@ -131,6 +199,9 @@ * Files in libpurple/ciphers have been renamed. To use a particular cipher, include its header. * Renamed ft.h file to xfer.h + * Renamed plugin.h file to plugins.h + * prpl.h has been split into protocol.h (PurpleProtocol GObject and + protocol interfaces) and protocols.h (protocols subsystem) * status.h has been split into status.h (Status API) and presence.h (Presence API) * account-authorization-requested signal merged with @@ -168,6 +239,7 @@ * purple_find_buddy renamed to purple_blist_find_buddy * purple_find_group renamed to purple_blist_find_group * purple_get_blist renamed to purple_blist_get_buddy_list + * PurpleBuddyIconSpec has been moved to buddyicon.h * purple_certificate_check_signature_chain now returns a list of failing PurpleCertificate*s as the second parameter * PurpleConversation is now an abstract type, and is a GObject. Please @@ -217,6 +289,28 @@ a GList * purple_notify_user_info_prepend_pair renamed to purple_notify_user_info_prepend_pair_html + * PurplePlugin is now a GObject (alias for GPluginPlugin). Please see + the documentation for details. + * PurplePluginInfo is now a GObject, inherits GPluginPluginInfo. Please + see the documentation for details. + * PurplePluginAction no longer has a context field. Use + PurpleProtocolAction for protocol actions. + * PURPLE_INIT_PLUGIN(pluginname, initfunc, plugininfo) is now + PURPLE_PLUGIN_INIT(pluginname, query_func, load_func, unload_func). + See "C Plugins tutorial" (tut_c_plugins.xml) for an example. + * purple_plugin_load and purple_plugin_unload now use an error argument + to return load/unload errors + * purple_plugins_get_all is now purple_plugins_find_all, and the + returned list of plugins MUST be free'd using g_list_free + * purple_plugin_get_* functions for plugin info are now + purple_plugin_info_get_* + * purple_plugin_get_author is now purple_plugin_info_get_authors, and + returns a NULL-terminated list of authors + * purple_plugins_probe is now purple_plugins_refresh + * purple_plugins_find_with_id renamed to purple_plugins_find_plugin + * purple_plugins_find_with_filename renamed to + purple_plugins_find_by_filename + * proto_chat_entry has been renamed to PurpleProtocolChatEntry * PurpleRoomlist is now a GObject. Please see the documentation for details. * purple_roomlist_room_get_type is now @@ -336,12 +430,30 @@ * purple_notify_searchresults_get_columns_count * purple_notify_searchresults_get_rows_count * purple_notify_searchresults_row_get + * PurplePluginType + * PurplePluginPriority + * PurplePluginLoaderInfo + * PurplePluginUiInfo + * purple_plugin_ipc_* + * purple_plugin_is_unloadable + * purple_plugin_new + * purple_plugin_probe + * purple_plugin_register + * purple_plugin_reload + * purple_plugins_destroy_all + * purple_plugins_enabled + * purple_plugins_find_with_basename + * purple_plugins_find_with_name + * purple_plugins_get_protocols + * purple_plugins_get_search_paths * purple_plugins_register_load_notify_cb * purple_plugins_register_probe_notify_cb * purple_plugins_register_unload_notify_cb * purple_plugins_unregister_load_notify_cb * purple_plugins_unregister_probe_notify_cb * purple_plugins_unregister_unload_notify_cb + * purple_plugins_unload + * purple_plugins_unload_all * purple_pounces_load * purple_prefs_set_generic * purple_prefs_update_old @@ -351,6 +463,7 @@ * purple_presence_new * purple_presence_new_for_account * purple_presence_new_for_buddy + * PurplePluginProtocolInfo * purple_proxy_connect_socks5 * purple_request_field_list_add * purple_srv_cancel @@ -410,6 +523,7 @@ Added: * pidgin_create_webview * PidginDockletFlag + * PidginPluginInfo, inherits PurplePluginInfo * Various WebKit-related functions in gtkwebview.h Changed: @@ -446,6 +560,9 @@ * struct _PidginImPane Finch: + Added: + * FinchPluginInfo, inherits PurplePluginInfo + Changed: * gntft.h file renamed to gntxfer.h
--- a/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -12,6 +12,7 @@ intltool-extract.in \ intltool-merge.in \ intltool-update.in \ + m4macros/introspection.m4 \ package_revision.h \ pidgin.apspec.in \ pidgin.spec.in \ @@ -120,6 +121,9 @@ distcleancheck_listfiles = find . -type f -a ! -name package_revision.h DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc +if PLUGINS +DISTCHECK_CONFIGURE_FLAGS += --enable-introspection +endif SUBDIRS = . m4macros libpurple $(GNT_DIR) $(GTK_DIR) $(PO_DIR) share/ca-certs share/sounds doc
--- a/acinclude.m4 Fri Jan 31 17:56:27 2014 +0530 +++ b/acinclude.m4 Fri Jan 31 18:02:20 2014 +0530 @@ -6,7 +6,7 @@ # Owen Taylor 1997-2001 dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]]) -dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject or +dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gobject or dnl gthread is specified in MODULES, pass to pkg-config dnl AC_DEFUN([AM_PATH_GLIB_2_0], @@ -20,9 +20,6 @@ for module in . $4 do case "$module" in - gmodule) - pkg_config_args="$pkg_config_args gmodule-2.0" - ;; gobject) pkg_config_args="$pkg_config_args gobject-2.0" ;;
--- a/autogen.sh Fri Jan 31 17:56:27 2014 +0530 +++ b/autogen.sh Fri Jan 31 18:02:20 2014 +0530 @@ -139,7 +139,7 @@ check "glib-gettextize"; GLIB_GETTEXTIZE=${BIN}; check "gtkdocize"; GTKDOCIZE=${BIN}; check "intltoolize"; INTLTOOLIZE=${BIN}; -check "sed"; SED=${BIN}; +check "sed"; SED=${BIN}; check "aclocal"; ACLOCAL=${BIN}; check "autoheader"; AUTOHEADER=${BIN}; check "automake"; AUTOMAKE=${BIN};
--- a/configure.ac Fri Jan 31 17:56:27 2014 +0530 +++ b/configure.ac Fri Jan 31 18:02:20 2014 +0530 @@ -74,6 +74,7 @@ AC_CANONICAL_HOST AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4macros]) AM_INIT_AUTOMAKE([1.11 -Wno-portability dist-bzip2]) AM_SILENT_RULES([yes]) @@ -335,10 +336,7 @@ dnl ####################################################################### dnl # Check for GLib 2.20 (required) dnl ####################################################################### -# TODO: gmodule-2.0 is only needed if enable_plugins is 'yes'. It -# might be nice to change this check so that it's not required -# if enable_plugins is 'no'. -PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.20.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [ +PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.20.0 gobject-2.0 gthread-2.0], , [ AC_MSG_RESULT(no) AC_MSG_ERROR([ @@ -468,7 +466,9 @@ AC_SUBST(GTK_LIBS) GTK_PC_MODULE="gtk+-${with_gtk}.0" + GTK_VERSION=${with_gtk} AC_SUBST(GTK_PC_MODULE) + AC_SUBST(GTK_VERSION) dnl We only really need Pango >= 1.4 for decent RTL support PKG_CHECK_MODULES(PANGO, [pango >= 1.4.0], @@ -1313,32 +1313,22 @@ fi AC_SUBST(STATIC_PRPLS) STATIC_LINK_LIBS= -extern_init= +extern_load= load_proto= +extern_unload= +unload_proto= for i in $STATIC_PRPLS ; do dnl Ugly special case for "libsilcpurple.la": - dnl ... and Ugly special case for multi-protocol oscar and yahoo - if test \( "x$i" = "xoscar" -o "x$i" = "xaim" -o "x$i" = "xicq" \) -a "x$static_oscar" != "xyes"; then - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/oscar/liboscar.la" - extern_init="$extern_init extern gboolean purple_init_aim_plugin();" - extern_init="$extern_init extern gboolean purple_init_icq_plugin();" - load_proto="$load_proto purple_init_aim_plugin();" - load_proto="$load_proto purple_init_icq_plugin();" - elif test "x$i" = "xyahoo"; then - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/yahoo/libymsg.la" - extern_init="$extern_init extern gboolean purple_init_yahoo_plugin();" - extern_init="$extern_init extern gboolean purple_init_yahoojp_plugin();" - load_proto="$load_proto purple_init_yahoo_plugin();" - load_proto="$load_proto purple_init_yahoojp_plugin();" + if test "x$i" = "xsilc"; then + STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib${i}purple.la" else - if test "x$i" = "xsilc"; then - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib${i}purple.la" - else - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.la" - fi - extern_init="$extern_init extern gboolean purple_init_${i}_plugin();" - load_proto="$load_proto purple_init_${i}_plugin();" + STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.la" fi + extern_load="$extern_load extern gboolean ${i}_plugin_load();" + load_proto="$load_proto ${i}_plugin_load();" + extern_unload="$extern_unload extern gboolean ${i}_plugin_unload();" + unload_proto="$unload_proto ${i}_plugin_unload();" + case $i in bonjour) static_bonjour=yes ;; gg) static_gg=yes ;; @@ -1374,8 +1364,10 @@ AM_CONDITIONAL(STATIC_YAHOO, test "x$static_yahoo" = "xyes") AM_CONDITIONAL(STATIC_ZEPHYR, test "x$static_zephyr" = "xyes") AC_SUBST(STATIC_LINK_LIBS) -AC_DEFINE_UNQUOTED(STATIC_PROTO_INIT, $extern_init static void static_proto_init(void) { $load_proto }, - [Loads static protocol plugin module initialization functions.]) +AC_DEFINE_UNQUOTED(STATIC_PROTO_LOAD, $extern_load static void static_proto_load(void) { $load_proto }, + [Loads protocols from static protocol plugin modules.]) +AC_DEFINE_UNQUOTED(STATIC_PROTO_UNLOAD, $extern_unload static void static_proto_unload(void) { $unload_proto }, + [Unloads protocols from static protocol plugin modules.]) AC_ARG_WITH(dynamic_prpls, [AS_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`]) if test "x$DYNAMIC_PRPLS" = "xall" ; then @@ -1758,6 +1750,30 @@ AM_CONDITIONAL(ENABLE_KWALLET, test "x$enable_kwallet" = "xyes") dnl ####################################################################### +dnl # Check for GPlugin 0.0.14 +dnl ####################################################################### +if test "x$enable_plugins" = "xyes" ; then + PKG_CHECK_MODULES(GPLUGIN, [gplugin >= 0.0.14 gmodule-2.0], , [ + AC_MSG_RESULT(no) + AC_MSG_ERROR([ + GPlugin 0.0.14 development headers not found, which are required if you wish to + enable plugins. + Use --disable-plugins if you want to disable plugins. + ])]) + AC_SUBST(GPLUGIN_CFLAGS) + AC_SUBST(GPLUGIN_LIBS) +else + enable_introspection=no +fi + +dnl # Check for gobject introspection +GOBJECT_INTROSPECTION_CHECK([1.30.0]) + +if test "x$enable_introspection" = "xyes" ; then + AC_DEFINE(ENABLE_INTROSPECTION, 1, [Define if GObject introspection is enabled.]) +fi + +dnl ####################################################################### dnl # Check for Python dnl ####################################################################### @@ -1887,191 +1903,6 @@ AC_SUBST(PY_LIBS) dnl ####################################################################### -dnl # Check for Mono support -dnl ####################################################################### -AC_ARG_ENABLE(mono, [AS_HELP_STRING([--enable-mono], [compile with Mono runtime support (experimental)])], , enable_mono=no) -if test x"$enable_mono" = x"yes" ; then - PKG_CHECK_MODULES(MONO, mono, [ - AC_SUBST(MONO_CFLAGS) - AC_SUBST(MONO_LIBS) - enable_mono=yes - ], [ - AC_MSG_RESULT(no) - AC_MSG_ERROR([ -Mono development headers not found. -Use --disable-mono if you do not need Mono support. -]) - ]) - if test x"$enable_mono" = x"yes"; then - oldLIBS="$LIBS" - LIBS="$LIBS $MONO_LIBS" - AC_MSG_CHECKING(for libmono) - AC_CHECK_FUNCS(mono_jit_init, [], enable_mono=no) - LIBS="$oldLIBS" - - oldCPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $MONO_CFLAGS" - AC_CHECK_HEADERS(mono/jit/jit.h, [], enable_mono=no) - AC_CHECK_HEADERS(mono/metadata/object.h, [], enable_mono=no) - CPPFLAGS="$oldCPPFLAGS" - - AC_DEFINE(ENABLE_MONO, 1, [Define if mono enabled.]) - fi -else - MONO_CFLAGS= - MONO_LIBS= - enable_mono=no -fi - -AC_SUBST(MONO_CFLAGS) -AC_SUBST(MONO_LIBS) -AM_CONDITIONAL(USE_MONO, test x"$enable_mono" = x"yes") - -dnl ####################################################################### -dnl # Check for Perl support -dnl ####################################################################### -AC_ARG_ENABLE(perl, [AS_HELP_STRING([--disable-perl], [compile without perl scripting])], , enable_perl=yes) - -if test "$enable_plugins" = no ; then - enable_perl=no -fi -looked_for_perl="no" -if test "$enable_perl" = yes ; then - looked_for_perl="yes" - AC_PATH_PROG(perlpath, perl) - AC_MSG_CHECKING(for Perl compile flags) - PERL_CFLAGS=`$perlpath -MExtUtils::Embed -e ccopts 2>/dev/null` - if test "_$PERL_CFLAGS" = _ ; then - AC_MSG_RESULT([not found, building without perl.]) - enable_perl=no - else - PERL_LIBS=`$perlpath -MExtUtils::Embed -e ldopts 2>/dev/null |$sedpath 's/-lgdbm //'` - PERL_LIBS=`echo $PERL_LIBS |$sedpath 's/-ldb //'` - PERL_LIBS=`echo $PERL_LIBS |$sedpath 's/-lndbm //'` - if test "$system" = "Linux"; then - PERL_LIBS=`echo $PERL_LIBS |$sedpath 's/-lnsl //'` - PERL_LIBS=`echo $PERL_LIBS |$sedpath 's/-lposix //'` - fi - PERL_LIBS=`echo $PERL_LIBS |$sedpath 's/-lc //'` - AC_MSG_RESULT(ok) - - oldLIBS="$LIBS" - LIBS="$LIBS $PERL_LIBS" - AC_MSG_CHECKING(for libperl) - AC_CHECK_FUNCS(perl_run, [], enable_perl=no) - LIBS="$oldLIBS" - - oldCPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $PERL_CFLAGS" - AC_CHECK_HEADERS(EXTERN.h) - AC_CHECK_HEADERS(perl.h, [], enable_perl=no, - [#if HAVE_EXTERN_H - # include <EXTERN.h> - #endif]) - CPPFLAGS="$oldCPPFLAGS" - fi -fi - -if test "$enable_perl" = yes ; then - AC_PROG_PERL_MODULES(ExtUtils::MakeMaker, , have_makemaker=no) - - if test "x$have_makemaker" = "xno"; then - enable_perl=no - PERL_CFLAGS= - PERL_LIBS= - AM_CONDITIONAL(USE_PERL, false) - AC_MSG_WARN(Compiling perl requires ExtUtils::MakeMaker) - else - AC_DEFINE(HAVE_PERL, [1], [Compile with support for perl]) - AC_SUBST(PERL_CFLAGS) - AC_SUBST(PERL_LIBS) - AM_CONDITIONAL(USE_PERL, true) - - dnl This is almost definitely wrong, but in case there's - dnl something I'm missing, I'll leave it in. - AC_CHECK_FUNCS(Perl_eval_pv) - - AC_MSG_CHECKING(for old perl) - PERL_OLD=`$perlpath -e 'if($]<5.006){printf"yes\n";}else{printf"no\n";}'` - - if test "x$PERL_OLD" = "xyes"; then - AC_DEFINE(OLD_PERL, 1, [Define if old perl is installed.]) - AC_MSG_RESULT(yes) - else - AC_MSG_RESULT(no) - fi - - AC_MSG_CHECKING(for DynaLoader.a) - DYNALOADER_A=`echo $PERL_LDFLAGS | $perlpath -pe 's/^(.* )*([[^ ]]*DynaLoader\.a).*/\2/'` - - dnl Don't check libperl.a if dynaloader.a wasn't found. - if test -n "$DYNALOADER_A"; then - AC_MSG_RESULT(yes) - - dnl Find either libperl.a or libperl.so - AC_MSG_CHECKING(for libperl.a or libperl.so) - LIBPERL_A=`echo "$PERL_LDFLAGS -L/usr/lib"|$perlpath -e 'foreach (split(/ /, <STDIN>)) { if (/^-L(.*)/) { my $dir=$1; if (\`ls $dir/libperl.so* 2>/dev/null\`) { print "-lperl"; last; }; if (-e "$dir/libperl.a") { print "$dir/libperl.a"; last } } };'` - if test -z "$LIBPERL_A"; then - AC_MSG_RESULT(no) - DYNALOADER_A= - else - AC_MSG_RESULT(yes) - - if test "$LIBPERL_A" = "-lperl"; then - LIBPERL_A= - fi - fi - - PERL_LIBS=`echo $PERL_LIBS | $perlpath -pe 's/^(.* )*[[^ ]]*DynaLoader\.a/\1libperl_dynaloader.la/'` - - if test -n "$LIBPERL_A"; then - PERL_LIBS=`echo $PERL_LDFLAGS | $sedpath -e 's/-lperl /libperl_orig.la /' -e 's/-lperl$/libperl_orig.la$/'` - fi - - AC_SUBST(DYNALOADER_A) - AC_SUBST(LIBPERL_A) - else - AC_MSG_RESULT(no) - fi - fi -else - PERL_CFLAGS= - PERL_LIBS= - AM_CONDITIONAL(USE_PERL, false) -fi - -if test "x$looked_for_perl" = "xyes" -a "x$enable_perl" = "xno" -a "x$force_deps" = "xyes"; then - AC_MSG_ERROR([ -Perl development headers not found. -Use --disable-perl if you do not need Perl scripting support. -]) -fi - -if test "$enable_perl" = yes ; then - AC_CACHE_CHECK(for new SvUPGRADE in perl API, ac_cv_perl_have_new_svupgrade, [ - orig_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $PERL_CFLAGS" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include <EXTERN.h> - #include <perl.h> - ]], [[ - PerlInterpreter *my_perl; - SV *sv; - if (!SvUPGRADE(sv, SVt_PV)) { - /* SvUPGRADE is an expression, so it doesn't - * terminate in case of failure */ - } - ]])], - [ac_cv_perl_have_new_svupgrade=no], - [ac_cv_perl_have_new_svupgrade=yes]) - CFLAGS="$orig_CFLAGS" - ]) - if test $ac_cv_perl_have_new_svupgrade = yes; then - AC_DEFINE(HAVE_NEW_SVUPGRADE, 1, [Define if you have SvUPGRADE terminating in case of failure.]) - fi -fi - -dnl ####################################################################### dnl # SSL support dnl # dnl # Thanks go to Evolution for the checks. @@ -2531,147 +2362,6 @@ ]) fi -dnl ####################################################################### -dnl # Check for Tcl -dnl ####################################################################### -AC_ARG_ENABLE(tcl, [AS_HELP_STRING([--disable-tcl], - [compile without Tcl scripting])], enable_tcl="$enableval", enable_tcl="yes") -AC_ARG_WITH(tclconfig, [AS_HELP_STRING([--with-tclconfig=DIR], - [directory containing tclConfig.sh])]) - -if test "$enable_plugins" = no; then - enable_tcl=no -fi - -if test "$enable_tcl" = yes; then - AC_MSG_CHECKING([for tclConfig.sh]) - TCLCONFIG=no - TCLCONFIGDIRS="/usr/lib \ - /usr/lib64 \ - /usr/lib/tcl8.5 \ - /usr/lib/tcl8.4 \ - /usr/lib/tcl8.3 \ - /usr/lib/tcl8.2 \ - /usr/lib64/tcl8.5 \ - /usr/lib64/tcl8.4 \ - /System/Library/Tcl/8.3 \ - /usr/local/lib" - for dir in $with_tclconfig $TCLCONFIGDIRS; do - if test -f $dir/tclConfig.sh; then - TCLCONFIG=$dir/tclConfig.sh - AC_MSG_RESULT([yes ($TCLCONFIG)]) - break - fi - done - if test "$TCLCONFIG" = "no"; then - AC_MSG_RESULT([no]) - enable_tcl=no - if test "x$force_deps" = "xyes" ; then - AC_MSG_ERROR([ -Tcl development headers not found. -Use --disable-tcl if you do not need Tcl scripting support. -]) - fi - else - . $TCLCONFIG - AC_MSG_CHECKING([Tcl version compatability]) - if test "$TCL_MAJOR_VERSION" -ne 8 -o "$TCL_MINOR_VERSION" -lt 3; then - AC_MSG_RESULT([bad, $TCL_VERSION found but 8.3 or later required]) - enable_tcl=no - else - AC_MSG_RESULT([ok, $TCL_VERSION]) - eval "TCL_LIB_SPEC=\"$TCL_LIB_SPEC\"" - AC_MSG_CHECKING([for Tcl linkability]) - oldCPPFLAGS=$CPPFLAGS - CPPFLAGS="$CPPFLAGS $TCL_INCLUDE_SPEC -I$TCL_PREFIX/include" - oldLIBS=$LIBS - LIBS="$LIBS $TCL_LIB_SPEC" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <tcl.h>]], - [[Tcl_Interp *interp=NULL; Tcl_Init(interp)]])], - [AC_MSG_RESULT([yes]);enable_tcl=yes], - [AC_MSG_RESULT([no]);enable_tcl=no]) - CPPFLAGS="$oldCPPFLAGS" - LIBS="$oldLIBS" - fi - fi -fi - -if test "$enable_tcl" = yes; then - AM_CONDITIONAL(USE_TCL, true) - TCL_LIBS=$TCL_LIB_SPEC - AC_DEFINE(HAVE_TCL, [1], [Compile with support for the Tcl toolkit]) - AC_SUBST(TCL_LIBS) - TCL_CFLAGS="$TCL_INCLUDE_SPEC -I$TCL_PREFIX/include" - if test "x$GCC" = "xyes"; then - TCL_CFLAGS="$TCL_CFLAGS -fno-strict-aliasing" - fi - AC_SUBST(TCL_CFLAGS) -else - AM_CONDITIONAL(USE_TCL, false) -fi - -dnl ####################################################################### -dnl # Check for Tk -dnl ####################################################################### -AC_ARG_ENABLE(tk, [AS_HELP_STRING([--disable-tk], - [compile without Tcl support for Tk])], enable_tk="$enableval", enable_tk="yes") -AC_ARG_WITH(tkconfig, [AS_HELP_STRING([--with-tkconfig=DIR], - [directory containing tkConfig.sh])]) - -if test "$enable_tcl" = yes -a "$enable_tk" = yes; then - AC_MSG_CHECKING([for tkConfig.sh]) - TKCONFIG=no - TKCONFIGDIRS="/usr/lib \ - /usr/lib64 \ - /usr/lib/tk8.5 \ - /usr/lib/tk8.4 \ - /usr/lib/tk8.3 \ - /usr/lib/tk8.2 \ - /usr/local/lib" - for dir in $with_tkconfig $TKCONFIGDIRS; do - if test -f $dir/tkConfig.sh; then - TKCONFIG=$dir/tkConfig.sh - AC_MSG_RESULT([yes ($TKCONFIG)]) - break - fi - done - if test "$TKCONFIG" = "no"; then - AC_MSG_RESULT([no]) - enable_tk=no - if test "x$force_deps" = "xyes" ; then - AC_MSG_ERROR([ -Tk development headers not found. -Use --disable-tk if you do not need Tk scripting support. -]) - fi - else - . $TKCONFIG - eval "TK_LIB_SPEC=\"$TK_LIB_SPEC\"" - AC_MSG_CHECKING([for Tk linkability]) - oldCPPFLAGS=$CPPFLAGS - CPPFLAGS="$CPPFLAGS $TCL_CFLAGS" - oldLIBS=$LIBS - LIBS="$LIBS $TCL_LIB_SPEC $TK_LIB_SPEC" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <tk.h>]], - [[Tcl_Interp *interp=NULL; Tcl_Init(interp); Tk_Init(interp);]])], - [AC_MSG_RESULT([yes]);enable_tk=yes], - [AC_MSG_RESULT([no]);enable_tk=no]) - CPPFLAGS="$oldCPPFLAGS" - LIBS="$oldLIBS" - fi -else - enable_tk=no -fi - -if test "$enable_tk" = yes; then - AM_CONDITIONAL(USE_TK, true) - AC_DEFINE(HAVE_TK, [1], [Compile with support for the Tk toolkit]) - TK_LIBS=$TK_LIB_SPEC - AC_SUBST(TK_LIBS) -else - AM_CONDITIONAL(USE_TK, false) -fi - if test "$ac_cv_cygwin" = yes ; then LDADD="$LDADD -static" AC_DEFINE(DEBUG, 1, [Define if debugging is enabled.]) @@ -2902,8 +2592,6 @@ pidgin/plugins/gestures/Makefile pidgin/plugins/gevolution/Makefile pidgin/plugins/musicmessaging/Makefile - pidgin/plugins/perl/Makefile - pidgin/plugins/perl/common/Makefile.PL pidgin/plugins/ticker/Makefile pidgin/themes/Makefile libpurple/example/Makefile @@ -2912,13 +2600,7 @@ libpurple/purple-3-uninstalled.pc libpurple/plugins/Makefile libpurple/plugins/keyrings/Makefile - libpurple/plugins/mono/Makefile - libpurple/plugins/mono/api/Makefile - libpurple/plugins/mono/loader/Makefile - libpurple/plugins/perl/Makefile - libpurple/plugins/perl/common/Makefile.PL libpurple/plugins/ssl/Makefile - libpurple/plugins/tcl/Makefile libpurple/Makefile libpurple/protocols/Makefile libpurple/protocols/bonjour/Makefile @@ -3002,10 +2684,7 @@ #echo Build with Secret Service..... : $enable_secret_service echo echo Build with plugin support..... : $enable_plugins -echo Build with Mono support....... : $enable_mono -echo Build with Perl support....... : $enable_perl -echo Build with Tcl support........ : $enable_tcl -echo Build with Tk support......... : $enable_tk +echo Enable Introspection...........: $enable_introspection echo echo Print debugging messages...... : $enable_debug echo Generate documentation........ : $enable_gtk_doc
--- a/doc/C-HOWTO.dox Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,259 +0,0 @@ -/** @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 Mercurial repository if you need - to use a new feature. Please do not abuse our Mercurial 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. Including purple.h - will define this for you. - - @section hello_world Hello World! - I know every tutorial has a hello world, so why should libpurple be any - different? - - @code -#include <purple.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, 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 including purple.h. This - file defines @c PURPLE_PLUGINS as described before so that we don't have to - manually define it. It also includes all the libpurple header files. - - @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
--- a/doc/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/doc/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -13,14 +13,8 @@ endif EXTRA_DIST = \ - C-HOWTO.dox \ - PERL-HOWTO.dox \ - TCL-HOWTO.dox \ funniest_home_convos.txt \ finch.1.in \ gtkrc-2.0 \ pidgin.1.in \ - plugin-i18n.dox \ - plugin-ids.dox \ - plugin-signals.dox \ the_penguin.txt
--- a/doc/PERL-HOWTO.dox Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,613 +0,0 @@ -/** @page perl-howto Perl Scripting HOWTO - -@section Introduction -libpurple Perl Plugins are set up very similarly to their C counterparts. Most of the API calls are implemented and are divided into packages. There are some significant differences between the Perl and C API. Much like the C API, the best place to seek guidance is the source, which is located in the *.xs files in the [libpurple|pidgin]/plugins/perl/common directories. The tutorial that follows will be example based and attempt to touch on some of the notable features of the embedded perl interpreter. It is also important to note that some of the C API is missing in libpurple's perl API. - -It is possible to get Gtk2-Perl to work with libpurple's Perl API, however you must not load the module with @c use, but rather with @c require. Keep this in mind if you would like to use other perl modules that are dynamically loaded. You can avoid the problem by always using @c require instead of @c use. - -@section first-script Writing your first script - -Let us start with a simple example of a perl plugin. The following code sample is a complete plugin that can be copied and used as-is. - -@code -use Purple; - -%PLUGIN_INFO = ( - perl_api_version => 2, - name => "Perl Test Plugin", - version => "0.1", - summary => "Test plugin for the Perl interpreter.", - description => "Your description here", - author => "John H. Kelm <johnhkelm\@gmail.com", - url => "https://pidgin.im", - - load => "plugin_load", - unload => "plugin_unload" -); - -sub plugin_init { - return %PLUGIN_INFO; -} - -sub plugin_load { - my $plugin = shift; - Purple::Debug::info("testplugin", "plugin_load() - Test Plugin Loaded.\n"); -} - -sub plugin_unload { - my $plugin = shift; - Purple::Debug::info("testplugin", "plugin_unload() - Test Plugin Unloaded.\n"); -} -@endcode - -It is necessary to load the libpurple perl package with the @code use Purple; @endcode line; this makes all the libpurple perl API available to the script. The @c \%PLUGIN_INFO hash contains all the information that will be displayed in the Plugin frame of the Preferences dialog, it also contains information about how the plugin is to be handled. The @c load and @c unload keys specify subroutines to be called when the plugin is loaded and unloaded. There are other key values that may be present in the @c \%PLUGIN_INFO hash; some of those will be covered in the following sections. - -The Perl subroutine @c plugin_init is executed when the plugin is probed by the plugin subsystem. What this means is that as soon as Pidgin or Finch is started, this subroutine is run once, regardless of whether or not the plugin is actually loaded. The other two subroutines present are the @c load and @c unload routines specified in the @c \%PLUGIN_INFO hash and will receive the plugin handle as an argument. When the plugin is loaded and subsequently unloaded, it will print a message to the debug window using the @c Purple::Debug::info() API call. - -In order to use this simple plugin, save the script text in a file with a @c .pl extension in your ~/.purple/plugins directory. After restarting Pidgin, you should see the plugin ("Perl Test Plugin") listed in the plugin list under "Tools -> Plugins". To view the debug output, make sure you run Pidgin from the console with the '-d' flag or open the Debug Window which is available in the "Help" menu. When you check the checkbox next the plugin, you should see a message appear in the Debug Window (or console); similarly, when you uncheck the checkbox you should see another message appear. You have now created a framework that will allow you to create almost any kind of libpurple plugin you can imagine. - -@section account-api Account and Account Option Functions - -The Account API is in the @c Purple::Account:: and @c Purple::Accounts:: packages; -both are nearly identical to their C counterparts, @c purple_account_ and @c -purple_accounts_. The Account Option API is in the @c Purple::Account::Option -package and is identical to the C implementation, @c purple_account_option. - -The Account* APIs allow scripts to create, remove, and edit accounts. An -account will have the Perl type of "PurpleAccount". (Note: Purple types have no real -meaning in perl scripts, other than that the types passed to perl subroutines -need to be correct.) This section will not go into detail about the @c -Purple::Account::Option package since its use is mainly in building protocol -plugins, which are outside the scope of this document. However, the API for the -@c Purple::Account::Option package should function as expected, should you need to -use it. - -To reduce redundant code, the following code examples will use the simple plugin -from the previous section as a template. To highlight some of the more useful -features of the Account API, we will be replacing the @c plugin_load perl -subroutine. For testing purposes, we will display output on the command line by -using perl @c print commands. - -@code -sub plugin_load { - $plugin = shift; - - # Testing was done using AIM, but this should work regardless of the protocol chosen - my $protocol = "prpl-aim"; - my $account_name = "test"; - - # Create a new Account - print "Testing: Purple::Account::new()... "; - $account = Purple::Account->new($account_name, $protocol); - if ($account) { print "ok.\n"; } else { print "fail.\n"; } - - # Add a new Account - print "Testing: Purple::Account::add()..."; - Purple::Accounts::add($account); - print "pending find...\n"; - - # Find the account we just added to verify its existence - print "Testing: Purple::Accounts::find()..."; - $account = Purple::Accounts::find($account_name, $protocol); - if ($account) { print "ok.\n"; } else { print "fail.\n"; } - - # Return the username - print "Testing: Purple::Account::get_username()... "; - $user_name = $account->get_username(); - if ($user_name) { - print "Success: $user_name.\n"; - } else { - print "Failed!\n"; - } - # Verify if the user is connected - print "Testing: Purple::Account::is_connected()"; - if ($account->is_connected()) { - print " Connected.\n"; - } else { - print " Disconnected.\n"; - } - - # The status mechanism is how users are Connected, set Away, - # Disconnected (status set to Offline), etc - # $status is now a Purple::Status perl type. - print "Testing: Purple::Accounts::get_active_status()... "; - if ($account->get_active_status()) { - print "Okay.\n"; - } else { - print "Failed!\n"; - } - - # It follows that to connect a user you must set the account status to - # "available" similarly we can disconnect a user by setting the account - # status to "offline" - - print "Testing: Purple::Accounts::connect()...pending...\n"; - - $account->set_status("available", TRUE); - $account->set_enabled(Purple::Core::get_ui(), TRUE); - $account->connect(); - -} -@endcode - -The above code is mostly explained in the comments, however there are a few -notable points to make. The variables above are all specialized Perl types that -contain pointers to the actual Purple types. They can be reassigned at will, just -like any other variable in Perl. The only way to edit the internal values of a -Purple type from within perl is to use the accessor methods, e.g. -@c Purple::Account::get_username(). If you would like to assign a C @c NULL value, -simply use @c undef. - -@section buddylist-api Buddylist, Group and Chat API - -The BuddyList, Group and Chat APIs are very similar and whatever is shown for -the @c Purple::BuddyList API should carry over to @c Purple::BuddyList::Chat and -@c Purple::BuddyList::Group. Note that a @c Purple::Find package was created to -keep the naming consistent with the C API. - -@code -sub plugin_load { - my $plugin = shift; - - my $protocol = "prpl-aim"; - my $account_name = "test"; - - # This is how we get an account to use in the following tests. You should replace the username - # with an existing user - $account = Purple::Accounts::find($account_name, $protocol); - - # Testing a find function: Note Purple::Find not Purple::Buddy:find! - # Furthermore, this should work the same for chats and groups - Purple::Debug::info("testplugin", "Testing: Purple::Find::buddy()..."); - $buddy = Purple::Find::buddy($account, "BUDDYNAME"); - Purple::Debug::info("", ($buddy ? "ok." : "fail.") . "\n"); - - # If you should need the handle for some reason, here is how you do it - Purple::Debug::info("testplugin", "Testing: Purple::BuddyList::get_handle()..."); - $handle = Purple::BuddyList::get_handle(); - Purple::Debug::info("", ($handle ? "ok." : "fail.") . "\n"); - - # This gets the Purple::BuddyList and references it by $blist - Purple::Debug::info("testplugin", "Testing: Purple::get_blist()..."); - $blist = Purple::get_blist(); - Purple::Debug::info("", ($blist ? "ok." : "fail.") . "\n"); - - # This is how you would add a buddy named "NEWNAME" with the alias "ALIAS" - Purple::Debug::info("testplugin", "Testing: Purple::BuddyList::Buddy::new..."); - $buddy = Purple::BuddyList::Buddy::new($account, "NEWNAME", "ALIAS"); - Purple::Debug::info("", ($buddy ? "ok." : "fail.") . "\n"); - - # Here we add the new buddy '$buddy' to the group "GROUP" - # so first we must find the group - Purple::Debug::info("testplugin", "Testing: Purple::Find::group..."); - $group = Purple::Find::group("GROUP"); - Purple::Debug::info("", ($group ? "ok." : "fail.") . "\n"); - - # To add the buddy we need to have the buddy, contact, group and node for insertion. - # For this example we can let contact be undef and set the insertion node as the group - Purple::Debug::info("testplugin", "Testing: Purple::BuddyList::add_buddy...\n"); - Purple::BuddyList::add_buddy($buddy, undef, $group, $group); - - # The example that follows gives an indication of how an API call that returns a list is handled. - # In this case the buddies of the account found earlier are retrieved and put in an array '@buddy_array' - # Further down an accessor method is used, 'get_name()' -- see source for details on the full set of methods - Purple::Debug::info("testplugin", "Testing: Purple::Find::buddies...\n"); - @buddy_array = Purple::Find::buddies($account, undef); - if (@buddy_array) { - Purple::Debug::info("testplugin", "Buddies in list (" . @buddy_array . "): \n"); - foreach $bud (@buddy_array) { - Purple::Debug::info("testplugin", Purple::BuddyList::Buddy::get_name($bud) . "\n"); - } - } -} -@endcode - -The BuddyList API allows plugins to edit buddies in the list, find the buddies -for a given account, assign aliases, and further manipulate the structure -as needed. It is also contains methods for accessing @c Purple::BuddyList::Group -and @c Purple::BuddyList::Chat types. - -@section conn-api Connection API - -The @c Purple::Connection API is one of the many packages that will not be covered -in-depth in this tutorial. It is most useful to protocol plugin developers. -However, the entire @c purple_connection_ API has corresponding, functioning perl subroutines. - -@section conv-api Conversation API - -The libpurple perl APIs for @c purple_conversation_ and @c pidgin_conv_window_ allow -plugins to interact with existing conversations, create new conversations, and -modify conversations at will. In the example script, a new window is created, -displayed and a new conversation instant message is created. The following -example again replaces the @c plugin_load subroutine in the simple plugin -template. The @c Purple::Conversation::Chat package handles the -@c purple_conv_chat_ portion of the API very similarly to how the -Purple::Conversation::IM package is used in the examples that follow. - -Notice that the interaction with the conversation window is in the @c Pidgin package as it -is UI-specific code interacting with Pidgin and not libpurple. -To use any of the Pidgin:: functionality, you will need to add the following to the top of your script: @code use Pidgin; @endcode - - -@code -sub plugin_load { - my $plugin = shift; - my $protocol = "prpl-aim"; - my $account_name = "test"; - - $account = Purple::Accounts::find($account_name, $protocol); - - # First we create two new conversations. - print "Testing Purple::Conversation->new()..."; - $conv1 = Purple::Conversation->new(1, $account, "Test Conversation 1"); - if ($conv1) { print "ok.\n"; } else { print "fail.\n"; } - - print "Testing Purple::Conversation->new()..."; - $conv2 = Purple::Conversation->new(1, $account, "Test Conversation 2"); - if ($conv2) { print "ok.\n"; } else { print "fail.\n"; } - - # Second we create a window to display the conversations in. - # Note that the package here is Pidgin::Conversation::Window - print "Testing Pidgin::Conversation::Window->new()...\n"; - $win = Pidgin::Conversation::Window->new(); - - # The third thing to do is to move second the conversation to a new window. - # The subroutine add_gtkconv() returns the number of conversations - # present in the window. - print "Testing Pidgin::Conversation::Window::add_conversation()..."; - $gtkconv2 = Pidgin::Conversation::get_gtkconv($conv2); - $gtkconv2->get_window()->remove_gtkconv($gtkconv2); - $conv_count = $win->add_gtkconv($gtkconv2); - if ($conv_count) { - print "ok..." . $conv_count . " conversations...\n"; - } else { - print "fail.\n"; - } - - # Now the window is displayed to the user. - print "Testing Pidgin::Conversation::Window::show()...\n"; - $win->show(); - - # Use get_im_data() to get a handle for the conversation - print "Testing Purple::Conversation::get_im_data()...\n"; - $im = $conv1->get_im_data(); - if ($im) { print "ok.\n"; } else { print "fail.\n"; } - - # Here we send messages to the conversation - print "Testing Purple::Conversation::IM::send()...\n"; - $im->send("Message Test."); - - print "Testing Purple::Conversation::IM::write()...\n"; - $conv2->get_im_data()->write("SENDER", "<b>Message</b> Test.", 0, 0); -} -@endcode - -The next block of code shows how a script can close a known conversation window -@c $win. - -@code - print "Testing Pidgin::Conversation::Window::get_gtkconv_count()...\n"; - $conv_count = $win->get_gtkconv_count(); - print "...and it returned $conv_count.\n"; - if ($conv_count > 0) { - print "Testing Pidgin::Conversation::Window::destroy()...\n"; - $win->destroy(); - } -@endcode - -@section plugin-pref-api Plugin Preference and Gtk Preference API - -The plugin preference API allows plugins to display options for manipulating the -plugin's behavior in a popup window. The method used for creating the pane in -native C plugins does not allow a direct mapping into perl. Therefore, perl -plugin writers must be aware that there will be significant differences in how -they create plugin preference panes. - -To first create a standard plugin preference tab, we need specify the -@c prefs_info key/value pair in the @c \%PLUGIN_INFO hash. This specifies the -name of the perl subroutine that will build and return a @c Purple::Pref::Frame. - -@code -%PLUGIN_INFO = { - ..., - prefs_info => "prefs_info_cb" -}; -@endcode - -The perl subroutine @c prefs_info_cb will be called to create the preferences -popup for the perl plugin. The following example will demonstrate creating a -preference frame. It is necessary to first initialize the preferences to be used -in the @c plugin_load subroutine, as follows. - -@code -sub plugin_load { - my $plugin = shift; - - # This is necessary to create each level in the preferences tree. - Purple::Prefs::add_none("/plugins/core/perl_test"); - # Here we are adding a set of preferences - # The second argument is the default value for the preference. - Purple::Prefs::add_bool("/plugins/core/perl_test/bool", 1); - Purple::Prefs::add_string("/plugins/core/perl_test/choice_str", "ch1"); - Purple::Prefs::add_int("/plugins/core/perl_test/choice_int", 1); - Purple::Prefs::add_string("/plugins/core/perl_test/text", "Foobar"); -} -@endcode - -Now we can provide an UI for manipulating these preferences in our @c prefs_info -function. - -@code -sub prefs_info_cb { - # The first step is to initialize the Purple::Pref::Frame that will be returned - $frame = Purple::PluginPref::Frame->new(); - - # Create a new boolean option with a label "Boolean Label" and then add - # it to the frame - $ppref = Purple::PluginPref->new_with_label("Boolean Label"); - $frame->add($ppref); - - $ppref = Purple::PluginPref->new_with_name_and_label( - "/plugins/core/perl_test/bool", "Boolean Preference"); - $frame->add($ppref); - - # Create a set of choices. To do so, we must set the type to 1 which is - # the numerical equivalent of the PurplePrefType for choice. - $ppref = Purple::PluginPref->new_with_name_and_label( - "/plugins/core/perl_test/choice_str", "Choice Preference"); - $ppref->set_type(1); - $ppref->add_choice("ch0", "ch0"); - # The following will be the default value as set from plugin_load - $ppref->add_choice("ch1", "ch1"); - $frame->add($ppref); - - # Create a set of choices. To do so we must set the type to 1 which is - # the numerical equivalent of the PurplePrefType for choice. - $ppref = Purple::PluginPref->new_with_name_and_label( - "/plugins/core/perl_test/choice_int", "Choice Preference 2"); - $ppref->set_type(1); - $ppref->add_choice("zero", 0); - # The following will be the default value as set from plugin_load - $ppref->add_choice("one", 1); - $frame->add($ppref); - - - # Create a text box. The default value will be "Foobar" as set by - # plugin_load - $ppref = Purple::PluginPref->new_with_name_and_label( - "/plugins/core/perl_test/text", "Text Box Preference"); - $ppref->set_type(2); - $ppref->set_max_length(16); - $frame->add($ppref); - - return $frame; -} -@endcode - -<!-- -Using the Gtk2-Perl module for Perl it is possible to create tailored @c GtkFrame elements and display them in a preference window. Note that Gtk2-Perl must be loaded with @c require and not @c use . The first step is to create the proper key/value pairs in the @c \%PLUGIN_INFO hash noting that the @c prefs_info key is no longer valid. Instead the keys @c GTK_UI and @c gtk_prefs_info must be set as follows. - -@code -%PLUGIN_INFO = { - ..., - # Used to differentiate between a regular and a Gtk preference frame - GTK_UI => TRUE, - gtk_prefs_info => "gtk_prefs_info_cb", -} -@endcode - -To finish this example @c gtk_prefs_info_cb needs to be defined. To introduce some of the flexibility of using Gtk2-Perl the example also includes a button and a callback for the button. Explaining Gtk2-Perl is beyond the scope of this tutorial and more info can be found at the project's website <a href="http://gtk2-perl.sourceforge.net/">http://gtk2-perl.sourceforge.net/</a>. - -@code -# A simple call back that prints out whatever value it is given as an argument. -sub button_cb { - my $widget = shift; - my $data = shift; - print "Clicked button with message: " . $data . "\n"; -} - -sub gtk_prefs_info_cb { - # Create a button that prints a message to the console and places it in the frame. - use Glib; - require Gtk2; - - $frame = Gtk2::Frame->new(\'Gtk Test Frame\'); - $button = Gtk2::Button->new(\'Print Message\'); - - $frame->set_border_width(10); - $button->set_border_width(150); - $button->signal_connect("clicked" => \&button_cb, "Message Text"); - $frame->add($button); - - $button->show(); - $frame->show(); - - return $frame; -} -@endcode ---> - -@section request-api Request Dialog Box API - -The @c Purple::Request package allows plugins to have interactive dialog boxes -without the need to directly interact with the UI (e.g. GTK+). This allows core -(libpurple) plugins to create a UI that can be displayed on whichever UI frontend -is being used. The portion of the Request API available to perl scripts is -listed below, followed by an example illustrating its use. - -These arguments are the same for each of the three request types: - @arg @em handle - The plugin handle. - @arg @em title - String title for the dialog. - @arg @em primary - The first sub-heading. - @arg @em secondary - The second sub-heading. - @arg @em ok_text - The Text for the OK button. - @arg @em ok_cb - The string name of the perl subroutine to call when the OK button is clicked. - @arg @em cancel_text - The text for the Cancel button. - @arg @em cancel_cb - The string name of the perl subroutine to call when the Cancel button is clicked. - @arg @em default_value - Default text string to display in the input box. - @arg @em multiline - Boolean where true indicates multiple line input boxes are allowed. - @arg @em masked - Boolean indicating if the user can edit the text. - @arg @em hint - See source for more information - can be left blank. - @arg @em filename - String default file name value. - @arg @em savedialog - Boolean where true indicates use as a save file dialog and false indicates an open file dialog. - -@code -# Create a simple text input box -Purple::Request::input(handle, title, primary, secondary, default_value, multiline, masked, hint, ok_text, ok_cb, cancel_text, cancel_cb); - -# Prompt user to select a file -Purple::Request::file(handle, title, filename, savedialog, ok_cb, cancel_cb); - -# Create a unique input dialog as shown in the following example -Purple::Request::fields(handle, title, primary, secondary, fields, ok_text, ok_cb, cancel_text, cancel_cb); -@endcode - -The following is an example of a @c Purple::Request::fields() dialog box with a -callback for the OK button and another for the Cancel Button. - -@code -sub ok_cb_test{ - # The $fields is passed to the callback function when the button is clicked. - # To access a specific field, it must be extracted from $fields by name. - $fields = shift; - $account = Purple::Request::Fields::get_account($fields, "acct_test"); - $int = Purple::Request::Fields::get_integer($fields, "int_test"); - $choice = Purple::Request::Fields::get_choice($fields, "ch_test"); -} - -sub cancel_cb_test{ - # Cancel does nothing but is equivalent to the ok_cb_test -} - -sub plugin_load { - my $plugin = shift; - - # Create a group to pool together multiple fields. - $group = Purple::Request::Field::Group::new("Group Name"); - - # Each field is created with Purple::Request::*_new(), is made viewable with Purple::Request::field_*_set_show_all() - # and is then added to the group with Purple::Request::field_group_add_field() - - # Add an account combobox containing all active accounts - $field = Purple::Request::Field::account_new("acct_test", "Account Text", undef); - Purple::Request::Field::account_set_show_all($field, 0); - Purple::Request::Field::Group::add_field($group, $field); - - # Add an integer input box - $field = Purple::Request::Field::int_new("int_test", "Integer Text", 33); - Purple::Request::Field::Group::add_field($group, $field); - - # Add a list of choices - $field = Purple::Request::Field::choice_new("ch_test", "Choice Text", 1); - Purple::Request::Field::choice_add($field, "Choice 0"); - Purple::Request::Field::choice_add($field, "Choice 1"); - Purple::Request::Field::choice_add($field, "Choice 2"); - - Purple::Request::Field::Group::add_field($group, $field); - - # Create a Purple::Request and add the group that was just created. - $request = Purple::Request::Fields::new(); - Purple::Request::Fields::add_group($request, $group); - - # Display the dialog box with the input fields added earlier with the appropriate titles. - Purple::Request::fields( - $plugin, - "Request Title!", - "Primary Title", - "Secondary Title", - $request, - "Ok Text", "ok_cb_test", - "Cancel Text", "cancel_cb_test"); -} -@endcode - -@section timeout-cb Misc: Plugin Actions, Timeouts and Callbacks - -This section of the manual covers some of the important features that can be -added to libpurple perl Plugins. Plugin actions are callback functions that are -accessible to the user from the Buddy List window under "Tools -> [Plugin Name]". -Timeouts allow a plugin to execute a perl subroutine after a given period of time. -Note that timeouts only occur once, so if the timeout must occur periodically, -the timeout needs to be re-added at the end of the timeout callback function. -Callbacks are functions that are called when an event occurs (such as a buddy -signing-on, or a message being received). The following three examples will -demonstrate the usage of these features. - -Plugin actions require the @c \%PLUGIN_INFO hash to have a key/value pair added, -specifying a perl subroutine which will list the available actions; each action -listed must have a definition, including a reference to a callback subroutine, -added to the @c \%plugin_actions hash. - -@code -%PLUGIN_INFO = { - ..., - plugin_action_sub => "plugin_actions_cb", -} - -sub plugin_actions_cb { - my @actions = ("Action 1", "Action 2"); -} - -%plugin_actions = ( - # The callback subroutine that is called when "Tools -> Plugin -> Action 1" is selected - "Action 1" => \&action1_cb, - # The callback subroutine that is called when "Tools -> Plugin -> Action 2" is selected - "Action 2" => \&action2_cb -); - -sub action1_cb { - Purple::Debug::info("Test Plugin", "Action 1 activated\n"); -} - -sub action2_cb { - Purple::Debug::info("Test Plugin", "Action 2 activated\n"); -} -@endcode - -Timeouts allow a perl subroutine to be executed after a specified time. They only occur once, so, as stated earlier, the timeout must be re-registered after every time it is called. - -@code -sub timeout_cb { - my $plugin = shift; - Purple::Debug::info("testplugin", "Timeout occurred.\n"); - - # Reschedule timeout - Purple::timeout_add($plugin, 10, \&timeout_cb, $plugin); -} - -sub plugin_load { - $plugin = shift; - - # Schedule a timeout for ten seconds from now - Purple::timeout_add($plugin, 10, \&timeout_cb, $plugin); -} -@endcode - -Callbacks are handled by creating a perl subroutine to serve as the callback and -then attaching the callback to a signal. - -@code -sub signal_cb { - # The signal data and the user data come in as arguments - my ($account, $data) = @_; - Purple::Debug::info("testplugin", "Account \"" . $account->get_username() . "\" just connected.\n"); -} - -sub plugin_load { - $plugin = shift; - - # User data to be given as an argument to the callback perl subroutine. - $data = ""; - - # A pointer to the handle to which the signal belongs needed by the callback function - $accounts_handle = Purple::Accounts::get_handle(); - - # Connect the perl subroutine 'signal_cb' to the event 'account-connecting' - Purple::Signal::connect($accounts_handle, "account-connecting", $plugin, \&signal_cb, $data); -} -@endcode - -@section Resources - @see API Documentation - -*/
--- a/doc/TCL-HOWTO.dox Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,362 +0,0 @@ -/** @page tcl-howto Tcl Scripting HOWTO - -@section Intoduction - -NOTA BENE: This documentation is badly out of date for 2.x. - -The libpurple Tcl interface provides a Tcl API for many useful libpurple -functions. Like the perl API, the Tcl API does not provide access to -every corner of libpurple exposed by the @e C interface. It does, -however, provide a very powerful interface to many of libpurple's -functions through a simple to learn and extend scripting language. - -If you are not familiar with Tcl, you will probably find it somewhat -different from what you are used to. Despite being somewhat unique -(more akin to shell programming than other traditional scripting -languages such as @e perl or @e python), it is simple to learn for -beginners and experienced programmers alike. There are numerous books -on the subject; we will not discuss it any further here. - -@section start Getting Started - -The only requirement placed on a purple Tcl script by libpurple is the -existence of a procedure called @c plugin_init. This procedure has -some limitations placed upon it; it will be parsed and evaluated before -the rest of the Tcl script, so it cannot reference any other variables -or procedures declared in the script. In practice this is not a -problem, as the only thing this procedure should do is return a simple -list containing five items: the @b name of the script, its @b version -number, a @b summary (just a few words) of its function, a short (longer -than the summary, but no more than a couple of sentences if possible) -@b description, the @b author, and a @b URL to web page. For example: - -@code -proc plugin_init { } { - return [ list "Example Plugin" \ - "1.0" \ - "Example plugin registration" \ - "Example of how to register a plugin for the Tcl HOWTO" \ - "Ethan Blanton <eblanton@cs.purdue.edu>" \ - "https://pidgin.im/" ] -} -@endcode - -The rest of the script will generally be registration to recieve -notification of various purple (or Pidgin, or finch, or ...) signals -(more about this below) and definitions of procedures to be executed -when those signals occur. - -@section details Interpreter Details - -libpurple initializes and drives the Tcl event loop (similar to Tk), -meaning that commands like @c fileevent and @c after are available and -do not require @c vwait etc. The @c vwait actually seems to be somewhat -broken due to a bug somewhere in the Tcl/Glib event loop glue, and it -should not be used for now. - -The purple-specific functions are provided in a statically-linked -package called @c purple; this means that if you spawn a child -interpreter and wish to use the purple-specific functions, you will need -to execute <tt>load {} purple</tt> in that interpreter. - -@section internals purple Internal Procedures and Variables - -All of the information provided for your use by purple will be in the @c -::purple namespace. This means that in order to access it you will either -have to import the purple namespace (e.g. via the command <tt>namespace -import purple::*</tt>) or reference it explicitly. The following -descriptions will reference it explicitly for clarity. - -@li Variables - -@code -purple::version -@endcode - - This contains the version of the libpurple library which loaded the - script. - -@li Commands - -@code -purple::account alias account -purple::account connect account -purple::account connection account -purple::account disconnect account -purple::account find username protocol -purple::account handle -purple::account isconnected account -purple::account list ?option? -purple::account protocol account -purple::account username account -@endcode - - The @c purple::account command consists of a set of subcommands - pertaining to purple accounts. - - @c alias returns the alias for the account @c account. If there is no - alias for the given account, it returns the empty string. - - The subcommand @c connect connects the named account if it is not - connected, and does nothing if it is. In either case, it returns - the @c gc for the account. - - @c connection returns the @c gc of the given account if it is connected, - or 0 if it is not. This @c gc is the gc used by purple::connection and - other functions. - - @c disconnect disconnects the given @c account if it is connected, or - does nothing if it is. - - @c find finds an account by its @c username and @c protocol (as returned by - <tt>purple::account username</tt> and <tt>purple::account protocol</tt>) and - returns the account if found, or 0 otherwise. - - @c handle returns the instance handle required to connect to account - signals. (See <tt>purple::signal connect</tt>). - - The @c isconnected query returns true if the given account is - connected and false otherwise. - - The @c list subcommand returns a list of all of the accounts known to - libpurple. The elements of this lists are accounts appropriate for the - @c account argument of the other subcommands. The @c -all option - (default) returns all accounts, while the @c -online option returns - only those accounts which are online. - - The @c protocol subcommand returns the protocol ID (e.g. "prpl-msn") - for the given account. - - The @c username subcommand returns the username for the account - @c account. - -@code -purple::buddy alias buddy -purple::buddy handle -purple::buddy info ( buddy | account username ) -purple::buddy list -@endcode - - @c purple::buddy is a set of commands for retrieving information about - buddies and manipulating the buddy list. For the purposes of Tcl, - a "buddy" is currently a list of several elements, the first of - which being the type. The currently recognized types are "group", - "buddy", and "chat". A group node looks like: -@code - { group name { buddies } } -@endcode - A buddy node is: -@code - { buddy name account } -@endcode - And a chat node is: -@code - { chat alias account } -@endcode - - The @c alias subcommand returns the alias for the given buddy if it - exists, or the empty string if it does not. - - @c handle returns the blist handle for the purposes of connecting - signals to buddy list events. (See <tt>purple::signal connect</tt>). - - @c info causes the purple-using UI to display the info dialog for the - given buddy. Since it is possible to request user info for a buddy - not in your buddy list, you may also specify a buddy by his or her - username and the account through which you wish to retrieve info. - - @c list returns a list of @c group structures, filled out with buddies - and chats as described above. - -@code -purple::connection account gc -purple::connection displayname gc -purple::connection handle -purple::connection list -purple::connection state -@endcode - - @c purple::connection is a collection of subcommands pertaining to - account connections. - - @c account returns the purple account associated with @c gc. This - account is the same account used by @c purple::account and other - commands. - - @c displayname returns the display name (duh) of @c gc as reported by - <tt>purple_connection_get_display_name(gc)</tt>. - - @c handle returns the purple connections instance handle. (See - <tt>purple::signal connect</tt>). - - @c list returns a list of all known connections. The elements of - this list are appropriate as @c gc arguments to the other - @c purple::connection subcommands or other commands requiring a gc. - - @c state returns the PurpleConnectionState of this account as one of - the strings "connected", "disconnected", or "connecting". - -@code -purple::conv_send account who text -@endcode - - @c purple::conv is simply a convenience wrapper for @c purple::send_im and - <tt>purple::conversation write</tt>. It sends the IM, determines the from - and to arguments for <tt>purple::conversation write</tt>, and prints the text - sent to the conversation as one would expect. For the curious, you - may view the source for it by typing <tt>info body purple::conv_send</tt> at - a Purple Commander prompt. - - Note that an error in either @c purple::send_im or <tt>purple::conversation - write</tt> will not be caught by this procedure, and will be propagated - to the caller. - -@code -purple::conversation find ?-account account? name -purple::conversation handle -purple::conversation list -purple::conversation new ?-chat? ?-im? account name -purple::conversation write conversation style from to text -@endcode - - @c purple::conversation provides an API for dealing with - conversations. Given that libpurple clients are instant messenger - programs, you'll probably spend a lot of time here. - - The command @c find attempts to find an existing conversation with - username @c name. If the @c -account option is given, it refines its - search to include only conversations on that account. - - @c handle returns the conversations instance handle for the purposes - of signal connection. (See <tt>purple::signal connect</tt>). - - @c list returns a list of all currently open conversations. - - The @c new subcommand can be used to create a new conversation with - a specified user on a specified account if one does not exist, or - retrieve the existing conversation if it does. The @c -chat and - @c -im options specify whether the created conversation should be a - chat or a standard IM, respectively. - - @c write is used to write to the specified conversation. The @c style - argument specifies how the text should be printed -- as text coming - from the purple user (style @c send), being sent to the purple user - (style @c recv), or as a system message (such as "so-and-so has - signed off", style @c system). From is the name to whom the text - should be attributed -- you probably want to check for aliases here, - lest you confuse the user. @c text is the text to print. - -@code -purple::core handle -purple::core quit -@endcode - - This command exposes functionality provided by the purple core API. - - <tt>purple::core handle</tt> returns a handle to the purple core for signal - connection. (See <tt>purple::signal connect</tt>). - - @c quit exits the libpurple client cleanly, and should be used in - preference to the tcl @c exit command. (Note that @c exit has not - been removed, however.) - -@code -purple::debug level category message -@endcode - - Equivalent to the C purple_debug function, this command outputs - debugging information to the libpurple UI's debug window (or, - typically, stdout if that UI is invoked with -d|--debug). The valid - levels are, in increasing level of severity, @c -misc, @c -info, @c - -warning, and, or @c -error. @c category is a short (a few characters - ... for instance, "tcl" or "tcl plugin") "topic" type name for this - message, and @c message is the text of the message. In the style of - Tcl @e puts (and differing from @e purple_debug), no trailing \\n is - required. (However, embedded newlines may be generated with \\n). - -@code -purple::notify ?type? title primary secondary -@endcode - - Also a direct equivalent to a C function, purple_notify, this command - causes libpurple to present the provided notification information to - the user via some appropriate UI method. The @c type argument, if - present, must be one of @c -error, @c -warning, or @c -info. The - following three arguments' absolute meanings may vary with the purple - UI being used, but @c title should generally be the title of the - window, and @c primary and @c secondary text within that window; in - the Pidgin UI, @c primary is slightly larger than @c secondary and - displayed in a @b boldface font. - -@code -purple::send_im gc who text -@endcode - - This sends an IM in the fashion of serv_send_im. @c gc is the GC of - the connection on which you wish to send (as returned by most event - handlers), @c who is the nick of the buddy to which you wish to send, - and @c text is the text of the message. - -@code -purple::signal connect instance signal args proc -purple::signal disconnect instance signal -@endcode - - @c purple::signal is a set of subcommands for dealing with purple - signals. - - The @c connect subcommand registers the procedure @c proc as a handler - for the signal @c signal on the instance @c instance. @c instance - should be an instance handle as returned by one of the @c handle - commands from the various parts of libpurple. @c args and @ proc are - as in the Tcl @e proc command; note that the number of arguments in @c - args must match the number of arguments emitted by the signal exactly, - although you need not use them all. The procedure @c proc may be - either a simple command or a procedure in curly brackets. Note that - only one procedure may be associated with each signal; an attempt to - connect a second procedure to the same signal will remove the existing - binding and replace it with the new procedure. <tt>purple::signal - connect</tt> returns 0 on success and 1 on failure. - - @c disconnect removes any existing signal handler for the named - signal and instance. - -@code -purple::unload -@endcode - - This unloads the current plugin. Note that preferences will not be - updated (yet). - -@section Signals - -Check the signals documentation for the meaning of these signals; this is -intended to be a list only of their arguments. Signal callbacks will -be made in their own namespace, and arguments to those signal -callbacks will live in the namespace @c event underneath that -namespace. To briefly illustrate, the signal @c receiving-im-msg is -provided with three arguments; the account on which the IM was -received, the name of the buddy sending the IM, and the text of -the IM. These arguments live in the variables @c event::account, -@c event::sender, and @c event::buffer, respectively. Therefore a callback -which notifies the user of an incoming IM containing the word 'shizzle' -might look like this: - -@code -purple::signal connect [purple::conversation handle] receiving-im-msg { - if {[ string match "*shizzle*" $event::buffer ]} { - purple::notify -info "tcl plugin" "Fo' shizzle" \ - "$event::sender is down with the shizzle" - } -} -@endcode - -Note that for some signals (notably @c receiving-im-msg, @c sending-im-msg, -and their chat counterparts), changes to the event arguments will -change the message itself from libpurple's vantage. For those signals -whose return value is meaningful, returning a value from the Tcl event -will return that value as it would in C. - -*/ - -// vim: syntax=c tw=72 et
--- a/doc/plugin-i18n.dox Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +0,0 @@ -/** @page plugin-i18n Third Party Plugin Translation Support - - @section Introduction - For the purpose of this document we're going to assume that your plugin: - - - Is set up to use autotools. It may be possible to add translation support - without autotools, but we have no idea how. We may not want to know, either ;) - - Has an autogen.sh. You may have also called this bootstrap.sh or similar. - - Resides in a source tree that has @c configure.ac and @c Makefile.am in the - top-level directory as well as a @c src directory in which the plugin's source - is located. A @c Makefile.am should also exist in the @c src directory. - - For a plugin to have translation support there are a few steps that need to - followed: - - - In your autogen.sh, add the following after your other utility checks: - @code -(intltoolize --version) < /dev/null > /dev/null 2>&1 || { - echo; - echo "You must have intltool installed to compile <YOUR PLUGIN NAME>"; - echo; - exit; -} - @endcode - Then before your call to aclocal add: - @code -intltoolize --force --copy - @endcode - - Now edit configure.ac and add the following: - @code -AC_PROG_INTLTOOL - -GETTEXT_PACKAGE=<YOUR PLUGIN NAME> -AC_SUBST(GETTEXT_PACKAGE) -AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, ["$GETTEXT_PACKAGE"], [Define the gettext package to be used]) - -ALL_LINGUAS="" -AM_GLIB_GNU_GETTEXT - @endcode - The position of these macros in the file don't really matter, but if you - have issues either play around with it or feel free to ask one of the Pidgin - developers. Finally add 'po/Makefile.in' to you 'AC_OUTPUT' command. - - Now create a directory named 'po'. - - 'cd' into the 'po' directory. - - Create/edit the file 'POTFILE.in' in your favorite editor. Each line - should be the name of a file that could or does have strings marked for - translating (we're getting to that step). These file names should be - relative to the top directory of your plugin's source tree. - - 'cd' back to the top directory of your plugin's source tree. - - Open 'Makefile.am' and add 'po' to your 'SUBDIRS' variable. - - While still in the top directory of your plugin's source tree, execute - 'intltool-prepare'. This will setup anything extra that intltool needs. - - Fire off 'autogen.sh' and when it's completed, verify that you have a - 'po/POTFILES' (notice the lack of a .in). If you do, everything should be - set on the autotools side. - - Take a break, stretch your legs, smoke a cigarette, whatever, because - we're done with the autotools part. - - When you're ready, 'cd' into the directory with the source files for your - plugin. - - Open the file containing the PurplePluginInfo structure. - - If you're not already, please make sure that you are including the - 'config.h' file for you plugin. Note that 'config.h' could be whatever - you told autohead to use with AM_CONFIG_HEADER. Also add the following: - @code -#include <glib/gi18n-lib.h> - @endcode - Make sure that this include is after you include of your 'config.h', - otherwise you will break your build. Also note that if you wish to - maintain compatibility with older versions of GLib, you will need to - include additional preprocessor directives, which we won't cover here. - - This is where things get a bit goofy. libpurple is going to try to - translate our strings using the libpurple gettext package. So we have to - convert them before libpurple attempts to. - - To do this, we're going to change the entries for name, summary, and - description to NULL. - - Next, locate your 'init_plugin' function. Your name for this function - may vary, but it's the second parameter to 'PURPLE_INIT_PLUGIN'. - - Now add the following within your 'init_plugin' function: - @code -#ifdef ENABLE_NLS - bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); -#endif /* ENABLE_NLS */ - - info.name = _("<YOUR PLUGIN NAME>"); - info.summary = _("<YOUR PLUGIN SUMMARY>"); - info.description = _("<YOUR PLUGIN DESCRIPTION>"); - @endcode - Note that the _() is intentional, and that it is telling intltool that - this string should be translated. There is also N_() which says that a - string should only be marked for translation but should not be translated - yet. - - Go through the rest of your code and mark all the other strings for - translation with _(). - - When thats done, feel free to commit your work, create your po template - (pot file) or whatever. - - To create you po template, 'cd' to 'po' and execute: - @code -intltool-update --pot - @endcode - - To add new translations to your plugin, all you have to do is add the - language code to the 'ALL_LINGUAS' variable in your configure.ac. Take - note that this list of languages should be separated by a space. After - you have added the language code to 'ALL_LINGUAS', drop the xx.po file - into 'po', and re-'autogen.sh'. After a full build you should now be - able to use the translation. - */
--- a/doc/plugin-ids.dox Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -/** @page plugin-ids Plugin IDs - - @section Introduction - Every plugin contains a unique identifier to prevent duplicate plugin - loading and conflicts. Third-party plugins (that is, plugins written by - anyone who is not a libpurple, Pidgin, or Finch developer) are expected - to use a plugin ID that follows a specific format. This format - categorizes plugins and makes duplicate IDs highly unlikely. - - @section Format - The basic format of a plugin ID is as follows: - - <tt><i>type</i>-<i>username</i>-<i>pluginname</i></tt> - - The @em type indicator specifies the type of plugin. This must be one - of the following: - - - core - A core libpurple plugin, capable of being loaded in any - program using libpurple. Core plugins may not contain any - UI-specific code. - - prpl - A protocol plugin. This is a special type of core plugin, - which provides libpurple the ability to connect to - another IM or chat network. - - lopl - A loader plugin, which loads scripts as plugins. Perl and - Tcl plugins are made possible by loader plugins. - - gtk - A GTK+ 2.x (a.k.a. Pidgin) plugin. These plugins may use - GTK+ code, but may not use window toolkit code, such as - X11, Win32, Cocoa, or Carbon. - - gtk-x11 - A GTK+ 2.x plugin that uses X11 code. These plugins may - use both GTK+ code and X11 code, allowing to hook into - features specific to X11. - - gtk-win32 - A GTK+ plugin that uses Win32 code. These plugins may use - both GTK+ code and Win32 code, allowing to hook into - features available on Windows. - - gnt - A GNT (a.k.a. Finch) plugin. These plugins may use GNT code. - - qpe - A plugin for the (now-abandoned) Qutopia user interface. - - The @em username must be a unique identifier for you. It - @em should be your https://developer.pidgin.im Trac user ID. Failing that, you - could use your SourceForge user ID or your Freenode IRC nickname, if you - have either. The https://developer.pidgin.im Trac user ID is preferred. - Do @em not leave this field blank! - - The @em pluginname is the name of your plugin. It is usually all - lowercase letters and matches the static plugin ID (the first argument to - the PURPLE_INIT_PLUGIN() macro call), although it can be anything you - like. Do @em not include version information in the plugin ID--the - #PurplePluginInfo structure already has a field for this. - - @section nospaces One Last Rule for Plugin IDs - - The last rule of plugin IDs is the most important of all. Plugin IDs may - @em NOT contain spaces. If you need a space, use another hyphen (-). - - @section exceptions Exceptions to the Rule - - As with any rule there are exceptions. If you browse through the source - tree you will see that the plugins we distribute with the Pidgin source - do not contain a username field. This is because while one developer may - have written each specific plugin, the plugins are maintained - collectively by the entire development team. This lack of a username - field is also an indicator that the plugin is one of our plugins and not - a third-party plugin. - - Another exception to the rule is the <a - href="http://plugins.guifications.org/trac/wiki/PluginPack">Purple Plugin - Pack</a>. All plugins whose lives started in the Purple Plugin Pack use - <tt>"plugin_pack"</tt> for the username field to indicate origination in - the Purple Plugin Pack. - - These two exceptions are mentioned here for completeness. We don't - encourage breaking the conventions set forth by the rules outlined above. - - @section examples Examples of Well-Chosen Plugin IDs - - The following is a list of well-chosen Plugin IDs listing a few good examples. - - - <tt>"gtk-amc_grim-guifications"</tt> - This is the plugin ID for the - Guifications 2.x plugin. - - <tt>"gtk-rlaager-album"</tt> - This is the plugin ID for the Album - plugin, which is now part of the - Purple Plugin Pack. Its ID follows the - rules because its life started prior - to its inclusion in the Plugin Pack. - - <tt>"core-rlaager-irchelper"</tt> - This is the plugin ID for the IRC - Helper plugin, which is now part - of the Purple Plugin Pack. Its ID - follows the rules because its - life started prior to its - inclusion in the Plugin Pack. - - @section plugin-db Plugin Database - Although it doesn't exist yet, in time there will be a plugin database - on the Pidgin website, where users can download and install new plugins. - Plugins will be accessed by your plugin ID, which is one reason why it - must be unique. - - */ - -// vim: syntax=c.doxygen tw=75 et
--- a/doc/plugin-signals.dox Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -/** @page plugin-signals Plugin Signals - - @signals - @signal plugin-load - @signal plugin-unload - @endsignals - - @see plugin.h - - <hr> - - @signaldef plugin-load - @signalproto -void (*plugin_load)(PurplePlugin *plugin); - @endsignalproto - @signaldesc - Emitted when a plugin is loaded. - @param plugin The plugin that was loaded. - @endsignaldef - - @signaldef plugin-unload - @signalproto -void (*plugin_unload)(PurplePlugin *plugin); - @endsignalproto - @signaldesc - Emitted when a plugin is unloaded. - @param plugin The plugin that was unloaded. - @endsignaldef - - */ -// vim: syntax=c.doxygen tw=75 et
--- a/doc/reference/libpurple/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/doc/reference/libpurple/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -80,6 +80,8 @@ # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). # e.g. content_files=running.sgml building.sgml changes-2.0.sgml content_files = version.xml \ + plugin_i18n.xml \ + plugin_ids.xml \ signals_account.xml \ signals_blist.xml \ signals_certificate.xml \ @@ -92,9 +94,12 @@ signals_jabber.xml \ signals_log.xml \ signals_notify.xml \ + signals_plugin.xml \ + signals_protocol.xml \ signals_savedstatus.xml \ signals_sound.xml \ signals_xfer.xml \ + tut_c_plugins.xml \ tut_signals.xml \ ui_ops.xml @@ -112,6 +117,7 @@ -I$(top_srcdir) \ -I$(top_srcdir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS) \ $(DBUS_CFLAGS) \ $(LIBXML_CFLAGS) \
--- a/doc/reference/libpurple/libpurple-docs.xml Fri Jan 31 17:56:27 2014 +0530 +++ b/doc/reference/libpurple/libpurple-docs.xml Fri Jan 31 18:02:20 2014 +0530 @@ -16,6 +16,7 @@ <reference label="I"> <title>Tutorials</title> + <xi:include href="tut_c_plugins.xml" /> <xi:include href="tut_signals.xml" /> </reference> @@ -75,12 +76,13 @@ <xi:include href="xml/notify.xml" /> <xi:include href="xml/ntlm.xml" /> <xi:include href="xml/pluginpref.xml" /> - <xi:include href="xml/plugin.xml" /> + <xi:include href="xml/plugins.xml" /> <xi:include href="xml/pounce.xml" /> <xi:include href="xml/prefs.xml" /> <xi:include href="xml/presence.xml" /> + <xi:include href="xml/protocols.xml" /> + <xi:include href="xml/protocol.xml" /> <xi:include href="xml/proxy.xml" /> - <xi:include href="xml/prpl.xml" /> <xi:include href="xml/purple-socket.xml" /> <xi:include href="xml/purple.xml" /> <xi:include href="xml/request.xml" /> @@ -129,12 +131,16 @@ <xi:include href="signals_imgstore.xml" /> <xi:include href="signals_log.xml" /> <xi:include href="signals_notify.xml" /> + <xi:include href="signals_plugin.xml" /> + <xi:include href="signals_protocol.xml" /> <xi:include href="signals_savedstatus.xml" /> <xi:include href="signals_sound.xml" /> <xi:include href="signals_xfer.xml" /> <xi:include href="signals_jabber.xml" /> </reference> + <xi:include href="plugin_ids.xml" /> + <xi:include href="plugin_i18n.xml" /> <xi:include href="ui_ops.xml" /> <index id="api-index-full">
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/reference/libpurple/plugin_i18n.xml Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,177 @@ +<?xml version='1.0' encoding="ISO-8859-1"?> +<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ +]> +<chapter id="chapter-plugin-i18n"> + <title>Third Party Plugin Translation</title> + + <sect2 id="plugin-i18n-introduction"> + <title>Introduction</title> + + <para> + For the purpose of this document we're going to assume that your plugin: + + <itemizedlist> + <listitem><para> +Is set up to use autotools. It may be possible to add translation support +without autotools, but we have no idea how. We may not want to know, either ;) + </para></listitem> + <listitem><para> +Has an autogen.sh. You may have also called this bootstrap.sh or similar. + </para></listitem> + <listitem><para> +Resides in a source tree that has <literal>configure.ac</literal> and +<literal>Makefile.am</literal> in the top-level directory as well as a +<literal>src</literal> directory in which the plugin's source is located. A +<literal>Makefile.am</literal> should also exist in the <literal>src</literal> +directory. + </para></listitem> + </itemizedlist> + </para> + </sect2> + + <sect2 id="plugin-i18n-steps"> + <title>Steps to follow</title> + + <para> + For a plugin to have translation support there are a few steps that need to + followed: + + <itemizedlist> + <listitem><para> +In your autogen.sh, add the following after your other utility checks: +<programlisting> +(intltoolize --version) < /dev/null > /dev/null 2>&1 || { + echo; + echo "You must have intltool installed to compile <YOUR PLUGIN NAME>"; + echo; + exit; +} +</programlisting> +Then before your call to aclocal add: +<programlisting> +intltoolize --force --copy +</programlisting> + </para></listitem> + <listitem><para> +Now edit configure.ac and add the following: +<programlisting> +AC_PROG_INTLTOOL + +GETTEXT_PACKAGE=<YOUR PLUGIN NAME> +AC_SUBST(GETTEXT_PACKAGE) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, ["$GETTEXT_PACKAGE"], [Define the gettext package to be used]) + +ALL_LINGUAS="" +AM_GLIB_GNU_GETTEXT +</programlisting> +The position of these macros in the file don't really matter, but if you +have issues either play around with it or feel free to ask one of the Pidgin +developers. Finally add 'po/Makefile.in' to you 'AC_OUTPUT' command. + </para></listitem> + <listitem><para> +Now create a directory named 'po'. + </para></listitem> + <listitem><para> +'cd' into the 'po' directory. + </para></listitem> + <listitem><para> +Create/edit the file 'POTFILE.in' in your favorite editor. Each line +should be the name of a file that could or does have strings marked for +translating (we're getting to that step). These file names should be +relative to the top directory of your plugin's source tree. + </para></listitem> + <listitem><para> +'cd' back to the top directory of your plugin's source tree. + </para></listitem> + <listitem><para> +Open 'Makefile.am' and add 'po' to your 'SUBDIRS' variable. + </para></listitem> + <listitem><para> +While still in the top directory of your plugin's source tree, execute +'intltool-prepare'. This will setup anything extra that intltool needs. + </para></listitem> + <listitem><para> +Fire off 'autogen.sh' and when it's completed, verify that you have a +'po/POTFILES' (notice the lack of a .in). If you do, everything should be +set on the autotools side. + </para></listitem> + <listitem><para> +Take a break, stretch your legs, smoke a cigarette, whatever, because +we're done with the autotools part. + </para></listitem> + <listitem><para> +When you're ready, 'cd' into the directory with the source files for your +plugin. + </para></listitem> + <listitem><para> +Open the file containing the PurplePluginInfo structure. + </para></listitem> + <listitem><para> +If you're not already, please make sure that you are including the +'config.h' file for you plugin. Note that 'config.h' could be whatever +you told autohead to use with AM_CONFIG_HEADER. Also add the following: +<programlisting> +#include <glib/gi18n-lib.h> +</programlisting> +Make sure that this include is after you include of your 'config.h', +otherwise you will break your build. Also note that if you wish to +maintain compatibility with older versions of GLib, you will need to +include additional preprocessor directives, which we won't cover here. + </para></listitem> + <listitem><para> +This is where things get a bit goofy. libpurple is going to try to +translate our strings using the libpurple gettext package. So we have to +convert them before libpurple attempts to. + </para></listitem> + <listitem><para> +To do this, we're going to change the entries for name, summary, and +description to NULL. + </para></listitem> + <listitem><para> +Next, locate your 'plugin_load' function. Your name for this function +may vary, but it's the third parameter to 'PURPLE_PLUGIN_INIT'. + </para></listitem> + <listitem><para> +Now add the following within your 'plugin_load' function: +<programlisting> +#ifdef ENABLE_NLS + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); +#endif /* ENABLE_NLS */ + + info.name = _("<YOUR PLUGIN NAME>"); + info.summary = _("<YOUR PLUGIN SUMMARY>"); + info.description = _("<YOUR PLUGIN DESCRIPTION>"); +</programlisting> +Note that the _() is intentional, and that it is telling intltool that +this string should be translated. There is also N_() which says that a +string should only be marked for translation but should not be translated +yet. + </para></listitem> + <listitem><para> +Go through the rest of your code and mark all the other strings for +translation with _(). + </para></listitem> + <listitem><para> +When thats done, feel free to commit your work, create your po template +(pot file) or whatever. + </para></listitem> + <listitem><para> +To create you po template, 'cd' to 'po' and execute: +<programlisting> +intltool-update --pot +</programlisting> + </para></listitem> + <listitem><para> +To add new translations to your plugin, all you have to do is add the +language code to the 'ALL_LINGUAS' variable in your configure.ac. Take +note that this list of languages should be separated by a space. After +you have added the language code to 'ALL_LINGUAS', drop the xx.po file +into 'po', and re-'autogen.sh'. After a full build you should now be +able to use the translation. + </para></listitem> + </itemizedlist> + </para> + </sect2> +</chapter>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/reference/libpurple/plugin_ids.xml Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,192 @@ +<?xml version='1.0' encoding="ISO-8859-1"?> +<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ +]> +<chapter id="chapter-plugin-ids"> + <title>Plugin IDs</title> + + <sect2 id="plugin-ids-introduction"> + <title>Introduction</title> + <para> +Every plugin contains a unique identifier. Third-party plugins (that is, +plugins written by anyone who is not a libpurple, Pidgin, or Finch developer) +are expected to use a plugin ID that follows a specific format. This format +categorizes plugins and makes duplicate IDs highly unlikely. + </para> + </sect2> + + <sect2 id="plugin-ids-format"> + <title>Format</title> + <para> +The basic format of a plugin ID is as follows: + +<programlisting> +type-username-pluginname +</programlisting> + </para> + + <para> +The <emphasis>type</emphasis> indicator specifies the type of plugin. This must be one +of the following: + + <table> + <title>Types of plugins</title> + <tgroup cols="2"> + <colspec colwidth="*" colnum="1" align="left"/> + <colspec colwidth="*" colnum="2" align="left"/> + <thead> + <row> +<entry><emphasis>type</emphasis></entry> +<entry>description</entry> + </row> + </thead> + <tbody> + <row> +<entry><literal>core</literal></entry> +<entry> +A core libpurple plugin, capable of being loaded in any program using libpurple. +Core plugins may not contain any UI-specific code. +</entry> + </row> + <row> +<entry><literal>prpl</literal></entry> +<entry> +A protocol plugin. This is a core plugin which provides libpurple the ability to +connect to another IM or chat +network. +</entry> + </row> + <row> +<entry><literal>gtk</literal></entry> +<entry> +A GTK+ (a.k.a. Pidgin) plugin. These plugins may use GTK+ code, but may not use +window toolkit code, such as X11, Win32, Cocoa, or Carbon. +</entry> + </row> + <row> +<entry><literal>gtk-x11</literal></entry> +<entry> +A GTK+ plugin that uses X11 code. These plugins may use both GTK+ code and X11 +code, allowing to hook into features specific to X11. +</entry> + </row> + <row> +<entry><literal>gtk-win32</literal></entry> +<entry> +A GTK+ plugin that uses Win32 code. These plugins may use both GTK+ code and +Win32 code, allowing to hook into features available on Windows. +</entry> + </row> + <row> +<entry><literal>gnt</literal></entry> +<entry> +A GNT (a.k.a. Finch) plugin. These plugins may use GNT code. +</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + + <para> +The <emphasis>username</emphasis> must be a unique identifier for you. It +<emphasis>should</emphasis> be your https://developer.pidgin.im Trac user ID. Failing that, you +could use your SourceForge user ID or your Freenode IRC nickname, if you +have either. The https://developer.pidgin.im Trac user ID is preferred. +Do <emphasis>not</emphasis> leave this field blank! + </para> + + <para> +The <emphasis>pluginname</emphasis> is the name of your plugin. It is usually all +lowercase letters and matches the static plugin ID (the first argument to +the PURPLE_PLUGIN_INIT() macro call), although it can be anything you +like. Do <emphasis>not</emphasis> include version information in the plugin ID--the +<literal>PurplePluginInfo</literal> object already has a property for this. + </para> + </sect2> + + <sect2 id="plugin-ids-nospaces"> + <title>One Last Rule for Plugin IDs</title> + <para> +Plugin IDs may <emphasis>NOT</emphasis> contain spaces. If you need a space, use another +hyphen (-). + </para> + </sect2> + + <sect2 id="plugin-ids-exceptions"> + <title>Exceptions to the Rule</title> + <para> +As with any rule there are exceptions. If you browse through the source +tree you will see that the plugins we distribute with the Pidgin source +do not contain a username field. This is because while one developer may +have written each specific plugin, the plugins are maintained +collectively by the entire development team. This lack of a username +field is also an indicator that the plugin is one of our plugins and not +a third-party plugin. + +Another exception to the rule is the <ulink +url="http://plugins.guifications.org/trac/wiki/PluginPack">Purple Plugin +Pack</ulink>. All plugins whose lives started in the Purple Plugin Pack use +<literal>"plugin_pack"</literal> for the username field to indicate origination in +the Purple Plugin Pack. + +These two exceptions are mentioned here for completeness. We don't +encourage breaking the conventions set forth by the rules outlined above. + </para> + </sect2> + + <sect2 id="plugin-ids-examples"> + <title>Examples of Well-Chosen Plugin IDs</title> + <para> +The following is a list of well-chosen Plugin IDs listing a few good examples. + + <table> + <title>Examples</title> + <tgroup cols="2"> + <colspec colwidth="*" colnum="1" align="left"/> + <colspec colwidth="*" colnum="2" align="left"/> + <thead> + <row> +<entry>id</entry> +<entry>description</entry> + </row> + </thead> + <tbody> + <row> +<entry><literal>"gtk-amc_grim-guifications"</literal></entry> +<entry> +This is the plugin ID for the Guifications 2.x plugin. +</entry> + </row> + <row> +<entry><literal>"gtk-rlaager-album"</literal></entry> +<entry> +This is the plugin ID for the Album plugin, which is now part of the +Purple Plugin Pack. Its ID follows the rules because its life started prior +to its inclusion in the Plugin Pack. +</entry> + </row> + <row> +<entry><literal>"core-rlaager-irchelper"</literal></entry> +<entry> +This is the plugin ID for the IRC Helper plugin, which is now part of the +Purple Plugin Pack. Its ID follows the rules because its life started prior +to its inclusion in the Plugin Pack. +</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + </sect2> + + <sect2 id="plugin-ids-plugin-db"> + <title>Plugin Database</title> + <para> +Although it doesn't exist yet, in time there will be a plugin database +on the Pidgin website, where users can download and install new plugins. +Plugins will be accessed by your plugin ID, which is one reason why it +must be unique. + </para> + </sect2> +</chapter>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/reference/libpurple/signals_plugin.xml Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,63 @@ +<?xml version='1.0' encoding="ISO-8859-1"?> +<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ +]> +<chapter id="chapter-signals-plugin"> +<title>Plugin signals</title> + +<refsect1 id="plugins.signals" role="signal_proto"> +<title role="signal_proto.title">List of signals</title> +<synopsis> + "<link linkend="plugins-plugin-load">plugin-load</link>" + "<link linkend="plugins-plugin-unload">plugin-unload</link>" +</synopsis> +</refsect1> + +<refsect1 id="plugins.signal-details" role="signals"> +<title role="signals.title">Signal details</title> + +<refsect2 id="plugins-plugin-load" role="signal"> + <title>The <literal>"plugin-load"</literal> signal</title> +<programlisting> +void user_function (PurplePlugin *plugin, + gpointer user_data) +</programlisting> + <para> +Emitted when a plugin is loaded. + </para> + <variablelist role="params"> + <varlistentry> + <term><parameter>plugin</parameter> :</term> + <listitem><simpara>The plugin that was loaded.</simpara></listitem> + </varlistentry> + <varlistentry> + <term><parameter>user_data</parameter> :</term> + <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem> + </varlistentry> + </variablelist> +</refsect2> + +<refsect2 id="plugins-plugin-unload" role="signal"> + <title>The <literal>"plugin-unload"</literal> signal</title> +<programlisting> +void user_function (PurplePlugin *plugin, + gpointer user_data) +</programlisting> + <para> +Emitted when a plugin is unloaded. + </para> + <variablelist role="params"> + <varlistentry> + <term><parameter>plugin</parameter> :</term> + <listitem><simpara>The plugin that was unloaded.</simpara></listitem> + </varlistentry> + <varlistentry> + <term><parameter>user_data</parameter> :</term> + <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem> + </varlistentry> + </variablelist> +</refsect2> + +</refsect1> + +</chapter>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/reference/libpurple/signals_protocol.xml Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,63 @@ +<?xml version='1.0' encoding="ISO-8859-1"?> +<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ +]> +<chapter id="chapter-signals-protocol"> +<title>Protocol signals</title> + +<refsect1 id="protocols.signals" role="signal_proto"> +<title role="signal_proto.title">List of signals</title> +<synopsis> + "<link linkend="protocols-protocol-added">protocol-added</link>" + "<link linkend="protocols-protocol-removed">protocol-removed</link>" +</synopsis> +</refsect1> + +<refsect1 id="protocols.signal-details" role="signals"> +<title role="signals.title">Signal details</title> + +<refsect2 id="protocols-protocol-added" role="signal"> + <title>The <literal>"protocol-added"</literal> signal</title> +<programlisting> +void user_function (PurpleProtocol *protocol, + gpointer user_data) +</programlisting> + <para> +Emitted when a protocol is added. + </para> + <variablelist role="params"> + <varlistentry> + <term><parameter>protocol</parameter> :</term> + <listitem><simpara>The protocol that was added.</simpara></listitem> + </varlistentry> + <varlistentry> + <term><parameter>user_data</parameter> :</term> + <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem> + </varlistentry> + </variablelist> +</refsect2> + +<refsect2 id="protocols-protocol-removed" role="signal"> + <title>The <literal>"protocol-removed"</literal> signal</title> +<programlisting> +void user_function (PurpleProtocol *protocol, + gpointer user_data) +</programlisting> + <para> +Emitted when a protocol is removed. + </para> + <variablelist role="params"> + <varlistentry> + <term><parameter>protocol</parameter> :</term> + <listitem><simpara>The protocol that was removed.</simpara></listitem> + </varlistentry> + <varlistentry> + <term><parameter>user_data</parameter> :</term> + <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem> + </varlistentry> + </variablelist> +</refsect2> + +</refsect1> + +</chapter>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/reference/libpurple/tut_c_plugins.xml Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,133 @@ +<?xml version='1.0' encoding="ISO-8859-1"?> +<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ +]> +<chapter id="chapter-tut-c-plugins"> + <title>C Plugins tutorial</title> + + <sect2 id="tut-c-plugins-introduction"> + <title>Introduction</title> + <para> + 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 are + also written in C. + </para> + </sect2> + + <sect2 id="tut-c-plugins-getting-started"> + <title>Getting Started</title> + <para> + 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 Mercurial repository if you need + to use a new feature. Please do not abuse our Mercurial repository, however. + </para> + + <para> + All plugins must have <literal>PURPLE_PLUGINS</literal> 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. + Including <literal>purple.h</literal> will define this for you. + </para> + </sect2> + + <sect2 id="tut-c-plugins-hello-world"> + <title>An Example</title> + <para> + I know every tutorial has a hello world, so why should libpurple be any + different? + +<example> +<title>Hello World!</title> +<programlisting> +#include <purple.h> + +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Author Name <e@mail>", + NULL + }; + + /* For specific notes on the meanings of each of these members, consult the + C Plugin Howto on the website. */ + return purple_plugin_info_new ( + "name", "Hello World!", + "version", VERSION, + "category", "Example", + "summary", "Hello World Plugin", + "description", "Hello World Plugin", + "authors", authors, + "website", "http://helloworld.tld", + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + purple_notify_message(plugin, PURPLE_NOTIFY_MSG_INFO, "Hello World!", + "This is the Hello World! plugin :)", + NULL, NULL, NULL, NULL); + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +PURPLE_PLUGIN_INIT(hello_world, plugin_query, plugin_load, plugin_unload); +</programlisting> +</example> + </para> + + <para> + Okay, so what does all this mean? We start off by including purple.h. This + file defines <literal>PURPLE_PLUGINS</literal> as described before so that we + don't have to manually define it. It also includes all the libpurple header + files. + </para> + + <para> + <literal>plugin_query</literal>, <literal>plugin_load</literal> and + <literal>plugin_unload</literal> must be implemented in every plugin. Each of + these functions can return an error on failure by using + <function>g_set_error()</function> on the <literal>error</literal> argument. + </para> + + <para> + <literal>plugin_query</literal> is called when the plugin is probed by the + plugin system, and returns various information about the plugin in form of a + newly created <literal>PurplePluginInfo</literal> instance. For a list of all + available properties, see + <link linkend="purple-plugin-info-new"><function>purple_plugin_info_new</function></link>. + </para> + + <para> + <literal>plugin_load</literal> is called when the plugin is loaded so that you + can initialize any variables, register dynamic types, and so on. Plugins may + also want to add their preferences to the pref tree--more about that later. + In this plugin we'll just use it to display a message. + </para> + + <para> + <literal>plugin_unload</literal> is called when the plugin is unloaded, and we + can use it to wrap up everything, and free our variables. + </para> + + <para> + Finally we have <literal>PURPLE_PLUGIN_INIT()</literal>. + <literal>PURPLE_PLUGIN_INIT</literal> is a macro that every plugin MUST have. + <literal>PURPLE_PLUGIN_INIT</literal> tells libpurple some basic things about + your plugin, like what name to use if the plugin is compiled statically, and + the <literal>plugin_query</literal>, <literal>plugin_load</literal>, and + <literal>plugin_unload</literal> functions. + </para> + </sect2> +</chapter>
--- a/doc/reference/libpurple/tut_signals.xml Fri Jan 31 17:56:27 2014 +0530 +++ b/doc/reference/libpurple/tut_signals.xml Fri Jan 31 18:02:20 2014 +0530 @@ -36,7 +36,7 @@ <link linkend="purple-signal-register"><function>purple_signal_register()</function></link>. Here is a slightly modified example from <link linkend="purple-plugins-init"><function>purple_plugins_init</function></link> - in <literal>plugin.c</literal>: + in <literal>plugins.c</literal>: <programlisting> purple_signal_register(purple_plugins_get_handle(), /* Instance */
--- a/finch/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -76,9 +76,11 @@ $(DBUS_LIBS) \ $(INTLLIBS) \ $(GLIB_LIBS) \ + $(GPLUGIN_LIBS) \ $(LIBXML_LIBS) \ $(GNT_LIBS) \ $(GSTREAMER_LIBS) \ + $(INTROSPECTION_LIBS) \ ./libgnt/libgnt.la \ $(top_builddir)/libpurple/libpurple.la @@ -97,7 +99,53 @@ -I$(srcdir)/libgnt/ \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DBUS_CFLAGS) \ $(LIBXML_CFLAGS) \ $(GSTREAMER_CFLAGS) \ + $(INTROSPECTION_CFLAGS) \ $(GNT_CFLAGS) + +if ENABLE_GNT +-include $(INTROSPECTION_MAKEFILE) +INTROSPECTION_GIRS = +INTROSPECTION_SCANNER_ARGS = --add-include-path=$(builddir) --add-include-path=$(top_builddir)/libpurple +INTROSPECTION_COMPILER_ARGS = --includedir=$(top_builddir)/libpurple + +if HAVE_INTROSPECTION +introspection_sources = $(libfinchinclude_HEADERS) + +Finch-$(PURPLE_MAJOR_VERSION).$(PURPLE_MINOR_VERSION).gir: $(builddir)/libfinch.la +Finch_3_0_gir_INCLUDES = GObject-2.0 Purple-$(PURPLE_MAJOR_VERSION).$(PURPLE_MINOR_VERSION) +Finch_3_0_gir_CFLAGS = \ + $(INCLUDES) \ + -DSTANDALONE \ + -DDATADIR=\"$(datadir)\" \ + -DLIBDIR=\"$(libdir)/finch/\" \ + -DLOCALEDIR=\"$(datadir)/locale\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -I$(top_srcdir)/libpurple/ \ + -I$(top_srcdir) \ + -I$(srcdir)/libgnt/ \ + $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(LIBXML_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(INTROSPECTION_CFLAGS) \ + $(GNT_CFLAGS) + +Finch_3_0_gir_LIBS = $(builddir)/libfinch.la +Finch_3_0_gir_FILES = $(introspection_sources) +INTROSPECTION_GIRS += Finch-$(PURPLE_MAJOR_VERSION).$(PURPLE_MINOR_VERSION).gir + +girdir = $(INTROSPECTION_GIRDIR) +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(INTROSPECTION_TYPELIBDIR) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES = $(gir_DATA) $(typelib_DATA) +endif + +endif # ENABLE_GNT
--- a/finch/gntaccount.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/gntaccount.c Fri Jan 31 18:02:20 2014 +0530 @@ -43,7 +43,7 @@ #include <accountopt.h> #include <connection.h> #include <notify.h> -#include <plugin.h> +#include <plugins.h> #include <request.h> #include <savedstatuses.h> @@ -74,8 +74,8 @@ GntWidget *splits; GList *split_entries; - GList *prpl_entries; - GntWidget *prpls; + GList *protocol_entries; + GntWidget *protocols; GntWidget *newmail; GntWidget *remember; @@ -101,7 +101,7 @@ edit_dialog_destroy(AccountEditDialog *dialog) { accountdialogs = g_list_remove(accountdialogs, dialog); - g_list_free(dialog->prpl_entries); + g_list_free(dialog->protocol_entries); g_list_free(dialog->split_entries); g_free(dialog); } @@ -110,15 +110,13 @@ save_account_cb(AccountEditDialog *dialog) { PurpleAccount *account; - PurplePlugin *plugin; - PurplePluginProtocolInfo *prplinfo; + PurpleProtocol *protocol; const char *value; GString *username; /* XXX: Do some error checking first. */ - plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); - prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); + protocol = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); /* Username && user-splits */ value = gnt_entry_get_text(GNT_ENTRY(dialog->username)); @@ -135,10 +133,10 @@ username = g_string_new(value); - if (prplinfo != NULL) + if (protocol != NULL) { GList *iter, *entries; - for (iter = prplinfo->user_splits, entries = dialog->split_entries; + for (iter = purple_protocol_get_user_splits(protocol), entries = dialog->split_entries; iter && entries; iter = iter->next, entries = entries->next) { PurpleAccountUserSplit *split = iter->data; @@ -155,7 +153,7 @@ if (dialog->account == NULL) { - account = purple_account_new(username->str, purple_plugin_get_id(plugin)); + account = purple_account_new(username->str, purple_protocol_get_id(protocol)); purple_accounts_add(account); } else @@ -164,12 +162,12 @@ /* Protocol */ if (purple_account_is_disconnected(account)) { - purple_account_set_protocol_id(account, purple_plugin_get_id(plugin)); + purple_account_set_protocol_id(account, purple_protocol_get_id(protocol)); purple_account_set_username(account, username->str); } else { const char *old = purple_account_get_protocol_id(account); - char *oldprpl; - if (strcmp(old, purple_plugin_get_id(plugin))) { + char *oldproto; + if (strcmp(old, purple_protocol_get_id(protocol))) { purple_notify_error(NULL, _("Error"), _("Account was not modified"), _("The account's protocol cannot be " @@ -180,8 +178,8 @@ return; } - oldprpl = g_strdup(purple_normalize(account, purple_account_get_username(account))); - if (g_utf8_collate(oldprpl, purple_normalize(account, username->str))) { + oldproto = g_strdup(purple_normalize(account, purple_account_get_username(account))); + if (g_utf8_collate(oldproto, purple_normalize(account, username->str))) { purple_notify_error(NULL, _("Error"), _("Account was not modified"), _("The account's username cannot be " @@ -189,10 +187,10 @@ "server."), purple_request_cpar_from_account( account)); - g_free(oldprpl); + g_free(oldproto); return; } - g_free(oldprpl); + g_free(oldproto); purple_account_set_username(account, username->str); } } @@ -216,11 +214,11 @@ gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->newmail))); /* Protocol options */ - if (prplinfo) + if (protocol) { GList *iter, *entries; - for (iter = prplinfo->protocol_options, entries = dialog->prpl_entries; + for (iter = purple_protocol_get_protocol_options(protocol), entries = dialog->protocol_entries; iter && entries; iter = iter->next, entries = entries->next) { PurpleAccountOption *option = iter->data; @@ -265,7 +263,7 @@ gnt_box_give_focus_to_child(GNT_BOX(accounts.window), accounts.tree); } - if (prplinfo && prplinfo->register_user && + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, register_user) && gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->regserver))) { purple_account_register(account); } else if (dialog->account == NULL) { @@ -297,8 +295,7 @@ update_user_splits(AccountEditDialog *dialog) { GntWidget *hbox; - PurplePlugin *plugin; - PurplePluginProtocolInfo *prplinfo; + PurpleProtocol *protocol; GList *iter, *entries; char *username = NULL; @@ -316,14 +313,13 @@ dialog->split_entries = NULL; - plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); - if (!plugin) + protocol = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); + if (!protocol) return; - prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); username = dialog->account ? g_strdup(purple_account_get_username(dialog->account)) : NULL; - for (iter = prplinfo->user_splits; iter; iter = iter->next) + for (iter = purple_protocol_get_user_splits(protocol); iter; iter = iter->next) { PurpleAccountUserSplit *split = iter->data; GntWidget *entry; @@ -342,7 +338,7 @@ g_free(buf); } - for (iter = g_list_last(prplinfo->user_splits), entries = g_list_last(dialog->split_entries); + for (iter = g_list_last(purple_protocol_get_user_splits(protocol)), entries = g_list_last(dialog->split_entries); iter && entries; iter = iter->prev, entries = entries->prev) { GntWidget *entry = entries->data; @@ -380,39 +376,36 @@ static void add_protocol_options(AccountEditDialog *dialog) { - PurplePlugin *plugin; - PurplePluginProtocolInfo *prplinfo; + PurpleProtocol *protocol; GList *iter; GntWidget *vbox, *box; PurpleAccount *account; - if (dialog->prpls) - gnt_box_remove_all(GNT_BOX(dialog->prpls)); + if (dialog->protocols) + gnt_box_remove_all(GNT_BOX(dialog->protocols)); else { - dialog->prpls = vbox = gnt_vbox_new(FALSE); + dialog->protocols = vbox = gnt_vbox_new(FALSE); gnt_box_set_pad(GNT_BOX(vbox), 0); gnt_box_set_alignment(GNT_BOX(vbox), GNT_ALIGN_LEFT); gnt_box_set_fill(GNT_BOX(vbox), TRUE); } - if (dialog->prpl_entries) + if (dialog->protocol_entries) { - g_list_free(dialog->prpl_entries); - dialog->prpl_entries = NULL; + g_list_free(dialog->protocol_entries); + dialog->protocol_entries = NULL; } - vbox = dialog->prpls; + vbox = dialog->protocols; - plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); - if (!plugin) + protocol = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); + if (!protocol) return; - prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - account = dialog->account; - for (iter = prplinfo->protocol_options; iter; iter = iter->next) + for (iter = purple_protocol_get_protocol_options(protocol); iter; iter = iter->next) { PurpleAccountOption *option = iter->data; PurplePrefType type = purple_account_option_get_type(option); @@ -425,7 +418,7 @@ { GntWidget *widget = gnt_check_box_new(purple_account_option_get_text(option)); gnt_box_add_widget(GNT_BOX(box), widget); - dialog->prpl_entries = g_list_append(dialog->prpl_entries, widget); + dialog->protocol_entries = g_list_append(dialog->protocol_entries, widget); if (account) gnt_check_box_set_checked(GNT_CHECK_BOX(widget), @@ -453,7 +446,7 @@ purple_account_option_get_setting(option), dv); gnt_box_add_widget(GNT_BOX(box), combo); - dialog->prpl_entries = g_list_append(dialog->prpl_entries, combo); + dialog->protocol_entries = g_list_append(dialog->protocol_entries, combo); for ( ; opt_iter; opt_iter = opt_iter->next) { @@ -468,7 +461,7 @@ { GntWidget *entry = gnt_entry_new(NULL); gnt_box_add_widget(GNT_BOX(box), entry); - dialog->prpl_entries = g_list_append(dialog->prpl_entries, entry); + dialog->protocol_entries = g_list_append(dialog->protocol_entries, entry); if (type == PURPLE_PREF_STRING) { @@ -501,29 +494,26 @@ } /* Show the registration checkbox only in a new account dialog, - * and when the selected prpl has the support for it. */ + * and when the selected protocol has the support for it. */ gnt_widget_set_visible(dialog->regserver, account == NULL && - prplinfo->register_user != NULL); + PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, register_user)); } static void update_user_options(AccountEditDialog *dialog) { - PurplePlugin *plugin; - PurplePluginProtocolInfo *prplinfo; + PurpleProtocol *protocol; - plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); - if (!plugin) + protocol = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); + if (!protocol) return; - prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - if (dialog->newmail == NULL) dialog->newmail = gnt_check_box_new(_("New mail notifications")); if (dialog->account) gnt_check_box_set_checked(GNT_CHECK_BOX(dialog->newmail), purple_account_get_check_mail(dialog->account)); - if (!prplinfo || !(prplinfo->options & OPT_PROTO_MAIL_CHECK)) + if (!protocol || !(purple_protocol_get_options(protocol) & OPT_PROTO_MAIL_CHECK)) gnt_widget_set_visible(dialog->newmail, FALSE); else gnt_widget_set_visible(dialog->newmail, TRUE); @@ -536,7 +526,7 @@ } static void -prpl_changed_cb(GntWidget *combo, PurplePlugin *old, PurplePlugin *new, AccountEditDialog *dialog) +protocol_changed_cb(GntWidget *combo, PurpleProtocol *old, PurpleProtocol *new, AccountEditDialog *dialog) { update_user_splits(dialog); add_protocol_options(dialog); @@ -553,7 +543,7 @@ GntWidget *combo, *button, *entry; GList *list, *iter; AccountEditDialog *dialog; - PurplePlugin *plugin; + PurpleProtocol *protocol; if (account) { @@ -566,10 +556,10 @@ } } - list = purple_plugins_get_protocols(); + list = purple_protocols_get_all(); if (list == NULL) { purple_notify_error(NULL, _("Error"), - _("There are no protocol plugins installed."), + _("There are no protocols installed."), _("(You probably forgot to 'make install'.)"), purple_request_cpar_from_account(account)); return; @@ -595,17 +585,17 @@ for (iter = list; iter; iter = iter->next) { gnt_combo_box_add_data(GNT_COMBO_BOX(combo), iter->data, - ((PurplePlugin*)iter->data)->info->name); + purple_protocol_get_name(PURPLE_PROTOCOL(iter->data))); } - plugin = purple_plugins_find_with_id(purple_account_get_protocol_id(account)); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); - if (account && plugin) - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), plugin); + if (account && protocol) + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), protocol); else gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), list->data); - g_signal_connect(G_OBJECT(combo), "selection-changed", G_CALLBACK(prpl_changed_cb), dialog); + g_signal_connect(G_OBJECT(combo), "selection-changed", G_CALLBACK(protocol_changed_cb), dialog); gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Protocol:"))); gnt_box_add_widget(GNT_BOX(hbox), combo); @@ -656,7 +646,7 @@ /* The advanced box */ add_protocol_options(dialog); - gnt_box_add_widget(GNT_BOX(window), dialog->prpls); + gnt_box_add_widget(GNT_BOX(window), dialog->protocols); /* TODO: Add proxy options */ @@ -678,6 +668,8 @@ gnt_widget_show(window); gnt_box_readjust(GNT_BOX(window)); gnt_widget_draw(window); + + g_list_free(list); } static void
--- a/finch/gntblist.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/gntblist.c Fri Jan 31 18:02:20 2014 +0530 @@ -31,6 +31,7 @@ #include <log.h> #include <notify.h> #include <request.h> +#include <plugins.h> #include <savedstatuses.h> #include <server.h> #include <signal.h> @@ -727,7 +728,7 @@ GHashTable *hash = NULL; PurpleConnection *gc; gboolean autojoin; - PurplePluginProtocolInfo *info; + PurpleProtocol *protocol; account = purple_request_fields_get_account(allfields, "account"); name = purple_request_fields_get_string(allfields, "name"); @@ -742,9 +743,8 @@ group = _("Chats"); gc = purple_account_get_connection(account); - info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - if (info->chat_info_defaults != NULL) - hash = info->chat_info_defaults(gc, name); + protocol = purple_connection_get_protocol(gc); + hash = purple_protocol_chat_iface_info_defaults(protocol, gc, name); chat = purple_chat_new(account, name, hash); @@ -1060,12 +1060,12 @@ append_proto_menu(GntMenu *menu, PurpleConnection *gc, PurpleBlistNode *node) { GList *list; - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - if(!prpl_info || !prpl_info->blist_node_menu) + PurpleProtocol *protocol = purple_connection_get_protocol(gc); + + if(!protocol || !PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, blist_node_menu)) return; - for(list = prpl_info->blist_node_menu(node); list; + for(list = purple_protocol_client_iface_blist_node_menu(protocol, node); list; list = g_list_delete_link(list, list)) { PurpleMenuAction *act = (PurpleMenuAction *) list->data; @@ -1118,13 +1118,13 @@ PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL); PurpleRequestField *field; GList *parts, *iter; - struct proto_chat_entry *pce; + PurpleProtocolChatEntry *pce; PurpleConnection *gc; purple_request_fields_add_group(fields, group); gc = purple_account_get_connection(purple_chat_get_account(chat)); - parts = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->chat_info(gc); + parts = purple_protocol_chat_iface_info(purple_connection_get_protocol(gc), gc); for (iter = parts; iter; iter = iter->next) { pce = iter->data; @@ -1271,11 +1271,11 @@ PurpleAccount *account; gboolean permitted; GntMenuItem *item; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - if (prpl_info && prpl_info->get_info) + protocol = purple_connection_get_protocol(gc); + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info)) { add_custom_action(menu, _("Get Info"), PURPLE_CALLBACK(finch_blist_get_buddy_info_cb), buddy); @@ -1284,10 +1284,10 @@ add_custom_action(menu, _("Add Buddy Pounce"), PURPLE_CALLBACK(finch_blist_pounce_node_cb), buddy); - if (prpl_info && prpl_info->send_file) + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, send)) { - if (!prpl_info->can_receive_file || - prpl_info->can_receive_file(gc, purple_buddy_get_name(buddy))) + if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, can_receive) || + purple_protocol_xfer_iface_can_receive(protocol, gc, purple_buddy_get_name(buddy))) add_custom_action(menu, _("Send File"), PURPLE_CALLBACK(finch_blist_menu_send_file_cb), buddy); } @@ -1408,12 +1408,12 @@ account = purple_buddy_get_account(b); } else if (PURPLE_IS_CHAT(node)) { PurpleChat *c = (PurpleChat*) node; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; type = PURPLE_LOG_CHAT; account = purple_chat_get_account(c); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account))); - if (prpl_info && prpl_info->get_chat_name) { - name = prpl_info->get_chat_name(purple_chat_get_components(c)); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if (protocol) { + name = purple_protocol_chat_iface_get_name(protocol, purple_chat_get_components(c)); } } else if (PURPLE_IS_CONTACT(node)) { finch_log_show_contact((PurpleContact *)node); @@ -1711,8 +1711,7 @@ static void tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleAccount *account; PurpleNotifyUserInfo *user_info; PurplePresence *presence; @@ -1734,10 +1733,9 @@ purple_notify_user_info_add_pair_plaintext(user_info, _("Account"), tmp); g_free(tmp); - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info && prpl_info->tooltip_text) { - prpl_info->tooltip_text(buddy, user_info, full); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if (protocol) { + purple_protocol_client_iface_tooltip_text(protocol, buddy, user_info, full); } if (purple_prefs_get_bool("/finch/blist/idletime")) { @@ -2449,25 +2447,60 @@ } static void -build_plugin_actions(GntMenuItem *item, PurplePlugin *plugin, gpointer context) +build_plugin_actions(GntMenuItem *item, PurplePlugin *plugin) +{ + GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP); + PurplePluginActionsCb actions_cb; + GList *actions; + GntMenuItem *menuitem; + + actions_cb = + purple_plugin_info_get_actions_cb(purple_plugin_get_info(plugin)); + + gnt_menuitem_set_submenu(item, GNT_MENU(sub)); + for (actions = actions_cb(plugin); actions; + actions = g_list_delete_link(actions, actions)) { + if (actions->data) { + PurplePluginAction *action = actions->data; + action->plugin = plugin; + menuitem = gnt_menuitem_new(action->label); + gnt_menu_add_item(GNT_MENU(sub), menuitem); + + gnt_menuitem_set_callback(menuitem, plugin_action, action); + g_object_set_data_full(G_OBJECT(menuitem), "plugin_action", + action, (GDestroyNotify)purple_plugin_action_free); + } + } +} + +static void +protocol_action(GntMenuItem *item, gpointer data) +{ + PurpleProtocolAction *action = data; + if (action && action->callback) + action->callback(action); +} + +static void +build_protocol_actions(GntMenuItem *item, PurpleProtocol *protocol, + PurpleConnection *gc) { GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP); GList *actions; GntMenuItem *menuitem; gnt_menuitem_set_submenu(item, GNT_MENU(sub)); - for (actions = PURPLE_PLUGIN_ACTIONS(plugin, context); actions; + for (actions = purple_protocol_client_iface_get_actions(protocol, gc); actions; actions = g_list_delete_link(actions, actions)) { if (actions->data) { - PurplePluginAction *action = actions->data; - action->plugin = plugin; - action->context = context; + PurpleProtocolAction *action = actions->data; + action->connection = gc; menuitem = gnt_menuitem_new(action->label); gnt_menu_add_item(GNT_MENU(sub), menuitem); - gnt_menuitem_set_callback(menuitem, plugin_action, action); - g_object_set_data_full(G_OBJECT(menuitem), "plugin_action", - action, (GDestroyNotify)purple_plugin_action_free); + gnt_menuitem_set_callback(menuitem, protocol_action, action); + g_object_set_data_full(G_OBJECT(menuitem), "protocol_action", + action, (GDestroyNotify)purple_protocol_action_free); } } } @@ -2537,16 +2570,15 @@ for (iter = purple_plugins_get_loaded(); iter; iter = iter->next) { PurplePlugin *plugin = iter->data; + PurplePluginInfo *info = purple_plugin_get_info(plugin); GntMenuItem *item; - if (PURPLE_IS_PROTOCOL_PLUGIN(plugin)) + + if (!purple_plugin_info_get_actions_cb(info)) continue; - if (!PURPLE_PLUGIN_HAS_ACTIONS(plugin)) - continue; - - item = gnt_menuitem_new(_(plugin->info->name)); + item = gnt_menuitem_new(_(purple_plugin_info_get_name(info))); gnt_menu_add_item(GNT_MENU(sub), item); - build_plugin_actions(item, plugin, NULL); + build_plugin_actions(item, plugin); } } @@ -2571,16 +2603,16 @@ iter = g_list_delete_link(iter, iter)) { PurpleAccount *account = iter->data; PurpleConnection *gc = purple_account_get_connection(account); - PurplePlugin *prpl; + PurpleProtocol *protocol; if (!gc || !PURPLE_CONNECTION_IS_CONNECTED(gc)) continue; - prpl = purple_connection_get_prpl(gc); - - if (PURPLE_PLUGIN_HAS_ACTIONS(prpl)) { + protocol = purple_connection_get_protocol(gc); + + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_actions)) { item = gnt_menuitem_new(purple_account_get_username(account)); gnt_menu_add_item(GNT_MENU(sub), item); - build_plugin_actions(item, prpl, gc); + build_protocol_actions(item, protocol, gc); } } } @@ -2778,9 +2810,8 @@ chat = purple_blist_find_chat(account, name); if (chat == NULL) { - PurplePluginProtocolInfo *info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - if (info->chat_info_defaults != NULL) - hash = info->chat_info_defaults(gc, name); + PurpleProtocol *protocol = purple_connection_get_protocol(gc); + hash = purple_protocol_chat_iface_info_defaults(protocol, gc, name); } else { hash = purple_chat_get_components(chat); }
--- a/finch/gntconv.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/gntconv.c Fri Jan 31 18:02:20 2014 +0530 @@ -175,7 +175,7 @@ purple_conversation_write(conv, "", _("That command only works in IMs, not chats."), PURPLE_MESSAGE_NO_LOG, time(NULL)); break; - case PURPLE_CMD_STATUS_WRONG_PRPL: + case PURPLE_CMD_STATUS_WRONG_PROTOCOL: purple_conversation_write(conv, "", _("That command doesn't work on this protocol."), PURPLE_MESSAGE_NO_LOG, time(NULL)); break; @@ -339,9 +339,9 @@ chat = find_chat_for_conversation(conv); if (chat == NULL) { - PurplePluginProtocolInfo *info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - if (info->chat_info_defaults != NULL) - comps = info->chat_info_defaults(gc, purple_conversation_get_name(conv)); + PurpleProtocol *protocol = purple_connection_get_protocol(gc); + comps = purple_protocol_chat_iface_info_defaults(protocol, gc, + purple_conversation_get_name(conv)); } else { comps = purple_chat_get_components(chat); } @@ -638,10 +638,10 @@ if (PURPLE_IS_IM_CONVERSATION(ggc->active_conv)) { PurpleAccount *account = purple_conversation_get_account(ggc->active_conv); PurpleConnection *gc = purple_account_get_connection(account); - PurplePluginProtocolInfo *pinfo = - gc ? PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)) : NULL; + PurpleProtocol *protocol = + gc ? purple_connection_get_protocol(gc) : NULL; - if (pinfo && pinfo->get_info) { + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info)) { item = gnt_menuitem_new(_("Get Info")); gnt_menu_add_item(GNT_MENU(sub), item); gnt_menuitem_set_callback(item, get_info_cb, ggc); @@ -651,9 +651,10 @@ gnt_menu_add_item(GNT_MENU(sub), item); gnt_menuitem_set_callback(item, add_pounce_cb, ggc); - if (pinfo && pinfo->send_file && - (!pinfo->can_receive_file || - pinfo->can_receive_file(gc, purple_conversation_get_name(ggc->active_conv)))) { + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, send) && + (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, can_receive) || + purple_protocol_xfer_iface_can_receive(protocol, gc, + purple_conversation_get_name(ggc->active_conv)))) { item = gnt_menuitem_new(_("Send File")); gnt_menu_add_item(GNT_MENU(sub), item); gnt_menuitem_set_callback(item, send_file_cb, ggc); @@ -694,8 +695,8 @@ { PurpleAccount *account = purple_conversation_get_account(fc->active_conv); PurpleConnection *gc = purple_account_get_connection(account); - PurplePluginProtocolInfo *prpl_info = NULL; - char *name, *realname; + PurpleProtocol *protocol = NULL; + char *name, *realname = NULL; if (!gc) { purple_conversation_write(fc->active_conv, NULL, _("You are not connected."), @@ -705,12 +706,12 @@ name = gnt_tree_get_selection_data(GNT_TREE(widget)); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_cb_real_name)) - realname = prpl_info->get_cb_real_name(gc, purple_chat_conversation_get_id( + protocol = purple_connection_get_protocol(gc); + if (protocol) + realname = purple_protocol_chat_iface_get_user_real_name(protocol, gc, + purple_chat_conversation_get_id( PURPLE_CHAT_CONVERSATION(fc->active_conv)), name); - else - realname = NULL; + purple_im_conversation_new(account, realname ? realname : name); g_free(realname); } @@ -1249,15 +1250,17 @@ tmp = g_strdup_printf("Using Finch v%s with libpurple v%s.", DISPLAY_VERSION, purple_core_get_version()); } else if (!g_ascii_strcasecmp(args[0], "plugins")) { - /* Show all the loaded plugins, including the protocol plugins and plugin loaders. - * This is intentional, since third party prpls are often sources of bugs, and some - * plugin loaders (e.g. mono) can also be buggy. + /* Show all the loaded plugins, including plugins marked internal. + * This is intentional, since third party protocols are often sources of bugs, and some + * loaders can also be buggy. */ GString *str = g_string_new("Loaded Plugins: "); const GList *plugins = purple_plugins_get_loaded(); if (plugins) { for (; plugins; plugins = plugins->next) { - str = g_string_append(str, purple_plugin_get_name(plugins->data)); + PurplePluginInfo *plugin_info = purple_plugin_get_info(plugins->data); + str = g_string_append(str, purple_plugin_info_get_name(plugin_info)); + if (plugins->next) str = g_string_append(str, ", "); }
--- a/finch/gntlog.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/gntlog.c Fri Jan 31 18:02:20 2014 +0530 @@ -505,7 +505,7 @@ for(accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) { PurpleAccount *account = (PurpleAccount *)accounts->data; - if(purple_find_prpl(purple_account_get_protocol_id(account)) == NULL) + if(purple_protocols_find(purple_account_get_protocol_id(account)) == NULL) continue; logs = g_list_concat(purple_log_get_system_logs(account), logs);
--- a/finch/gntmedia.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/gntmedia.c Fri Jan 31 18:02:20 2014 +0530 @@ -405,7 +405,7 @@ { PurpleAccount *account = purple_conversation_get_account(conv); - if (!purple_prpl_initiate_media(account, + if (!purple_protocol_initiate_media(account, purple_conversation_get_name(conv), PURPLE_MEDIA_AUDIO)) return PURPLE_CMD_RET_FAILED;
--- a/finch/gntplugin.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/gntplugin.c Fri Jan 31 18:02:20 2014 +0530 @@ -42,6 +42,21 @@ #include "gntplugin.h" #include "gntrequest.h" +#define FINCH_PLUGIN_INFO_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), FINCH_TYPE_PLUGIN_INFO, FinchPluginInfoPrivate)) + +typedef struct +{ + FinchPluginPrefFrameCb pref_frame_cb; +} FinchPluginInfoPrivate; + +enum +{ + PROP_0, + PROP_GNT_PREF_FRAME_CB, + PROP_LAST +}; + static struct { GntWidget *tree; @@ -67,6 +82,98 @@ static GntWidget *process_pref_frame(PurplePluginPrefFrame *frame); +/* Set method for GObject properties */ +static void +finch_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value, + GParamSpec *pspec) +{ + FinchPluginInfoPrivate *priv = FINCH_PLUGIN_INFO_GET_PRIVATE(obj); + + switch (param_id) { + case PROP_GNT_PREF_FRAME_CB: + priv->pref_frame_cb = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +/* Get method for GObject properties */ +static void +finch_plugin_info_get_property(GObject *obj, guint param_id, GValue *value, + GParamSpec *pspec) +{ + FinchPluginInfoPrivate *priv = FINCH_PLUGIN_INFO_GET_PRIVATE(obj); + + switch (param_id) { + case PROP_GNT_PREF_FRAME_CB: + g_value_set_pointer(value, priv->pref_frame_cb); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +/* Class initializer function */ +static void finch_plugin_info_class_init(FinchPluginInfoClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(FinchPluginInfoPrivate)); + + /* Setup properties */ + obj_class->get_property = finch_plugin_info_get_property; + obj_class->set_property = finch_plugin_info_set_property; + + g_object_class_install_property(obj_class, PROP_GNT_PREF_FRAME_CB, + g_param_spec_pointer("gnt-pref-frame-cb", + "GNT preferences frame callback", + "Callback that returns a GNT preferences frame", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +GType +finch_plugin_info_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(FinchPluginInfoClass), + .class_init = (GClassInitFunc)finch_plugin_info_class_init, + .instance_size = sizeof(FinchPluginInfo), + }; + + type = g_type_register_static(PURPLE_TYPE_PLUGIN_INFO, + "FinchPluginInfo", &info, 0); + } + + return type; +} + +FinchPluginInfo * +finch_plugin_info_new(const char *first_property, ...) +{ + GObject *info; + va_list var_args; + + /* at least ID is required */ + if (!first_property) + return NULL; + + va_start(var_args, first_property); + info = g_object_new_valist(FINCH_TYPE_PLUGIN_INFO, first_property, + var_args); + va_end(var_args); + + g_object_set(info, "ui-requirement", FINCH_UI, NULL); + + return FINCH_PLUGIN_INFO(info); +} + static void free_stringlist(GList *list) { @@ -77,22 +184,23 @@ static gboolean has_prefs(PurplePlugin *plugin) { - PurplePluginUiInfo *pinfo; + PurplePluginInfo *info = purple_plugin_get_info(plugin); + FinchPluginInfoPrivate *priv = NULL; + gboolean ret; + + g_return_val_if_fail(plugin != NULL, FALSE); if (!purple_plugin_is_loaded(plugin)) return FALSE; - if (PURPLE_IS_GNT_PLUGIN(plugin) && - FINCH_PLUGIN_UI_INFO(plugin) != NULL) - { - return TRUE; - } + if (FINCH_IS_PLUGIN_INFO(info)) + priv = FINCH_PLUGIN_INFO_GET_PRIVATE(info); - pinfo = plugin->info->prefs_info; - if (!pinfo) - return FALSE; + ret = ((priv && priv->pref_frame_cb) || + purple_plugin_info_get_pref_frame_cb(info) || + purple_plugin_info_get_pref_request_cb(info)); - return (pinfo->get_plugin_pref_frame || pinfo->get_plugin_pref_request); + return ret; } static void @@ -110,13 +218,16 @@ static void finch_plugin_pref_close(PurplePlugin *plugin) { + PurplePluginInfo *info; FinchPluginUiData *ui_data; g_return_if_fail(plugin != NULL); - if (plugin->ui_data == NULL) + info = purple_plugin_get_info(plugin); + ui_data = purple_plugin_info_get_ui_data(info); + + if (!ui_data) return; - ui_data = plugin->ui_data; if (ui_data->type == FINCH_PLUGIN_UI_DATA_TYPE_REQUEST) { purple_request_close(PURPLE_REQUEST_FIELDS, @@ -129,25 +240,29 @@ gnt_widget_destroy(ui_data->u.window); g_free(ui_data); - plugin->ui_data = NULL; + purple_plugin_info_set_ui_data(info, NULL); } static void plugin_toggled_cb(GntWidget *tree, PurplePlugin *plugin, gpointer null) { + GError *error = NULL; + if (gnt_tree_get_choice(GNT_TREE(tree), plugin)) { - if (!purple_plugin_load(plugin)) { - purple_notify_error(NULL, _("ERROR"), _("loading plugin failed"), NULL, NULL); + if (!purple_plugin_load(plugin, &error)) { + purple_notify_error(NULL, _("ERROR"), _("loading plugin failed"), error->message, NULL); gnt_tree_set_choice(GNT_TREE(tree), plugin, FALSE); + g_error_free(error); } } else { - if (!purple_plugin_unload(plugin)) { - purple_notify_error(NULL, _("ERROR"), _("unloading plugin failed"), NULL, NULL); + if (!purple_plugin_unload(plugin, &error)) { + purple_notify_error(NULL, _("ERROR"), _("unloading plugin failed"), error->message, NULL); purple_plugin_disable(plugin); gnt_tree_set_choice(GNT_TREE(tree), plugin, TRUE); + g_error_free(error); } finch_plugin_pref_close(plugin); @@ -167,12 +282,20 @@ selection_changed(GntWidget *widget, gpointer old, gpointer current, gpointer null) { PurplePlugin *plugin = current; - char *text; + PurplePluginInfo *info; + char *text, *authors = NULL; + const char * const *authorlist; GList *list = NULL, *iter = NULL; if (!plugin) return; + info = purple_plugin_get_info(plugin); + authorlist = purple_plugin_info_get_authors(info); + + if (authorlist) + authors = g_strjoinv(", ", (gchar **)authorlist); + /* If the selected plugin was unseen before, mark it as seen. But save the list * only when the plugin list is closed. So if the user enables a plugin, and it * crashes, it won't get marked as seen so the user can fix the bug and still @@ -180,21 +303,32 @@ * I probably mean 'plugin developers' by 'users' here. */ list = g_object_get_data(G_OBJECT(widget), "seen-list"); if (list) - iter = g_list_find_custom(list, plugin->path, (GCompareFunc)strcmp); + iter = g_list_find_custom(list, purple_plugin_get_filename(plugin), + (GCompareFunc)strcmp); if (!iter) { - list = g_list_prepend(list, g_strdup(plugin->path)); + list = g_list_prepend(list, g_strdup(purple_plugin_get_filename(plugin))); g_object_set_data(G_OBJECT(widget), "seen-list", list); } /* XXX: Use formatting and stuff */ gnt_text_view_clear(GNT_TEXT_VIEW(plugins.aboot)); - text = g_strdup_printf(_("Name: %s\nVersion: %s\nDescription: %s\nAuthor: %s\nWebsite: %s\nFilename: %s\n"), - SAFE(_(plugin->info->name)), SAFE(_(plugin->info->version)), SAFE(_(plugin->info->description)), - SAFE(_(plugin->info->author)), SAFE(_(plugin->info->homepage)), SAFE(plugin->path)); + text = g_strdup_printf((g_strv_length((gchar **)authorlist) > 1 ? + _("Name: %s\nVersion: %s\nDescription: %s\nAuthors: %s\nWebsite: %s\nFilename: %s\n") : + _("Name: %s\nVersion: %s\nDescription: %s\nAuthor: %s\nWebsite: %s\nFilename: %s\n")), + SAFE(_(purple_plugin_info_get_name(info))), + SAFE(_(purple_plugin_info_get_version(info))), + SAFE(_(purple_plugin_info_get_description(info))), + SAFE(authors), + SAFE(_(purple_plugin_info_get_website(info))), + SAFE(purple_plugin_get_filename(plugin))); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(plugins.aboot), text, GNT_TEXT_FLAG_NORMAL); gnt_text_view_scroll(GNT_TEXT_VIEW(plugins.aboot), 0); + g_free(text); + g_free(authors); + decide_conf_button(plugin); } @@ -214,11 +348,12 @@ static int plugin_compare(PurplePlugin *p1, PurplePlugin *p2) { - char *s1 = g_utf8_strup(p1->info->name, -1); - char *s2 = g_utf8_strup(p2->info->name, -1); + char *s1 = g_utf8_strup(purple_plugin_info_get_name(purple_plugin_get_info(p1)), -1); + char *s2 = g_utf8_strup(purple_plugin_info_get_name(purple_plugin_get_info(p2)), -1); int ret = g_utf8_collate(s1, s2); g_free(s1); g_free(s2); + return ret; } @@ -226,16 +361,18 @@ remove_confwin(GntWidget *window, gpointer _plugin) { PurplePlugin *plugin = _plugin; + PurplePluginInfo *info = purple_plugin_get_info(plugin); - g_free(plugin->ui_data); - plugin->ui_data = NULL; + g_free(info->ui_data); + purple_plugin_info_set_ui_data(info, NULL); } static void configure_plugin_cb(GntWidget *button, gpointer null) { PurplePlugin *plugin; - FinchPluginFrame callback; + PurplePluginInfo *info; + FinchPluginInfoPrivate *priv = NULL; FinchPluginUiData *ui_data; g_return_if_fail(plugins.tree != NULL); @@ -248,21 +385,27 @@ return; } - if (plugin->ui_data != NULL) + info = purple_plugin_get_info(plugin); + + if (purple_plugin_info_get_ui_data(info)) return; - plugin->ui_data = ui_data = g_new0(FinchPluginUiData, 1); + ui_data = g_new0(FinchPluginUiData, 1); + purple_plugin_info_set_ui_data(info, ui_data); - if (PURPLE_IS_GNT_PLUGIN(plugin) && - (callback = FINCH_PLUGIN_UI_INFO(plugin)) != NULL) + if (FINCH_IS_PLUGIN_INFO(info)) + priv = FINCH_PLUGIN_INFO_GET_PRIVATE(info); + + if (priv && priv->pref_frame_cb != NULL) { GntWidget *window = gnt_vbox_new(FALSE); GntWidget *box, *button; gnt_box_set_toplevel(GNT_BOX(window), TRUE); - gnt_box_set_title(GNT_BOX(window), plugin->info->name); + gnt_box_set_title(GNT_BOX(window), + purple_plugin_info_get_name(info)); gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID); - box = callback(); + box = priv->pref_frame_cb(); gnt_box_add_widget(GNT_BOX(window), box); box = gnt_hbox_new(FALSE); @@ -279,22 +422,21 @@ ui_data->type = FINCH_PLUGIN_UI_DATA_TYPE_WINDOW; ui_data->u.window = window; } - else if (plugin->info->prefs_info && - plugin->info->prefs_info->get_plugin_pref_request) + else if (purple_plugin_info_get_pref_request_cb(info)) { + PurplePluginPrefRequestCb pref_request_cb = purple_plugin_info_get_pref_request_cb(info); gpointer handle; ui_data->type = FINCH_PLUGIN_UI_DATA_TYPE_REQUEST; - ui_data->u.request_handle = handle = plugin->info->prefs_info-> - get_plugin_pref_request(plugin); + ui_data->u.request_handle = handle = pref_request_cb(plugin); purple_request_add_close_notify(handle, - purple_callback_set_zero, &plugin->ui_data); + purple_callback_set_zero, &info->ui_data); purple_request_add_close_notify(handle, g_free, ui_data); } - else if (plugin->info->prefs_info && - plugin->info->prefs_info->get_plugin_pref_frame) + else if (purple_plugin_info_get_pref_frame_cb(info)) { - GntWidget *win = process_pref_frame(plugin->info->prefs_info->get_plugin_pref_frame(plugin)); + PurplePluginPrefFrameCb pref_frame_cb = purple_plugin_info_get_pref_frame_cb(info); + GntWidget *win = process_pref_frame(pref_frame_cb(plugin)); g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(remove_confwin), plugin); ui_data->type = FINCH_PLUGIN_UI_DATA_TYPE_WINDOW; @@ -305,10 +447,11 @@ purple_notify_info(plugin, _("Error"), _("No configuration " "options for this plugin."), NULL, NULL); g_free(ui_data); - plugin->ui_data = NULL; + purple_plugin_info_set_ui_data(info, NULL); } } +#if 0 static void install_selected_file_cb(gpointer handle, const char *filename) { @@ -395,11 +538,12 @@ NULL, &handle); g_signal_connect_swapped(G_OBJECT(w), "destroy", G_CALLBACK(purple_request_close_with_handle), &handle); } +#endif -void finch_plugins_show_all() +void finch_plugins_show_all(void) { GntWidget *window, *tree, *box, *aboot, *button; - GList *iter; + GList *plugin_list, *iter; GList *seen; if (plugins.window) { @@ -407,7 +551,7 @@ return; } - purple_plugins_probe(G_MODULE_SUFFIX); + purple_plugins_refresh(); plugins.window = window = gnt_vbox_new(FALSE); gnt_box_set_toplevel(GNT_BOX(window), TRUE); @@ -436,29 +580,26 @@ gnt_box_add_widget(GNT_BOX(box), aboot); seen = purple_prefs_get_path_list("/finch/plugins/seen"); - for (iter = purple_plugins_get_all(); iter; iter = iter->next) - { - PurplePlugin *plug = iter->data; - if (plug->info->type == PURPLE_PLUGIN_LOADER) { - GList *cur; - for (cur = PURPLE_PLUGIN_LOADER_INFO(plug)->exts; cur != NULL; - cur = cur->next) - purple_plugins_probe(cur->data); - continue; - } + plugin_list = purple_plugins_find_all(); + for (iter = plugin_list; iter; iter = iter->next) + { + PurplePlugin *plug = PURPLE_PLUGIN(iter->data); - if (plug->info->type != PURPLE_PLUGIN_STANDARD || - (plug->info->flags & PURPLE_PLUGIN_FLAG_INVISIBLE) || - plug->error) + if (purple_plugin_is_internal(plug)) continue; gnt_tree_add_choice(GNT_TREE(tree), plug, - gnt_tree_create_row(GNT_TREE(tree), plug->info->name), NULL, NULL); + gnt_tree_create_row(GNT_TREE(tree), + purple_plugin_info_get_name(purple_plugin_get_info(plug))), + NULL, NULL); gnt_tree_set_choice(GNT_TREE(tree), plug, purple_plugin_is_loaded(plug)); - if (!g_list_find_custom(seen, plug->path, (GCompareFunc)strcmp)) + if (!g_list_find_custom(seen, purple_plugin_get_filename(plug), + (GCompareFunc)strcmp)) gnt_tree_set_row_flags(GNT_TREE(tree), plug, GNT_TEXT_FLAG_BOLD); } + g_list_free(plugin_list); + gnt_tree_set_col_width(GNT_TREE(tree), 0, 30); g_signal_connect(G_OBJECT(tree), "toggled", G_CALLBACK(plugin_toggled_cb), NULL); g_signal_connect(G_OBJECT(tree), "selection_changed", G_CALLBACK(selection_changed), NULL); @@ -467,10 +608,12 @@ box = gnt_hbox_new(FALSE); gnt_box_add_widget(GNT_BOX(window), box); +#if 0 button = gnt_button_new(_("Install Plugin...")); gnt_box_add_widget(GNT_BOX(box), button); gnt_util_set_trigger_widget(GNT_WIDGET(tree), GNT_KEY_INS, button); g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(install_plugin_cb), NULL); +#endif button = gnt_button_new(_("Close")); gnt_box_add_widget(GNT_BOX(box), button);
--- a/finch/gntplugin.h Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/gntplugin.h Fri Jan 31 18:02:20 2014 +0530 @@ -28,36 +28,87 @@ #include <gnt.h> -#include <plugin.h> +#include <plugins.h> #include <pluginpref.h> #include <string.h> #include "finch.h" +#define FINCH_TYPE_PLUGIN_INFO (finch_plugin_info_get_type()) +#define FINCH_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FINCH_TYPE_PLUGIN_INFO, FinchPluginInfo)) +#define FINCH_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FINCH_TYPE_PLUGIN_INFO, FinchPluginInfoClass)) +#define FINCH_IS_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FINCH_TYPE_PLUGIN_INFO)) +#define FINCH_IS_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FINCH_TYPE_PLUGIN_INFO)) +#define FINCH_PLUGIN_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FINCH_TYPE_PLUGIN_INFO, FinchPluginInfoClass)) + +/** @copydoc _FinchPluginInfo */ +typedef struct _FinchPluginInfo FinchPluginInfo; +/** @copydoc _FinchPluginInfoClass */ +typedef struct _FinchPluginInfoClass FinchPluginInfoClass; + +typedef GntWidget* (*FinchPluginPrefFrameCb) (void); + +/** + * Extends #PurplePluginInfo to hold UI information for finch. + */ +struct _FinchPluginInfo { + PurplePluginInfo parent; +}; + +/** + * FinchPluginInfoClass: + * + * The base class for all #FinchPluginInfo's. + */ +struct _FinchPluginInfoClass { + PurplePluginInfoClass parent_class; + + /*< private >*/ + void (*_gnt_reserved1)(void); + void (*_gnt_reserved2)(void); + void (*_gnt_reserved3)(void); + void (*_gnt_reserved4)(void); +}; + +/********************************************************************** + * @name Plugin Info API + **********************************************************************/ +/*@{*/ + +/** + * Returns the GType for the FinchPluginInfo object. + */ +GType finch_plugin_info_get_type(void); + +/** + * Creates a new #FinchPluginInfo instance to be returned from + * gplugin_plugin_query() of a finch plugin, using the provided name/value + * pairs. + * + * See purple_plugin_info_new() for a list of available property names. + * Additionally, you can provide the property "gnt-pref-frame-cb", + * which should be a callback that returns a GntWidget for the plugin's + * preferences (see FinchPluginPrefFrameCb). + * + * @param first_property The first property name + * @param ... The value of the first property, followed optionally by more + * name/value pairs, followed by @c NULL + * + * @return A new #FinchPluginInfo instance. + * + * @see purple_plugin_info_new() + */ +FinchPluginInfo *finch_plugin_info_new(const char *first_property, ...) + G_GNUC_NULL_TERMINATED; + +/*@}*/ + /********************************************************************** * @name GNT Plugins API **********************************************************************/ /*@{*/ -typedef GntWidget* (*FinchPluginFrame) (void); - -/* Guess where these came from */ -#define FINCH_PLUGIN_TYPE FINCH_UI - -/** - * Decide whether a plugin is a GNT-plugin. - */ -#define PURPLE_IS_GNT_PLUGIN(plugin) \ - ((plugin)->info != NULL && (plugin)->info->ui_info != NULL && \ - !strcmp((plugin)->info->ui_requirement, FINCH_PLUGIN_TYPE)) - -/** - * Get the ui-info from GNT-plugins. - */ -#define FINCH_PLUGIN_UI_INFO(plugin) \ - (FinchPluginFrame)((plugin)->info->ui_info) - /** * Show a list of plugins. */
--- a/finch/gntpounce.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/gntpounce.c Fri Jan 31 18:02:20 2014 +0530 @@ -43,7 +43,7 @@ #include "conversation.h" #include "debug.h" #include "notify.h" -#include "prpl.h" +#include "protocol.h" #include "request.h" #include "server.h" #include "util.h"
--- a/finch/gntroomlist.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/gntroomlist.c Fri Jan 31 18:02:20 2014 +0530 @@ -113,15 +113,15 @@ char *name; PurpleRoomlistRoom *room = gnt_tree_get_selection_data(GNT_TREE(froomlist.tree)); PurpleConnection *gc = purple_account_get_connection(froomlist.account); - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; if (gc == NULL || room == NULL) return; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + protocol = purple_connection_get_protocol(gc); - if(prpl_info != NULL && prpl_info->roomlist_room_serialize) - name = prpl_info->roomlist_room_serialize(room); + if(protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST_IFACE, room_serialize)) + name = purple_protocol_roomlist_iface_room_serialize(protocol, room); else name = g_strdup(purple_roomlist_room_get_name(room)); @@ -237,12 +237,12 @@ GntComboBox *accounts = GNT_COMBO_BOX(froomlist.accounts); gnt_combo_box_remove_all(accounts); for (list = purple_connections_get_all(); list; list = list->next) { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc = list->data; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + protocol = purple_connection_get_protocol(gc); if (PURPLE_CONNECTION_IS_CONNECTED(gc) && - prpl_info->roomlist_get_list != NULL) { + PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST_IFACE, get_list)) { PurpleAccount *account = purple_connection_get_account(gc); char *text = g_strdup_printf("%s (%s)", purple_account_get_username(account),
--- a/finch/gntxfer.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/gntxfer.c Fri Jan 31 18:02:20 2014 +0530 @@ -36,7 +36,7 @@ #include "debug.h" #include "notify.h" #include "xfer.h" -#include "prpl.h" +#include "protocol.h" #include "util.h" #include "gntxfer.h"
--- a/finch/libfinch.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/libfinch.c Fri Jan 31 18:02:20 2014 +0530 @@ -30,8 +30,8 @@ #include "glibcompat.h" #include "log.h" #include "notify.h" -#include "prefs.h" -#include "prpl.h" +#include "plugins.h" +#include "protocol.h" #include "pounce.h" #include "savedstatuses.h" #include "sound.h" @@ -359,6 +359,14 @@ purple_eventloop_set_ui_ops(gnt_eventloop_get_ui_ops()); purple_idle_set_ui_ops(finch_idle_get_ui_ops()); + if (!purple_core_init(FINCH_UI)) + { + fprintf(stderr, + "Initialization of the Purple core failed. Dumping core.\n" + "Please report this!\n"); + abort(); + } + path = g_build_filename(purple_user_dir(), "plugins", NULL); if (!g_stat(path, &st)) g_mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR); @@ -366,14 +374,7 @@ g_free(path); purple_plugins_add_search_path(LIBDIR); - - if (!purple_core_init(FINCH_UI)) - { - fprintf(stderr, - "Initialization of the Purple core failed. Dumping core.\n" - "Please report this!\n"); - abort(); - } + purple_plugins_refresh(); /* TODO: should this be moved into finch_prefs_init() ? */ finch_prefs_update_old();
--- a/finch/libgnt/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/libgnt/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -97,3 +97,4 @@ $(DEBUG_CFLAGS) \ $(LIBXML_CFLAGS) \ $(PY_CFLAGS) +
--- a/finch/libgnt/wms/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/libgnt/wms/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -35,6 +35,7 @@ -I$(top_srcdir)/finch/libgnt \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(GNT_CFLAGS) \ $(PLUGIN_CFLAGS)
--- a/finch/plugins/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/plugins/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -47,6 +47,7 @@ -I$(top_srcdir)/finch/libgnt \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(GNT_CFLAGS) \ $(PLUGIN_CFLAGS)
--- a/finch/plugins/gntclipboard.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/plugins/gntclipboard.c Fri Jan 31 18:02:20 2014 +0530 @@ -22,7 +22,9 @@ #include "internal.h" #include <glib.h> -#define PLUGIN_STATIC_NAME GntClipboard +#define PLUGIN_ID "gntclipboard" +#define PLUGIN_DOMAIN (g_quark_from_static_string(PLUGIN_ID)) +#define PLUGIN_STATIC_NAME GntClipboard #ifdef HAVE_X11 #include <X11/Xlib.h> @@ -35,7 +37,7 @@ #include <glib.h> -#include <plugin.h> +#include <plugins.h> #include <version.h> #include <debug.h> #include <notify.h> @@ -106,8 +108,31 @@ } #endif +static FinchPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Richard Nelson <wabz@whatsbeef.net>", + NULL + }; + + return finch_plugin_info_new( + "id", PLUGIN_ID, + "name", N_("GntClipboard"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Clipboard plugin"), + "description", N_("When the gnt clipboard contents change, the " + "contents are made available to X, if possible."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { #ifdef HAVE_X11 if (!XOpenDisplay(NULL)) { @@ -125,14 +150,14 @@ sig_handle = g_signal_connect(G_OBJECT(gnt_get_clipboard()), "clipboard_changed", G_CALLBACK(clipboard_changed), NULL); return TRUE; #else - purple_notify_error(NULL, _("Error"), _("Error loading the plugin."), - _("This plugin cannot be loaded because it was not built with X11 support."), NULL); + g_set_error(error, PLUGIN_DOMAIN, 0, _("This plugin cannot be loaded " + "because it was not built with X11 support.")); return FALSE; #endif } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { #ifdef HAVE_X11 if (child) { @@ -144,42 +169,4 @@ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - FINCH_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - "gntclipboard", - N_("GntClipboard"), - DISPLAY_VERSION, - N_("Clipboard plugin"), - N_("When the gnt clipboard contents change, " - "the contents are made available to X, if possible."), - "Richard Nelson <wabz@whatsbeef.net>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, - NULL, - NULL, - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) +PURPLE_PLUGIN_INIT(PLUGIN_STATIC_NAME, plugin_query, plugin_load, plugin_unload);
--- a/finch/plugins/gntgf.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/plugins/gntgf.c Fri Jan 31 18:02:20 2014 +0530 @@ -42,7 +42,7 @@ #include <glib.h> -#include <plugin.h> +#include <plugins.h> #include <version.h> #include <buddylist.h> #include <conversation.h> @@ -269,35 +269,6 @@ notify(conv, _("%s sent a message in %s"), sender, purple_conversation_get_name(conv)); } -static gboolean -plugin_load(PurplePlugin *plugin) -{ - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", plugin, - PURPLE_CALLBACK(buddy_signed_on), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", plugin, - PURPLE_CALLBACK(buddy_signed_off), NULL); - purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", plugin, - PURPLE_CALLBACK(received_im_msg), NULL); - purple_signal_connect(purple_conversations_get_handle(), "received-chat-msg", plugin, - PURPLE_CALLBACK(received_chat_msg), NULL); - - memset(&gpsy, 0, sizeof(gpsy)); - memset(&gpsw, 0, sizeof(gpsw)); - - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - while (toasters) - { - GntToast *toast = toasters->data; - destroy_toaster(toast); - } - return TRUE; -} - static struct { char *pref; @@ -365,40 +336,31 @@ return window; } -static PurplePluginInfo info = +static FinchPluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - FINCH_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - "gntgf", - N_("GntGf"), - DISPLAY_VERSION, - N_("Toaster plugin"), - N_("Toaster plugin"), - "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, - config_frame, - NULL, - NULL, - NULL, + const gchar * const authors[] = { + "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", + NULL + }; - /* padding */ - NULL, - NULL, - NULL, - NULL -}; + return finch_plugin_info_new( + "id", "gntgf", + "name", N_("GntGf"), + "version", DISPLAY_VERSION, + "category", N_("Notification"), + "summary", N_("Toaster plugin"), + "description", N_("Toaster plugin"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gnt-pref-frame-cb", config_frame, + NULL + ); +} -static void -init_plugin(PurplePlugin *plugin) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) { purple_prefs_add_none("/plugins"); purple_prefs_add_none("/plugins/gnt"); @@ -415,6 +377,31 @@ #ifdef HAVE_X11 purple_prefs_add_bool(PREFS_URGENT, FALSE); #endif + + purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", plugin, + PURPLE_CALLBACK(buddy_signed_on), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", plugin, + PURPLE_CALLBACK(buddy_signed_off), NULL); + purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", plugin, + PURPLE_CALLBACK(received_im_msg), NULL); + purple_signal_connect(purple_conversations_get_handle(), "received-chat-msg", plugin, + PURPLE_CALLBACK(received_chat_msg), NULL); + + memset(&gpsy, 0, sizeof(gpsy)); + memset(&gpsw, 0, sizeof(gpsw)); + + return TRUE; } -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + while (toasters) + { + GntToast *toast = toasters->data; + destroy_toaster(toast); + } + return TRUE; +} + +PURPLE_PLUGIN_INIT(PLUGIN_STATIC_NAME, plugin_query, plugin_load, plugin_unload);
--- a/finch/plugins/gnthistory.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/plugins/gnthistory.c Fri Jan 31 18:02:20 2014 +0530 @@ -188,8 +188,34 @@ history_prefs_check((PurplePlugin *)data); } +static FinchPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Sean Egan <seanegan@gmail.com>", + "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", + NULL + }; + + return finch_plugin_info_new( + "id", HISTORY_PLUGIN_ID, + "name", N_("GntHistory"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Shows recently logged conversations in new " + "conversations."), + "description", N_("When a new conversation is opened this plugin will " + "insert the last conversation into the current " + "conversation."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { purple_signal_connect(purple_conversations_get_handle(), "conversation-created", @@ -205,44 +231,10 @@ return TRUE; } -static PurplePluginInfo info = +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - NULL, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - HISTORY_PLUGIN_ID, - N_("GntHistory"), - DISPLAY_VERSION, - N_("Shows recently logged conversations in new conversations."), - N_("When a new conversation is opened this plugin will insert " - "the last conversation into the current conversation."), - "Sean Egan <seanegan@gmail.com>\n" - "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", - PURPLE_WEBSITE, - plugin_load, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ + return TRUE; } -PURPLE_INIT_PLUGIN(gnthistory, init_plugin, info) - +PURPLE_PLUGIN_INIT(gnthistory, plugin_query, plugin_load, plugin_unload);
--- a/finch/plugins/gnttinyurl.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/plugins/gnttinyurl.c Fri Jan 31 18:02:20 2014 +0530 @@ -34,7 +34,7 @@ #include <glib.h> -#include <plugin.h> +#include <plugins.h> #include <version.h> #include <debug.h> #include <notify.h> @@ -409,11 +409,59 @@ return win; } +static PurplePluginPrefFrame * +get_plugin_pref_frame(PurplePlugin *plugin) { + + PurplePluginPrefFrame *frame; + PurplePluginPref *pref; + + frame = purple_plugin_pref_frame_new(); + + pref = purple_plugin_pref_new_with_name(PREF_LENGTH); + purple_plugin_pref_set_label(pref, _("Only create TinyURL for URLs" + " of this length or greater")); + purple_plugin_pref_frame_add(frame, pref); + pref = purple_plugin_pref_new_with_name(PREF_URL); + purple_plugin_pref_set_label(pref, _("TinyURL (or other) address prefix")); + purple_plugin_pref_frame_add(frame, pref); + + return frame; +} + +static FinchPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Richard Nelson <wabz@whatsbeef.net>", + NULL + }; + + return finch_plugin_info_new( + "id", "TinyURL", + "name", N_("TinyURL"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("TinyURL plugin"), + "description", N_("When receiving a message with URL(s), " + "use TinyURL for easier copying"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { PurpleNotifyUiOps *ops = purple_notify_get_ui_ops(); - plugin->extra = ops->notify_uri; + + purple_prefs_add_none(PREFS_BASE); + purple_prefs_add_int(PREF_LENGTH, 30); + purple_prefs_add_string(PREF_URL, "http://tinyurl.com/api-create.php?url="); + + g_object_set_data(G_OBJECT(plugin), "notify-uri", ops->notify_uri); ops->notify_uri = tinyurl_notify_uri; purple_signal_connect(purple_conversations_get_handle(), @@ -436,81 +484,12 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { PurpleNotifyUiOps *ops = purple_notify_get_ui_ops(); if (ops->notify_uri == tinyurl_notify_uri) - ops->notify_uri = plugin->extra; + ops->notify_uri = g_object_get_data(G_OBJECT(plugin), "notify-uri"); return TRUE; } -static PurplePluginPrefFrame * -get_plugin_pref_frame(PurplePlugin *plugin) { - - PurplePluginPrefFrame *frame; - PurplePluginPref *pref; - - frame = purple_plugin_pref_frame_new(); - - pref = purple_plugin_pref_new_with_name(PREF_LENGTH); - purple_plugin_pref_set_label(pref, _("Only create TinyURL for URLs" - " of this length or greater")); - purple_plugin_pref_frame_add(frame, pref); - pref = purple_plugin_pref_new_with_name(PREF_URL); - purple_plugin_pref_set_label(pref, _("TinyURL (or other) address prefix")); - purple_plugin_pref_frame_add(frame, pref); - - return frame; -} - -static PurplePluginUiInfo prefs_info = { - get_plugin_pref_frame, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - FINCH_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - "TinyURL", - N_("TinyURL"), - DISPLAY_VERSION, - N_("TinyURL plugin"), - N_("When receiving a message with URL(s), use TinyURL for easier copying"), - "Richard Nelson <wabz@whatsbeef.net>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, - NULL, - NULL, - &prefs_info, /**< prefs_info */ - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) { - purple_prefs_add_none(PREFS_BASE); - purple_prefs_add_int(PREF_LENGTH, 30); - purple_prefs_add_string(PREF_URL, "http://tinyurl.com/api-create.php?url="); -} - -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) +PURPLE_PLUGIN_INIT(PLUGIN_STATIC_NAME, plugin_query, plugin_load, plugin_unload);
--- a/finch/plugins/grouping.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/plugins/grouping.c Fri Jan 31 18:02:20 2014 +0530 @@ -17,9 +17,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -#define PURPLE_PLUGIN - #include "internal.h" #include "purple.h" @@ -45,32 +42,19 @@ /** * GObject code */ -static GType -finch_grouping_node_get_type(void) +static void +finch_grouping_node_init(FinchGroupingNode *node) { - static GType type = 0; +} - if(type == 0) { - static const GTypeInfo info = { - sizeof(FinchGroupingNodeClass), - NULL, - NULL, - NULL, - NULL, - NULL, - sizeof(FinchGroupingNode), - 0, - NULL, - NULL, - }; +static void +finch_grouping_node_class_init(FinchGroupingNodeClass *klass) +{ +} - type = g_type_register_static(PURPLE_TYPE_BLIST_NODE, - "FinchGroupingNode", - &info, 0); - } - - return type; -} +GType finch_grouping_node_get_type(void); +PURPLE_DEFINE_TYPE(FinchGroupingNode, finch_grouping_node, + PURPLE_TYPE_BLIST_NODE); /** * Online/Offline @@ -351,11 +335,38 @@ .can_add_node = nested_group_can_add_node, }; +static FinchPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", + NULL + }; + + return finch_plugin_info_new( + "id", "grouping", + "name", N_("Grouping"), + "version", VERSION, + "category", N_("User interface"), + "summary", N_("Provides alternate buddylist grouping options."), + "description", N_("Provides alternate buddylist grouping options."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { + finch_grouping_node_register_type(plugin); + default_manager = finch_blist_manager_find("default"); + online = g_object_new(FINCH_TYPE_GROUPING_NODE, NULL); + offline = g_object_new(FINCH_TYPE_GROUPING_NODE, NULL); + finch_blist_install_manager(&on_offline); finch_blist_install_manager(&meebo_group); finch_blist_install_manager(&no_group); @@ -364,49 +375,17 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { finch_blist_uninstall_manager(&on_offline); finch_blist_uninstall_manager(&meebo_group); finch_blist_uninstall_manager(&no_group); finch_blist_uninstall_manager(&nested_group); + + g_object_unref(online); + g_object_unref(offline); + return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - FINCH_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - "grouping", - N_("Grouping"), - VERSION, - N_("Provides alternate buddylist grouping options."), - N_("Provides alternate buddylist grouping options."), - "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL,NULL,NULL,NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - online = g_object_new(FINCH_TYPE_GROUPING_NODE, NULL); - offline = g_object_new(FINCH_TYPE_GROUPING_NODE, NULL); -} - -PURPLE_INIT_PLUGIN(grouping, init_plugin, info) - - +PURPLE_PLUGIN_INIT(grouping, plugin_query, plugin_load, plugin_unload);
--- a/finch/plugins/lastlog.c Fri Jan 31 17:56:27 2014 +0530 +++ b/finch/plugins/lastlog.c Fri Jan 31 18:02:20 2014 +0530 @@ -23,7 +23,7 @@ #include "internal.h" -#include <plugin.h> +#include <plugins.h> #include <version.h> #include <cmds.h> @@ -91,8 +91,30 @@ return PURPLE_CMD_RET_OK; } +static FinchPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", + NULL + }; + + return finch_plugin_info_new( + "id", "gntlastlog", + "name", N_("GntLastlog"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Lastlog plugin."), + "description", N_("Lastlog plugin."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { cmd = purple_cmd_register("lastlog", "s", PURPLE_CMD_P_DEFAULT, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL, @@ -102,48 +124,10 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { purple_cmd_unregister(cmd); return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - FINCH_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - "gntlastlog", - N_("GntLastlog"), - DISPLAY_VERSION, - N_("Lastlog plugin."), - N_("Lastlog plugin."), - "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, - NULL, - NULL, - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) - +PURPLE_PLUGIN_INIT(PLUGIN_STATIC_NAME, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -93,13 +93,14 @@ network.c \ ntlm.c \ notify.c \ - plugin.c \ + plugins.c \ pluginpref.c \ pounce.c \ prefs.c \ presence.c \ proxy.c \ - prpl.c \ + protocol.c \ + protocols.c \ purple-socket.c \ request.c \ request-datasheet.c \ @@ -166,13 +167,14 @@ network.h \ notify.h \ ntlm.h \ - plugin.h \ + plugins.h \ pluginpref.h \ pounce.h \ prefs.h \ presence.h \ proxy.h \ - prpl.h \ + protocol.h \ + protocols.h \ purple-socket.h \ request.h \ request-datasheet.h \ @@ -225,6 +227,9 @@ connection.h \ conversation.h \ conversationtypes.h \ + plugins.h \ + protocol.h \ + protocols.h \ roomlist.h \ status.h \ xfer.h @@ -264,13 +269,13 @@ # purple dbus server dbus_sources = dbus-server.c dbus-useful.c -dbus_headers = dbus-bindings.h dbus-purple.h dbus-server.h dbus-useful.h dbus-define-api.h dbus-types.h +dbus_headers = dbus-server.h dbus-bindings.h dbus-purple.h dbus-useful.h dbus-define-api.h dbus-types.h dbus_exported = dbus-useful.h dbus-define-api.h account.h accounts.h blistnode.h \ blistnodetypes.h buddylist.h buddyicon.h connection.h conversation.h \ conversationtypes.h conversations.h core.h xfer.h log.h notify.h \ prefs.h presence.h roomlist.h savedstatuses.h smiley.h status.h \ - server.h util.h xmlnode.h prpl.h + server.h util.h xmlnode.h protocol.h protocols.h purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \ $(addprefix $(srcdir)/ciphers/, $(purple_cipherheaders)) \ @@ -280,7 +285,7 @@ # We should probably make this better dbus_signals = $(addprefix $(srcdir)/, $(purple_coresources)) \ $(srcdir)/protocols/irc/irc.c \ - $(srcdir)/protocols/jabber/libxmpp.c + $(srcdir)/protocols/jabber/jabber.c dbus-types.c: dbus-analyze-types.py $(purple_build_coreheaders) $(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@ @@ -383,6 +388,7 @@ $(STATIC_LINK_LIBS) \ $(DBUS_LIBS) \ $(GLIB_LIBS) \ + $(GPLUGIN_LIBS) \ $(LIBXML_LIBS) \ $(NETWORKMANAGER_LIBS) \ $(INTLLIBS) \ @@ -394,6 +400,8 @@ $(JSON_LIBS) \ $(GNUTLS_LIBS) \ $(NSS_LIBS) \ + $(ZLIB_LIBS) \ + $(INTROSPECTION_LIBS) \ -lm AM_CPPFLAGS = \ @@ -402,6 +410,7 @@ -DLOCALEDIR=\"$(datadir)/locale\" \ -DSYSCONFDIR=\"$(sysconfdir)\" \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS) \ $(DBUS_CFLAGS) \ $(LIBXML_CFLAGS) \ @@ -413,10 +422,67 @@ $(NETWORKMANAGER_CFLAGS) \ $(JSON_CFLAGS) \ $(GNUTLS_CFLAGS) \ - $(NSS_CFLAGS) + $(NSS_CFLAGS) \ + $(ZLIB_CFLAGS) \ + $(INTROSPECTION_CFLAGS) # INSTALL_SSL_CERTIFICATES is true when SSL_CERTIFICATES_DIR is empty. # We want to use SSL_CERTIFICATES_DIR when it's not empty. if ! INSTALL_SSL_CERTIFICATES AM_CPPFLAGS += -DSSL_CERTIFICATES_DIR=\"$(SSL_CERTIFICATES_DIR)\" endif + +-include $(INTROSPECTION_MAKEFILE) +INTROSPECTION_GIRS = +INTROSPECTION_SCANNER_ARGS = --add-include-path=$(builddir) +INTROSPECTION_COMPILER_ARGS = + +if HAVE_INTROSPECTION +introspection_sources = \ + $(libpurpleinclude_HEADERS) \ + $(addprefix ciphers/, $(purple_cipherheaders)) \ + $(addprefix media/, $(purple_mediaheaders)) + +Purple-$(PURPLE_MAJOR_VERSION).$(PURPLE_MINOR_VERSION).gir: $(builddir)/libpurple.la +Purple_3_0_gir_INCLUDES = GObject-2.0 +if PLUGINS +Purple_3_0_gir_INCLUDES += GPlugin-0.0 +endif +if ENABLE_DBUS +Purple_3_0_gir_INCLUDES += DBus-1.0 DBusGLib-1.0 +endif + +Purple_3_0_gir_CFLAGS = \ + $(INCLUDES) \ + -DDATADIR=\"$(datadir)\" \ + -DLIBDIR=\"$(libdir)/purple-$(PURPLE_MAJOR_VERSION)/\" \ + -DLOCALEDIR=\"$(datadir)/locale\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(LIBXML_CFLAGS) \ + $(FARSTREAM_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(GSTVIDEO_CFLAGS) \ + $(GSTINTERFACES_CFLAGS) \ + $(IDN_CFLAGS) \ + $(NETWORKMANAGER_CFLAGS) \ + $(JSON_CFLAGS) \ + $(GNUTLS_CFLAGS) \ + $(NSS_CFLAGS) \ + $(ZLIB_CFLAGS) \ + $(INTROSPECTION_CFLAGS) + +Purple_3_0_gir_LIBS = $(builddir)/libpurple.la +Purple_3_0_gir_FILES = $(introspection_sources) +INTROSPECTION_GIRS += Purple-$(PURPLE_MAJOR_VERSION).$(PURPLE_MINOR_VERSION).gir + +girdir = $(INTROSPECTION_GIRDIR) +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(INTROSPECTION_TYPELIBDIR) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES += $(gir_DATA) $(typelib_DATA) +endif
--- a/libpurple/Makefile.mingw Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/Makefile.mingw Fri Jan 31 18:02:20 2014 +0530 @@ -107,13 +107,14 @@ network.c \ notify.c \ ntlm.c \ - plugin.c \ + plugins.c \ pluginpref.c \ pounce.c \ prefs.c \ presence.c \ proxy.c \ - prpl.c \ + protocol.c \ + protocols.c \ purple-socket.c \ request.c \ request-datasheet.c \
--- a/libpurple/account.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/account.c Fri Jan 31 18:02:20 2014 +0530 @@ -313,11 +313,11 @@ purple_account_connect_got_password_cb(PurpleAccount *account, const gchar *password, GError *error, gpointer data) { - PurplePluginProtocolInfo *prpl_info = data; + PurpleProtocol *protocol = data; if ((password == NULL || *password == '\0') && - !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && - !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) + !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) && + !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL)) purple_account_request_password(account, G_CALLBACK(request_password_ok_cb), G_CALLBACK(request_password_cancel_cb), account); @@ -328,9 +328,8 @@ void purple_account_connect(PurpleAccount *account) { - PurplePlugin *prpl; const char *username; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleAccountPrivate *priv; g_return_if_fail(PURPLE_IS_ACCOUNT(account)); @@ -344,11 +343,11 @@ return; } - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - if (prpl == NULL) { + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if (protocol == NULL) { gchar *message; - message = g_strdup_printf(_("Missing protocol plugin for %s"), username); + message = g_strdup_printf(_("Missing protocol for %s"), username); purple_notify_error(account, _("Connection Error"), message, NULL, purple_request_cpar_from_account(account)); g_free(message); @@ -359,13 +358,12 @@ purple_debug_info("account", "Connecting to account %s.\n", username); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); if (priv->password != NULL) { purple_account_connect_got_password_cb(account, - priv->password, NULL, prpl_info); + priv->password, NULL, protocol); } else { purple_keyring_get_password(account, - purple_account_connect_got_password_cb, prpl_info); + purple_account_connect_got_password_cb, protocol); } } @@ -640,8 +638,7 @@ PurpleRequestFieldGroup *group; PurpleRequestField *field; PurpleConnection *gc; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; char primary[256]; g_return_if_fail(PURPLE_IS_ACCOUNT(account)); @@ -649,9 +646,7 @@ gc = purple_account_get_connection(account); if (gc != NULL) - prpl = purple_connection_get_prpl(gc); - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + protocol = purple_connection_get_protocol(gc); fields = purple_request_fields_new(); @@ -661,7 +656,7 @@ field = purple_request_field_string_new("password", _("Original password"), NULL, FALSE); purple_request_field_string_set_masked(field, TRUE); - if (!prpl_info || !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) + if (!protocol || !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL)) purple_request_field_set_required(field, TRUE); purple_request_field_group_add_field(group, field); @@ -669,7 +664,7 @@ _("New password"), NULL, FALSE); purple_request_field_string_set_masked(field, TRUE); - if (!prpl_info || !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) + if (!protocol || !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL)) purple_request_field_set_required(field, TRUE); purple_request_field_group_add_field(group, field); @@ -677,7 +672,7 @@ _("New password (again)"), NULL, FALSE); purple_request_field_string_set_masked(field, TRUE); - if (!prpl_info || !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) + if (!protocol || !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL)) purple_request_field_set_required(field, TRUE); purple_request_field_group_add_field(group, field); @@ -1061,18 +1056,16 @@ PurpleSetPublicAliasFailureCallback failure_cb) { PurpleConnection *gc; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; g_return_if_fail(PURPLE_IS_ACCOUNT(account)); g_return_if_fail(purple_account_is_connected(account)); gc = purple_account_get_connection(account); - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, set_public_alias)) - prpl_info->set_public_alias(gc, alias, success_cb, failure_cb); + protocol = purple_connection_get_protocol(gc); + + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, set_public_alias)) + purple_protocol_server_iface_set_public_alias(protocol, gc, alias, success_cb, failure_cb); else if (failure_cb) { struct public_alias_closure *closure = g_new0(struct public_alias_closure, 1); @@ -1103,18 +1096,16 @@ PurpleGetPublicAliasFailureCallback failure_cb) { PurpleConnection *gc; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; g_return_if_fail(PURPLE_IS_ACCOUNT(account)); g_return_if_fail(purple_account_is_connected(account)); gc = purple_account_get_connection(account); - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_public_alias)) - prpl_info->get_public_alias(gc, success_cb, failure_cb); + protocol = purple_connection_get_protocol(gc); + + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_public_alias)) + purple_protocol_server_iface_get_public_alias(protocol, gc, success_cb, failure_cb); else if (failure_cb) { struct public_alias_closure *closure = g_new0(struct public_alias_closure, 1); @@ -1472,13 +1463,14 @@ const char * purple_account_get_protocol_name(const PurpleAccount *account) { - PurplePlugin *p; + PurpleProtocol *p; g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); - p = purple_find_prpl(purple_account_get_protocol_id(account)); - - return ((p && p->info->name) ? _(p->info->name) : _("Unknown")); + p = purple_protocols_find(purple_account_get_protocol_id(account)); + + return (p && purple_protocol_get_name(p) ? + _(purple_protocol_get_name(p)) : _("Unknown")); } PurpleConnection * @@ -2261,40 +2253,31 @@ void purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy, const char *message) { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc; - PurplePlugin *prpl = NULL; g_return_if_fail(PURPLE_IS_ACCOUNT(account)); g_return_if_fail(PURPLE_IS_BUDDY(buddy)); gc = purple_account_get_connection(account); if (gc != NULL) - prpl = purple_connection_get_prpl(gc); - - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info != NULL) { - if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy)) - prpl_info->add_buddy(gc, buddy, purple_buddy_get_group(buddy), message); - } + protocol = purple_connection_get_protocol(gc); + + if (protocol != NULL) + purple_protocol_server_iface_add_buddy(protocol, gc, buddy, + purple_buddy_get_group(buddy), message); } void purple_account_add_buddies(PurpleAccount *account, GList *buddies, const char *message) { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc = purple_account_get_connection(account); - PurplePlugin *prpl = NULL; if (gc != NULL) - prpl = purple_connection_get_prpl(gc); - - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info) { + protocol = purple_connection_get_protocol(gc); + + if (protocol) { GList *cur, *groups = NULL; /* Make a list of what group each buddy is in */ @@ -2303,13 +2286,13 @@ groups = g_list_append(groups, purple_buddy_get_group(buddy)); } - if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies)) - prpl_info->add_buddies(gc, buddies, groups, message); - else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy)) { + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, add_buddies)) + purple_protocol_server_iface_add_buddies(protocol, gc, buddies, groups, message); + else if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, add_buddy)) { GList *curb = buddies, *curg = groups; while ((curb != NULL) && (curg != NULL)) { - prpl_info->add_buddy(gc, curb->data, curg->data, message); + purple_protocol_server_iface_add_buddy(protocol, gc, curb->data, curg->data, message); curb = curb->next; curg = curg->next; } @@ -2323,36 +2306,28 @@ purple_account_remove_buddy(PurpleAccount *account, PurpleBuddy *buddy, PurpleGroup *group) { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc = purple_account_get_connection(account); - PurplePlugin *prpl = NULL; if (gc != NULL) - prpl = purple_connection_get_prpl(gc); - - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && prpl_info->remove_buddy) - prpl_info->remove_buddy(gc, buddy, group); + protocol = purple_connection_get_protocol(gc); + + if (protocol) + purple_protocol_server_iface_remove_buddy(protocol, gc, buddy, group); } void purple_account_remove_buddies(PurpleAccount *account, GList *buddies, GList *groups) { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc = purple_account_get_connection(account); - PurplePlugin *prpl = NULL; if (gc != NULL) - prpl = purple_connection_get_prpl(gc); - - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info) { - if (prpl_info->remove_buddies) - prpl_info->remove_buddies(gc, buddies, groups); + protocol = purple_connection_get_protocol(gc); + + if (protocol) { + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, remove_buddies)) + purple_protocol_server_iface_remove_buddies(protocol, gc, buddies, groups); else { GList *curb = buddies; GList *curg = groups; @@ -2368,45 +2343,36 @@ void purple_account_remove_group(PurpleAccount *account, PurpleGroup *group) { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc = purple_account_get_connection(account); - PurplePlugin *prpl = NULL; if (gc != NULL) - prpl = purple_connection_get_prpl(gc); - - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && prpl_info->remove_group) - prpl_info->remove_group(gc, group); + protocol = purple_connection_get_protocol(gc); + + if (protocol) + purple_protocol_server_iface_remove_group(protocol, gc, group); } void purple_account_change_password(PurpleAccount *account, const char *orig_pw, const char *new_pw) { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc = purple_account_get_connection(account); - PurplePlugin *prpl = NULL; purple_account_set_password(account, new_pw, NULL, NULL); if (gc != NULL) - prpl = purple_connection_get_prpl(gc); - - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && prpl_info->change_passwd) - prpl_info->change_passwd(gc, orig_pw, new_pw); + protocol = purple_connection_get_protocol(gc); + + if (protocol) + purple_protocol_server_iface_change_passwd(protocol, gc, orig_pw, new_pw); } gboolean purple_account_supports_offline_message(PurpleAccount *account, PurpleBuddy *buddy) { PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info = NULL; - PurplePlugin *prpl = NULL; + PurpleProtocol *protocol = NULL; g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE); g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), FALSE); @@ -2415,14 +2381,11 @@ if (gc == NULL) return FALSE; - prpl = purple_connection_get_prpl(gc); - - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (!prpl_info || !prpl_info->offline_message) + protocol = purple_connection_get_protocol(gc); + + if (!protocol) return FALSE; - return prpl_info->offline_message(buddy); + return purple_protocol_client_iface_offline_message(protocol, buddy); } void @@ -2938,8 +2901,7 @@ PurpleAccount *account = PURPLE_ACCOUNT(object); PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account); gchar *username, *protocol_id; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleStatusType *status_type; parent_class->constructed(object); @@ -2952,17 +2914,15 @@ purple_signal_emit(purple_accounts_get_handle(), "account-created", account); - prpl = purple_find_prpl(protocol_id); - if (prpl == NULL) { + protocol = purple_protocols_find(protocol_id); + if (protocol == NULL) { g_free(username); g_free(protocol_id); return; } - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info != NULL && prpl_info->status_types != NULL) - purple_account_set_status_types(account, - prpl_info->status_types(account)); + purple_account_set_status_types(account, + purple_protocol_class_status_types(protocol, account)); priv->presence = PURPLE_PRESENCE(purple_account_presence_new(account));
--- a/libpurple/account.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/account.h Fri Jan 31 18:02:20 2014 +0530 @@ -53,7 +53,7 @@ #include "connection.h" #include "log.h" #include "proxy.h" -#include "prpl.h" +#include "protocol.h" #include "status.h" #include "keyring.h" #include "xmlnode.h" @@ -468,7 +468,7 @@ * * @param account The account * @param success_cb A callback which will be called with the alias - * @param failure_cb A callback which will be called if the prpl is + * @param failure_cb A callback which will be called if the protocol is * unable to retrieve the server-side alias. */ void purple_account_get_public_alias(PurpleAccount *account, @@ -585,7 +585,7 @@ * * @return The UI data associated with this account. This is a * convenience field provided to the UIs--it is not - * used by the libuprple core. + * used by the libpurple core. */ gpointer purple_account_get_ui_data(const PurpleAccount *account); @@ -1068,7 +1068,7 @@ * * @param account The account. * @param buddy The buddy to add. - * @param message The invite message. This may be ignored by a prpl. + * @param message The invite message. This may be ignored by a protocol. */ void purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy, const char *message); @@ -1077,7 +1077,7 @@ * * @param account The account. * @param buddies The list of PurpleBlistNodes representing the buddies to add. - * @param message The invite message. This may be ignored by a prpl. + * @param message The invite message. This may be ignored by a protocol. */ void purple_account_add_buddies(PurpleAccount *account, GList *buddies, const char *message);
--- a/libpurple/accountopt.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/accountopt.c Fri Jan 31 18:02:20 2014 +0530 @@ -32,7 +32,7 @@ /** * An option for an account. * - * This is set by protocol plugins, and appears in the account settings + * This is set by protocols, and appears in the account settings * dialogs. */ struct _PurpleAccountOption
--- a/libpurple/accounts.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/accounts.c Fri Jan 31 18:02:20 2014 +0530 @@ -101,7 +101,7 @@ migrate_yahoo_japan(PurpleAccount *account) { /* detect a Yahoo! JAPAN account that existed prior to 2.6.0 and convert it - * to use the new prpl-yahoojp. Also remove the account-specific settings + * to use the new yahoojp protocol. Also remove the account-specific settings * we no longer need */ if(purple_strequal(purple_account_get_protocol_id(account), "prpl-yahoo")) {
--- a/libpurple/blistnodetypes.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/blistnodetypes.c Fri Jan 31 18:02:20 2014 +0530 @@ -58,7 +58,7 @@ char *local_alias; /**< The user-set alias of the buddy */ char *server_alias; /**< The server-specified alias of the buddy. (i.e. MSN "Friendly Names") */ - void *proto_data; /**< This allows the prpl to associate + void *proto_data; /**< This allows the protocol to associate whatever data it wants with a buddy. */ PurpleBuddyIcon *icon; /**< The buddy icon. */ PurpleAccount *account; /**< the account this buddy belongs to */ @@ -671,19 +671,15 @@ { PurpleBuddy *buddy = PURPLE_BUDDY(object); PurpleBuddyPrivate *priv = PURPLE_BUDDY_GET_PRIVATE(buddy); - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; /* - * Tell the owner PRPL that we're about to free the buddy so it + * Tell the owner protocol that we're about to free the buddy so it * can free proto_data */ - prpl = purple_find_prpl(purple_account_get_protocol_id(priv->account)); - if (prpl) { - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info && prpl_info->buddy_free) - prpl_info->buddy_free(buddy); - } + protocol = purple_protocols_find(purple_account_get_protocol_id(priv->account)); + if (protocol) + purple_protocol_client_iface_buddy_free(protocol, buddy); g_free(priv->name); g_free(priv->local_alias); @@ -1138,18 +1134,16 @@ const char *purple_chat_get_name_only(PurpleChat *chat) { char *ret = NULL; - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleChatPrivate *priv = PURPLE_CHAT_GET_PRIVATE(chat); g_return_val_if_fail(priv != NULL, NULL); - prpl = purple_find_prpl(purple_account_get_protocol_id(priv->account)); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + protocol = purple_protocols_find(purple_account_get_protocol_id(priv->account)); - if (prpl_info->chat_info) { - struct proto_chat_entry *pce; - GList *parts = prpl_info->chat_info(purple_account_get_connection(priv->account)); + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, info)) { + PurpleProtocolChatEntry *pce; + GList *parts = purple_protocol_chat_iface_info(protocol, purple_account_get_connection(priv->account)); pce = parts->data; ret = g_hash_table_lookup(priv->components, pce->identifier); g_list_foreach(parts, (GFunc)g_free, NULL); @@ -1475,7 +1469,7 @@ /* * TODO: This seems like a dumb way to do this... why not just * append all children from the old group to the end of the new - * one? PRPLs might be expecting to receive an add_buddy() for + * one? Protocols might be expecting to receive an add_buddy() for * each moved buddy... */ while (child) @@ -1532,25 +1526,21 @@ if (ops && ops->update) ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(source)); - /* Notify all PRPLs */ + /* Notify all protocols */ /* TODO: Is this condition needed? Seems like it would always be TRUE */ if(old_name && !purple_strequal(priv->name, old_name)) { for (accts = purple_group_get_accounts(source); accts; accts = g_slist_remove(accts, accts->data)) { PurpleAccount *account = accts->data; PurpleConnection *gc = NULL; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; GList *l = NULL, *buddies = NULL; gc = purple_account_get_connection(account); if(gc) - prpl = purple_connection_get_prpl(gc); + protocol = purple_connection_get_protocol(gc); - if(gc && prpl) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if(!prpl_info) + if(!protocol) continue; for(l = moved_buddies; l; l = l->next) { @@ -1560,8 +1550,8 @@ buddies = g_list_append(buddies, (PurpleBlistNode *)buddy); } - if(prpl_info->rename_group) { - prpl_info->rename_group(gc, old_name, source, buddies); + if(PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, rename_group)) { + purple_protocol_server_iface_rename_group(protocol, gc, old_name, source, buddies); } else { GList *cur, *groups = NULL;
--- a/libpurple/blistnodetypes.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/blistnodetypes.h Fri Jan 31 18:02:20 2014 +0530 @@ -255,7 +255,7 @@ /** * Returns a buddy's protocol-specific data. * - * This should only be called from the associated prpl. + * This should only be called from the associated protocol. * * @param buddy The buddy. * @return The protocol data. @@ -267,7 +267,7 @@ /** * Sets a buddy's protocol-specific data. * - * This should only be called from the associated prpl. + * This should only be called from the associated protocol. * * @param buddy The buddy. * @param data The data. @@ -486,7 +486,7 @@ * * @param account The account this chat will get added to * @param alias The alias of the new chat - * @param components The info the prpl needs to join the chat. The + * @param components The info the protocol needs to join the chat. The * hash function should be g_str_hash() and the * equal function should be g_str_equal(). * @return A newly allocated chat
--- a/libpurple/buddyicon.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/buddyicon.c Fri Jan 31 18:02:20 2014 +0530 @@ -745,13 +745,13 @@ if (!purple_account_is_disconnected(account)) { PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; gc = purple_account_get_connection(account); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + protocol = purple_connection_get_protocol(gc); - if (prpl_info && prpl_info->set_buddy_icon) - prpl_info->set_buddy_icon(gc, img); + if (protocol) + purple_protocol_server_iface_set_buddy_icon(protocol, gc, img); } if (old_img) @@ -1103,7 +1103,55 @@ cache_dir = NULL; } -void purple_buddy_icon_get_scale_size(PurpleBuddyIconSpec *spec, int *width, int *height) +GType +purple_buddy_icon_get_type(void) +{ + static GType type = 0; + + if (type == 0) { + type = g_boxed_type_register_static("PurpleBuddyIcon", + (GBoxedCopyFunc)purple_buddy_icon_ref, + (GBoxedFreeFunc)purple_buddy_icon_unref); + } + + return type; +} + +PurpleBuddyIconSpec * +purple_buddy_icon_spec_new(char *format, int min_width, int min_height, + int max_width, int max_height, size_t max_filesize, + PurpleIconScaleRules scale_rules) +{ + PurpleBuddyIconSpec *icon_spec; + + icon_spec = g_new0(PurpleBuddyIconSpec, 1); + + icon_spec->format = format; + icon_spec->min_width = min_width; + icon_spec->min_height = min_height; + icon_spec->max_width = max_width; + icon_spec->max_height = max_height; + icon_spec->max_filesize = max_filesize; + icon_spec->scale_rules = scale_rules; + + return icon_spec; +} + +static PurpleBuddyIconSpec * +purple_buddy_icon_spec_copy(PurpleBuddyIconSpec *icon_spec) +{ + PurpleBuddyIconSpec *icon_spec_copy; + + g_return_val_if_fail(icon_spec != NULL, NULL); + + icon_spec_copy = g_new0(PurpleBuddyIconSpec, 1); + *icon_spec_copy = *icon_spec; + + return icon_spec_copy; +} + +void purple_buddy_icon_spec_get_scaled_size(PurpleBuddyIconSpec *spec, + int *width, int *height) { int new_width, new_height; @@ -1133,14 +1181,14 @@ } GType -purple_buddy_icon_get_type(void) +purple_buddy_icon_spec_get_type(void) { static GType type = 0; if (type == 0) { - type = g_boxed_type_register_static("PurpleBuddyIcon", - (GBoxedCopyFunc)purple_buddy_icon_ref, - (GBoxedFreeFunc)purple_buddy_icon_unref); + type = g_boxed_type_register_static("PurpleBuddyIconSpec", + (GBoxedCopyFunc)purple_buddy_icon_spec_copy, + (GBoxedFreeFunc)g_free); } return type;
--- a/libpurple/buddyicon.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/buddyicon.h Fri Jan 31 18:02:20 2014 +0530 @@ -35,12 +35,39 @@ */ typedef struct _PurpleBuddyIcon PurpleBuddyIcon; +#define PURPLE_TYPE_BUDDY_ICON_SPEC (purple_buddy_icon_spec_get_type()) + +/** + * A description of a Buddy Icon specification. This tells Purple what kind of + * image file it should give a protocol, and what kind of image file it should + * expect back. Dimensions less than 1 should be ignored and the image not + * scaled. + */ +typedef struct _PurpleBuddyIconSpec PurpleBuddyIconSpec; + #include "account.h" #include "buddylist.h" #include "imgstore.h" -#include "prpl.h" +#include "protocols.h" #include "util.h" +/** @copydoc PurpleBuddyIconSpec */ +struct _PurpleBuddyIconSpec { + /** This is a comma-delimited list of image formats or @c NULL if icons + * are not supported. Neither the core nor the protocol will actually + * check to see if the data it's given matches this; it's entirely up + * to the UI to do what it wants + */ + char *format; + + int min_width; /**< Minimum width of this icon */ + int min_height; /**< Minimum height of this icon */ + int max_width; /**< Maximum width of this icon */ + int max_height; /**< Maximum height of this icon */ + size_t max_filesize; /**< Maximum size in bytes */ + PurpleIconScaleRules scale_rules; /**< How to stretch this icon */ +}; + G_BEGIN_DECLS /**************************************************************************/ @@ -63,7 +90,7 @@ * @param username The username the icon belongs to. * @param icon_data The buddy icon data. * @param icon_len The buddy icon length. - * @param checksum A protocol checksum from the prpl or @c NULL. + * @param checksum A protocol checksum from the protocol or @c NULL. * * @return The buddy icon structure, with a reference for the caller. */ @@ -103,7 +130,7 @@ * @param data The buddy icon data, which the buddy icon code * takes ownership of and will free. * @param len The length of the data in @a data. - * @param checksum A protocol checksum from the prpl or @c NULL. + * @param checksum A protocol checksum from the protocol or @c NULL. */ void purple_buddy_icon_set_data(PurpleBuddyIcon *icon, guchar *data, @@ -130,7 +157,7 @@ /** * Returns the buddy icon's checksum. * - * This function is really only for prpl use. + * This function is really only for protocol use. * * @param icon The buddy icon. * @@ -190,7 +217,7 @@ * @param icon_data The buddy icon data, which the buddy icon code * takes ownership of and will free. * @param icon_len The length of the icon data. - * @param checksum A protocol checksum from the prpl or @c NULL. + * @param checksum A protocol checksum from the protocol or @c NULL. */ void purple_buddy_icons_set_for_user(PurpleAccount *account, const char *username, @@ -388,14 +415,38 @@ /*@}*/ /**************************************************************************/ -/** @name Buddy Icon Helper API */ +/** @name Buddy Icon Spec API */ /**************************************************************************/ /*@{*/ /** + * Returns the GType for the #PurpleBuddyIconSpec boxed structure. + */ +GType purple_buddy_icon_spec_get_type(void); + +/** + * Creates a new #PurpleBuddyIconSpec instance. + * + * @param format A comma-delimited list of image formats or @c NULL if + * icons are not supported + * @param min_width Minimum width of an icon + * @param min_height Minimum height of an icon + * @param max_width Maximum width of an icon + * @param max_height Maximum height of an icon + * @param max_filesize Maximum file size in bytes + * @param scale_rules How to stretch this icon + * + * @return A new buddy icon spec. + */ +PurpleBuddyIconSpec *purple_buddy_icon_spec_new(char *format, int min_width, + int min_height, int max_width, int max_height, size_t max_filesize, + PurpleIconScaleRules scale_rules); + +/** * Gets display size for a buddy icon */ -void purple_buddy_icon_get_scale_size(PurpleBuddyIconSpec *spec, int *width, int *height); +void purple_buddy_icon_spec_get_scaled_size(PurpleBuddyIconSpec *spec, + int *width, int *height); /*@}*/
--- a/libpurple/buddylist.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/buddylist.c Fri Jan 31 18:02:20 2014 +0530 @@ -28,7 +28,7 @@ #include "notify.h" #include "pounce.h" #include "prefs.h" -#include "prpl.h" +#include "protocol.h" #include "server.h" #include "signals.h" #include "util.h" @@ -1614,9 +1614,8 @@ { char *chat_name; PurpleChat *chat; - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info = NULL; - struct proto_chat_entry *pce; + PurpleProtocol *protocol = NULL; + PurpleProtocolChatEntry *pce; PurpleBlistNode *node, *group; GList *parts; char *normname; @@ -1627,11 +1626,10 @@ if (!purple_account_is_connected(account)) return NULL; - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); - if (prpl_info->find_blist_chat != NULL) - return prpl_info->find_blist_chat(account, name); + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, find_blist_chat)) + return purple_protocol_client_iface_find_blist_chat(protocol, account, name); normname = g_strdup(purple_normalize(account, name)); for (group = purplebuddylist->root; group != NULL; group = group->next) { @@ -1643,7 +1641,7 @@ if (account != purple_chat_get_account(chat)) continue; - parts = prpl_info->chat_info( + parts = purple_protocol_chat_iface_info(protocol, purple_account_get_connection(purple_chat_get_account(chat))); pce = parts->data;
--- a/libpurple/cmds.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/cmds.c Fri Jan 31 18:02:20 2014 +0530 @@ -36,7 +36,7 @@ gchar *args; PurpleCmdPriority priority; PurpleCmdFlag flags; - gchar *prpl_id; + gchar *protocol_id; PurpleCmdFunc func; gchar *help; void *data; @@ -54,7 +54,7 @@ PurpleCmdId purple_cmd_register(const gchar *cmd, const gchar *args, PurpleCmdPriority p, PurpleCmdFlag f, - const gchar *prpl_id, PurpleCmdFunc func, + const gchar *protocol_id, PurpleCmdFunc func, const gchar *helpstr, void *data) { PurpleCmdId id; @@ -72,7 +72,7 @@ c->args = g_strdup(args); c->priority = p; c->flags = f; - c->prpl_id = g_strdup(prpl_id); + c->protocol_id = g_strdup(protocol_id); c->func = func; c->help = g_strdup(helpstr); c->data = data; @@ -88,7 +88,7 @@ { g_free(c->cmd); g_free(c->args); - g_free(c->prpl_id); + g_free(c->protocol_id); g_free(c->help); g_free(c); } @@ -203,14 +203,14 @@ GList *l; gchar *err = NULL; gboolean is_im = TRUE; - gboolean found = FALSE, tried_cmd = FALSE, right_type = FALSE, right_prpl = FALSE; - const gchar *prpl_id; + gboolean found = FALSE, tried_cmd = FALSE, right_type = FALSE, right_protocol = FALSE; + const gchar *protocol_id; gchar **args = NULL; gchar *cmd, *rest, *mrest; PurpleCmdRet ret = PURPLE_CMD_RET_CONTINUE; *error = NULL; - prpl_id = purple_account_get_protocol_id(purple_conversation_get_account(conv)); + protocol_id = purple_account_get_protocol_id(purple_conversation_get_account(conv)); if (PURPLE_IS_CHAT_CONVERSATION(conv)) is_im = FALSE; @@ -244,11 +244,11 @@ right_type = TRUE; - if ((c->flags & PURPLE_CMD_FLAG_PRPL_ONLY) && - !purple_strequal(c->prpl_id, prpl_id)) + if ((c->flags & PURPLE_CMD_FLAG_PROTOCOL_ONLY) && + !purple_strequal(c->protocol_id, protocol_id)) continue; - right_prpl = TRUE; + right_protocol = TRUE; /* this checks the allow bad args flag for us */ if (!purple_cmd_parse_args(c, rest, mrest, &args)) { @@ -280,8 +280,8 @@ if (!right_type) return PURPLE_CMD_STATUS_WRONG_TYPE; - if (!right_prpl) - return PURPLE_CMD_STATUS_WRONG_PRPL; + if (!right_protocol) + return PURPLE_CMD_STATUS_WRONG_PROTOCOL; if (!tried_cmd) return PURPLE_CMD_STATUS_WRONG_ARGS; @@ -314,8 +314,8 @@ if (!(c->flags & PURPLE_CMD_FLAG_CHAT)) continue; - if (conv && (c->flags & PURPLE_CMD_FLAG_PRPL_ONLY) && - !purple_strequal(c->prpl_id, purple_account_get_protocol_id(purple_conversation_get_account(conv)))) + if (conv && (c->flags & PURPLE_CMD_FLAG_PROTOCOL_ONLY) && + !purple_strequal(c->protocol_id, purple_account_get_protocol_id(purple_conversation_get_account(conv)))) continue; ret = g_list_append(ret, c->cmd); @@ -346,8 +346,8 @@ if (!(c->flags & PURPLE_CMD_FLAG_CHAT)) continue; - if (conv && (c->flags & PURPLE_CMD_FLAG_PRPL_ONLY) && - !purple_strequal(c->prpl_id, purple_account_get_protocol_id(purple_conversation_get_account(conv)))) + if (conv && (c->flags & PURPLE_CMD_FLAG_PROTOCOL_ONLY) && + !purple_strequal(c->protocol_id, purple_account_get_protocol_id(purple_conversation_get_account(conv)))) continue; ret = g_list_append(ret, c->help);
--- a/libpurple/cmds.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/cmds.h Fri Jan 31 18:02:20 2014 +0530 @@ -37,7 +37,7 @@ PURPLE_CMD_STATUS_FAILED, PURPLE_CMD_STATUS_NOT_FOUND, PURPLE_CMD_STATUS_WRONG_ARGS, - PURPLE_CMD_STATUS_WRONG_PRPL, + PURPLE_CMD_STATUS_WRONG_PROTOCOL, PURPLE_CMD_STATUS_WRONG_TYPE } PurpleCmdStatus; @@ -72,7 +72,7 @@ PURPLE_CMD_P_VERY_LOW = -1000, PURPLE_CMD_P_LOW = 0, PURPLE_CMD_P_DEFAULT = 1000, - PURPLE_CMD_P_PRPL = 2000, + PURPLE_CMD_P_PROTOCOL = 2000, PURPLE_CMD_P_PLUGIN = 3000, PURPLE_CMD_P_ALIAS = 4000, PURPLE_CMD_P_HIGH = 5000, @@ -90,8 +90,8 @@ PURPLE_CMD_FLAG_IM = 0x01, /** Command is usable in multi-user chats. */ PURPLE_CMD_FLAG_CHAT = 0x02, - /** Command is usable only for a particular prpl. */ - PURPLE_CMD_FLAG_PRPL_ONLY = 0x04, + /** Command is usable only for a particular protocol. */ + PURPLE_CMD_FLAG_PROTOCOL_ONLY = 0x04, /** Incorrect arguments to this command should be accepted anyway. */ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS = 0x08 } PurpleCmdFlag; @@ -139,8 +139,8 @@ * <tt>|</tt> (bitwise OR). You need to at least pass one of * #PURPLE_CMD_FLAG_IM or #PURPLE_CMD_FLAG_CHAT (you may pass both) in * order for the command to ever actually be called. - * @param prpl_id If the #PURPLE_CMD_FLAG_PRPL_ONLY flag is set, this is the id - * of the prpl to which the command applies (such as + * @param protocol_id If the #PURPLE_CMD_FLAG_PROTOCOL_ONLY flag is set, this is the id + * of the protocol to which the command applies (such as * <tt>"prpl-msn"</tt>). If the flag is not set, this parameter * is ignored; pass @c NULL (or a humourous string of your * choice!). @@ -157,7 +157,7 @@ * #purple_cmd_unregister, or @a 0 on failure. */ PurpleCmdId purple_cmd_register(const gchar *cmd, const gchar *args, PurpleCmdPriority p, PurpleCmdFlag f, - const gchar *prpl_id, PurpleCmdFunc func, const gchar *helpstr, void *data); + const gchar *protocol_id, PurpleCmdFunc func, const gchar *helpstr, void *data); /** * Unregister a command with the core.
--- a/libpurple/connection.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/connection.c Fri Jan 31 18:02:20 2014 +0530 @@ -55,7 +55,7 @@ /** Private data for a connection */ struct _PurpleConnectionPrivate { - PurplePlugin *prpl; /**< The protocol plugin. */ + PurpleProtocol *protocol; /**< The protocol. */ PurpleConnectionFlags flags; /**< Connection flags. */ PurpleConnectionState state; /**< The connection state. */ @@ -74,7 +74,7 @@ /** Wants to Die state. This is set when the user chooses to log out, or * when the protocol is disconnected and should not be automatically - * reconnected (incorrect password, etc.). prpls should rely on + * reconnected (incorrect password, etc.). Protocols should rely on * purple_connection_error() to set this for them rather than * setting it themselves. * @see purple_connection_error_is_fatal @@ -88,14 +88,14 @@ guint disconnect_timeout; /**< Timer used for nasty stack tricks */ time_t last_received; /**< When we last received a packet. Set by the - prpl to avoid sending unneeded keepalives */ + protocols to avoid sending unneeded keepalives */ }; /* GObject property enums */ enum { PROP_0, - PROP_PRPL, + PROP_PROTOCOL, PROP_FLAGS, PROP_STATE, PROP_ACCOUNT, @@ -124,7 +124,6 @@ send_keepalive(gpointer data) { PurpleConnection *gc = data; - PurplePluginProtocolInfo *prpl_info; PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc); /* Only send keep-alives if we haven't heard from the @@ -133,9 +132,7 @@ if ((time(NULL) - priv->last_received) < KEEPALIVE_INTERVAL) return TRUE; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(priv->prpl); - if (prpl_info->keepalive) - prpl_info->keepalive(gc); + purple_protocol_server_iface_keepalive(priv->protocol, gc); return TRUE; } @@ -143,13 +140,11 @@ static void update_keepalive(PurpleConnection *gc, gboolean on) { - PurplePluginProtocolInfo *prpl_info = NULL; PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc); - if (priv != NULL && priv->prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(priv->prpl); + g_return_if_fail(priv != NULL); - if (!prpl_info || !prpl_info->keepalive) + if (!PURPLE_PROTOCOL_IMPLEMENTS(priv->protocol, SERVER_IFACE, keepalive)) return; if (on && !priv->keepalive) @@ -328,14 +323,14 @@ return priv->account; } -PurplePlugin * -purple_connection_get_prpl(const PurpleConnection *gc) +PurpleProtocol * +purple_connection_get_protocol(const PurpleConnection *gc) { PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc); g_return_val_if_fail(priv != NULL, NULL); - return priv->prpl; + return priv->protocol; } const char * @@ -643,8 +638,8 @@ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc); switch (param_id) { - case PROP_PRPL: - priv->prpl = g_value_get_pointer(value); + case PROP_PROTOCOL: + priv->protocol = g_value_get_object(value); break; case PROP_FLAGS: purple_connection_set_flags(gc, g_value_get_flags(value)); @@ -676,8 +671,8 @@ PurpleConnection *gc = PURPLE_CONNECTION(obj); switch (param_id) { - case PROP_PRPL: - g_value_set_pointer(value, purple_connection_get_prpl(gc)); + case PROP_PROTOCOL: + g_value_set_object(value, purple_connection_get_protocol(gc)); break; case PROP_FLAGS: g_value_set_flags(value, purple_connection_get_flags(gc)); @@ -736,7 +731,6 @@ PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc); PurpleAccount *account; GSList *buddies; - PurplePluginProtocolInfo *prpl_info = NULL; gboolean remove = FALSE; priv->is_finalizing = TRUE; @@ -760,11 +754,9 @@ update_keepalive(gc, FALSE); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(priv->prpl); - if (prpl_info->close) - (prpl_info->close)(gc); + purple_protocol_class_close(priv->protocol, gc); - /* Clear out the proto data that was freed in the prpl close method */ + /* Clear out the proto data that was freed in the protocol's close method */ buddies = purple_blist_find_buddies(account, NULL); while (buddies != NULL) { PurpleBuddy *buddy = buddies->data; @@ -822,8 +814,9 @@ g_type_class_add_private(klass, sizeof(PurpleConnectionPrivate)); - properties[PROP_PRPL] = g_param_spec_pointer("prpl", "Protocol plugin", - "The prpl that is using the connection.", + properties[PROP_PROTOCOL] = g_param_spec_object("protocol", "Protocol", + "The protocol that the connection is using.", + PURPLE_TYPE_PROTOCOL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); @@ -885,22 +878,19 @@ _purple_connection_new(PurpleAccount *account, gboolean regist, const char *password) { PurpleConnection *gc; - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; g_return_if_fail(PURPLE_IS_ACCOUNT(account)); if (!purple_account_is_disconnected(account)) return; - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - else { + if (protocol == NULL) { gchar *message; - message = g_strdup_printf(_("Missing protocol plugin for %s"), + message = g_strdup_printf(_("Missing protocol for %s"), purple_account_get_username(account)); purple_notify_error(NULL, regist ? _("Registration Error") : _("Connection Error"), message, NULL, @@ -911,14 +901,14 @@ if (regist) { - if (prpl_info->register_user == NULL) + if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, register_user)) return; } else { if (((password == NULL) || (*password == '\0')) && - !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && - !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) + !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) && + !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL)) { purple_debug_error("connection", "Cannot connect to account %s without " "a password.\n", purple_account_get_username(account)); @@ -926,11 +916,17 @@ } } - gc = g_object_new(PURPLE_TYPE_CONNECTION, - "prpl", prpl, - "password", password, - "account", account, - NULL); + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, connection_new)) + gc = purple_protocol_factory_iface_connection_new(protocol, account, + password); + else + gc = g_object_new(PURPLE_TYPE_CONNECTION, + "protocol", protocol, + "account", account, + "password", password, + NULL); + + g_return_if_fail(gc != NULL); if (regist) { @@ -939,14 +935,14 @@ /* set this so we don't auto-reconnect after registering */ PURPLE_CONNECTION_GET_PRIVATE(gc)->wants_to_die = TRUE; - prpl_info->register_user(account); + purple_protocol_server_iface_register_user(protocol, account); } else { purple_debug_info("connection", "Connecting. gc = %p\n", gc); purple_signal_emit(purple_accounts_get_handle(), "account-connecting", account); - prpl_info->login(account); + purple_protocol_class_login(protocol, account); } } @@ -956,19 +952,16 @@ { /* Lots of copy/pasted code to avoid API changes. You might want to integrate that into the previous function when posssible. */ PurpleConnection *gc; - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; g_return_if_fail(PURPLE_IS_ACCOUNT(account)); - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - else { + if (protocol == NULL) { gchar *message; - message = g_strdup_printf(_("Missing protocol plugin for %s"), + message = g_strdup_printf(_("Missing protocol for %s"), purple_account_get_username(account)); purple_notify_error(NULL, _("Unregistration Error"), message, NULL, purple_request_cpar_from_account(account)); @@ -977,28 +970,34 @@ } if (!purple_account_is_disconnected(account)) { - prpl_info->unregister_user(account, cb, user_data); + purple_protocol_server_iface_unregister_user(protocol, account, cb, user_data); return; } if (((password == NULL) || (*password == '\0')) && - !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && - !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) + !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) && + !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL)) { purple_debug_error("connection", "Cannot connect to account %s without " "a password.\n", purple_account_get_username(account)); return; } - gc = g_object_new(PURPLE_TYPE_CONNECTION, - "prpl", prpl, - "password", password, - "account", account, - NULL); + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, connection_new)) + gc = purple_protocol_factory_iface_connection_new(protocol, account, + password); + else + gc = g_object_new(PURPLE_TYPE_CONNECTION, + "protocol", protocol, + "account", account, + "password", password, + NULL); + + g_return_if_fail(gc != NULL); purple_debug_info("connection", "Unregistering. gc = %p\n", gc); - prpl_info->unregister_user(account, cb, user_data); + purple_protocol_server_iface_unregister_user(protocol, account, cb, user_data); } /**************************************************************************
--- a/libpurple/connection.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/connection.h Fri Jan 31 18:02:20 2014 +0530 @@ -155,7 +155,7 @@ #include <time.h> #include "account.h" -#include "plugin.h" +#include "protocol.h" #include "status.h" #include "sslconn.h" @@ -273,10 +273,10 @@ GType purple_connection_error_info_get_type(void); /** - * Sets the connection state. PRPLs should call this and pass in + * Sets the connection state. Protocols should call this and pass in * the state #PURPLE_CONNECTION_CONNECTED when the account is completely * signed on. What does it mean to be completely signed on? If - * the core can call prpl->set_status, and it successfully changes + * the core can call protocol's set_status, and it successfully changes * your status, then the account is online. * * @param gc The connection. @@ -344,13 +344,13 @@ PurpleAccount *purple_connection_get_account(const PurpleConnection *gc); /** - * Returns the protocol plugin managing a connection. + * Returns the protocol managing a connection. * * @param gc The connection. * - * @return The protocol plugin. + * @return The protocol. */ -PurplePlugin * purple_connection_get_prpl(const PurpleConnection *gc); +PurpleProtocol *purple_connection_get_protocol(const PurpleConnection *gc); /** * Returns the connection's password. @@ -462,7 +462,7 @@ /** * Indicate that a packet was received on the connection. - * Set by the prpl to avoid sending unneeded keepalives. + * Set by the protocol to avoid sending unneeded keepalives. * * @param gc The connection. */
--- a/libpurple/conversation.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/conversation.c Fri Jan 31 18:02:20 2014 +0530 @@ -31,7 +31,7 @@ #include "imgstore.h" #include "notify.h" #include "prefs.h" -#include "prpl.h" +#include "protocol.h" #include "request.h" #include "signals.h" #include "util.h" @@ -544,7 +544,7 @@ const char *message, PurpleMessageFlags flags, time_t mtime) { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc = NULL; PurpleAccount *account; PurpleConversationUiOps *ops; @@ -594,10 +594,10 @@ } if (account != NULL) { - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account))); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); if (PURPLE_IS_IM_CONVERSATION(conv) || - !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { + !(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) { if (flags & PURPLE_MESSAGE_SEND) { b = purple_blist_find_buddy(account, @@ -955,22 +955,16 @@ gssize purple_conversation_get_max_message_size(PurpleConversation *conv) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), 0); - prpl = purple_connection_get_prpl( + protocol = purple_connection_get_protocol( purple_conversation_get_connection(conv)); - g_return_val_if_fail(prpl != NULL, 0); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - g_return_val_if_fail(prpl_info != NULL, 0); + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), 0); - if (!PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_max_message_size)) - return 0; - - return prpl_info->get_max_message_size(conv); + return purple_protocol_client_iface_get_max_message_size(protocol, conv); } /**************************************************************************
--- a/libpurple/conversationtypes.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/conversationtypes.c Fri Jan 31 18:02:20 2014 +0530 @@ -480,19 +480,18 @@ { PurpleIMConversation *im = PURPLE_IM_CONVERSATION(object); PurpleConnection *gc = purple_conversation_get_connection(PURPLE_CONVERSATION(im)); - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; const char *name = purple_conversation_get_name(PURPLE_CONVERSATION(im)); if (gc != NULL) { /* Still connected */ - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + protocol = purple_connection_get_protocol(gc); if (purple_prefs_get_bool("/purple/conversations/im/send_typing")) serv_send_typing(gc, name, PURPLE_IM_NOT_TYPING); - if (gc && prpl_info->convo_closed != NULL) - prpl_info->convo_closed(gc, name); + purple_protocol_client_iface_convo_closed(protocol, gc, name); } purple_im_conversation_stop_typing_timeout(im); @@ -867,7 +866,7 @@ PurpleChatConversationPrivate *priv; PurpleAccount *account; PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; GList *ul, *fl; GList *cbuddies = NULL; @@ -882,8 +881,8 @@ account = purple_conversation_get_account(conv); gc = purple_conversation_get_connection(conv); g_return_if_fail(PURPLE_IS_CONNECTION(gc)); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - g_return_if_fail(prpl_info != NULL); + protocol = purple_connection_get_protocol(gc); + g_return_if_fail(PURPLE_IS_PROTOCOL(protocol)); ul = users; fl = flags; @@ -894,7 +893,7 @@ PurpleChatUserFlags flag = GPOINTER_TO_INT(fl->data); const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL); - if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { + if(!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) { if (purple_strequal(priv->nick, purple_normalize(account, user))) { const char *alias2 = purple_account_get_private_alias(account); if (alias2 != NULL) @@ -968,7 +967,7 @@ PurpleConversationUiOps *ops; PurpleAccount *account; PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleChatUser *cb; PurpleChatUserFlags flags; PurpleChatConversationPrivate *priv; @@ -988,8 +987,8 @@ gc = purple_conversation_get_connection(conv); g_return_if_fail(PURPLE_IS_CONNECTION(gc)); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - g_return_if_fail(prpl_info != NULL); + protocol = purple_connection_get_protocol(gc); + g_return_if_fail(PURPLE_IS_PROTOCOL(protocol)); if (purple_strequal(priv->nick, purple_normalize(account, old_user))) { const char *alias; @@ -997,7 +996,7 @@ /* Note this for later. */ is_me = TRUE; - if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { + if(!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) { alias = purple_account_get_private_alias(account); if (alias != NULL) new_alias = alias; @@ -1008,7 +1007,7 @@ new_alias = display_name; } } - } else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { + } else if (!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) { PurpleBuddy *buddy; if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), new_user)) != NULL) new_alias = purple_buddy_get_contact_alias(buddy); @@ -1056,7 +1055,7 @@ char *escaped; char *escaped2; - if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { + if (!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) { PurpleBuddy *buddy; if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), old_user)) != NULL) @@ -1094,7 +1093,7 @@ { PurpleConversation *conv; PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleConversationUiOps *ops; PurpleChatUser *cb; PurpleChatConversationPrivate *priv; @@ -1110,8 +1109,8 @@ gc = purple_conversation_get_connection(conv); g_return_if_fail(PURPLE_IS_CONNECTION(gc)); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - g_return_if_fail(prpl_info != NULL); + protocol = purple_connection_get_protocol(gc); + g_return_if_fail(PURPLE_IS_PROTOCOL(protocol)); ops = purple_conversation_get_ui_ops(conv); @@ -1136,7 +1135,7 @@ char *alias_esc; char *tmp; - if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { + if (!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) { PurpleBuddy *buddy; if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), user)) != NULL) @@ -1498,7 +1497,7 @@ * which leads to two calls here.. We can't just return after * this, because then it'll return on the next pass. So, since * serv_got_chat_left(), which is eventually called from the - * prpl that serv_chat_leave() calls, removes this conversation + * protocol that serv_chat_leave() calls, removes this conversation * from the gc's buddy_chats list, we're going to check to see * if this exists in the list. If so, we want to return after * calling this, because it'll be called again. If not, fall @@ -1518,8 +1517,8 @@ #endif /* * Instead of all of that, lets just close the window when - * the user tells us to, and let the prpl deal with the - * internals on it's own time. Don't do this if the prpl already + * the user tells us to, and let the protocol deal with the + * internals on it's own time. Don't do this if the protocol already * knows it left the chat. */ if (!purple_chat_conversation_has_left(chat)) @@ -1647,7 +1646,7 @@ "will be removed in libpurple 3.0.0", name); } else { /* - * This hack is necessary because some prpls (MSN) have unnamed chats + * This hack is necessary because some protocols (MSN) have unnamed chats * that all use the same name. A PurpleConversation for one of those * is only ever re-used if the user has left, so calls to * purple_conversation_new need to fall-through to creating a new
--- a/libpurple/core.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/core.c Fri Jan 31 18:02:20 2014 +0530 @@ -40,7 +40,7 @@ #include "keyring.h" #include "network.h" #include "notify.h" -#include "plugin.h" +#include "plugins.h" #include "pounce.h" #include "prefs.h" #include "proxy.h" @@ -75,7 +75,8 @@ static PurpleCoreUiOps *_ops = NULL; static PurpleCore *_core = NULL; -STATIC_PROTO_INIT +STATIC_PROTO_LOAD +STATIC_PROTO_UNLOAD static void purple_core_print_version(void) @@ -166,17 +167,16 @@ #endif purple_cmds_init(); + purple_protocols_init(); + + /* Load all static protocols. */ + static_proto_load(); /* Since plugins get probed so early we should probably initialize their * subsystem right away too. */ purple_plugins_init(); - /* Initialize all static protocols. */ - static_proto_init(); - - purple_plugins_probe(G_MODULE_SUFFIX); - purple_keyring_init(); /* before accounts */ purple_theme_manager_init(); @@ -253,11 +253,6 @@ /* The SSL plugins must be uninit before they're unloaded */ purple_ssl_uninit(); - /* Unload all non-loader, non-prpl plugins before shutting down - * subsystems. */ - purple_debug_info("main", "Unloading normal plugins\n"); - purple_plugins_unload(PURPLE_PLUGIN_STANDARD); - /* Save .xml files, remove signals, etc. */ purple_smileys_uninit(); purple_http_uninit(); @@ -280,11 +275,6 @@ purple_imgstore_uninit(); purple_network_uninit(); - /* Everything after unloading all plugins must not fail if prpls aren't - * around */ - purple_debug_info("main", "Unloading all plugins\n"); - purple_plugins_destroy_all(); - ops = purple_core_get_ui_ops(); if (ops != NULL && ops->quit != NULL) ops->quit(); @@ -292,6 +282,10 @@ /* Everything after prefs_uninit must not try to read any prefs */ purple_prefs_uninit(); purple_plugins_uninit(); + + static_proto_unload(); + purple_protocols_uninit(); + #ifdef HAVE_DBUS purple_dbus_uninit(); #endif
--- a/libpurple/dbus-analyze-functions.py Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/dbus-analyze-functions.py Fri Jan 31 18:02:20 2014 +0530 @@ -31,6 +31,9 @@ "purple_account_set_register_callback", "purple_account_unregister", + # Similar to the above: + "purple_protocol_server_iface_unregister_user", + # Similar to the above, again "purple_menu_action_new", "purple_menu_action_set_callback", @@ -65,7 +68,7 @@ ] # This is a list of functions that return a GList* or GSList* that should -# not be freed. Ideally, this information should be obtained from the Doxygen +# not be freed. Ideally, this information should be obtained from the gtk-doc # documentation at some point. constlists = [ "purple_account_get_status_types",
--- a/libpurple/dbus-define-api.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/dbus-define-api.h Fri Jan 31 18:02:20 2014 +0530 @@ -1,3 +1,4 @@ +#ifndef __GI_SCANNER__ /* hide this file from g-ir-scanner */ #error "This file is not a valid C code and is not intended to be compiled." /* This file contains some of the macros from other header files as @@ -12,3 +13,4 @@ gboolean PURPLE_CONNECTION_IS_CONNECTED(PurpleConnection *connection); gboolean PURPLE_CONNECTION_IS_VALID(PurpleConnection *connection); +#endif /* __GI_SCANNER__ */
--- a/libpurple/dbus-server.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/dbus-server.h Fri Jan 31 18:02:20 2014 +0530 @@ -80,7 +80,8 @@ if (purple_dbus_get_init_error() != NULL) \ { \ gchar *title; \ - title = g_strdup_printf("Unable to Load %s Plugin", plugin->info->name); \ + title = g_strdup_printf("Unable to Load %s Plugin", \ + purple_plugin_info_get_name(purple_plugin_get_info(plugin))); \ purple_notify_error(NULL, title, \ _("Purple's D-BUS server is not running for the reason listed below"), \ _(purple_dbus_get_init_error()), NULL); \
--- a/libpurple/example/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/example/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -7,6 +7,7 @@ $(DBUS_LIBS) \ $(INTLLIBS) \ $(GLIB_LIBS) \ + $(GPLUGIN_LIBS) \ $(LIBXML_LIBS) \ $(top_builddir)/libpurple/libpurple.la @@ -21,5 +22,6 @@ -I$(top_srcdir) \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DBUS_CFLAGS) \ $(LIBXML_CFLAGS)
--- a/libpurple/example/nullclient.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/example/nullclient.c Fri Jan 31 18:02:20 2014 +0530 @@ -200,11 +200,6 @@ * copy this verbatim. */ purple_eventloop_set_ui_ops(&glib_eventloops); - /* Set path to search for plugins. The core (libpurple) takes care of loading the - * core-plugins, which includes the protocol-plugins. So it is not essential to add - * any path here, but it might be desired, especially for ui-specific plugins. */ - purple_plugins_add_search_path(CUSTOM_PLUGIN_PATH); - /* Now that all the essential stuff has been set, let's try to init the core. It's * necessary to provide a non-NULL name for the current ui to the core. This name * is used by stuff that depends on this ui, for example the ui-specific plugins. */ @@ -216,6 +211,12 @@ abort(); } + /* Set path to search for plugins. The core (libpurple) takes care of loading the + * core-plugins, which includes the in-tree protocols. So it is not essential to add + * any path here, but it might be desired, especially for ui-specific plugins. */ + purple_plugins_add_search_path(CUSTOM_PLUGIN_PATH); + purple_plugins_refresh(); + /* Load the preferences. */ purple_prefs_load(); @@ -241,10 +242,10 @@ int main(int argc, char *argv[]) { - GList *iter; + GList *list, *iter; int i, num; GList *names = NULL; - const char *prpl = NULL; + const char *protocol = NULL; char name[128]; char *password; GMainLoop *loop = g_main_loop_new(NULL, FALSE); @@ -265,15 +266,16 @@ printf("libpurple initialized.\n"); - iter = purple_plugins_get_protocols(); - for (i = 0; iter; iter = iter->next) { - PurplePlugin *plugin = iter->data; - PurplePluginInfo *info = plugin->info; - if (info && info->name) { - printf("\t%d: %s\n", i++, info->name); - names = g_list_append(names, (gpointer)info->id); + list = purple_protocols_get_all(); + for (i = 0, iter = list; iter; iter = iter->next) { + PurpleProtocol *protocol = iter->data; + if (protocol && purple_protocol_get_name(protocol)) { + printf("\t%d: %s\n", i++, purple_protocol_get_name(protocol)); + names = g_list_append(names, (gpointer)purple_protocol_get_id(protocol)); } } + g_list_free(list); + printf("Select the protocol [0-%d]: ", i-1); res = fgets(name, sizeof(name), stdin); if (!res) { @@ -281,8 +283,8 @@ abort(); } if (sscanf(name, "%d", &num) == 1) - prpl = g_list_nth_data(names, num); - if (!prpl) { + protocol = g_list_nth_data(names, num); + if (!protocol) { fprintf(stderr, "Failed to gets protocol."); abort(); } @@ -296,7 +298,7 @@ name[strlen(name) - 1] = 0; /* strip the \n at the end */ /* Create the account */ - account = purple_account_new(name, prpl); + account = purple_account_new(name, protocol); /* Get the password for the account */ password = getpass("Password: ");
--- a/libpurple/internal.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/internal.h Fri Jan 31 18:02:20 2014 +0530 @@ -99,8 +99,6 @@ #include <langinfo.h> #endif -#include <gmodule.h> - #ifdef PURPLE_PLUGINS # ifdef HAVE_DLFCN_H # include <dlfcn.h>
--- a/libpurple/keyring.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/keyring.c Fri Jan 31 18:02:20 2014 +0530 @@ -33,6 +33,7 @@ #include "debug.h" #include "internal.h" #include "dbus-maybe.h" +#include "plugins.h" struct _PurpleKeyring { @@ -1230,7 +1231,7 @@ purple_keyring_init(void) { const gchar *touse; - GList *it; + GList *plugins, *it; purple_keyring_keyrings = NULL; purple_keyring_inuse = NULL; @@ -1284,24 +1285,25 @@ purple_keyring_pref_connect(); - for (it = purple_plugins_get_all(); it != NULL; it = it->next) + plugins = purple_plugins_find_all(); + for (it = plugins; it != NULL; it = it->next) { - PurplePlugin *plugin = (PurplePlugin *)it->data; + PurplePlugin *plugin = PURPLE_PLUGIN(it->data); + PurplePluginInfo *info = purple_plugin_get_info(plugin); - if (plugin->info == NULL || plugin->info->id == NULL) - continue; - if (strncmp(plugin->info->id, "keyring-", 8) != 0) + if (strncmp(purple_plugin_info_get_id(info), "keyring-", 8) != 0) continue; if (purple_plugin_is_loaded(plugin)) continue; - if (purple_plugin_load(plugin)) + if (purple_plugin_load(plugin, NULL)) { purple_keyring_loaded_plugins = g_list_append( purple_keyring_loaded_plugins, plugin); } } + g_list_free(plugins); if (purple_keyring_inuse == NULL) purple_debug_error("keyring", "Selected keyring failed to load\n"); @@ -1330,10 +1332,10 @@ for (it = g_list_first(purple_keyring_loaded_plugins); it != NULL; it = g_list_next(it)) { - PurplePlugin *plugin = (PurplePlugin *)it->data; + PurplePlugin *plugin = PURPLE_PLUGIN(it->data); if (g_list_find(purple_plugins_get_loaded(), plugin) == NULL) continue; - purple_plugin_unload(plugin); + purple_plugin_unload(plugin, NULL); } g_list_free(purple_keyring_loaded_plugins); purple_keyring_loaded_plugins = NULL;
--- a/libpurple/log.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/log.c Fri Jan 31 18:02:20 2014 +0530 @@ -336,18 +336,17 @@ char * purple_log_get_log_dir(PurpleLogType type, const char *name, PurpleAccount *account) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; - const char *prpl_name; + PurpleProtocol *protocol; + const char *protocol_name; char *acct_name; const char *target; char *dir; - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - if (!prpl) + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if (!protocol) return NULL; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - prpl_name = prpl_info->list_icon(account, NULL); + + protocol_name = purple_protocol_class_list_icon(protocol, account, NULL); acct_name = g_strdup(purple_escape_filename(purple_normalize(account, purple_account_get_username(account)))); @@ -362,7 +361,7 @@ target = purple_escape_filename(purple_normalize(account, name)); } - dir = g_build_filename(purple_user_dir(), "logs", prpl_name, acct_name, target, NULL); + dir = g_build_filename(purple_user_dir(), "logs", protocol_name, acct_name, target, NULL); g_free(acct_name); @@ -1099,20 +1098,18 @@ } /* Using g_strdup() to cover the one-in-a-million chance that a - * prpl's list_icon function uses purple_unescape_filename(). */ + * protocol's list_icon function uses purple_unescape_filename(). */ protocol_unescaped = g_strdup(purple_unescape_filename(protocol)); /* Find all the accounts for protocol. */ for (account_iter = purple_accounts_get_all() ; account_iter != NULL ; account_iter = account_iter->next) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; - prpl = purple_find_prpl(purple_account_get_protocol_id((PurpleAccount *)account_iter->data)); - if (!prpl) + protocol = purple_protocols_find(purple_account_get_protocol_id((PurpleAccount *)account_iter->data)); + if (!protocol) continue; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (purple_strequal(protocol_unescaped, prpl_info->list_icon((PurpleAccount *)account_iter->data, NULL))) + if (purple_strequal(protocol_unescaped, purple_protocol_class_list_icon(protocol, (PurpleAccount *)account_iter->data, NULL))) accounts = g_list_prepend(accounts, account_iter->data); } g_free(protocol_unescaped); @@ -1341,7 +1338,7 @@ date = purple_utf8_strftime("%Y-%m-%d %H:%M:%S", localtime(&log->time)); fprintf(log->logger_data, "<conversation time='%s' screenname='%s' protocol='%s'>\n", - date, log->name, prpl); + date, log->name, protocol); } /* if we can't write to the file, give up before we hurt ourselves */ @@ -1406,13 +1403,13 @@ char *date; char *header; char *escaped_from; - PurplePlugin *plugin = purple_find_prpl(purple_account_get_protocol_id(log->account)); + PurpleProtocol *protocol = + purple_protocols_find(purple_account_get_protocol_id(log->account)); PurpleLogCommonLoggerData *data = log->logger_data; gsize written = 0; if(!data) { - const char *prpl = - PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL); + const char *proto = purple_protocol_class_list_icon(protocol, log->account, NULL); const char *date; purple_log_common_writer(log, ".html"); @@ -1429,10 +1426,10 @@ written += fprintf(data->file, "<title>"); if (log->type == PURPLE_LOG_SYSTEM) header = g_strdup_printf("System log for account %s (%s) connected at %s", - purple_account_get_username(log->account), prpl, date); + purple_account_get_username(log->account), proto, date); else header = g_strdup_printf("Conversation with %s at %s on %s (%s)", - log->name, date, purple_account_get_username(log->account), prpl); + log->name, date, purple_account_get_username(log->account), proto); written += fprintf(data->file, "%s", header); written += fprintf(data->file, "</title></head><body>"); @@ -1561,7 +1558,8 @@ const char *from, time_t time, const char *message) { char *date; - PurplePlugin *plugin = purple_find_prpl(purple_account_get_protocol_id(log->account)); + PurpleProtocol *protocol = + purple_protocols_find(purple_account_get_protocol_id(log->account)); PurpleLogCommonLoggerData *data = log->logger_data; char *stripped = NULL; @@ -1572,8 +1570,7 @@ * creating a new file there would result in empty files in the case * that you open a convo with someone, but don't say anything. */ - const char *prpl = - PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL); + const char *proto = purple_protocol_class_list_icon(protocol, log->account, NULL); purple_log_common_writer(log, ".txt"); data = log->logger_data; @@ -1584,12 +1581,12 @@ if (log->type == PURPLE_LOG_SYSTEM) written += fprintf(data->file, "System log for account %s (%s) connected at %s\n", - purple_account_get_username(log->account), prpl, + purple_account_get_username(log->account), proto, purple_date_format_full(localtime(&log->time))); else written += fprintf(data->file, "Conversation with %s at %s on %s (%s)\n", log->name, purple_date_format_full(localtime(&log->time)), - purple_account_get_username(log->account), prpl); + purple_account_get_username(log->account), proto); } /* if we can't write to the file, give up before we hurt ourselves */
--- a/libpurple/log.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/log.h Fri Jan 31 18:02:20 2014 +0530 @@ -51,7 +51,7 @@ } PurpleLogReadFlags; #include "account.h" -#include "conversation.h" +#include "conversations.h" typedef void (*PurpleLogSetCallback) (GHashTable *sets, PurpleLogSet *set);
--- a/libpurple/media.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/media.c Fri Jan 31 18:02:20 2014 +0530 @@ -78,7 +78,7 @@ PurpleMediaBackend *backend; gchar *conference_type; gboolean initiator; - gpointer prpl_data; + gpointer protocol_data; GHashTable *sessions; /* PurpleMediaSession table */ GList *participants; @@ -135,7 +135,7 @@ PROP_ACCOUNT, PROP_CONFERENCE_TYPE, PROP_INITIATOR, - PROP_PRPL_DATA, + PROP_PROTOCOL_DATA, }; #endif @@ -221,10 +221,10 @@ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property(gobject_class, PROP_PRPL_DATA, - g_param_spec_pointer("prpl-data", + g_object_class_install_property(gobject_class, PROP_PROTOCOL_DATA, + g_param_spec_pointer("protocol-data", "gpointer", - "Data the prpl plugin set on the media session.", + "Data the protocol set on the media session.", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); purple_media_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass), @@ -399,8 +399,8 @@ case PROP_INITIATOR: media->priv->initiator = g_value_get_boolean(value); break; - case PROP_PRPL_DATA: - media->priv->prpl_data = g_value_get_pointer(value); + case PROP_PROTOCOL_DATA: + media->priv->protocol_data = g_value_get_pointer(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -433,8 +433,8 @@ case PROP_INITIATOR: g_value_set_boolean(value, media->priv->initiator); break; - case PROP_PRPL_DATA: - g_value_set_pointer(value, media->priv->prpl_data); + case PROP_PROTOCOL_DATA: + g_value_set_pointer(value, media->priv->protocol_data); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -593,24 +593,24 @@ } gpointer -purple_media_get_prpl_data(PurpleMedia *media) +purple_media_get_protocol_data(PurpleMedia *media) { #ifdef USE_VV - gpointer prpl_data; + gpointer protocol_data; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); - g_object_get(G_OBJECT(media), "prpl-data", &prpl_data, NULL); - return prpl_data; + g_object_get(G_OBJECT(media), "protocol-data", &protocol_data, NULL); + return protocol_data; #else return NULL; #endif } void -purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data) +purple_media_set_protocol_data(PurpleMedia *media, gpointer protocol_data) { #ifdef USE_VV g_return_if_fail(PURPLE_IS_MEDIA(media)); - g_object_set(G_OBJECT(media), "prpl-data", prpl_data, NULL); + g_object_set(G_OBJECT(media), "protocol-data", protocol_data, NULL); #endif }
--- a/libpurple/media.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/media.h Fri Jan 31 18:02:20 2014 +0530 @@ -104,21 +104,21 @@ PurpleAccount *purple_media_get_account(PurpleMedia *media); /** - * Gets the prpl data from the media session. + * Gets the protocol data from the media session. * - * @param media The media session to retrieve the prpl data from. + * @param media The media session to retrieve the protocol data from. * - * @return The prpl data retrieved. + * @return The protocol data retrieved. */ -gpointer purple_media_get_prpl_data(PurpleMedia *media); +gpointer purple_media_get_protocol_data(PurpleMedia *media); /** - * Sets the prpl data on the media session. + * Sets the protocol data on the media session. * - * @param media The media session to set the prpl data on. - * @param prpl_data The data to set on the media session. + * @param media The media session to set the protocol data on. + * @param protocol_data The data to set on the media session. */ -void purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data); +void purple_media_set_protocol_data(PurpleMedia *media, gpointer protocol_data); /** * Signals an error in the media session.
--- a/libpurple/media/backend-fs2.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/media/backend-fs2.c Fri Jan 31 18:02:20 2014 +0530 @@ -2034,10 +2034,10 @@ PurpleMediaBackendFs2Session *session; PurpleMediaBackendFs2Stream *stream; FsParticipant *participant; - /* check if the prpl has already specified a relay-info + /* check if the protocol has already specified a relay-info we need to do this to allow them to override when using non-standard TURN modes, like Google f.ex. */ - gboolean got_turn_from_prpl = FALSE; + gboolean got_turn_from_protocol = FALSE; guint i; session = get_session(self, sess_id); @@ -2077,7 +2077,7 @@ for (i = 0 ; i < num_params ; i++) { if (purple_strequal(params[i].name, "relay-info")) { - got_turn_from_prpl = TRUE; + got_turn_from_protocol = TRUE; break; } } @@ -2101,7 +2101,7 @@ ++_num_params; } - if (turn_ip && !strcmp("nice", transmitter) && !got_turn_from_prpl) { + if (turn_ip && !strcmp("nice", transmitter) && !got_turn_from_protocol) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS GValueArray *relay_info = g_value_array_new(0); G_GNUC_END_IGNORE_DEPRECATIONS
--- a/libpurple/media/backend-iface.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/media/backend-iface.c Fri Jan 31 18:02:20 2014 +0530 @@ -56,13 +56,18 @@ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property(iface, - g_param_spec_object("media", - "Purple Media", +#ifdef USE_VV + g_param_spec_object( +#else + g_param_spec_pointer( +#endif + "media", "Purple Media", "The media object that this backend is bound to.", +#ifdef USE_VV PURPLE_TYPE_MEDIA, +#endif G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - purple_media_backend_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(iface), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
--- a/libpurple/mediamanager.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/mediamanager.c Fri Jan 31 18:02:20 2014 +0530 @@ -441,7 +441,7 @@ GstElement *src = gst_bin_get_by_name(GST_BIN(manager->priv->pipeline), id); if (src) { - GstElement *capsfilter = gst_bin_get_by_name(GST_BIN(src), "prpl_video_caps"); + GstElement *capsfilter = gst_bin_get_by_name(GST_BIN(src), "protocol_video_caps"); g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL); } @@ -515,7 +515,7 @@ GstElement *capsfilter; videoscale = gst_element_factory_make("videoscale", NULL); - capsfilter = gst_element_factory_make("capsfilter", "prpl_video_caps"); + capsfilter = gst_element_factory_make("capsfilter", "protocol_video_caps"); g_object_set(G_OBJECT(capsfilter), "caps", purple_media_manager_get_video_caps(manager), NULL);
--- a/libpurple/network.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/network.h Fri Jan 31 18:02:20 2014 +0530 @@ -224,7 +224,7 @@ * * This is what backs the --force-online command line argument in Pidgin, * for example. This is useful for offline testing, especially when - * combined with nullprpl. + * combined with nullprotocol. */ void purple_network_force_online(void);
--- a/libpurple/notify.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/notify.h Fri Jan 31 18:02:20 2014 +0530 @@ -55,8 +55,8 @@ typedef enum { PURPLE_NOTIFY_MESSAGE = 0, /**< Message notification. */ - PURPLE_NOTIFY_EMAIL, /**< Single email notification. */ - PURPLE_NOTIFY_EMAILS, /**< Multiple email notification. */ + PURPLE_NOTIFY_EMAIL, /**< Single email notification. */ + PURPLE_NOTIFY_EMAILS, /**< Multiple email notification. */ PURPLE_NOTIFY_FORMATTED, /**< Formatted text. */ PURPLE_NOTIFY_SEARCHRESULTS, /**< Buddy search results. */ PURPLE_NOTIFY_USERINFO, /**< Formatted userinfo text. */ @@ -733,7 +733,7 @@ /*@}*/ /**************************************************************************/ -/** @name Notify Subsystem */ +/** @name Notify Subsystem */ /**************************************************************************/ /*@{*/
--- a/libpurple/plugin.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1626 +0,0 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#define _PURPLE_PLUGIN_C_ - -#include "internal.h" - -#include "accountopt.h" -#include "core.h" -#include "dbus-maybe.h" -#include "debug.h" -#include "notify.h" -#include "prefs.h" -#include "prpl.h" -#include "request.h" -#include "signals.h" -#include "util.h" -#include "valgrind.h" -#include "version.h" - -typedef struct -{ - GHashTable *commands; - size_t command_count; - -} PurplePluginIpcInfo; - -typedef struct -{ - PurpleCallback func; - PurpleSignalMarshalFunc marshal; - - int num_params; - GType *param_types; - GType ret_type; - -} PurplePluginIpcCommand; - -static GList *search_paths = NULL; -static GList *plugins = NULL; -static GList *loaded_plugins = NULL; -static GList *protocol_plugins = NULL; -#ifdef PURPLE_PLUGINS -static GList *load_queue = NULL; -static GList *plugin_loaders = NULL; -static GList *plugins_to_disable = NULL; -#endif - -#ifdef PURPLE_PLUGINS - -static gboolean -has_file_extension(const char *filename, const char *ext) -{ - int len, extlen; - - if (filename == NULL || *filename == '\0' || ext == NULL) - return 0; - - extlen = strlen(ext); - len = strlen(filename) - extlen; - - if (len < 0) - return 0; - - return (strncmp(filename + len, ext, extlen) == 0); -} - -static gboolean -is_native(const char *filename) -{ - const char *last_period; - - last_period = strrchr(filename, '.'); - if (last_period == NULL) - return FALSE; - - return !(strcmp(last_period, ".dll") & - strcmp(last_period, ".sl") & - strcmp(last_period, ".so")); -} - -static char * -purple_plugin_get_basename(const char *filename) -{ - const char *basename; - const char *last_period; - - basename = strrchr(filename, G_DIR_SEPARATOR); - if (basename != NULL) - basename++; - else - basename = filename; - - if (is_native(basename) && - ((last_period = strrchr(basename, '.')) != NULL)) - return g_strndup(basename, (last_period - basename)); - - return g_strdup(basename); -} - -static gboolean -loader_supports_file(PurplePlugin *loader, const char *filename) -{ - GList *exts; - - for (exts = PURPLE_PLUGIN_LOADER_INFO(loader)->exts; exts != NULL; exts = exts->next) { - if (has_file_extension(filename, (char *)exts->data)) { - return TRUE; - } - } - - return FALSE; -} - -static PurplePlugin * -find_loader_for_plugin(const PurplePlugin *plugin) -{ - PurplePlugin *loader; - GList *l; - - if (plugin->path == NULL) - return NULL; - - for (l = purple_plugins_get_loaded(); l != NULL; l = l->next) { - loader = l->data; - - if (loader->info->type == PURPLE_PLUGIN_LOADER && - loader_supports_file(loader, plugin->path)) { - - return loader; - } - - loader = NULL; - } - - return NULL; -} - -#endif /* PURPLE_PLUGINS */ - -/** - * Negative if a before b, 0 if equal, positive if a after b. - */ -static gint -compare_prpl(PurplePlugin *a, PurplePlugin *b) -{ - if(PURPLE_IS_PROTOCOL_PLUGIN(a)) { - if(PURPLE_IS_PROTOCOL_PLUGIN(b)) - return strcmp(a->info->name, b->info->name); - else - return -1; - } else { - if(PURPLE_IS_PROTOCOL_PLUGIN(b)) - return 1; - else - return 0; - } -} - -PurplePlugin * -purple_plugin_new(gboolean native, const char *path) -{ - PurplePlugin *plugin; - - plugin = g_new0(PurplePlugin, 1); - - plugin->native_plugin = native; - plugin->path = g_strdup(path); - - PURPLE_DBUS_REGISTER_POINTER(plugin, PurplePlugin); - - return plugin; -} - -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 */ -} - -#ifdef PURPLE_PLUGINS -static gint -compare_plugins(gconstpointer a, gconstpointer b) -{ - const PurplePlugin *plugina = a; - const PurplePlugin *pluginb = b; - - return strcmp(plugina->info->name, pluginb->info->name); -} -#endif /* PURPLE_PLUGINS */ - -gboolean -purple_plugin_load(PurplePlugin *plugin) -{ -#ifdef PURPLE_PLUGINS - GList *dep_list = NULL; - GList *l; - - g_return_val_if_fail(plugin != NULL, FALSE); - - if (purple_plugin_is_loaded(plugin)) - return TRUE; - - if (purple_plugin_is_unloadable(plugin)) - return FALSE; - - g_return_val_if_fail(plugin->error == NULL, FALSE); - - /* - * Go through the list of the plugin's dependencies. - * - * First pass: Make sure all the plugins needed are probed. - */ - for (l = plugin->info->dependencies; l != NULL; l = l->next) - { - const char *dep_name = (const char *)l->data; - PurplePlugin *dep_plugin; - - dep_plugin = purple_plugins_find_with_id(dep_name); - - if (dep_plugin == NULL) - { - char *tmp; - - tmp = g_strdup_printf(_("The required plugin %s was not found. " - "Please install this plugin and try again."), - dep_name); - - purple_notify_error(NULL, NULL, - _("Unable to load the plugin"), tmp, NULL); - g_free(tmp); - - g_list_free(dep_list); - - return FALSE; - } - - dep_list = g_list_append(dep_list, dep_plugin); - } - - /* Second pass: load all the required plugins. */ - for (l = dep_list; l != NULL; l = l->next) - { - PurplePlugin *dep_plugin = (PurplePlugin *)l->data; - - if (!purple_plugin_is_loaded(dep_plugin)) - { - if (!purple_plugin_load(dep_plugin)) - { - char *tmp; - - tmp = g_strdup_printf(_("The required plugin %s was unable to load."), - plugin->info->name); - - purple_notify_error(NULL, NULL, - _("Unable to load your plugin."), tmp, NULL); - g_free(tmp); - - g_list_free(dep_list); - - return FALSE; - } - } - } - - /* Third pass: note that other plugins are dependencies of this plugin. - * This is done separately in case we had to bail out earlier. */ - for (l = dep_list; l != NULL; l = l->next) - { - PurplePlugin *dep_plugin = (PurplePlugin *)l->data; - dep_plugin->dependent_plugins = g_list_prepend(dep_plugin->dependent_plugins, (gpointer)plugin->info->id); - } - - g_list_free(dep_list); - - if (plugin->native_plugin) - { - if (plugin->info->load != NULL && !plugin->info->load(plugin)) - return FALSE; - } - else { - PurplePlugin *loader; - PurplePluginLoaderInfo *loader_info; - - loader = find_loader_for_plugin(plugin); - - if (loader == NULL) - return FALSE; - - loader_info = PURPLE_PLUGIN_LOADER_INFO(loader); - - if (loader_info->load != NULL) - { - if (!loader_info->load(plugin)) - return FALSE; - } - } - - loaded_plugins = g_list_insert_sorted(loaded_plugins, plugin, compare_plugins); - - plugin->loaded = TRUE; - - purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin); - - return TRUE; - -#else - return TRUE; -#endif /* !PURPLE_PLUGINS */ -} - -gboolean -purple_plugin_unload(PurplePlugin *plugin) -{ -#ifdef PURPLE_PLUGINS - GList *l; - GList *ll; - - g_return_val_if_fail(plugin != NULL, FALSE); - g_return_val_if_fail(purple_plugin_is_loaded(plugin), FALSE); - - purple_debug_info("plugins", "Unloading plugin %s\n", plugin->info->name); - - /* Unload all plugins that depend on this plugin. */ - for (l = plugin->dependent_plugins; l != NULL; l = ll) { - const char * dep_name = (const char *)l->data; - PurplePlugin *dep_plugin; - - /* Store a pointer to the next element in the list. - * This is because we'll be modifying this list in the loop. */ - ll = l->next; - - dep_plugin = purple_plugins_find_with_id(dep_name); - - if (dep_plugin != NULL && purple_plugin_is_loaded(dep_plugin)) - { - if (!purple_plugin_unload(dep_plugin)) - { - g_free(plugin->error); - plugin->error = g_strdup_printf(_("%s requires %s, but it failed to unload."), - _(plugin->info->name), - _(dep_plugin->info->name)); - return FALSE; - } - else - { -#if 0 - /* This isn't necessary. This has already been done when unloading dep_plugin. */ - plugin->dependent_plugins = g_list_delete_link(plugin->dependent_plugins, l); -#endif - } - } - } - - /* Remove this plugin from each dependency's dependent_plugins list. */ - for (l = plugin->info->dependencies; l != NULL; l = l->next) - { - const char *dep_name = (const char *)l->data; - PurplePlugin *dependency; - - dependency = purple_plugins_find_with_id(dep_name); - - if (dependency != NULL) - dependency->dependent_plugins = g_list_remove(dependency->dependent_plugins, plugin->info->id); - else - purple_debug_error("plugins", "Unable to remove from dependency list for %s\n", dep_name); - } - - if (plugin->native_plugin) { - if (plugin->info->unload && !plugin->info->unload(plugin)) - return FALSE; - - if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) { - PurplePluginProtocolInfo *prpl_info; - GList *l; - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - - for (l = prpl_info->user_splits; l != NULL; l = l->next) - purple_account_user_split_destroy(l->data); - - for (l = prpl_info->protocol_options; l != NULL; l = l->next) - purple_account_option_destroy(l->data); - - if (prpl_info->user_splits != NULL) { - g_list_free(prpl_info->user_splits); - prpl_info->user_splits = NULL; - } - - if (prpl_info->protocol_options != NULL) { - g_list_free(prpl_info->protocol_options); - prpl_info->protocol_options = NULL; - } - } - } else { - PurplePlugin *loader; - PurplePluginLoaderInfo *loader_info; - - loader = find_loader_for_plugin(plugin); - - if (loader == NULL) - return FALSE; - - loader_info = PURPLE_PLUGIN_LOADER_INFO(loader); - - if (loader_info->unload && !loader_info->unload(plugin)) - return FALSE; - } - - /* cancel any pending dialogs the plugin has */ - purple_request_close_with_handle(plugin); - purple_notify_close_with_handle(plugin); - - purple_signals_disconnect_by_handle(plugin); - purple_plugin_ipc_unregister_all(plugin); - - loaded_plugins = g_list_remove(loaded_plugins, plugin); - if ((plugin->info != NULL) && PURPLE_IS_PROTOCOL_PLUGIN(plugin)) - protocol_plugins = g_list_remove(protocol_plugins, plugin); - plugins_to_disable = g_list_remove(plugins_to_disable, plugin); - plugin->loaded = FALSE; - - /* We wouldn't be anywhere near here if the plugin wasn't loaded, so - * if plugin->error is set at all, it had to be from a previous - * unload failure. It's obviously okay now. - */ - g_free(plugin->error); - plugin->error = NULL; - - purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin); - - purple_prefs_disconnect_by_handle(plugin); - - return TRUE; -#else - return TRUE; -#endif /* PURPLE_PLUGINS */ -} - -void -purple_plugin_disable(PurplePlugin *plugin) -{ -#ifdef PURPLE_PLUGINS - g_return_if_fail(plugin != NULL); - - if (!g_list_find(plugins_to_disable, plugin)) - plugins_to_disable = g_list_prepend(plugins_to_disable, plugin); -#endif -} - -gboolean -purple_plugin_reload(PurplePlugin *plugin) -{ -#ifdef PURPLE_PLUGINS - g_return_val_if_fail(plugin != NULL, FALSE); - g_return_val_if_fail(purple_plugin_is_loaded(plugin), FALSE); - - if (!purple_plugin_unload(plugin)) - return FALSE; - - if (!purple_plugin_load(plugin)) - return FALSE; - - return TRUE; -#else - return TRUE; -#endif /* !PURPLE_PLUGINS */ -} - -void -purple_plugin_destroy(PurplePlugin *plugin) -{ -#ifdef PURPLE_PLUGINS - g_return_if_fail(plugin != NULL); - - if (purple_plugin_is_loaded(plugin)) - purple_plugin_unload(plugin); - - plugins = g_list_remove(plugins, plugin); - - if (load_queue != NULL) - load_queue = g_list_remove(load_queue, plugin); - - /* true, this may leak a little memory if there is a major version - * mismatch, but it's a lot better than trying to free something - * we shouldn't, and crashing while trying to load an old plugin */ - if(plugin->info == NULL || plugin->info->magic != PURPLE_PLUGIN_MAGIC || - plugin->info->major_version != PURPLE_MAJOR_VERSION) - { - if(plugin->handle) - g_module_close(plugin->handle); - - g_free(plugin->path); - g_free(plugin->error); - - PURPLE_DBUS_UNREGISTER_POINTER(plugin); - - g_free(plugin); - return; - } - - if (plugin->info != NULL) - g_list_free(plugin->info->dependencies); - - if (plugin->native_plugin) - { - if (plugin->info != NULL && plugin->info->type == PURPLE_PLUGIN_LOADER) - { - PurplePluginLoaderInfo *loader_info; - GList *exts, *l, *next_l; - PurplePlugin *p2; - - loader_info = PURPLE_PLUGIN_LOADER_INFO(plugin); - - if (loader_info != NULL && loader_info->exts != NULL) - { - for (exts = PURPLE_PLUGIN_LOADER_INFO(plugin)->exts; - exts != NULL; - exts = exts->next) { - - for (l = purple_plugins_get_all(); l != NULL; l = next_l) - { - next_l = l->next; - - p2 = l->data; - - if (p2->path != NULL && - has_file_extension(p2->path, exts->data)) - { - purple_plugin_destroy(p2); - } - } - } - - g_list_free(loader_info->exts); - loader_info->exts = NULL; - } - - plugin_loaders = g_list_remove(plugin_loaders, plugin); - } - - if (plugin->info != NULL && plugin->info->destroy != NULL) - plugin->info->destroy(plugin); - - /* - * I find it extremely useful to do this when using valgrind, as - * it keeps all the plugins open, meaning that valgrind is able to - * resolve symbol names in leak traces from plugins. - */ - if (!g_getenv("PURPLE_LEAKCHECK_HELP") && !RUNNING_ON_VALGRIND) - { - if (plugin->handle != NULL) - g_module_close(plugin->handle); - } - } - else - { - PurplePlugin *loader; - PurplePluginLoaderInfo *loader_info; - - loader = find_loader_for_plugin(plugin); - - if (loader != NULL) - { - loader_info = PURPLE_PLUGIN_LOADER_INFO(loader); - - if (loader_info->destroy != NULL) - loader_info->destroy(plugin); - } - } - - g_free(plugin->path); - g_free(plugin->error); - - PURPLE_DBUS_UNREGISTER_POINTER(plugin); - - g_free(plugin); -#endif /* !PURPLE_PLUGINS */ -} - -gboolean -purple_plugin_is_loaded(const PurplePlugin *plugin) -{ - g_return_val_if_fail(plugin != NULL, FALSE); - - return plugin->loaded; -} - -gboolean -purple_plugin_is_unloadable(const PurplePlugin *plugin) -{ - g_return_val_if_fail(plugin != NULL, FALSE); - - return plugin->unloadable; -} - -const gchar * -purple_plugin_get_id(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return plugin->info->id; -} - -const gchar * -purple_plugin_get_name(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return _(plugin->info->name); -} - -const gchar * -purple_plugin_get_version(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return plugin->info->version; -} - -const gchar * -purple_plugin_get_summary(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return _(plugin->info->summary); -} - -const gchar * -purple_plugin_get_description(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return _(plugin->info->description); -} - -const gchar * -purple_plugin_get_author(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return _(plugin->info->author); -} - -const gchar * -purple_plugin_get_homepage(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return plugin->info->homepage; -} - -/************************************************************************** - * Plugin IPC - **************************************************************************/ -static void -destroy_ipc_info(void *data) -{ - PurplePluginIpcCommand *ipc_command = (PurplePluginIpcCommand *)data; - - g_free(ipc_command->param_types); - g_free(ipc_command); -} - -gboolean -purple_plugin_ipc_register(PurplePlugin *plugin, const char *command, - PurpleCallback func, PurpleSignalMarshalFunc marshal, - GType ret_type, int num_params, ...) -{ - PurplePluginIpcInfo *ipc_info; - PurplePluginIpcCommand *ipc_command; - - g_return_val_if_fail(plugin != NULL, FALSE); - g_return_val_if_fail(command != NULL, FALSE); - g_return_val_if_fail(func != NULL, FALSE); - g_return_val_if_fail(marshal != NULL, FALSE); - - if (plugin->ipc_data == NULL) - { - ipc_info = plugin->ipc_data = g_new0(PurplePluginIpcInfo, 1); - ipc_info->commands = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, destroy_ipc_info); - } - else - ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data; - - ipc_command = g_new0(PurplePluginIpcCommand, 1); - ipc_command->func = func; - ipc_command->marshal = marshal; - ipc_command->num_params = num_params; - ipc_command->ret_type = ret_type; - - if (num_params > 0) - { - va_list args; - int i; - - ipc_command->param_types = g_new0(GType, num_params); - - va_start(args, num_params); - - for (i = 0; i < num_params; i++) - ipc_command->param_types[i] = va_arg(args, GType); - - va_end(args); - } - - g_hash_table_replace(ipc_info->commands, g_strdup(command), ipc_command); - - ipc_info->command_count++; - - return TRUE; -} - -void -purple_plugin_ipc_unregister(PurplePlugin *plugin, const char *command) -{ - PurplePluginIpcInfo *ipc_info; - - g_return_if_fail(plugin != NULL); - g_return_if_fail(command != NULL); - - ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data; - - if (ipc_info == NULL || - g_hash_table_lookup(ipc_info->commands, command) == NULL) - { - purple_debug_error("plugins", - "IPC command '%s' was not registered for plugin %s\n", - command, plugin->info->name); - return; - } - - g_hash_table_remove(ipc_info->commands, command); - - ipc_info->command_count--; - - if (ipc_info->command_count == 0) - { - g_hash_table_destroy(ipc_info->commands); - g_free(ipc_info); - - plugin->ipc_data = NULL; - } -} - -void -purple_plugin_ipc_unregister_all(PurplePlugin *plugin) -{ - PurplePluginIpcInfo *ipc_info; - - g_return_if_fail(plugin != NULL); - - if (plugin->ipc_data == NULL) - return; /* Silently ignore it. */ - - ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data; - - g_hash_table_destroy(ipc_info->commands); - g_free(ipc_info); - - plugin->ipc_data = NULL; -} - -gboolean -purple_plugin_ipc_get_types(PurplePlugin *plugin, const char *command, - GType *ret_type, int *num_params, - GType **param_types) -{ - PurplePluginIpcInfo *ipc_info; - PurplePluginIpcCommand *ipc_command; - - g_return_val_if_fail(plugin != NULL, FALSE); - g_return_val_if_fail(command != NULL, FALSE); - - ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data; - - if (ipc_info == NULL || - (ipc_command = g_hash_table_lookup(ipc_info->commands, - command)) == NULL) - { - purple_debug_error("plugins", - "IPC command '%s' was not registered for plugin %s\n", - command, plugin->info->name); - - return FALSE; - } - - if (num_params != NULL) - *num_params = ipc_command->num_params; - - if (param_types != NULL) - *param_types = ipc_command->param_types; - - if (ret_type != NULL) - *ret_type = ipc_command->ret_type; - - return TRUE; -} - -void * -purple_plugin_ipc_call(PurplePlugin *plugin, const char *command, - gboolean *ok, ...) -{ - PurplePluginIpcInfo *ipc_info; - PurplePluginIpcCommand *ipc_command; - va_list args; - void *ret_value; - - if (ok != NULL) - *ok = FALSE; - - g_return_val_if_fail(plugin != NULL, NULL); - g_return_val_if_fail(command != NULL, NULL); - - ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data; - - if (ipc_info == NULL || - (ipc_command = g_hash_table_lookup(ipc_info->commands, - command)) == NULL) - { - purple_debug_error("plugins", - "IPC command '%s' was not registered for plugin %s\n", - command, plugin->info->name); - - return NULL; - } - - va_start(args, ok); - ipc_command->marshal(ipc_command->func, args, NULL, &ret_value); - va_end(args); - - if (ok != NULL) - *ok = TRUE; - - return ret_value; -} - -/************************************************************************** - * Plugins subsystem - **************************************************************************/ -void * -purple_plugins_get_handle(void) { - static int handle; - - return &handle; -} - -void -purple_plugins_init(void) { - void *handle = purple_plugins_get_handle(); - - purple_plugins_add_search_path(LIBDIR); - - purple_signal_register(handle, "plugin-load", - purple_marshal_VOID__POINTER, - G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN); - purple_signal_register(handle, "plugin-unload", - purple_marshal_VOID__POINTER, - G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN); -} - -void -purple_plugins_uninit(void) -{ - void *handle = purple_plugins_get_handle(); - - purple_signals_disconnect_by_handle(handle); - purple_signals_unregister_by_instance(handle); - - while (search_paths) { - g_free(search_paths->data); - search_paths = g_list_delete_link(search_paths, search_paths); - } -} - -/************************************************************************** - * Plugins API - **************************************************************************/ -void -purple_plugins_add_search_path(const char *path) -{ - g_return_if_fail(path != NULL); - - if (g_list_find_custom(search_paths, path, (GCompareFunc)strcmp)) - return; - - search_paths = g_list_append(search_paths, g_strdup(path)); -} - -GList * -purple_plugins_get_search_paths() -{ - return search_paths; -} - -void -purple_plugins_unload_all(void) -{ -#ifdef PURPLE_PLUGINS - - while (loaded_plugins != NULL) - purple_plugin_unload(loaded_plugins->data); - -#endif /* PURPLE_PLUGINS */ -} - -void -purple_plugins_unload(PurplePluginType type) -{ -#ifdef PURPLE_PLUGINS - GList *l; - - for (l = plugins; l; l = l->next) { - PurplePlugin *plugin = l->data; - if (plugin->info->type == type && purple_plugin_is_loaded(plugin)) - purple_plugin_unload(plugin); - } - -#endif /* PURPLE_PLUGINS */ -} - -void -purple_plugins_destroy_all(void) -{ -#ifdef PURPLE_PLUGINS - - while (plugins != NULL) - purple_plugin_destroy(plugins->data); - -#endif /* PURPLE_PLUGINS */ -} - -void -purple_plugins_save_loaded(const char *key) -{ -#ifdef PURPLE_PLUGINS - GList *pl; - GList *files = NULL; - - for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) { - PurplePlugin *plugin = pl->data; - - if (plugin->info->type != PURPLE_PLUGIN_PROTOCOL && - plugin->info->type != PURPLE_PLUGIN_LOADER && - !g_list_find(plugins_to_disable, plugin)) { - files = g_list_append(files, plugin->path); - } - } - - purple_prefs_set_path_list(key, files); - g_list_free(files); -#endif -} - -void -purple_plugins_load_saved(const char *key) -{ -#ifdef PURPLE_PLUGINS - GList *f, *files; - - g_return_if_fail(key != NULL); - - files = purple_prefs_get_path_list(key); - - for (f = files; f; f = f->next) - { - char *filename; - char *basename; - PurplePlugin *plugin; - - if (f->data == NULL) - continue; - - filename = f->data; - - /* - * We don't know if the filename uses Windows or Unix path - * separators (because people might be sharing a prefs.xml - * file across systems), so we find the last occurrence - * of either. - */ - basename = strrchr(filename, '/'); - if ((basename == NULL) || (basename < strrchr(filename, '\\'))) - basename = strrchr(filename, '\\'); - if (basename != NULL) - basename++; - - /* Strip the extension */ - if (basename) - basename = purple_plugin_get_basename(basename); - - if (((plugin = purple_plugins_find_with_filename(filename)) != NULL) || - (basename && (plugin = purple_plugins_find_with_basename(basename)) != NULL) || - ((plugin = purple_plugin_probe(filename)) != NULL)) - { - purple_debug_info("plugins", "Loading saved plugin %s\n", - plugin->path); - purple_plugin_load(plugin); - } - else - { - purple_debug_error("plugins", "Unable to find saved plugin %s\n", - filename); - } - - g_free(basename); - - g_free(f->data); - } - - g_list_free(files); -#endif /* PURPLE_PLUGINS */ -} - - -void -purple_plugins_probe(const char *ext) -{ -#ifdef PURPLE_PLUGINS - GDir *dir; - const gchar *file; - gchar *path; - PurplePlugin *plugin; - GList *cur; - const char *search_path; - - if (!g_module_supported()) - return; - - /* Probe plugins */ - for (cur = search_paths; cur != NULL; cur = cur->next) - { - search_path = cur->data; - - dir = g_dir_open(search_path, 0, NULL); - - if (dir != NULL) - { - while ((file = g_dir_read_name(dir)) != NULL) - { - path = g_build_filename(search_path, file, NULL); - - if (ext == NULL || has_file_extension(file, ext)) - purple_plugin_probe(path); - - g_free(path); - } - - g_dir_close(dir); - } - } - - /* See if we have any plugins waiting to load */ - while (load_queue != NULL) - { - plugin = (PurplePlugin *)load_queue->data; - - load_queue = g_list_remove(load_queue, plugin); - - if (plugin == NULL || plugin->info == NULL) - continue; - - if (plugin->info->type == PURPLE_PLUGIN_LOADER) - { - /* We'll just load this right now. */ - if (!purple_plugin_load(plugin)) - { - purple_plugin_destroy(plugin); - - continue; - } - - plugin_loaders = g_list_append(plugin_loaders, plugin); - - for (cur = PURPLE_PLUGIN_LOADER_INFO(plugin)->exts; - cur != NULL; - cur = cur->next) - { - purple_plugins_probe(cur->data); - } - } - else if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) - { - /* We'll just load this right now. */ - if (!purple_plugin_load(plugin)) - { - purple_plugin_destroy(plugin); - - continue; - } - - /* Make sure we don't load two PRPLs with the same name? */ - if (purple_find_prpl(plugin->info->id)) - { - /* Nothing to see here--move along, move along */ - purple_plugin_destroy(plugin); - - continue; - } - - protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin, - (GCompareFunc)compare_prpl); - } - } -#endif /* PURPLE_PLUGINS */ -} - -gboolean -purple_plugin_register(PurplePlugin *plugin) -{ - g_return_val_if_fail(plugin != NULL, FALSE); - - /* If this plugin has been registered already then exit */ - if (g_list_find(plugins, plugin)) - return TRUE; - - /* Ensure the plugin has the requisite information */ - if (plugin->info->type == PURPLE_PLUGIN_LOADER) - { - PurplePluginLoaderInfo *loader_info; - - loader_info = PURPLE_PLUGIN_LOADER_INFO(plugin); - - if (loader_info == NULL) - { - purple_debug_error("plugins", "%s is not loadable, loader plugin missing loader_info\n", - plugin->path); - return FALSE; - } - } - else if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) - { - PurplePluginProtocolInfo *prpl_info; - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - - if (prpl_info == NULL) - { - purple_debug_error("plugins", "%s is not loadable, protocol plugin missing prpl_info\n", - plugin->path); - return FALSE; - } - } - -#ifdef PURPLE_PLUGINS - /* This plugin should be probed and maybe loaded--add it to the queue */ - load_queue = g_list_append(load_queue, plugin); -#else - if (plugin->info != NULL) - { - if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) - protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin, - (GCompareFunc)compare_prpl); - if (plugin->info->load != NULL) - if (!plugin->info->load(plugin)) - return FALSE; - } -#endif - - plugins = g_list_append(plugins, plugin); - - return TRUE; -} - -gboolean -purple_plugins_enabled(void) -{ -#ifdef PURPLE_PLUGINS - return TRUE; -#else - return FALSE; -#endif -} - -PurplePlugin * -purple_plugins_find_with_name(const char *name) -{ - PurplePlugin *plugin; - GList *l; - - for (l = plugins; l != NULL; l = l->next) { - plugin = l->data; - - if (purple_strequal(plugin->info->name, name)) - return plugin; - } - - return NULL; -} - -PurplePlugin * -purple_plugins_find_with_filename(const char *filename) -{ - PurplePlugin *plugin; - GList *l; - - for (l = plugins; l != NULL; l = l->next) { - plugin = l->data; - - if (purple_strequal(plugin->path, filename)) - return plugin; - } - - return NULL; -} - -PurplePlugin * -purple_plugins_find_with_basename(const char *basename) -{ -#ifdef PURPLE_PLUGINS - PurplePlugin *plugin; - GList *l; - char *tmp; - - g_return_val_if_fail(basename != NULL, NULL); - - for (l = plugins; l != NULL; l = l->next) - { - plugin = (PurplePlugin *)l->data; - - if (plugin->path != NULL) { - tmp = purple_plugin_get_basename(plugin->path); - if (purple_strequal(tmp, basename)) - { - g_free(tmp); - return plugin; - } - g_free(tmp); - } - } - -#endif /* PURPLE_PLUGINS */ - - return NULL; -} - -PurplePlugin * -purple_plugins_find_with_id(const char *id) -{ - PurplePlugin *plugin; - GList *l; - - g_return_val_if_fail(id != NULL, NULL); - - for (l = plugins; l != NULL; l = l->next) - { - plugin = l->data; - - if (purple_strequal(plugin->info->id, id)) - return plugin; - } - - return NULL; -} - -GList * -purple_plugins_get_loaded(void) -{ - return loaded_plugins; -} - -GList * -purple_plugins_get_protocols(void) -{ - return protocol_plugins; -} - -GList * -purple_plugins_get_all(void) -{ - return plugins; -} - - -PurplePluginAction * -purple_plugin_action_new(const char* label, void (*callback)(PurplePluginAction *)) -{ - PurplePluginAction *act = g_new0(PurplePluginAction, 1); - - act->label = g_strdup(label); - act->callback = callback; - - return act; -} - -void -purple_plugin_action_free(PurplePluginAction *action) -{ - g_return_if_fail(action != NULL); - - g_free(action->label); - g_free(action); -} - -static PurplePlugin * -purple_plugin_copy(PurplePlugin *plugin) -{ - PurplePlugin *plugin_copy; - - g_return_val_if_fail(plugin != NULL, NULL); - - plugin_copy = g_new(PurplePlugin, 1); - *plugin_copy = *plugin; - - return plugin_copy; -} - -GType -purple_plugin_get_type(void) -{ - static GType type = 0; - - if (type == 0) { - type = g_boxed_type_register_static("PurplePlugin", - (GBoxedCopyFunc)purple_plugin_copy, - (GBoxedFreeFunc)g_free); - } - - return type; -}
--- a/libpurple/plugin.h Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,674 +0,0 @@ -/** - * @file plugin.h Plugin API - * @ingroup core - * @see @ref plugin-signals - * @see @ref plugin-ids - * @see @ref plugin-i18n - */ - -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _PURPLE_PLUGIN_H_ -#define _PURPLE_PLUGIN_H_ - -#include <glib.h> -#include <gmodule.h> -#include "signals.h" - -/** Returns the GType for the PurplePlugin boxed structure */ -#define PURPLE_TYPE_PLUGIN (purple_plugin_get_type()) - -/** @copydoc _PurplePlugin */ -typedef struct _PurplePlugin PurplePlugin; -/** @copydoc _PurplePluginInfo */ -typedef struct _PurplePluginInfo PurplePluginInfo; -/** @copydoc _PurplePluginUiInfo */ -typedef struct _PurplePluginUiInfo PurplePluginUiInfo; -/** @copydoc _PurplePluginLoaderInfo */ -typedef struct _PurplePluginLoaderInfo PurplePluginLoaderInfo; - -/** @copydoc _PurplePluginAction */ -typedef struct _PurplePluginAction PurplePluginAction; - -typedef int PurplePluginPriority; /**< Plugin priority. */ - -#include "pluginpref.h" - -/** - * Plugin types. - */ -typedef enum -{ - PURPLE_PLUGIN_UNKNOWN = -1, /**< Unknown type. */ - PURPLE_PLUGIN_STANDARD = 0, /**< Standard plugin. */ - PURPLE_PLUGIN_LOADER, /**< Loader plugin. */ - PURPLE_PLUGIN_PROTOCOL /**< Protocol plugin. */ - -} PurplePluginType; - -#define PURPLE_PRIORITY_DEFAULT 0 -#define PURPLE_PRIORITY_HIGHEST 9999 -#define PURPLE_PRIORITY_LOWEST -9999 - -#define PURPLE_PLUGIN_FLAG_INVISIBLE 0x01 - -#define PURPLE_PLUGIN_MAGIC 5 /* once we hit 6.0.0 I think we can remove this */ - -/** - * Detailed information about a plugin. - * - * This is used in the version 2.0 API and up. - */ -struct _PurplePluginInfo -{ - unsigned int magic; - unsigned int major_version; - unsigned int minor_version; - PurplePluginType type; - char *ui_requirement; - unsigned long flags; - GList *dependencies; - PurplePluginPriority priority; - - const char *id; - const char *name; - const char *version; - const char *summary; - const char *description; - const char *author; - const char *homepage; - - /** - * If a plugin defines a 'load' function, and it returns FALSE, - * then the plugin will not be loaded. - */ - gboolean (*load)(PurplePlugin *plugin); - gboolean (*unload)(PurplePlugin *plugin); - void (*destroy)(PurplePlugin *plugin); - - void *ui_info; /**< Used only by UI-specific plugins to build a preference screen with a custom UI */ - void *extra_info; - PurplePluginUiInfo *prefs_info; /**< Used by any plugin to display preferences. If #ui_info has been specified, this will be ignored. */ - - /** - * This callback has a different use depending on whether this - * plugin type is PURPLE_PLUGIN_STANDARD or PURPLE_PLUGIN_PROTOCOL. - * - * If PURPLE_PLUGIN_STANDARD then the list of actions will show up - * in the Tools menu, under a submenu with the name of the plugin. - * context will be NULL. - * - * If PURPLE_PLUGIN_PROTOCOL then the list of actions will show up - * in the Accounts menu, under a submenu with the name of the - * account. context will be set to the PurpleConnection for that - * account. This callback will only be called for online accounts. - */ - GList *(*actions)(PurplePlugin *plugin, gpointer context); - - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); -}; - -/** - * Extra information for loader plugins. - */ -struct _PurplePluginLoaderInfo -{ - GList *exts; - - gboolean (*probe)(PurplePlugin *plugin); - gboolean (*load)(PurplePlugin *plugin); - gboolean (*unload)(PurplePlugin *plugin); - void (*destroy)(PurplePlugin *plugin); - - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); -}; - -/** - * A plugin handle. - */ -struct _PurplePlugin -{ - gboolean native_plugin; /**< Native C plugin. */ - gboolean loaded; /**< The loaded state. */ - void *handle; /**< The module handle. */ - char *path; /**< The path to the plugin. */ - PurplePluginInfo *info; /**< The plugin information. */ - char *error; - void *ipc_data; /**< IPC data. */ - void *extra; /**< Plugin-specific data. */ - gboolean unloadable; /**< Unloadable */ - GList *dependent_plugins; /**< Plugins depending on this */ - gpointer ui_data; /**< The UI data. */ - - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); -}; - -#define PURPLE_PLUGIN_LOADER_INFO(plugin) \ - ((PurplePluginLoaderInfo *)(plugin)->info->extra_info) - -struct _PurplePluginUiInfo { - PurplePluginPrefFrame *(*get_plugin_pref_frame)(PurplePlugin *plugin); - gpointer (*get_plugin_pref_request)(PurplePlugin *plugin); - - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); -}; - -#define PURPLE_PLUGIN_HAS_PREF_FRAME(plugin) \ - ((plugin)->info != NULL && (plugin)->info->prefs_info != NULL) - -#define PURPLE_PLUGIN_UI_INFO(plugin) \ - ((PurplePluginUiInfo*)(plugin)->info->prefs_info) - - -/** - * The structure used in the actions member of PurplePluginInfo - */ -struct _PurplePluginAction { - char *label; - void (*callback)(PurplePluginAction *); - - /** set to the owning plugin */ - PurplePlugin *plugin; - - /** NULL for plugin actions menu, set to the PurpleConnection for - account actions menu */ - gpointer context; - - gpointer user_data; -}; - -#define PURPLE_PLUGIN_HAS_ACTIONS(plugin) \ - ((plugin)->info != NULL && (plugin)->info->actions != NULL) - -#define PURPLE_PLUGIN_ACTIONS(plugin, context) \ - (PURPLE_PLUGIN_HAS_ACTIONS(plugin)? \ - (plugin)->info->actions(plugin, context): NULL) - - -/** - * Handles the initialization of modules. - */ -#if !defined(PURPLE_PLUGINS) || defined(PURPLE_STATIC_PRPL) -# define _FUNC_NAME(x) purple_init_##x##_plugin -# define PURPLE_INIT_PLUGIN(pluginname, initfunc, plugininfo) \ - gboolean _FUNC_NAME(pluginname)(void);\ - gboolean _FUNC_NAME(pluginname)(void) { \ - PurplePlugin *plugin = purple_plugin_new(TRUE, NULL); \ - plugin->info = &(plugininfo); \ - initfunc((plugin)); \ - purple_plugin_load((plugin)); \ - return purple_plugin_register(plugin); \ - } -#else /* PURPLE_PLUGINS && !PURPLE_STATIC_PRPL */ -# define PURPLE_INIT_PLUGIN(pluginname, initfunc, plugininfo) \ - G_MODULE_EXPORT gboolean purple_init_plugin(PurplePlugin *plugin); \ - G_MODULE_EXPORT gboolean purple_init_plugin(PurplePlugin *plugin) { \ - plugin->info = &(plugininfo); \ - initfunc((plugin)); \ - return purple_plugin_register(plugin); \ - } -#endif - - -G_BEGIN_DECLS - -/**************************************************************************/ -/** @name Plugin API */ -/**************************************************************************/ -/*@{*/ - -/** - * Returns the GType for the PurplePlugin boxed structure. - * TODO Boxing of PurplePlugin is a temporary solution to having a GType for - * plugins. This should rather be a GObject instead of a GBoxed. - */ -GType purple_plugin_get_type(void); - -/** - * Creates a new plugin structure. - * - * @param native Whether or not the plugin is native. - * @param path The path to the plugin, or @c NULL if statically compiled. - * - * @return A new PurplePlugin structure. - */ -PurplePlugin *purple_plugin_new(gboolean native, const char *path); - -/** - * Probes a plugin, retrieving the information on it and adding it to the - * list of available plugins. - * - * @param filename The plugin's filename. - * - * @return The plugin handle. - * - * @see purple_plugin_load() - * @see purple_plugin_destroy() - */ -PurplePlugin *purple_plugin_probe(const char *filename); - -/** - * Registers a plugin and prepares it for loading. - * - * This shouldn't be called by anything but the internal module code. - * Plugins should use the PURPLE_INIT_PLUGIN() macro to register themselves - * with the core. - * - * @param plugin The plugin to register. - * - * @return @c TRUE if the plugin was registered successfully. Otherwise - * @c FALSE is returned (this happens if the plugin does not contain - * the necessary information). - */ -gboolean purple_plugin_register(PurplePlugin *plugin); - -/** - * Attempts to load a previously probed plugin. - * - * @param plugin The plugin to load. - * - * @return @c TRUE if successful, or @c FALSE otherwise. - * - * @see purple_plugin_reload() - * @see purple_plugin_unload() - */ -gboolean purple_plugin_load(PurplePlugin *plugin); - -/** - * Unloads the specified plugin. - * - * @param plugin The plugin handle. - * - * @return @c TRUE if successful, or @c FALSE otherwise. - * - * @see purple_plugin_load() - * @see purple_plugin_reload() - */ -gboolean purple_plugin_unload(PurplePlugin *plugin); - -/** - * Disable a plugin. - * - * This function adds the plugin to a list of plugins to "disable at the next - * startup" by excluding said plugins from the list of plugins to save. The - * UI needs to call purple_plugins_save_loaded() after calling this for it - * to have any effect. - */ -void purple_plugin_disable(PurplePlugin *plugin); - -/** - * Reloads a plugin. - * - * @param plugin The old plugin handle. - * - * @return @c TRUE if successful, or @c FALSE otherwise. - * - * @see purple_plugin_load() - * @see purple_plugin_unload() - */ -gboolean purple_plugin_reload(PurplePlugin *plugin); - -/** - * Unloads a plugin and destroys the structure from memory. - * - * @param plugin The plugin handle. - */ -void purple_plugin_destroy(PurplePlugin *plugin); - -/** - * Returns whether or not a plugin is currently loaded. - * - * @param plugin The plugin. - * - * @return @c TRUE if loaded, or @c FALSE otherwise. - */ -gboolean purple_plugin_is_loaded(const PurplePlugin *plugin); - -/** - * Returns whether or not a plugin is unloadable. - * - * If this returns @c TRUE, the plugin is guaranteed to not - * be loadable. However, a return value of @c FALSE does not - * guarantee the plugin is loadable. - * - * @param plugin The plugin. - * - * @return @c TRUE if the plugin is known to be unloadable,\ - * @c FALSE otherwise - */ -gboolean purple_plugin_is_unloadable(const PurplePlugin *plugin); - -/** - * Returns a plugin's id. - * - * @param plugin The plugin. - * - * @return The plugin's id. - */ -const gchar *purple_plugin_get_id(const PurplePlugin *plugin); - -/** - * Returns a plugin's name. - * - * @param plugin The plugin. - * - * @return THe name of the plugin, or @c NULL. - */ -const gchar *purple_plugin_get_name(const PurplePlugin *plugin); - -/** - * Returns a plugin's version. - * - * @param plugin The plugin. - * - * @return The plugin's version or @c NULL. - */ -const gchar *purple_plugin_get_version(const PurplePlugin *plugin); - -/** - * Returns a plugin's summary. - * - * @param plugin The plugin. - * - * @return The plugin's summary. - */ -const gchar *purple_plugin_get_summary(const PurplePlugin *plugin); - -/** - * Returns a plugin's description. - * - * @param plugin The plugin. - * - * @return The plugin's description. - */ -const gchar *purple_plugin_get_description(const PurplePlugin *plugin); - -/** - * Returns a plugin's author. - * - * @param plugin The plugin. - * - * @return The plugin's author. - */ -const gchar *purple_plugin_get_author(const PurplePlugin *plugin); - -/** - * Returns a plugin's homepage. - * - * @param plugin The plugin. - * - * @return The plugin's homepage. - */ -const gchar *purple_plugin_get_homepage(const PurplePlugin *plugin); - -/*@}*/ - -/**************************************************************************/ -/** @name Plugin IPC API */ -/**************************************************************************/ -/*@{*/ - -/** - * Registers an IPC command in a plugin. - * - * @param plugin The plugin to register the command with. - * @param command The name of the command. - * @param func The function to execute. - * @param marshal The marshalling function. - * @param ret_type The return type. - * @param num_params The number of parameters. - * @param ... The parameter types. - * - * @return TRUE if the function was registered successfully, or - * FALSE otherwise. - */ -gboolean purple_plugin_ipc_register(PurplePlugin *plugin, const char *command, - PurpleCallback func, - PurpleSignalMarshalFunc marshal, - GType ret_type, int num_params, ...); - -/** - * Unregisters an IPC command in a plugin. - * - * @param plugin The plugin to unregister the command from. - * @param command The name of the command. - */ -void purple_plugin_ipc_unregister(PurplePlugin *plugin, const char *command); - -/** - * Unregisters all IPC commands in a plugin. - * - * @param plugin The plugin to unregister the commands from. - */ -void purple_plugin_ipc_unregister_all(PurplePlugin *plugin); - -/** - * Returns a list of value types used for an IPC command. - * - * @param plugin The plugin. - * @param command The name of the command. - * @param ret_type The returned return type. - * @param num_params The returned number of parameters. - * @param param_types The returned list of parameter types. - * - * @return TRUE if the command was found, or FALSE otherwise. - */ -gboolean purple_plugin_ipc_get_types(PurplePlugin *plugin, const char *command, - GType *ret_type, int *num_params, - GType **param_types); - -/** - * Executes an IPC command. - * - * @param plugin The plugin to execute the command on. - * @param command The name of the command. - * @param ok TRUE if the call was successful, or FALSE otherwise. - * @param ... The parameters to pass. - * - * @return The return value, which will be NULL if the command doesn't - * return a value. - */ -void *purple_plugin_ipc_call(PurplePlugin *plugin, const char *command, - gboolean *ok, ...); - -/*@}*/ - -/**************************************************************************/ -/** @name Plugins API */ -/**************************************************************************/ -/*@{*/ - -/** - * Add a new directory to search for plugins - * - * @param path The new search path. - */ -void purple_plugins_add_search_path(const char *path); - -/** - * Returns a list of plugin search paths. - * - * @constreturn A list of searched paths. - */ -GList *purple_plugins_get_search_paths(void); - -/** - * Unloads all loaded plugins. - */ -void purple_plugins_unload_all(void); - -/** - * Unloads all plugins of a specific type. - */ -void purple_plugins_unload(PurplePluginType type); - -/** - * Destroys all registered plugins. - */ -void purple_plugins_destroy_all(void); - -/** - * Saves the list of loaded plugins to the specified preference key - * - * @param key The preference key to save the list of plugins to. - */ -void purple_plugins_save_loaded(const char *key); - -/** - * Attempts to load all the plugins in the specified preference key - * that were loaded when purple last quit. - * - * @param key The preference key containing the list of plugins. - */ -void purple_plugins_load_saved(const char *key); - -/** - * Probes for plugins in the registered module paths. - * - * @param ext The extension type to probe for, or @c NULL for all. - * - * @see purple_plugin_set_probe_path() - */ -void purple_plugins_probe(const char *ext); - -/** - * Returns whether or not plugin support is enabled. - * - * @return TRUE if plugin support is enabled, or FALSE otherwise. - */ -gboolean purple_plugins_enabled(void); - -/** - * Finds a plugin with the specified name. - * - * @param name The plugin name. - * - * @return The plugin if found, or @c NULL if not found. - */ -PurplePlugin *purple_plugins_find_with_name(const char *name); - -/** - * Finds a plugin with the specified filename (filename with a path). - * - * @param filename The plugin filename. - * - * @return The plugin if found, or @c NULL if not found. - */ -PurplePlugin *purple_plugins_find_with_filename(const char *filename); - -/** - * Finds a plugin with the specified basename (filename without a path). - * - * @param basename The plugin basename. - * - * @return The plugin if found, or @c NULL if not found. - */ -PurplePlugin *purple_plugins_find_with_basename(const char *basename); - -/** - * Finds a plugin with the specified plugin ID. - * - * @param id The plugin ID. - * - * @return The plugin if found, or @c NULL if not found. - */ -PurplePlugin *purple_plugins_find_with_id(const char *id); - -/** - * Returns a list of all loaded plugins. - * - * @constreturn A list of all loaded plugins. - */ -GList *purple_plugins_get_loaded(void); - -/** - * Returns a list of all valid protocol plugins. A protocol - * plugin is considered invalid if it does not contain the call - * to the PURPLE_INIT_PLUGIN() macro, or if it was compiled - * against an incompatable API version. - * - * @constreturn A list of all protocol plugins. - */ -GList *purple_plugins_get_protocols(void); - -/** - * Returns a list of all plugins, whether loaded or not. - * - * @constreturn A list of all plugins. - */ -GList *purple_plugins_get_all(void); - -/*@}*/ - -/**************************************************************************/ -/** @name Plugins SubSytem API */ -/**************************************************************************/ -/*@{*/ - -/** - * Returns the plugin subsystem handle. - * - * @return The plugin sybsystem handle. - */ -void *purple_plugins_get_handle(void); - -/** - * Initializes the plugin subsystem - */ -void purple_plugins_init(void); - -/** - * Uninitializes the plugin subsystem - */ -void purple_plugins_uninit(void); - -/*@}*/ - -/** - * Allocates and returns a new PurplePluginAction. - * - * @param label The description of the action to show to the user. - * @param callback The callback to call when the user selects this action. - */ -PurplePluginAction *purple_plugin_action_new(const char* label, void (*callback)(PurplePluginAction *)); - -/** - * Frees a PurplePluginAction - * - * @param action The PurplePluginAction to free. - */ -void purple_plugin_action_free(PurplePluginAction *action); - -G_END_DECLS - -#endif /* _PURPLE_PLUGIN_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,1160 @@ +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ +#include "internal.h" + +#include "core.h" +#include "debug.h" +#include "dbus-maybe.h" +#include "enums.h" +#include "plugins.h" + +#define PURPLE_PLUGIN_INFO_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_PLUGIN_INFO, PurplePluginInfoPrivate)) + +/** @copydoc _PurplePluginInfoPrivate */ +typedef struct _PurplePluginInfoPrivate PurplePluginInfoPrivate; + +/************************************************************************** + * Plugin info private data + **************************************************************************/ +struct _PurplePluginInfoPrivate { + char *ui_requirement; /**< ID of UI that is required to load the plugin */ + char *error; /**< Why a plugin is not loadable */ + + PurplePluginInfoFlags flags; /**< Flags for the plugin */ + + /** Callback that returns a list of actions the plugin can perform */ + PurplePluginActionsCb actions_cb; + + /** Callback that returns extra information about a plugin */ + PurplePluginExtraCb extra_cb; + + /** Callback that returns a preferences frame for a plugin */ + PurplePluginPrefFrameCb pref_frame_cb; + + /** Callback that returns a preferences request handle for a plugin */ + PurplePluginPrefRequestCb pref_request_cb; + + /** TRUE if a plugin has been unloaded at least once. Auto-load + * plugins that have been unloaded once will not be auto-loaded again. */ + gboolean unloaded; +}; + +enum +{ + PROP_0, + PROP_UI_REQUIREMENT, + PROP_ACTIONS_CB, + PROP_EXTRA_CB, + PROP_PREF_FRAME_CB, + PROP_PREF_REQUEST_CB, + PROP_FLAGS, + PROP_LAST +}; + +static GObjectClass *parent_class; + +/************************************************************************** + * Globals + **************************************************************************/ +#ifdef PURPLE_PLUGINS +static GList *loaded_plugins = NULL; +static GList *plugins_to_disable = NULL; +#endif + +/************************************************************************** + * Plugin API + **************************************************************************/ +#ifdef PURPLE_PLUGINS +static gboolean +plugin_loading_cb(GObject *manager, PurplePlugin *plugin, GError **error, + gpointer data) +{ + PurplePluginInfo *info; + PurplePluginInfoPrivate *priv; + + g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE); + + info = purple_plugin_get_info(plugin); + if (!info) + return TRUE; /* a GPlugin internal plugin */ + + priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + + if (priv->error) { + purple_debug_error("plugins", "Failed to load plugin %s: %s", + purple_plugin_get_filename(plugin), priv->error); + + g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0, + "Plugin is not loadable: %s", priv->error); + + return FALSE; + } + + return TRUE; +} + +static void +plugin_loaded_cb(GObject *manager, PurplePlugin *plugin) +{ + PurplePluginInfo *info; + + g_return_if_fail(PURPLE_IS_PLUGIN(plugin)); + + info = purple_plugin_get_info(plugin); + if (!info) + return; /* a GPlugin internal plugin */ + + loaded_plugins = g_list_prepend(loaded_plugins, plugin); + + purple_debug_info("plugins", "Loaded plugin %s\n", + purple_plugin_get_filename(plugin)); + + purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin); +} + +static gboolean +plugin_unloading_cb(GObject *manager, PurplePlugin *plugin, GError **error, + gpointer data) +{ + PurplePluginInfo *info; + + g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE); + + info = purple_plugin_get_info(plugin); + if (info) { + purple_debug_info("plugins", "Unloading plugin %s\n", + purple_plugin_get_filename(plugin)); + } + + return TRUE; +} + +static void +plugin_unloaded_cb(GObject *manager, PurplePlugin *plugin) +{ + PurplePluginInfo *info; + PurplePluginInfoPrivate *priv; + + g_return_if_fail(PURPLE_IS_PLUGIN(plugin)); + + info = purple_plugin_get_info(plugin); + if (!info) + return; /* a GPlugin internal plugin */ + + priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + + /* cancel any pending dialogs the plugin has */ + purple_request_close_with_handle(plugin); + purple_notify_close_with_handle(plugin); + + purple_signals_disconnect_by_handle(plugin); + purple_signals_unregister_by_instance(plugin); + + priv->unloaded = TRUE; + + loaded_plugins = g_list_remove(loaded_plugins, plugin); + plugins_to_disable = g_list_remove(plugins_to_disable, plugin); + + purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin); + + purple_prefs_disconnect_by_handle(plugin); +} +#endif /* PURPLE_PLUGINS */ + +gboolean +purple_plugin_load(PurplePlugin *plugin, GError **error) +{ +#ifdef PURPLE_PLUGINS + GError *err = NULL; + + g_return_val_if_fail(plugin != NULL, FALSE); + + if (purple_plugin_is_loaded(plugin)) + return TRUE; + + if (!gplugin_manager_load_plugin(plugin, &err)) { + purple_debug_error("plugins", "Failed to load plugin %s: %s", + purple_plugin_get_filename(plugin), + (err ? err->message : "Unknown reason")); + + if (error) + *error = g_error_copy(err); + g_error_free(err); + + return FALSE; + } + + return TRUE; + +#else + g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0, "Plugin support is disabled."); + return FALSE; +#endif /* PURPLE_PLUGINS */ +} + +gboolean +purple_plugin_unload(PurplePlugin *plugin, GError **error) +{ +#ifdef PURPLE_PLUGINS + GError *err = NULL; + + g_return_val_if_fail(plugin != NULL, FALSE); + + if (!purple_plugin_is_loaded(plugin)) + return TRUE; + + if (!gplugin_manager_unload_plugin(plugin, &err)) { + purple_debug_error("plugins", "Failed to unload plugin %s: %s", + purple_plugin_get_filename(plugin), + (err ? err->message : "Unknown reason")); + + if (error) + *error = g_error_copy(err); + g_error_free(err); + + return FALSE; + } + + return TRUE; + +#else + g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0, "Plugin support is disabled."); + return FALSE; +#endif /* PURPLE_PLUGINS */ +} + +gboolean +purple_plugin_is_loaded(const PurplePlugin *plugin) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(plugin != NULL, FALSE); + + return (gplugin_plugin_get_state(plugin) == GPLUGIN_PLUGIN_STATE_LOADED); + +#else + return FALSE; +#endif +} + +const gchar * +purple_plugin_get_filename(const PurplePlugin *plugin) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(plugin != NULL, NULL); + + return gplugin_plugin_get_filename(plugin); + +#else + return NULL; +#endif +} + +PurplePluginInfo * +purple_plugin_get_info(const PurplePlugin *plugin) +{ +#ifdef PURPLE_PLUGINS + GPluginPluginInfo *info; + + g_return_val_if_fail(plugin != NULL, NULL); + + info = gplugin_plugin_get_info(plugin); + + /* GPlugin refs the plugin info object before returning it. This workaround + * is to avoid managing the reference counts everywhere in our codebase + * where we use the plugin info. The plugin info instance is guaranteed to + * exist as long as the plugin exists. */ + g_object_unref(info); + + if (PURPLE_IS_PLUGIN_INFO(info)) + return PURPLE_PLUGIN_INFO(info); + else + return NULL; +#else + return NULL; +#endif +} + +void +purple_plugin_disable(PurplePlugin *plugin) +{ +#ifdef PURPLE_PLUGINS + g_return_if_fail(plugin != NULL); + + if (!g_list_find(plugins_to_disable, plugin)) + plugins_to_disable = g_list_prepend(plugins_to_disable, plugin); +#endif +} + +GType +purple_plugin_register_type(PurplePlugin *plugin, GType parent, + const gchar *name, const GTypeInfo *info, + GTypeFlags flags) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(GPLUGIN_IS_NATIVE_PLUGIN(plugin), G_TYPE_INVALID); + + return gplugin_native_plugin_register_type(GPLUGIN_NATIVE_PLUGIN(plugin), + parent, name, info, flags); + +#else + return G_TYPE_INVALID; +#endif +} + +void +purple_plugin_add_interface(PurplePlugin *plugin, GType instance_type, + GType interface_type, + const GInterfaceInfo *interface_info) +{ +#ifdef PURPLE_PLUGINS + g_return_if_fail(GPLUGIN_IS_NATIVE_PLUGIN(plugin)); + + gplugin_native_plugin_add_interface(GPLUGIN_NATIVE_PLUGIN(plugin), + instance_type, interface_type, + interface_info); +#endif +} + +gboolean +purple_plugin_is_internal(const PurplePlugin *plugin) +{ + PurplePluginInfo *info; + + g_return_val_if_fail(plugin != NULL, FALSE); + + info = purple_plugin_get_info(plugin); + if (!info) + return TRUE; + + return (purple_plugin_info_get_flags(info) & + PURPLE_PLUGIN_INFO_FLAGS_INTERNAL); +} + +GSList * +purple_plugin_get_dependent_plugins(const PurplePlugin *plugin) +{ +#warning TODO: Implement this when GPlugin can return dependent plugins. + +#ifdef PURPLE_PLUGINS + return NULL; +#else + return NULL; +#endif +} + +/************************************************************************** + * GObject code for PurplePluginInfo + **************************************************************************/ +/* GObject initialization function */ +static void +purple_plugin_info_init(GTypeInstance *instance, gpointer klass) +{ + PURPLE_DBUS_REGISTER_POINTER(PURPLE_PLUGIN_INFO(instance), PurplePluginInfo); +} + +/* Set method for GObject properties */ +static void +purple_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value, + GParamSpec *pspec) +{ + PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj); + PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + + switch (param_id) { + case PROP_UI_REQUIREMENT: + priv->ui_requirement = g_strdup(g_value_get_string(value)); + break; + case PROP_ACTIONS_CB: + priv->actions_cb = g_value_get_pointer(value); + break; + case PROP_EXTRA_CB: + priv->extra_cb = g_value_get_pointer(value); + break; + case PROP_PREF_FRAME_CB: + priv->pref_frame_cb = g_value_get_pointer(value); + break; + case PROP_PREF_REQUEST_CB: + priv->pref_request_cb = g_value_get_pointer(value); + break; + case PROP_FLAGS: + priv->flags = g_value_get_flags(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +/* Get method for GObject properties */ +static void +purple_plugin_info_get_property(GObject *obj, guint param_id, GValue *value, + GParamSpec *pspec) +{ + PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj); + + switch (param_id) { + case PROP_ACTIONS_CB: + g_value_set_pointer(value, + purple_plugin_info_get_actions_cb(info)); + break; + case PROP_EXTRA_CB: + g_value_set_pointer(value, + purple_plugin_info_get_extra_cb(info)); + break; + case PROP_PREF_FRAME_CB: + g_value_set_pointer(value, + purple_plugin_info_get_pref_frame_cb(info)); + break; + case PROP_PREF_REQUEST_CB: + g_value_set_pointer(value, + purple_plugin_info_get_pref_request_cb(info)); + break; + case PROP_FLAGS: + g_value_set_flags(value, purple_plugin_info_get_flags(info)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +/* Called when done constructing */ +static void +purple_plugin_info_constructed(GObject *object) +{ + PurplePluginInfo *info = PURPLE_PLUGIN_INFO(object); + PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + const char *id = purple_plugin_info_get_id(info); + guint32 version; + + parent_class->constructed(object); + + if (id == NULL || *id == '\0') + priv->error = g_strdup(_("This plugin has not defined an ID.")); + + if (priv->ui_requirement && !purple_strequal(priv->ui_requirement, purple_core_get_ui())) + { + priv->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."), + purple_core_get_ui(), priv->ui_requirement); + purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n", + id, priv->error); + } + + version = purple_plugin_info_get_abi_version(info); + if (PURPLE_PLUGIN_ABI_MAJOR_VERSION(version) != PURPLE_MAJOR_VERSION || + PURPLE_PLUGIN_ABI_MINOR_VERSION(version) > PURPLE_MINOR_VERSION) + { + priv->error = g_strdup_printf(_("Your libpurple version is %d.%d.x (need %d.%d.x)"), + PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, + PURPLE_PLUGIN_ABI_MAJOR_VERSION(version), + PURPLE_PLUGIN_ABI_MINOR_VERSION(version)); + purple_debug_error("plugins", "%s is not loadable: libpurple version is %d.%d.x (need %d.%d.x)\n", + id, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, + PURPLE_PLUGIN_ABI_MAJOR_VERSION(version), + PURPLE_PLUGIN_ABI_MINOR_VERSION(version)); + } +} + +/* GObject finalize function */ +static void +purple_plugin_info_finalize(GObject *object) +{ + PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(object); + + g_free(priv->ui_requirement); + g_free(priv->error); + + PURPLE_DBUS_UNREGISTER_POINTER(object); + + parent_class->finalize(object); +} + +/* Class initializer function */ +static void purple_plugin_info_class_init(PurplePluginInfoClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + g_type_class_add_private(klass, sizeof(PurplePluginInfoPrivate)); + + obj_class->constructed = purple_plugin_info_constructed; + obj_class->finalize = purple_plugin_info_finalize; + + /* Setup properties */ + obj_class->get_property = purple_plugin_info_get_property; + obj_class->set_property = purple_plugin_info_set_property; + + g_object_class_install_property(obj_class, PROP_UI_REQUIREMENT, + g_param_spec_string("ui-requirement", + "UI Requirement", + "ID of UI that is required by this plugin", NULL, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(obj_class, PROP_ACTIONS_CB, + g_param_spec_pointer("actions-cb", + "Plugin actions", + "Callback that returns list of plugin's actions", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(obj_class, PROP_EXTRA_CB, + g_param_spec_pointer("extra-cb", + "Extra info callback", + "Callback that returns extra info about the plugin", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(obj_class, PROP_PREF_FRAME_CB, + g_param_spec_pointer("pref-frame-cb", + "Preferences frame callback", + "The callback that returns the preferences frame", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(obj_class, PROP_PREF_REQUEST_CB, + g_param_spec_pointer("pref-request-cb", + "Preferences request callback", + "Callback that returns preferences request handle", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(obj_class, PROP_FLAGS, + g_param_spec_flags("flags", + "Plugin flags", + "The flags for the plugin", + PURPLE_TYPE_PLUGIN_INFO_FLAGS, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +/************************************************************************** + * PluginInfo API + **************************************************************************/ +GType +purple_plugin_info_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurplePluginInfoClass), + .class_init = (GClassInitFunc)purple_plugin_info_class_init, + .instance_size = sizeof(PurplePluginInfo), + .instance_init = (GInstanceInitFunc)purple_plugin_info_init, + }; + + type = g_type_register_static( +#ifdef PURPLE_PLUGINS + GPLUGIN_TYPE_PLUGIN_INFO, +#else + G_TYPE_OBJECT, +#endif + "PurplePluginInfo", &info, 0); + } + + return type; +} + +PurplePluginInfo * +purple_plugin_info_new(const char *first_property, ...) +{ + GObject *info; + va_list var_args; + + /* at least ID is required */ + if (!first_property) + return NULL; + + va_start(var_args, first_property); + info = g_object_new_valist(PURPLE_TYPE_PLUGIN_INFO, first_property, + var_args); + va_end(var_args); + + return PURPLE_PLUGIN_INFO(info); +} + +const gchar * +purple_plugin_info_get_id(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_id(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * +purple_plugin_info_get_name(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * +purple_plugin_info_get_version(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_version(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * +purple_plugin_info_get_category(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_category(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * +purple_plugin_info_get_summary(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_summary(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * +purple_plugin_info_get_description(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_description(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * const * +purple_plugin_info_get_authors(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_authors(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * +purple_plugin_info_get_website(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_website(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * +purple_plugin_info_get_icon(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_icon(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * +purple_plugin_info_get_license_id(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_license_id(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * +purple_plugin_info_get_license_text(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_license_text(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * +purple_plugin_info_get_license_url(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_license_url(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +const gchar * const * +purple_plugin_info_get_dependencies(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, NULL); + + return gplugin_plugin_info_get_dependencies(GPLUGIN_PLUGIN_INFO(info)); + +#else + return NULL; +#endif +} + +guint32 +purple_plugin_info_get_abi_version(const PurplePluginInfo *info) +{ +#ifdef PURPLE_PLUGINS + g_return_val_if_fail(info != NULL, 0); + + return gplugin_plugin_info_get_abi_version(GPLUGIN_PLUGIN_INFO(info)); + +#else + return 0; +#endif +} + +PurplePluginActionsCb +purple_plugin_info_get_actions_cb(const PurplePluginInfo *info) +{ + PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + + g_return_val_if_fail(priv != NULL, NULL); + + return priv->actions_cb; +} + +PurplePluginExtraCb +purple_plugin_info_get_extra_cb(const PurplePluginInfo *info) +{ + PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + + g_return_val_if_fail(priv != NULL, NULL); + + return priv->extra_cb; +} + +PurplePluginPrefFrameCb +purple_plugin_info_get_pref_frame_cb(const PurplePluginInfo *info) +{ + PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + + g_return_val_if_fail(priv != NULL, NULL); + + return priv->pref_frame_cb; +} + +PurplePluginPrefRequestCb +purple_plugin_info_get_pref_request_cb(const PurplePluginInfo *info) +{ + PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + + g_return_val_if_fail(priv != NULL, NULL); + + return priv->pref_request_cb; +} + +PurplePluginInfoFlags +purple_plugin_info_get_flags(const PurplePluginInfo *info) +{ + PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + + g_return_val_if_fail(priv != NULL, 0); + + return priv->flags; +} + +const gchar * +purple_plugin_info_get_error(const PurplePluginInfo *info) +{ + PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + + g_return_val_if_fail(priv != NULL, NULL); + + return priv->error; +} + +void +purple_plugin_info_set_ui_data(PurplePluginInfo *info, gpointer ui_data) +{ + g_return_if_fail(PURPLE_IS_PLUGIN_INFO(info)); + + info->ui_data = ui_data; +} + +gpointer +purple_plugin_info_get_ui_data(const PurplePluginInfo *info) +{ + g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL); + + return info->ui_data; +} + +/************************************************************************** + * PluginAction API + **************************************************************************/ +PurplePluginAction * +purple_plugin_action_new(const char* label, PurplePluginActionCb callback) +{ + PurplePluginAction *action; + + g_return_val_if_fail(label != NULL && callback != NULL, NULL); + + action = g_new0(PurplePluginAction, 1); + + action->label = g_strdup(label); + action->callback = callback; + + return action; +} + +void +purple_plugin_action_free(PurplePluginAction *action) +{ + g_return_if_fail(action != NULL); + + g_free(action->label); + g_free(action); +} + +static PurplePluginAction * +purple_plugin_action_copy(PurplePluginAction *action) +{ + PurplePluginAction *action_copy; + + g_return_val_if_fail(action != NULL, NULL); + + action_copy = g_new(PurplePluginAction, 1); + *action_copy = *action; + + action_copy->label = g_strdup(action->label); + + return action_copy; +} + +GType +purple_plugin_action_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + type = g_boxed_type_register_static("PurplePluginAction", + (GBoxedCopyFunc)purple_plugin_action_copy, + (GBoxedFreeFunc)purple_plugin_action_free); + } + + return type; +} + +/************************************************************************** + * Plugins API + **************************************************************************/ +GList * +purple_plugins_find_all(void) +{ +#ifdef PURPLE_PLUGINS + GList *ret = NULL, *ids, *l; + GSList *plugins, *ll; + + ids = gplugin_manager_list_plugins(); + + for (l = ids; l; l = l->next) { + plugins = gplugin_manager_find_plugins(l->data); + + for (ll = plugins; ll; ll = ll->next) { + PurplePlugin *plugin = PURPLE_PLUGIN(ll->data); + if (purple_plugin_get_info(plugin)) + ret = g_list_append(ret, plugin); + } + + gplugin_manager_free_plugin_list(plugins); + } + g_list_free(ids); + + return ret; + +#else + return NULL; +#endif +} + +GList * +purple_plugins_get_loaded(void) +{ +#ifdef PURPLE_PLUGINS + return loaded_plugins; +#else + return NULL; +#endif +} + +void +purple_plugins_add_search_path(const gchar *path) +{ +#ifdef PURPLE_PLUGINS + gplugin_manager_append_path(path); +#endif +} + +void +purple_plugins_refresh(void) +{ +#ifdef PURPLE_PLUGINS + GList *plugins, *l; + + gplugin_manager_refresh(); + + plugins = purple_plugins_find_all(); + for (l = plugins; l != NULL; l = l->next) { + PurplePlugin *plugin = PURPLE_PLUGIN(l->data); + PurplePluginInfo *info; + PurplePluginInfoPrivate *priv; + + if (purple_plugin_is_loaded(plugin)) + continue; + + info = purple_plugin_get_info(plugin); + priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info); + + if (!priv->unloaded && purple_plugin_info_get_flags(info) & + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) { + purple_debug_info("plugins", "Auto-loading plugin %s\n", + purple_plugin_get_filename(plugin)); + purple_plugin_load(plugin, NULL); + } + } + + g_list_free(plugins); +#endif +} + +PurplePlugin * +purple_plugins_find_plugin(const gchar *id) +{ +#ifdef PURPLE_PLUGINS + PurplePlugin *plugin; + + g_return_val_if_fail(id != NULL && *id != '\0', NULL); + + plugin = gplugin_manager_find_plugin(id); + + /* GPlugin refs the plugin object before returning it. This workaround is + * to avoid managing the reference counts everywhere in our codebase where + * we use plugin instances. A plugin object will exist till the plugins + * subsystem is uninitialized. */ + g_object_unref(plugin); + + return plugin; + +#else + return NULL; +#endif +} + +PurplePlugin * +purple_plugins_find_by_filename(const char *filename) +{ + GList *plugins, *l; + + g_return_val_if_fail(filename != NULL && *filename != '\0', NULL); + + plugins = purple_plugins_find_all(); + + for (l = plugins; l != NULL; l = l->next) { + PurplePlugin *plugin = PURPLE_PLUGIN(l->data); + + if (purple_strequal(purple_plugin_get_filename(plugin), filename)) { + g_list_free(plugins); + return plugin; + } + } + g_list_free(plugins); + + return NULL; +} + +void +purple_plugins_save_loaded(const char *key) +{ +#ifdef PURPLE_PLUGINS + GList *pl; + GList *files = NULL; + + g_return_if_fail(key != NULL && *key != '\0'); + + for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) { + PurplePlugin *plugin = PURPLE_PLUGIN(pl->data); + PurplePluginInfo *info = purple_plugin_get_info(plugin); + if (!info) + continue; + + if (purple_plugin_info_get_flags(info) & + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) + continue; + + if (!g_list_find(plugins_to_disable, plugin)) + files = g_list_append(files, (gchar *)purple_plugin_get_filename(plugin)); + } + + purple_prefs_set_path_list(key, files); + g_list_free(files); +#endif +} + +void +purple_plugins_load_saved(const char *key) +{ +#ifdef PURPLE_PLUGINS + GList *l, *files; + + g_return_if_fail(key != NULL && *key != '\0'); + + files = purple_prefs_get_path_list(key); + + for (l = files; l; l = l->next) + { + char *file; + PurplePlugin *plugin; + + if (l->data == NULL) + continue; + + file = l->data; + plugin = purple_plugins_find_by_filename(file); + + if (plugin) { + purple_debug_info("plugins", "Loading saved plugin %s\n", file); + purple_plugin_load(plugin, NULL); + } else { + purple_debug_error("plugins", "Unable to find saved plugin %s\n", file); + } + + g_free(l->data); + } + + g_list_free(files); +#endif /* PURPLE_PLUGINS */ +} + +/************************************************************************** + * Plugins Subsystem API + **************************************************************************/ +void * +purple_plugins_get_handle(void) +{ + static int handle; + + return &handle; +} + +void +purple_plugins_init(void) +{ + void *handle = purple_plugins_get_handle(); + + purple_signal_register(handle, "plugin-load", + purple_marshal_VOID__POINTER, + G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN); + purple_signal_register(handle, "plugin-unload", + purple_marshal_VOID__POINTER, + G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN); + +#ifdef PURPLE_PLUGINS + gplugin_init(); + gplugin_manager_add_default_paths(); + + purple_plugins_add_search_path(LIBDIR); + + g_signal_connect(gplugin_manager_get_instance(), "loading-plugin", + G_CALLBACK(plugin_loading_cb), NULL); + g_signal_connect(gplugin_manager_get_instance(), "loaded-plugin", + G_CALLBACK(plugin_loaded_cb), NULL); + g_signal_connect(gplugin_manager_get_instance(), "unloading-plugin", + G_CALLBACK(plugin_unloading_cb), NULL); + g_signal_connect(gplugin_manager_get_instance(), "unloaded-plugin", + G_CALLBACK(plugin_unloaded_cb), NULL); + + purple_plugins_refresh(); +#endif +} + +void +purple_plugins_uninit(void) +{ + void *handle = purple_plugins_get_handle(); + +#ifdef PURPLE_PLUGINS + purple_debug_info("plugins", "Unloading all plugins\n"); + while (loaded_plugins != NULL) + purple_plugin_unload(loaded_plugins->data, NULL); +#endif + + purple_signals_disconnect_by_handle(handle); + purple_signals_unregister_by_instance(handle); + +#ifdef PURPLE_PLUGINS + gplugin_uninit(); +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,894 @@ +/** + * @file plugins.h Plugins API + * @ingroup core + * @see @ref plugin-signals + * @see @ref plugin-ids + * @see @ref plugin-i18n + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ +#ifndef _PURPLE_PLUGINS_H_ +#define _PURPLE_PLUGINS_H_ + +#ifdef PURPLE_PLUGINS +#include <gplugin.h> +#include <gplugin-native.h> +#else +#include <glib.h> +#include <glib-object.h> +#endif + +#include "version.h" + +#define PURPLE_PLUGINS_DOMAIN (g_quark_from_static_string("plugins")) + +#ifdef PURPLE_PLUGINS + +#define PURPLE_TYPE_PLUGIN GPLUGIN_TYPE_PLUGIN +#define PURPLE_PLUGIN(obj) GPLUGIN_PLUGIN(obj) +#define PURPLE_PLUGIN_CLASS(klass) GPLUGIN_PLUGIN_CLASS(klass) +#define PURPLE_IS_PLUGIN(obj) GPLUGIN_IS_PLUGIN(obj) +#define PURPLE_IS_PLUGIN_CLASS(klass) GPLUGIN_IS_PLUGIN_CLASS(klass) +#define PURPLE_PLUGIN_GET_CLASS(obj) GPLUGIN_PLUGIN_GET_CLASS(obj) + +/** + * Represents a plugin handle. + * This type is an alias for GPluginPlugin. + */ +typedef GPluginPlugin PurplePlugin; + +/** + * The base class for all #PurplePlugin's. + * This type is an alias for GPluginPluginClass. + */ +typedef GPluginPluginClass PurplePluginClass; + +#else /* !defined(PURPLE_PLUGINS) */ + +#define PURPLE_TYPE_PLUGIN G_TYPE_OBJECT +#define PURPLE_PLUGIN(obj) G_OBJECT(obj) +#define PURPLE_PLUGIN_CLASS(klass) G_OBJECT_CLASS(klass) +#define PURPLE_IS_PLUGIN(obj) G_IS_OBJECT(obj) +#define PURPLE_IS_PLUGIN_CLASS(klass) G_IS_OBJECT_CLASS(klass) +#define PURPLE_PLUGIN_GET_CLASS(obj) G_OBJECT_GET_CLASS(obj) + +typedef GObject PurplePlugin; +typedef GObjectClass PurplePluginClass; + +#endif /* PURPLE_PLUGINS */ + +#define PURPLE_TYPE_PLUGIN_INFO (purple_plugin_info_get_type()) +#define PURPLE_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_PLUGIN_INFO, PurplePluginInfo)) +#define PURPLE_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_PLUGIN_INFO, PurplePluginInfoClass)) +#define PURPLE_IS_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PLUGIN_INFO)) +#define PURPLE_IS_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_PLUGIN_INFO)) +#define PURPLE_PLUGIN_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_PLUGIN_INFO, PurplePluginInfoClass)) + +/** @copydoc _PurplePluginInfo */ +typedef struct _PurplePluginInfo PurplePluginInfo; +/** @copydoc _PurplePluginInfoClass */ +typedef struct _PurplePluginInfoClass PurplePluginInfoClass; + +#define PURPLE_TYPE_PLUGIN_ACTION (purple_plugin_action_get_type()) + +/** @copydoc _PurplePluginAction */ +typedef struct _PurplePluginAction PurplePluginAction; + +#include "pluginpref.h" + +typedef void (*PurplePluginActionCb)(PurplePluginAction *); +typedef GList *(*PurplePluginActionsCb)(PurplePlugin *); +typedef gchar *(*PurplePluginExtraCb)(PurplePlugin *); +typedef PurplePluginPrefFrame *(*PurplePluginPrefFrameCb)(PurplePlugin *); +typedef gpointer (*PurplePluginPrefRequestCb)(PurplePlugin *); + +/** + * Flags that can be used to treat plugins differently. + */ +typedef enum /*< flags >*/ +{ + PURPLE_PLUGIN_INFO_FLAGS_INTERNAL = 1 << 1, /**< Plugin is not shown in UI lists */ + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD = 1 << 2, /**< Auto-load the plugin */ +} PurplePluginInfoFlags; + +/** + * Holds information about a plugin. + */ +struct _PurplePluginInfo { +#ifdef PURPLE_PLUGINS + GPluginPluginInfo parent; +#else + GObject parent; +#endif + + /** The UI data associated with the plugin. This is a convenience + * field provided to the UIs -- it is not used by the libpurple core. + */ + gpointer ui_data; +}; + +/** + * PurplePluginInfoClass: + * + * The base class for all #PurplePluginInfo's. + */ +struct _PurplePluginInfoClass { +#ifdef PURPLE_PLUGINS + GPluginPluginInfoClass parent_class; +#else + GObjectClass parent_class; +#endif + + /*< private >*/ + void (*_purple_reserved1)(void); + void (*_purple_reserved2)(void); + void (*_purple_reserved3)(void); + void (*_purple_reserved4)(void); +}; + +/** + * Represents an action that the plugin can perform. This shows up in the Tools + * menu, under a submenu with the name of the plugin. + */ +struct _PurplePluginAction { + char *label; + PurplePluginActionCb callback; + PurplePlugin *plugin; + gpointer user_data; +}; + +/** + * Returns an ABI version to set in plugins using major and minor versions. + * + * @note The lower six nibbles represent the ABI version for libpurple, the + * rest are required by GPlugin. + */ +#define PURPLE_PLUGIN_ABI_VERSION(major,minor) \ + (0x01000000 | ((major) << 16) | (minor)) + +/** Returns the major version from an ABI version */ +#define PURPLE_PLUGIN_ABI_MAJOR_VERSION(abi) \ + ((abi >> 16) & 0xff) + +/** Returns the minor version from an ABI version */ +#define PURPLE_PLUGIN_ABI_MINOR_VERSION(abi) \ + (abi & 0xffff) + +/** + * A convenience‎ macro that returns an ABI version using PURPLE_MAJOR_VERSION + * and PURPLE_MINOR_VERSION + */ +#define PURPLE_ABI_VERSION PURPLE_PLUGIN_ABI_VERSION(PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION) + +/** + * PURPLE_PLUGIN_INIT: + * + * Defines the plugin's entry points. + */ +#if !defined(PURPLE_PLUGINS) || defined(PURPLE_STATIC_PRPL) +#define PURPLE_PLUGIN_INIT(pluginname,pluginquery,pluginload,pluginunload) \ + PurplePluginInfo * pluginname##_plugin_query(void); \ + PurplePluginInfo * pluginname##_plugin_query(void) { \ + return pluginquery(NULL); \ + } \ + gboolean pluginname##_plugin_load(void); \ + gboolean pluginname##_plugin_load(void) { \ + GError *e = NULL; \ + gboolean loaded = pluginload(NULL, &e); \ + if (e) g_error_free(e); \ + return loaded; \ + } \ + gboolean pluginname##_plugin_unload(void); \ + gboolean pluginname##_plugin_unload(void) { \ + GError *e = NULL; \ + gboolean unloaded = pluginunload(NULL, &e); \ + if (e) g_error_free(e); \ + return unloaded; \ + } +#else /* PURPLE_PLUGINS && !PURPLE_STATIC_PRPL */ +#define PURPLE_PLUGIN_INIT(pluginname,pluginquery,pluginload,pluginunload) \ + G_MODULE_EXPORT GPluginPluginInfo *gplugin_query(GError **e); \ + G_MODULE_EXPORT GPluginPluginInfo *gplugin_query(GError **e) { \ + return GPLUGIN_PLUGIN_INFO(pluginquery(e)); \ + } \ + G_MODULE_EXPORT gboolean gplugin_load(GPluginNativePlugin *p, GError **e); \ + G_MODULE_EXPORT gboolean gplugin_load(GPluginNativePlugin *p, GError **e) { \ + return pluginload(PURPLE_PLUGIN(p), e); \ + } \ + G_MODULE_EXPORT gboolean gplugin_unload(GPluginNativePlugin *p, GError **e); \ + G_MODULE_EXPORT gboolean gplugin_unload(GPluginNativePlugin *p, GError **e) { \ + return pluginunload(PURPLE_PLUGIN(p), e); \ + } +#endif + +/** + * PURPLE_DEFINE_TYPE: + * + * A convenience macro for type implementations, which defines a *_get_type() + * function; and a *_register_type() function for use in your plugin's load + * function. You must define an instance initialization function *_init() + * and a class initialization function *_class_init() for the type. + * + * The type will be registered statically if used in a static protocol or if + * plugins support is disabled. + * + * @param TN The name of the new type, in Camel case. + * @param t_n The name of the new type, in lowercase, words separated by '_'. + * @param T_P The #GType of the parent type. + */ +#if !defined(PURPLE_PLUGINS) || defined(PURPLE_STATIC_PRPL) +#define PURPLE_DEFINE_TYPE(TN, t_n, T_P) \ + PURPLE_DEFINE_STATIC_TYPE(TN, t_n, T_P) +#else +#define PURPLE_DEFINE_TYPE(TN, t_n, T_P) \ + PURPLE_DEFINE_DYNAMIC_TYPE(TN, t_n, T_P) +#endif + +/** + * PURPLE_DEFINE_TYPE_EXTENDED: + * + * A more general version of PURPLE_DEFINE_TYPE() which allows you to + * specify #GTypeFlags and custom code. + * + * @param TN The name of the new type, in Camel case. + * @param t_n The name of the new type, in lowercase, words separated by '_'. + * @param T_P The #GType of the parent type. + * @param flags #GTypeFlags to register the type with. + * @param CODE Custom code that gets inserted in *_get_type(). + */ +#if !defined(PURPLE_PLUGINS) || defined(PURPLE_STATIC_PRPL) +#define PURPLE_DEFINE_TYPE_EXTENDED \ + PURPLE_DEFINE_STATIC_TYPE_EXTENDED +#else +#define PURPLE_DEFINE_TYPE_EXTENDED \ + PURPLE_DEFINE_DYNAMIC_TYPE_EXTENDED +#endif + +/** + * PURPLE_IMPLEMENT_INTERFACE_STATIC: + * + * A convenience macro to ease static interface addition in the CODE section + * of PURPLE_DEFINE_TYPE_EXTENDED(). You should use this macro if the + * interface is a part of the libpurple core. + * + * @param TYPE_IFACE The #GType of the interface to add. + * @param iface_init The interface init function. + */ +#define PURPLE_IMPLEMENT_INTERFACE_STATIC(TYPE_IFACE, iface_init) { \ + const GInterfaceInfo interface_info = { \ + (GInterfaceInitFunc) iface_init, NULL, NULL \ + }; \ + g_type_add_interface_static(type_id, TYPE_IFACE, &interface_info); \ +} + +/** + * PURPLE_IMPLEMENT_INTERFACE: + * + * A convenience macro to ease interface addition in the CODE section + * of PURPLE_DEFINE_TYPE_EXTENDED(). You should use this macro if the + * interface lives in the plugin. + * + * @param TYPE_IFACE The #GType of the interface to add. + * @param iface_init The interface init function. + */ +#if !defined(PURPLE_PLUGINS) || defined(PURPLE_STATIC_PRPL) +#define PURPLE_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init) \ + PURPLE_IMPLEMENT_INTERFACE_STATIC(TYPE_IFACE, iface_init) +#else +#define PURPLE_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init) \ + PURPLE_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init) +#endif + +/** A convenience macro for dynamic type implementations. */ +#define PURPLE_DEFINE_DYNAMIC_TYPE(TN, t_n, T_P) \ + PURPLE_DEFINE_DYNAMIC_TYPE_EXTENDED (TN, t_n, T_P, 0, {}) + +/** A more general version of PURPLE_DEFINE_DYNAMIC_TYPE(). */ +#define PURPLE_DEFINE_DYNAMIC_TYPE_EXTENDED(TypeName, type_name, TYPE_PARENT, flags, CODE) \ +static GType type_name##_type_id = 0; \ +G_MODULE_EXPORT GType type_name##_get_type(void) { \ + return type_name##_type_id; \ +} \ +void type_name##_register_type(PurplePlugin *); \ +void type_name##_register_type(PurplePlugin *plugin) { \ + GType type_id; \ + const GTypeInfo type_info = { \ + sizeof (TypeName##Class), \ + (GBaseInitFunc) NULL, \ + (GBaseFinalizeFunc) NULL, \ + (GClassInitFunc) type_name##_class_init, \ + (GClassFinalizeFunc) NULL, \ + NULL, \ + sizeof (TypeName), \ + 0, \ + (GInstanceInitFunc) type_name##_init, \ + NULL \ + }; \ + type_id = purple_plugin_register_type(plugin, TYPE_PARENT, #TypeName, \ + &type_info, (GTypeFlags) flags); \ + type_name##_type_id = type_id; \ + { CODE ; } \ +} + +/** A convenience macro to ease dynamic interface addition. */ +#define PURPLE_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init) { \ + const GInterfaceInfo interface_info = { \ + (GInterfaceInitFunc) iface_init, NULL, NULL \ + }; \ + purple_plugin_add_interface(plugin, type_id, TYPE_IFACE, &interface_info); \ +} + +/** A convenience macro for static type implementations. */ +#define PURPLE_DEFINE_STATIC_TYPE(TN, t_n, T_P) \ + PURPLE_DEFINE_STATIC_TYPE_EXTENDED (TN, t_n, T_P, 0, {}) + +/** A more general version of PURPLE_DEFINE_STATIC_TYPE(). */ +#define PURPLE_DEFINE_STATIC_TYPE_EXTENDED(TypeName, type_name, TYPE_PARENT, flags, CODE) \ +static GType type_name##_type_id = 0; \ +GType type_name##_get_type(void) { \ + if (G_UNLIKELY(type_name##_type_id == 0)) { \ + GType type_id; \ + const GTypeInfo type_info = { \ + sizeof (TypeName##Class), \ + (GBaseInitFunc) NULL, \ + (GBaseFinalizeFunc) NULL, \ + (GClassInitFunc) type_name##_class_init, \ + (GClassFinalizeFunc) NULL, \ + NULL, \ + sizeof (TypeName), \ + 0, \ + (GInstanceInitFunc) type_name##_init, \ + NULL \ + }; \ + type_id = g_type_register_static(TYPE_PARENT, #TypeName, &type_info, \ + (GTypeFlags) flags); \ + type_name##_type_id = type_id; \ + { CODE ; } \ + } \ + return type_name##_type_id; \ +} \ +void type_name##_register_type(PurplePlugin *); \ +void type_name##_register_type(PurplePlugin *plugin) { } + +G_BEGIN_DECLS + +/**************************************************************************/ +/** @name Plugin API */ +/**************************************************************************/ +/*@{*/ + +/** + * Attempts to load a plugin. + * + * @param plugin The plugin to load. + * @param error Return location for a #GError or @c NULL. If provided, this + * will be set to the reason if the load fails. + * + * @return @c TRUE if successful or already loaded, @c FALSE otherwise. + * + * @see purple_plugin_unload() + */ +gboolean purple_plugin_load(PurplePlugin *plugin, GError **error); + +/** + * Unloads the specified plugin. + * + * @param plugin The plugin handle. + * @param error Return location for a #GError or @c NULL. If provided, this + * will be set to the reason if the unload fails. + * + * @return @c TRUE if successful or not loaded, @c FALSE otherwise. + * + * @see purple_plugin_load() + */ +gboolean purple_plugin_unload(PurplePlugin *plugin, GError **error); + +/** + * Returns whether or not a plugin is currently loaded. + * + * @param plugin The plugin. + * + * @return @c TRUE if loaded, or @c FALSE otherwise. + */ +gboolean purple_plugin_is_loaded(const PurplePlugin *plugin); + +/** + * Returns a plugin's filename, along with the path. + * + * @param info The plugin. + * + * @return The plugin's filename. + */ +const gchar *purple_plugin_get_filename(const PurplePlugin *plugin); + +/** + * Returns a plugin's #PurplePluginInfo instance. + * + * @param info The plugin. + * + * @return The plugin's #PurplePluginInfo instance. + */ +PurplePluginInfo *purple_plugin_get_info(const PurplePlugin *plugin); + +/** + * Disable a plugin. + * + * This function adds the plugin to a list of plugins to "disable at the next + * startup" by excluding said plugins from the list of plugins to save. The + * UI needs to call purple_plugins_save_loaded() after calling this for it + * to have any effect. + */ +void purple_plugin_disable(PurplePlugin *plugin); + +/** + * Registers a new dynamic type. + * + * @param plugin The plugin that is registering the type. + * @param parent Type from which this type will be derived. + * @param name Name of the new type. + * @param info Information to initialize and destroy a type's classes and + * instances. + * @param flags Bitwise combination of values that determines the nature + * (e.g. abstract or not) of the type. + * + * @return The new GType, or @c G_TYPE_INVALID if registration failed. + */ +GType purple_plugin_register_type(PurplePlugin *plugin, GType parent, + const gchar *name, const GTypeInfo *info, + GTypeFlags flags); + +/** + * Adds a dynamic interface type to an instantiable type. + * + * @param plugin The plugin that is adding the interface type. + * @param instance_type The GType of the instantiable type. + * @param interface_type The GType of the interface type. + * @param interface_info Information used to manage the interface type. + */ +void purple_plugin_add_interface(PurplePlugin *plugin, GType instance_type, + GType interface_type, + const GInterfaceInfo *interface_info); + +/** + * Returns whether a plugin is an internal plugin. Internal plugins provide + * required additional functionality to the libpurple core. These plugins must + * not be shown in plugin lists. Examples of such plugins are in-tree protocol + * plugins, loaders etc. + * + * @param plugin The plugin. + * + * @return @c TRUE if the plugin is an internal plugin, @c FALSE otherwise. + */ +gboolean purple_plugin_is_internal(const PurplePlugin *plugin); + +/** + * Returns a list of plugins that depend on a particular plugin. + * + * @param plugin The plugin whose dependent plugins are returned. + * + * @constreturn The list of a plugins that depend on the specified plugin. + */ +GSList *purple_plugin_get_dependent_plugins(const PurplePlugin *plugin); + +/*@}*/ + +/**************************************************************************/ +/** @name PluginInfo API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the PurplePluginInfo object. + */ +GType purple_plugin_info_get_type(void); + +/** + * Creates a new #PurplePluginInfo instance to be returned from + * gplugin_plugin_query() of a plugin, using the provided name/value pairs. + * + * All properties except "id" and "purple-abi" are optional. + * + * Valid property names are: \n + * "id" (string) The ID of the plugin. \n + * "name" (string) The translated name of the plugin. \n + * "version" (string) Version of the plugin. \n + * "category" (string) Primary category of the plugin. \n + * "summary" (string) Brief summary of the plugin. \n + * "description" (string) Full description of the plugin. \n + * "authors" (const gchar * const *) A NULL-terminated list of plugin + * authors. format: First Last <user@domain.com> \n + * "website" (string) Website of the plugin. \n + * "icon" (string) Path to a plugin's icon. \n + * "license-id" (string) Short name of the plugin's license. This should + * either be an identifier of the license from + * http://dep.debian.net/deps/dep5/#license-specification + * or "Other" for custom licenses. \n + * "license-text" (string) The text of the plugin's license, if unlisted on + * DEP5. \n + * "license-url" (string) The plugin's license URL, if unlisted on DEP5. \n + * "dependencies" (const gchar * const *) A NULL-terminated list of plugin + * IDs required by the plugin. \n + * "abi-version" (guint32) The ABI version required by the plugin. \n + * "actions-cb" (PurplePluginActionsCb) Callback that returns a list of + * actions the plugin can perform. \n + * "extra-cb" (PurplePluginExtraCb) Callback that returns a newly + * allocated string denoting extra information + * about a plugin. \n + * "pref-frame-cb" (PurplePluginPrefFrameCb) Callback that returns a + * preferences frame for the plugin. \n + * "pref-request-cb" (PurplePluginPrefRequestCb) Callback that returns a + * preferences request handle for the plugin. \n + * "flags" (PurplePluginInfoFlags) The flags for a plugin. \n + * + * @param first_property The first property name + * @param ... The value of the first property, followed optionally by more + * name/value pairs, followed by @c NULL + * + * @return A new #PurplePluginInfo instance. + * + * @see PURPLE_PLUGIN_ABI_VERSION + * @see @ref plugin-ids + */ +PurplePluginInfo *purple_plugin_info_new(const char *first_property, ...) + G_GNUC_NULL_TERMINATED; + +/** + * Returns a plugin's ID. + * + * @param info The plugin's info instance. + * + * @return The plugin's ID. + */ +const gchar *purple_plugin_info_get_id(const PurplePluginInfo *info); + +/** + * Returns a plugin's translated name. + * + * @param info The plugin's info instance. + * + * @return The name of the plugin, or @c NULL. + */ +const gchar *purple_plugin_info_get_name(const PurplePluginInfo *info); + +/** + * Returns a plugin's version. + * + * @param info The plugin's info instance. + * + * @return The version of the plugin, or @c NULL. + */ +const gchar *purple_plugin_info_get_version(const PurplePluginInfo *info); + +/** + * Returns a plugin's primary category. + * + * @param info The plugin's info instance. + * + * @return The primary category of the plugin, or @c NULL. + */ +const gchar *purple_plugin_info_get_category(const PurplePluginInfo *info); + +/** + * Returns a plugin's summary. + * + * @param info The plugin's info instance. + * + * @return The summary of the plugin, or @c NULL. + */ +const gchar *purple_plugin_info_get_summary(const PurplePluginInfo *info); + +/** + * Returns a plugin's description. + * + * @param info The plugin's info instance. + * + * @return The description of the plugin, or @c NULL. + */ +const gchar *purple_plugin_info_get_description(const PurplePluginInfo *info); + +/** + * Returns a NULL-terminated list of the plugin's authors. + * + * @param info The plugin's info instance. + * + * @return The authors of the plugin, or @c NULL. + */ +const gchar * const * +purple_plugin_info_get_authors(const PurplePluginInfo *info); + +/** + * Returns a plugin's website. + * + * @param info The plugin's info instance. + * + * @return The website of the plugin, or @c NULL. + */ +const gchar *purple_plugin_info_get_website(const PurplePluginInfo *info); + +/** + * Returns the path to a plugin's icon. + * + * @param info The plugin's info instance. + * + * @return The path to the plugin's icon, or @c NULL. + */ +const gchar *purple_plugin_info_get_icon(const PurplePluginInfo *info); + +/** + * Returns a short name of the plugin's license. + * + * @param info The plugin's info instance. + * + * @return The license name of the plugin, or @c NULL. + */ +const gchar *purple_plugin_info_get_license_id(const PurplePluginInfo *info); + +/** + * Returns the text of a plugin's license. + * + * @param info The plugin's info instance. + * + * @return The license text of the plugin, or @c NULL. + */ +const gchar *purple_plugin_info_get_license_text(const PurplePluginInfo *info); + +/** + * Returns the URL of a plugin's license. + * + * @param info The plugin's info instance. + * + * @return The license URL of the plugin, or @c NULL. + */ +const gchar *purple_plugin_info_get_license_url(const PurplePluginInfo *info); + +/** + * Returns a NULL-terminated list of IDs of plugins required by a plugin. + * + * @param info The plugin's info instance. + * + * @return The dependencies of the plugin, or @c NULL. + */ +const gchar * const * +purple_plugin_info_get_dependencies(const PurplePluginInfo *info); + +/** + * Returns the required purple ABI version for a plugin. + * + * @param info The plugin's info instance. + * + * @return The required purple ABI version for the plugin. + */ +guint32 purple_plugin_info_get_abi_version(const PurplePluginInfo *info); + +/** + * Returns the callback that retrieves the list of actions a plugin can perform + * at that moment. + * + * @param info The plugin info to get the callback from. + * + * @return The callback that returns a list of #PurplePluginAction + * instances corresponding to the actions a plugin can perform. + */ +PurplePluginActionsCb +purple_plugin_info_get_actions_cb(const PurplePluginInfo *info); + +/** + * Returns a callback that gives extra information about a plugin. You must + * free the string returned by this callback. + * + * @param info The plugin info to get extra information from. + * + * @return The callback that returns extra information about a plugin. + */ +PurplePluginExtraCb +purple_plugin_info_get_extra_cb(const PurplePluginInfo *info); + +/** + * Returns the callback that retrieves the preferences frame for a plugin, set + * via the "pref-frame-cb" property of the plugin info. + * + * @param info The plugin info to get the callback from. + * + * @return The callback that returns the preferences frame. + */ +PurplePluginPrefFrameCb +purple_plugin_info_get_pref_frame_cb(const PurplePluginInfo *info); + +/** + * Returns the callback that retrieves the preferences request handle for a + * plugin, set via the "pref-request-cb" property of the plugin info. + * + * @param info The plugin info to get the callback from. + * + * @return The callback that returns the preferences request handle. + */ +PurplePluginPrefRequestCb +purple_plugin_info_get_pref_request_cb(const PurplePluginInfo *info); + +/** + * Returns the plugin's flags. + * + * @param info The plugin's info instance. + * + * @return The flags of the plugin. + */ +PurplePluginInfoFlags +purple_plugin_info_get_flags(const PurplePluginInfo *info); + +/** + * Returns an error in the plugin info that would prevent the plugin from being + * loaded. + * + * @param info The plugin info. + * + * @return The plugin info error, or @c NULL. + */ +const gchar *purple_plugin_info_get_error(const PurplePluginInfo *info); + +/** + * Set the UI data associated with a plugin. + * + * @param info The plugin's info instance. + * @param ui_data A pointer to associate with this object. + */ +void purple_plugin_info_set_ui_data(PurplePluginInfo *info, gpointer ui_data); + +/** + * Returns the UI data associated with a plugin. + * + * @param info The plugin's info instance. + * + * @return The UI data associated with this plugin. This is a + * convenience field provided to the UIs--it is not + * used by the libpurple core. + */ +gpointer purple_plugin_info_get_ui_data(const PurplePluginInfo *info); + +/*@}*/ + +/**************************************************************************/ +/** @name PluginAction API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the PurplePluginAction boxed structure. + */ +GType purple_plugin_action_get_type(void); + +/** + * Allocates and returns a new PurplePluginAction. Use this to add actions in a + * list in the "actions-cb" callback for your plugin. + * + * @param label The description of the action to show to the user. + * @param callback The callback to call when the user selects this action. + */ +PurplePluginAction *purple_plugin_action_new(const char* label, + PurplePluginActionCb callback); + +/** + * Frees a PurplePluginAction + * + * @param action The PurplePluginAction to free. + */ +void purple_plugin_action_free(PurplePluginAction *action); + +/*@}*/ + +/**************************************************************************/ +/** @name Plugins API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns a list of all plugins, whether loaded or not. + * + * @return A list of all plugins. The list is owned by the caller, and must be + * g_list_free()d to avoid leaking the nodes. + */ +GList *purple_plugins_find_all(void); + +/** + * Returns a list of all loaded plugins. + * + * @constreturn A list of all loaded plugins. + */ +GList *purple_plugins_get_loaded(void); + +/** + * Add a new directory to search for plugins + * + * @param path The new search path. + */ +void purple_plugins_add_search_path(const gchar *path); + +/** + * Forces a refresh of all plugins found in the search paths, and loads plugins + * that are to be auto-loaded. + * + * @see purple_plugins_add_search_path() + */ +void purple_plugins_refresh(void); + +/** + * Finds a plugin with the specified plugin ID. + * + * @param id The plugin ID. + * + * @return The plugin if found, or @c NULL if not found. + */ +PurplePlugin *purple_plugins_find_plugin(const gchar *id); + +/** + * Finds a plugin with the specified filename (filename with a path). + * + * @param filename The plugin filename. + * + * @return The plugin if found, or @c NULL if not found. + */ +PurplePlugin *purple_plugins_find_by_filename(const char *filename); + +/** + * Saves the list of loaded plugins to the specified preference key. + * Plugins that are set to auto-load are not saved. + * + * @param key The preference key to save the list of plugins to. + */ +void purple_plugins_save_loaded(const char *key); + +/** + * Attempts to load all the plugins in the specified preference key + * that were loaded when purple last quit. + * + * @param key The preference key containing the list of plugins. + */ +void purple_plugins_load_saved(const char *key); + +/*@}*/ + +/**************************************************************************/ +/** @name Plugins Subsystem API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the plugin subsystem handle. + * + * @return The plugin sybsystem handle. + */ +void *purple_plugins_get_handle(void); + +/** + * Initializes the plugin subsystem + */ +void purple_plugins_init(void); + +/** + * Uninitializes the plugin subsystem + */ +void purple_plugins_uninit(void); + +/*@}*/ + +G_END_DECLS + +#endif /* _PURPLE_PLUGINS_H_ */
--- a/libpurple/plugins/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -1,32 +1,19 @@ -DIST_SUBDIRS = mono perl ssl tcl keyrings - -if USE_PERL -PERL_DIR = perl -endif - -if USE_TCL -TCL_DIR = tcl -endif +DIST_SUBDIRS = ssl keyrings if ENABLE_DBUS DBUS_LTLIB = dbus-example.la endif -if USE_MONO -MONO_DIR = mono -endif - SUBDIRS = \ - $(MONO_DIR) \ - $(PERL_DIR) \ ssl \ - $(TCL_DIR) \ keyrings plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) autoaccept_la_LDFLAGS = -module -avoid-version buddynote_la_LDFLAGS = -module -avoid-version +caesarcipher_la_LDFLAGS = -module -avoid-version +caesarcipher_consumer_la_LDFLAGS = -module -avoid-version ciphertest_la_LDFLAGS = -module -avoid-version codeinline_la_LDFLAGS = -module -avoid-version debug_example_la_LDFLAGS = -module -avoid-version @@ -62,6 +49,8 @@ $(DBUS_LTLIB) noinst_LTLIBRARIES = \ + caesarcipher.la \ + caesarcipher_consumer.la \ ciphertest.la \ codeinline.la \ debug_example.la \ @@ -74,6 +63,8 @@ autoaccept_la_SOURCES = autoaccept.c buddynote_la_SOURCES = buddynote.c +caesarcipher_la_SOURCES = caesarcipher.c caesarcipher.h +caesarcipher_consumer_la_SOURCES = caesarcipher_consumer.c ciphertest_la_SOURCES = ciphertest.c codeinline_la_SOURCES = codeinline.c debug_example_la_SOURCES = debug_example.c @@ -93,6 +84,8 @@ autoaccept_la_LIBADD = $(GLIB_LIBS) buddynote_la_LIBADD = $(GLIB_LIBS) +caesarcipher_la_LIBADD = $(GLIB_LIBS) +caesarcipher_consumer_la_LIBADD = $(GLIB_LIBS) ciphertest_la_LIBADD = $(GLIB_LIBS) codeinline_la_LIBADD = $(GLIB_LIBS) idle_la_LIBADD = $(GLIB_LIBS) @@ -136,8 +129,6 @@ dbus-buddyicons-example.py \ filectl.c \ fortuneprofile.pl \ - ipc-test-client.c \ - ipc-test-server.c \ startup.py AM_CPPFLAGS = \ @@ -146,6 +137,7 @@ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(PLUGIN_CFLAGS) \ $(DBUS_CFLAGS) \ $(NSS_CFLAGS)
--- a/libpurple/plugins/autoaccept.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/autoaccept.c Fri Jan 31 18:02:20 2014 +0530 @@ -21,17 +21,18 @@ #define PLUGIN_ID "core-plugin_pack-autoaccept" #define PLUGIN_NAME N_("Autoaccept") +#define PLUGIN_CATEGORY N_("Utility") #define PLUGIN_STATIC_NAME Autoaccept #define PLUGIN_SUMMARY N_("Auto-accept file transfer requests from selected users.") #define PLUGIN_DESCRIPTION N_("Auto-accept file transfer requests from selected users.") -#define PLUGIN_AUTHOR "Sadrul H Chowdhury <sadrul@users.sourceforge.net>" +#define PLUGIN_AUTHORS {"Sadrul H Chowdhury <sadrul@users.sourceforge.net>", NULL} /* System headers */ #include <glib.h> #include <glib/gstdio.h> /* Purple headers */ -#include <plugin.h> +#include <plugins.h> #include <version.h> #include <buddylist.h> @@ -227,37 +228,6 @@ (*menu) = g_list_prepend(*menu, action); } -static gboolean -plugin_load(PurplePlugin *plugin) -{ - /* migrate the old pref (we should only care if the plugin is actually *used*) */ - /* - * TODO: We should eventually call purple_prefs_remove(PREFS_STRANGER_OLD) - * to clean up after ourselves, but we don't want to do it yet - * so that we don't break users who share a .purple directory - * between old libpurple clients and new libpurple clients. - * --Mark Doliner, 2011-01-03 - */ - if (!purple_prefs_exists(PREF_STRANGER)) { - if (purple_prefs_exists(PREF_STRANGER_OLD) && purple_prefs_get_bool(PREF_STRANGER_OLD)) - purple_prefs_add_int(PREF_STRANGER, FT_REJECT); - else - purple_prefs_set_int(PREF_STRANGER, FT_ASK); - } - - purple_signal_connect(purple_xfers_get_handle(), "file-recv-request", plugin, - PURPLE_CALLBACK(file_recv_request_cb), plugin); - purple_signal_connect(purple_blist_get_handle(), "blist-node-extended-menu", plugin, - PURPLE_CALLBACK(context_menu), plugin); - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - return TRUE; -} - static PurplePluginPrefFrame * get_plugin_pref_frame(PurplePlugin *plugin) { @@ -296,53 +266,29 @@ return frame; } -static PurplePluginUiInfo prefs_info = { - get_plugin_pref_frame, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, /* Magic */ - PURPLE_MAJOR_VERSION, /* Purple Major Version */ - PURPLE_MINOR_VERSION, /* Purple Minor Version */ - PURPLE_PLUGIN_STANDARD, /* plugin type */ - NULL, /* ui requirement */ - 0, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = PLUGIN_AUTHORS; - PLUGIN_ID, /* plugin id */ - PLUGIN_NAME, /* name */ - DISPLAY_VERSION, /* version */ - PLUGIN_SUMMARY, /* summary */ - PLUGIN_DESCRIPTION, /* description */ - PLUGIN_AUTHOR, /* author */ - PURPLE_WEBSITE, /* website */ - - plugin_load, /* load */ - plugin_unload, /* unload */ - NULL, /* destroy */ + return purple_plugin_info_new( + "id", PLUGIN_ID, + "name", PLUGIN_NAME, + "version", DISPLAY_VERSION, + "category", PLUGIN_CATEGORY, + "summary", PLUGIN_SUMMARY, + "description", PLUGIN_DESCRIPTION, + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} - NULL, /* ui_info */ - NULL, /* extra_info */ - &prefs_info, /* prefs_info */ - NULL, /* actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) { +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ char *dirname; dirname = g_build_filename(purple_user_dir(), "autoaccept", NULL); @@ -352,6 +298,33 @@ purple_prefs_add_bool(PREF_NEWDIR, TRUE); purple_prefs_add_bool(PREF_ESCAPE, TRUE); g_free(dirname); + + /* migrate the old pref (we should only care if the plugin is actually *used*) */ + /* + * TODO: We should eventually call purple_prefs_remove(PREFS_STRANGER_OLD) + * to clean up after ourselves, but we don't want to do it yet + * so that we don't break users who share a .purple directory + * between old libpurple clients and new libpurple clients. + * --Mark Doliner, 2011-01-03 + */ + if (!purple_prefs_exists(PREF_STRANGER)) { + if (purple_prefs_exists(PREF_STRANGER_OLD) && purple_prefs_get_bool(PREF_STRANGER_OLD)) + purple_prefs_add_int(PREF_STRANGER, FT_REJECT); + else + purple_prefs_set_int(PREF_STRANGER, FT_ASK); + } + + purple_signal_connect(purple_xfers_get_handle(), "file-recv-request", plugin, + PURPLE_CALLBACK(file_recv_request_cb), plugin); + purple_signal_connect(purple_blist_get_handle(), "blist-node-extended-menu", plugin, + PURPLE_CALLBACK(context_menu), plugin); + return TRUE; } -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +PURPLE_PLUGIN_INIT(PLUGIN_STATIC_NAME, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/buddynote.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/buddynote.c Fri Jan 31 18:02:20 2014 +0530 @@ -65,8 +65,31 @@ *m = g_list_append(*m, bna); } +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Stu Tomlinson <stu@nosnilmot.com>", + NULL + }; + + return purple_plugin_info_new( + "id", "core-plugin_pack-buddynote", + "name", N_("Buddy Notes"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Store notes on particular buddies."), + "description", N_("Adds the option to store notes for buddies on your " + "buddy list."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { purple_signal_connect(purple_blist_get_handle(), "blist-node-extended-menu", @@ -75,42 +98,10 @@ return TRUE; } -static PurplePluginInfo info = +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, /**< major version */ - PURPLE_MINOR_VERSION, /**< minor version */ - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - "core-plugin_pack-buddynote", /**< id */ - N_("Buddy Notes"), /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Store notes on particular buddies."), /**< summary */ - N_("Adds the option to store notes for buddies " - "on your buddy list."), /**< description */ - "Stu Tomlinson <stu@nosnilmot.com>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - - -static void -init_plugin(PurplePlugin *plugin) { + return TRUE; } -PURPLE_INIT_PLUGIN(buddynote, init_plugin, info) +PURPLE_PLUGIN_INIT(buddynote, plugin_query, plugin_load, plugin_unload);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/caesarcipher.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,216 @@ +/* + * An example plugin that demonstrates exporting of a cipher object + * type to be used in another plugin. + * + * This plugin only provides the CaesarCipher type. See caesarcipher_consumer + * plugin for its use. + * + * Copyright (C) 2013, Ankit Vani <a@nevitus.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02111-1301, USA. + */ + +/* When writing a third-party plugin, do not include libpurple's internal.h + * included below. This file is for internal libpurple use only. We're including + * it here for our own convenience. */ +#include "internal.h" + +/* This file defines PURPLE_PLUGINS and includes all the libpurple headers */ +#include <purple.h> + +#include "caesarcipher.h" + +#define CAESAR_CIPHER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), CAESAR_TYPE_CIPHER, CaesarCipherPrivate)) + +typedef struct { + gint8 offset; +} CaesarCipherPrivate; + +enum { + PROP_NONE, + PROP_OFFSET, + PROP_LAST, +}; + +/****************************************************************************** + * Cipher stuff + *****************************************************************************/ +static void +caesar_shift(const guchar input[], size_t in_len, guchar output[], gint8 offset) +{ + size_t i; + + for (i = 0; i < in_len; ++i) { + if (input[i] >= 'a' && input[i] <= 'z') + output[i] = (((input[i] - 'a') + offset + 26) % 26) + 'a'; + else if (input[i] >= 'A' && input[i] <= 'Z') + output[i] = (((input[i] - 'A') + offset + 26) % 26) + 'A'; + else + output[i] = input[i]; + } + + output[i] = '\0'; +} + +static void +caesar_cipher_set_offset(PurpleCipher *cipher, gint8 offset) +{ + CaesarCipherPrivate *priv = CAESAR_CIPHER_GET_PRIVATE(cipher); + + priv->offset = offset % 26; +} + +static ssize_t +caesar_cipher_encrypt(PurpleCipher *cipher, const guchar input[], size_t in_len, + guchar output[], size_t out_size) +{ + CaesarCipherPrivate *priv = CAESAR_CIPHER_GET_PRIVATE(cipher); + + g_return_val_if_fail(out_size > in_len, -1); + + caesar_shift(input, in_len, output, priv->offset); + + return in_len; +} + +static ssize_t +caesar_cipher_decrypt(PurpleCipher *cipher, const guchar input[], size_t in_len, + guchar output[], size_t out_size) +{ + CaesarCipherPrivate *priv = CAESAR_CIPHER_GET_PRIVATE(cipher); + + g_return_val_if_fail(out_size > in_len, -1); + + caesar_shift(input, in_len, output, -priv->offset); + + return in_len; +} + +static void +caesar_cipher_set_key(PurpleCipher *cipher, const guchar *key, size_t len) +{ + caesar_cipher_set_offset(cipher, len); +} + +/****************************************************************************** + * Object stuff + *****************************************************************************/ +static void +caesar_cipher_set_property(GObject *obj, guint param_id, const GValue *value, + GParamSpec *pspec) +{ + PurpleCipher *cipher = PURPLE_CIPHER(obj); + + switch(param_id) { + case PROP_OFFSET: +#if GLIB_CHECK_VERSION(2, 32, 0) + caesar_cipher_set_offset(cipher, g_value_get_schar(value)); +#else + caesar_cipher_set_offset(cipher, g_value_get_char(value)); +#endif + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +/* initialize the cipher object. used in PURPLE_DEFINE_TYPE. */ +static void +caesar_cipher_init(CaesarCipher *cipher) +{ + /* classic caesar cipher uses a shift of 3 */ + CAESAR_CIPHER_GET_PRIVATE(cipher)->offset = 3; +} + +/* initialize the cipher class. used in PURPLE_DEFINE_TYPE. */ +static void +caesar_cipher_class_init(PurpleCipherClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + obj_class->set_property = caesar_cipher_set_property; + g_type_class_add_private(obj_class, sizeof(CaesarCipherPrivate)); + + klass->encrypt = caesar_cipher_encrypt; + klass->decrypt = caesar_cipher_decrypt; + klass->set_key = caesar_cipher_set_key; + + g_object_class_install_property(obj_class, PROP_OFFSET, + g_param_spec_char("offset", "offset", + "The offset by which to shift alphabets", + -25, 25, 3, + G_PARAM_WRITABLE) + ); +} + +/* + * define the CaesarCipher type. this function defines + * caesar_cipher_get_type() and caesar_cipher_register_type(). + */ +PURPLE_DEFINE_TYPE(CaesarCipher, caesar_cipher, PURPLE_TYPE_CIPHER); + +G_MODULE_EXPORT PurpleCipher * +caesar_cipher_new(void) +{ + return g_object_new(CAESAR_TYPE_CIPHER, NULL); +} + +/****************************************************************************** + * Plugin stuff + *****************************************************************************/ +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Ankit Vani <a@nevitus.org>", + NULL + }; + + return purple_plugin_info_new( + "id", "core-caesarcipher", + "name", N_("Caesar Cipher Provider Example"), + "version", DISPLAY_VERSION, + "category", N_("Example"), + "summary", N_("An example plugin that demonstrates exporting of a " + "cipher type."), + "description", N_("An example plugin that demonstrates exporting of " + "a cipher object type to be used in another " + "plugin."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + /* register the CaesarCipher type in the type system */ + caesar_cipher_register_type(plugin); + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +PURPLE_PLUGIN_INIT(caesarcipher, plugin_query, plugin_load, plugin_unload);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/caesarcipher.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,67 @@ +/* + * An example plugin that demonstrates exporting of a cipher object + * type to be used in another plugin. + * + * Copyright (C) 2013, Ankit Vani <a@nevitus.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02111-1301, USA. + */ + +#ifndef _CAESAR_CIPHER_H_ +#define _CAESAR_CIPHER_H_ + +#include "cipher.h" + +#define CAESAR_TYPE_CIPHER (caesar_cipher_get_type()) +#define CAESAR_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), CAESAR_TYPE_CIPHER, CaesarCipher)) +#define CAESAR_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), CAESAR_TYPE_CIPHER, CaesarCipherClass)) +#define CAESAR_IS_CIPHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), CAESAR_TYPE_CIPHER)) +#define CAESAR_IS_CIPHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), CAESAR_TYPE_CIPHER)) +#define CAESAR_CIPHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), CAESAR_TYPE_CIPHER, CaesarCipherClass)) + +typedef struct _CaesarCipher CaesarCipher; +typedef struct _CaesarCipherClass CaesarCipherClass; + +/* + * A caesar cipher object. + * + * The Caesar cipher is one of the simplest and most widely known encryption + * techniques. It is a type of substitution cipher in which each letter in the + * plaintext is replaced by a letter some fixed number of positions down the + * alphabet. + */ +struct _CaesarCipher +{ + PurpleCipher gparent; +}; + +/* The base class for all CaesarCipher's. */ +struct _CaesarCipherClass +{ + PurpleCipherClass gparent; +}; + +/* + * Returns the GType for the CaesarCipher object. + */ +G_MODULE_EXPORT GType caesar_cipher_get_type(void); + +/* + * Creates a new CaesarCipher instance and returns it. + */ +G_MODULE_EXPORT PurpleCipher *caesar_cipher_new(void); + +#endif /* _CAESAR_CIPHER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/caesarcipher_consumer.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,117 @@ +/* + * An example plugin that demonstrates usage of a cipher object type + * registered in another plugin. + * + * This plugin requires the caesarcipher plugin to be loaded to use the + * CaesarCipher type. + * + * Copyright (C) 2013, Ankit Vani <a@nevitus.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02111-1301, USA. + */ + +/* When writing a third-party plugin, do not include libpurple's internal.h + * included below. This file is for internal libpurple use only. We're including + * it here for our own convenience. */ +#include "internal.h" + +/* This file defines PURPLE_PLUGINS and includes all the libpurple headers */ +#include <purple.h> + +#include "caesarcipher.h" + +static void +debug_cipher(PurpleCipher *cipher, const gchar *input) +{ + gchar ciphertext[512], plaintext[512]; + + purple_debug_info("caesarcipher_consumer", "Encrypting..."); + + purple_debug_info("caesarcipher_consumer", "INPUT: %s\n", input); + purple_cipher_encrypt(cipher, (const guchar *)input, strlen(input), + (guchar *)ciphertext, 512); + purple_debug_info("caesarcipher_consumer", "OUTPUT: %s\n", ciphertext); + + purple_debug_info("caesarcipher_consumer", "Decrypting..."); + + purple_debug_info("caesarcipher_consumer", "INPUT: %s\n", ciphertext); + purple_cipher_decrypt(cipher, (const guchar *)ciphertext, + strlen(ciphertext), (guchar *)plaintext, 512); + purple_debug_info("caesarcipher_consumer", "OUTPUT: %s\n", plaintext); +} + +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Ankit Vani <a@nevitus.org>", + NULL + }; + + /* we need to ensure the object provider is loaded for its type to be + * registered in the type system. */ + const gchar * const dependencies[] = { + "core-caesarcipher", + NULL + }; + + return purple_plugin_info_new( + "id", "core-caesarcipher_consumer", + "name", N_("Caesar Cipher Consumer Example"), + "version", DISPLAY_VERSION, + "category", N_("Example"), + "summary", N_("An example plugin that demonstrates usage of a " + "loaded cipher type."), + "description", N_("An example plugin that demonstrates usage of " + "a cipher object type registered in another " + "plugin."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "dependencies", dependencies, + + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + PurpleCipher *cipher = caesar_cipher_new(); + purple_debug_info("caesarcipher_consumer", "Created caesar cipher " + "object.\n"); + + debug_cipher(cipher, "Input string for cipher!"); + + purple_cipher_set_key(cipher, NULL, 13); + purple_debug_info("caesarcipher_consumer", "Offset set to 13.\n"); + + debug_cipher(cipher, "An0ther input 4 cipher.."); + + g_object_unref(cipher); + purple_debug_info("caesarcipher_consumer", "Destroyed caesar cipher " + "object.\n"); + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +PURPLE_PLUGIN_INIT(caesarcipher_consumer, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/ciphertest.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/ciphertest.c Fri Jan 31 18:02:20 2014 +0530 @@ -33,7 +33,7 @@ #include <string.h> #include "debug.h" -#include "plugin.h" +#include "plugins.h" #include "version.h" #include "util.h" @@ -580,8 +580,29 @@ /************************************************************************** * Plugin stuff **************************************************************************/ +static PurplePluginInfo * +plugin_query(GError **error) { + const gchar * const authors[] = { + "Gary Kramlich <amc_grim@users.sf.net>", + NULL + }; + + return purple_plugin_info_new( + "id", "core-cipher-test", + "name", N_("Cipher Test"), + "version", DISPLAY_VERSION, + "category", N_("Testing"), + "summary", N_("Tests the ciphers that ship with libpurple."), + "description", N_("Tests the ciphers that ship with libpurple."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) { +plugin_load(PurplePlugin *plugin, GError **error) { cipher_test_md5(); cipher_test_sha1(); cipher_test_digest(); @@ -592,48 +613,8 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) { +plugin_unload(PurplePlugin *plugin, GError **error) { return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "core-cipher-test", /**< id */ - N_("Cipher Test"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Tests the ciphers that ship with libpurple."), - /** description */ - N_("Tests the ciphers that ship with libpurple."), - "Gary Kramlich <amc_grim@users.sf.net>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) { -} - -PURPLE_INIT_PLUGIN(cipher_test, init_plugin, info) +PURPLE_PLUGIN_INIT(cipher_test, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/codeinline.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/codeinline.c Fri Jan 31 18:02:20 2014 +0530 @@ -21,7 +21,7 @@ */ #include "internal.h" -#include "plugin.h" +#include "plugins.h" #include "notify.h" #include "util.h" #include "version.h" @@ -44,8 +44,32 @@ return FALSE; } +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Sean Egan <seanegan@gmail.com>", + NULL + }; + + return purple_plugin_info_new( + "id", "codeinline", + "name", "Code Inline", + "version", "1.0", + "category", "Formatting", + "summary", "Formats text as code", + "description", "Changes the formatting of any outgoing text such " + "that anything underlined will be received green and " + "monospace.", + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { void *handle = purple_conversations_get_handle(); plugin_handle = plugin; @@ -57,42 +81,10 @@ return TRUE; } - -static PurplePluginInfo info = +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - NULL, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - "codeinline", - "Code Inline", - "1.0", - "Formats text as code", - "Changes the formatting of any outgoing text such that " - "anything underlined will be received green and monospace.", - "Sean Egan <seanegan@gmail.com>", - PURPLE_WEBSITE, - plugin_load, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - /* padding */ - NULL, - NULL, - NULL, - NULL -}; + return TRUE; +} - static void - init_plugin(PurplePlugin *plugin) - { - } - -PURPLE_INIT_PLUGIN(codeinline, init_plugin, info) +PURPLE_PLUGIN_INIT(codeinline, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/dbus-example.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/dbus-example.c Fri Jan 31 18:02:20 2014 +0530 @@ -107,8 +107,30 @@ /* And now standard plugin stuff */ +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Piotr Zielinski (http://cl.cam.ac.uk/~pz215)", + NULL + }; + + return purple_plugin_info_new( + "id", "dbus-example", + "name", N_("DBus Example"), + "version", DISPLAY_VERSION, + "category", N_("Example"), + "summary", N_("DBus Plugin Example"), + "description", N_("DBus Plugin Example"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { PURPLE_DBUS_RETURN_FALSE_IF_DISABLED(plugin); @@ -131,7 +153,7 @@ static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { g_free(hello.text); @@ -141,45 +163,4 @@ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "dbus-example", /**< id */ - N_("DBus Example"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("DBus Plugin Example"), - /** description */ - N_("DBus Plugin Example"), - "Piotr Zielinski (http://cl.cam.ac.uk/~pz215)", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(dbus_example, init_plugin, info) +PURPLE_PLUGIN_INIT(dbus_example, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/debug_example.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/debug_example.c Fri Jan 31 18:02:20 2014 +0530 @@ -35,14 +35,32 @@ /* Common practice in third-party plugins is to define convenience macros for * many of the fields of the plugin info struct, so we'll do that for the * purposes of demonstration. */ -#define PLUGIN_AUTHOR "John Bailey <rekkanoryo@cpw.pidgin.im>" +#define PLUGIN_AUTHORS { "John Bailey <rekkanoryo@cpw.pidgin.im>", NULL } + +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = PLUGIN_AUTHORS; -/* As we've covered before, libpurple calls this function, if present, when it - * loads the plugin. Here we're using it to show off the capabilities of the - * debug API and just blindly returning TRUE to tell libpurple it's safe to - * continue loading. */ + return purple_plugin_info_new( + "id", PLUGIN_ID, + "name", "Debug API Example", + "version", DISPLAY_VERSION, + "category", "Example", + "summary", "Debug API Example", + "description", "Debug API Example", + "authors", authors, + "website", "https://pidgin.im", + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + +/* As we've covered before, this function is called when the plugin is loaded. + * Here we're using it to show off the capabilities of the debug API and just + * blindly returning TRUE to tell libpurple it's safe to continue loading. */ static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { /* Define these for convenience--we're just using them to show the * similarities of the debug functions to the standard printf(). */ @@ -74,42 +92,10 @@ return TRUE; } -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, /* magic number */ - PURPLE_MAJOR_VERSION, /* purple major */ - PURPLE_MINOR_VERSION, /* purple minor */ - PURPLE_PLUGIN_STANDARD, /* plugin type */ - NULL, /* UI requirement */ - 0, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ - - PLUGIN_ID, /* id */ - "Debug API Example", /* name */ - DISPLAY_VERSION, /* version */ - "Debug API Example", /* summary */ - "Debug API Example", /* description */ - PLUGIN_AUTHOR, /* author */ - "https://pidgin.im", /* homepage */ - - plugin_load, /* load */ - NULL, /* unload */ - NULL, /* destroy */ - - NULL, /* ui info */ - NULL, /* extra info */ - NULL, /* prefs info */ - NULL, /* actions */ - NULL, /* reserved */ - NULL, /* reserved */ - NULL, /* reserved */ - NULL /* reserved */ -}; - -static void -init_plugin(PurplePlugin *plugin) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { + return TRUE; } -PURPLE_INIT_PLUGIN(debugexample, init_plugin, info) - +PURPLE_PLUGIN_INIT(debugexample, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/filectl.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/filectl.c Fri Jan 31 18:02:20 2014 +0530 @@ -15,13 +15,13 @@ #include <string.h> #include <ctype.h> +#include "internal.h" #include "account.h" #include "config.h" #include "core.h" #include "conversation.h" #include "debug.h" #include "eventloop.h" -#include "internal.h" #include "util.h" #include "version.h" @@ -216,8 +216,30 @@ * EXPORTED FUNCTIONS */ +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Eric Warmenhoven <eric@warmenhoven.org>", + NULL + }; + + return purple_plugin_info_new( + "id", FILECTL_PLUGIN_ID, + "name", N_("File Control"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Allows control by entering commands in a file."), + "description", N_("Allows control by entering commands in a file."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { init_file(); check = purple_timeout_add_seconds(5, (GSourceFunc)check_file, NULL); @@ -226,45 +248,11 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { purple_timeout_remove(check); return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - FILECTL_PLUGIN_ID, /**< id */ - N_("File Control"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Allows control by entering commands in a file."), - /** description */ - N_("Allows control by entering commands in a file."), - "Eric Warmenhoven <eric@warmenhoven.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL /**< extra_info */ -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(filectl, init_plugin, info) +PURPLE_PLUGIN_INIT(filectl, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/helloworld.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/helloworld.c Fri Jan 31 18:02:20 2014 +0530 @@ -44,7 +44,7 @@ * get a list of plugin actions to use for the plugin. This function gives * libpurple that list of actions. */ static GList * -plugin_actions (PurplePlugin * plugin, gpointer context) +plugin_actions (PurplePlugin * plugin) { /* some C89 (a.k.a. ANSI C) compilers will warn if any variable declaration * includes an initilization that calls a function. To avoid that, we @@ -65,8 +65,35 @@ return list; } +static PurplePluginInfo * +plugin_query (GError ** error) +{ + const gchar * const authors[] = { + "John Bailey <rekkanoryo@cpw.pidgin.im>", /* correct author */ + NULL + }; + + /* For specific notes on the meanings of each of these members, consult the + C Plugin Howto on the website. */ + return purple_plugin_info_new ( + "id", "core-hello_world", + "name", "Hello World!", + "version", DISPLAY_VERSION, /* This constant is defined in config.h, but you shouldn't use it for your + own plugins. We use it here because it's our plugin. And we're lazy. */ + "category", "Example", + "summary", "Hello World Plugin", + "description", "Hello World Plugin", + "authors", authors, + "website", "http://helloworld.tld", + "abi-version", PURPLE_ABI_VERSION, + "actions-cb", plugin_actions, /* this tells libpurple the address of the function to call to get the list + of plugin actions. */ + NULL + ); +} + static gboolean -plugin_load (PurplePlugin * plugin) +plugin_load (PurplePlugin * plugin, GError ** error) { purple_notify_message (plugin, PURPLE_NOTIFY_MSG_INFO, "Hello World!", "This is the Hello World! plugin :)", NULL, NULL, @@ -75,47 +102,10 @@ return TRUE; } -/* For specific notes on the meanings of each of these members, consult the C Plugin Howto - * on the website. */ -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!", - DISPLAY_VERSION, /* This constant is defined in config.h, but you shouldn't use it for - your own plugins. We use it here because it's our plugin. And we're lazy. */ - - "Hello World Plugin", - "Hello World Plugin", - "John Bailey <rekkanoryo@cpw.pidgin.im>", /* correct author */ - "http://helloworld.tld", - - - plugin_load, - NULL, - NULL, - - NULL, - NULL, - NULL, - plugin_actions, /* this tells libpurple the address of the function to call - to get the list of plugin actions. */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin (PurplePlugin * plugin) +static gboolean +plugin_unload (PurplePlugin * plugin, GError ** error) { + return TRUE; } -PURPLE_INIT_PLUGIN (hello_world, init_plugin, info) +PURPLE_PLUGIN_INIT (hello_world, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/idle.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/idle.c Fri Jan 31 18:02:20 2014 +0530 @@ -27,7 +27,7 @@ #include "connection.h" #include "debug.h" #include "notify.h" -#include "plugin.h" +#include "plugins.h" #include "presence.h" #include "request.h" #include "server.h" @@ -51,12 +51,12 @@ static gboolean idleable_filter(PurpleAccount *account) { - PurplePlugin *prpl; + PurpleProtocol *protocol; - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - g_return_val_if_fail(prpl != NULL, FALSE); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + g_return_val_if_fail(protocol != NULL, FALSE); - return (PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->set_idle != NULL); + return PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, set_idle); } static void @@ -244,7 +244,7 @@ } static GList * -actions(PurplePlugin *plugin, gpointer context) +actions(PurplePlugin *plugin) { GList *l = NULL; PurplePluginAction *act = NULL; @@ -277,8 +277,33 @@ idled_accts = g_list_remove(idled_accts, account); } +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Eric Warmenhoven <eric@warmenhoven.org>", + NULL + }; + + return purple_plugin_info_new( + "id", IDLE_PLUGIN_ID, + /* This is a cultural reference. Dy'er Mak'er is a song by Led Zeppelin. + If that doesn't translate well into your language, drop the 's before translating. */ + "name", N_("I'dle Mak'er"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Allows you to hand-configure how long you've been idle"), + "description", N_("Allows you to hand-configure how long you've been idle"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "actions-cb", actions, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { purple_signal_connect(purple_connections_get_handle(), "signing-off", plugin, @@ -288,53 +313,12 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { unidle_all_action(NULL); return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - NULL, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - IDLE_PLUGIN_ID, +PURPLE_PLUGIN_INIT(idle, plugin_query, plugin_load, plugin_unload); - /* This is a cultural reference. Dy'er Mak'er is a song by Led Zeppelin. - If that doesn't translate well into your language, drop the 's before translating. */ - N_("I'dle Mak'er"), - DISPLAY_VERSION, - N_("Allows you to hand-configure how long you've been idle"), - N_("Allows you to hand-configure how long you've been idle"), - "Eric Warmenhoven <eric@warmenhoven.org>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, - NULL, - NULL, - NULL, - actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - - -PURPLE_INIT_PLUGIN(idle, init_plugin, info) -
--- a/libpurple/plugins/ipc-test-client.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -/* - * IPC test client plugin. - * - * Copyright (C) 2003 Christian Hammond. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02111-1301, USA. - */ -#include "internal.h" -#include "debug.h" -#include "plugin.h" -#include "version.h" - -#define IPC_TEST_CLIENT_PLUGIN_ID "core-ipc-test-client" - -static gboolean -plugin_load(PurplePlugin *plugin) -{ - PurplePlugin *server_plugin; - gboolean ok; - int result; - - server_plugin = purple_plugins_find_with_id("core-ipc-test-server"); - - if (server_plugin == NULL) - { - purple_debug_error("ipc-test-client", - "Unable to locate plugin core-ipc-test-server, " - "needed for IPC.\n"); - - return TRUE; - } - - result = (int)purple_plugin_ipc_call(server_plugin, "add", &ok, 36, 6); - - if (!ok) - { - purple_debug_error("ipc-test-client", - "Unable to call IPC function 'add' in " - "core-ipc-test-server plugin."); - - return TRUE; - } - - purple_debug_info("ipc-test-client", "36 + 6 = %d\n", result); - - result = (int)purple_plugin_ipc_call(server_plugin, "sub", &ok, 50, 8); - - if (!ok) - { - purple_debug_error("ipc-test-client", - "Unable to call IPC function 'sub' in " - "core-ipc-test-server plugin."); - - return TRUE; - } - - purple_debug_info("ipc-test-client", "50 - 8 = %d\n", result); - - return TRUE; -} - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - IPC_TEST_CLIENT_PLUGIN_ID, /**< id */ - N_("IPC Test Client"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Test plugin IPC support, as a client."), - /** description */ - N_("Test plugin IPC support, as a client. This locates the server " - "plugin and calls the commands registered."), - "Christian Hammond <chipx86@gnupdate.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - info.dependencies = g_list_append(info.dependencies, - "core-ipc-test-server"); -} - -PURPLE_INIT_PLUGIN(ipctestclient, init_plugin, info)
--- a/libpurple/plugins/ipc-test-server.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ -/* - * IPC test server plugin. - * - * Copyright (C) 2003 Christian Hammond. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02111-1301, USA. - */ -#define IPC_TEST_SERVER_PLUGIN_ID "core-ipc-test-server" - -#include "internal.h" -#include "debug.h" -#include "plugin.h" -#include "version.h" - -static int -add_func(int i1, int i2) -{ - purple_debug_misc("ipc-test-server", "Got %d, %d, returning %d\n", - i1, i2, i1 + i2); - return i1 + i2; -} - -static int -sub_func(int i1, int i2) -{ - purple_debug_misc("ipc-test-server", "Got %d, %d, returning %d\n", - i1, i2, i1 - i2); - return i1 - i2; -} - -static gboolean -plugin_load(PurplePlugin *plugin) -{ - purple_plugin_ipc_register(plugin, "add", PURPLE_CALLBACK(add_func), - purple_marshal_INT__INT_INT, - purple_value_new(PURPLE_TYPE_INT), 2, - purple_value_new(PURPLE_TYPE_INT), - purple_value_new(PURPLE_TYPE_INT)); - - purple_plugin_ipc_register(plugin, "sub", PURPLE_CALLBACK(sub_func), - purple_marshal_INT__INT_INT, - purple_value_new(PURPLE_TYPE_INT), 2, - purple_value_new(PURPLE_TYPE_INT), - purple_value_new(PURPLE_TYPE_INT)); - - return TRUE; -} - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - IPC_TEST_SERVER_PLUGIN_ID, /**< id */ - N_("IPC Test Server"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Test plugin IPC support, as a server."), - /** description */ - N_("Test plugin IPC support, as a server. This registers the IPC " - "commands."), - "Christian Hammond <chipx86@gnupdate.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(ipctestserver, init_plugin, info)
--- a/libpurple/plugins/joinpart.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/joinpart.c Fri Jan 31 18:02:20 2014 +0530 @@ -23,7 +23,7 @@ #include "internal.h" #include "conversation.h" #include "debug.h" -#include "plugin.h" +#include "plugins.h" #include "version.h" #define JOINPART_PLUGIN_ID "core-rlaager-joinpart" @@ -173,52 +173,6 @@ return TRUE; } -static gboolean plugin_load(PurplePlugin *plugin) -{ - void *conv_handle; - GHashTable *users; - guint id; - gpointer *data; - - users = g_hash_table_new_full((GHashFunc)joinpart_key_hash, - (GEqualFunc)joinpart_key_equal, - (GDestroyNotify)joinpart_key_destroy, - g_free); - - conv_handle = purple_conversations_get_handle(); - purple_signal_connect(conv_handle, "chat-user-joining", plugin, - PURPLE_CALLBACK(chat_user_joining_cb), users); - purple_signal_connect(conv_handle, "chat-user-leaving", plugin, - PURPLE_CALLBACK(chat_user_leaving_cb), users); - purple_signal_connect(conv_handle, "received-chat-msg", plugin, - PURPLE_CALLBACK(received_chat_msg_cb), users); - - /* Cleanup every 5 minutes */ - id = purple_timeout_add_seconds(60 * 5, (GSourceFunc)clean_users_hash, users); - - data = g_new(gpointer, 2); - data[0] = users; - data[1] = GUINT_TO_POINTER(id); - plugin->extra = data; - - return TRUE; -} - -static gboolean plugin_unload(PurplePlugin *plugin) -{ - gpointer *data = plugin->extra; - - /* Destroy the hash table. The core plugin code will - * disconnect the signals, and since Purple is single-threaded, - * we don't have to worry one will be called after this. */ - g_hash_table_destroy((GHashTable *)data[0]); - - purple_timeout_remove(GPOINTER_TO_UINT(data[1])); - g_free(data); - - return TRUE; -} - static PurplePluginPrefFrame * get_plugin_pref_frame(PurplePlugin *plugin) { @@ -250,64 +204,75 @@ return frame; } -static PurplePluginUiInfo prefs_info = { - get_plugin_pref_frame, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = +static PurplePluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ + const gchar * const authors[] = { + "Richard Laager <rlaager@pidgin.im>", + NULL + }; - JOINPART_PLUGIN_ID, /**< id */ - N_("Join/Part Hiding"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Hides extraneous join/part messages."), - /** description */ - N_("This plugin hides join/part messages in large " - "rooms, except for those users actively taking " - "part in a conversation."), - "Richard Laager <rlaager@pidgin.im>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ + return purple_plugin_info_new( + "id", JOINPART_PLUGIN_ID, + "name", N_("Join/Part Hiding"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Hides extraneous join/part messages."), + "description", N_("This plugin hides join/part messages in " + "large rooms, except for those users actively " + "taking part in a conversation."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} - NULL, /**< ui_info */ - NULL, /**< extra_info */ - &prefs_info, /**< prefs_info */ - NULL, /**< actions */ +static gboolean plugin_load(PurplePlugin *plugin, GError **error) +{ + void *conv_handle; + GHashTable *users; + guint id; - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ purple_prefs_add_none("/plugins/core/joinpart"); purple_prefs_add_int(DELAY_PREF, DELAY_DEFAULT); purple_prefs_add_int(THRESHOLD_PREF, THRESHOLD_DEFAULT); purple_prefs_add_bool(HIDE_BUDDIES_PREF, HIDE_BUDDIES_DEFAULT); + + users = g_hash_table_new_full((GHashFunc)joinpart_key_hash, + (GEqualFunc)joinpart_key_equal, + (GDestroyNotify)joinpart_key_destroy, + g_free); + + conv_handle = purple_conversations_get_handle(); + purple_signal_connect(conv_handle, "chat-user-joining", plugin, + PURPLE_CALLBACK(chat_user_joining_cb), users); + purple_signal_connect(conv_handle, "chat-user-leaving", plugin, + PURPLE_CALLBACK(chat_user_leaving_cb), users); + purple_signal_connect(conv_handle, "received-chat-msg", plugin, + PURPLE_CALLBACK(received_chat_msg_cb), users); + + /* Cleanup every 5 minutes */ + id = purple_timeout_add_seconds(60 * 5, (GSourceFunc)clean_users_hash, users); + + g_object_set_data(G_OBJECT(plugin), "users", users); + g_object_set_data(G_OBJECT(plugin), "id", GUINT_TO_POINTER(id)); + + return TRUE; } -PURPLE_INIT_PLUGIN(joinpart, init_plugin, info) +static gboolean plugin_unload(PurplePlugin *plugin, GError **error) +{ + /* Destroy the hash table. The core plugin code will + * disconnect the signals, and since Purple is single-threaded, + * we don't have to worry one will be called after this. */ + g_hash_table_destroy((GHashTable *)g_object_get_data(G_OBJECT(plugin), "users")); + + purple_timeout_remove(GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(plugin), "id"))); + + return TRUE; +} + +PURPLE_PLUGIN_INIT(joinpart, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/keyrings/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/keyrings/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -72,5 +72,6 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CPPFLAGS) \ $(PLUGIN_CFLAGS)
--- a/libpurple/plugins/keyrings/gnomekeyring.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/keyrings/gnomekeyring.c Fri Jan 31 18:02:20 2014 +0530 @@ -29,7 +29,7 @@ #include "debug.h" #include "glibcompat.h" #include "keyring.h" -#include "plugin.h" +#include "plugins.h" #include "version.h" #include <gnome-keyring.h> @@ -38,8 +38,11 @@ #define GNOMEKEYRING_NAME N_("GNOME Keyring") #define GNOMEKEYRING_DESCRIPTION N_("This plugin will store passwords in " \ "GNOME Keyring.") -#define GNOMEKEYRING_AUTHOR "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)" #define GNOMEKEYRING_ID "keyring-gnomekeyring" +#define GNOMEKEYRING_AUTHORS \ + { "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)", NULL } + +#define GNOMEKEYRING_DOMAIN (g_quark_from_static_string(GNOMEKEYRING_ID)) static PurpleKeyring *keyring_handler = NULL; static GList *request_queue = NULL; @@ -378,8 +381,28 @@ gnomekeyring_cancel(); } +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = GNOMEKEYRING_AUTHORS; + + return purple_plugin_info_new( + "id", GNOMEKEYRING_ID, + "name", GNOMEKEYRING_NAME, + "version", DISPLAY_VERSION, + "category", N_("Keyring"), + "summary", "GNOME Keyring Plugin", + "description", GNOMEKEYRING_DESCRIPTION, + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL, + NULL + ); +} + static gboolean -gnomekeyring_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { GModule *gkr_module; @@ -395,6 +418,8 @@ g_module_make_resident(gkr_module); if (!gnome_keyring_is_available()) { + g_set_error(error, GNOMEKEYRING_DOMAIN, 0, "GNOME Keyring service is " + "disabled."); purple_debug_info("keyring-gnome", "GNOME Keyring service is " "disabled\n"); return FALSE; @@ -416,9 +441,11 @@ } static gboolean -gnomekeyring_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { if (purple_keyring_get_inuse() == keyring_handler) { + g_set_error(error, GNOMEKEYRING_DOMAIN, 0, "The keyring is currently " + "in use."); purple_debug_warning("keyring-gnome", "keyring in use, cannot unload\n"); return FALSE; @@ -433,36 +460,4 @@ return TRUE; } -PurplePluginInfo plugininfo = -{ - PURPLE_PLUGIN_MAGIC, /* magic */ - PURPLE_MAJOR_VERSION, /* major_version */ - PURPLE_MINOR_VERSION, /* minor_version */ - PURPLE_PLUGIN_STANDARD, /* type */ - NULL, /* ui_requirement */ - PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ - GNOMEKEYRING_ID, /* id */ - GNOMEKEYRING_NAME, /* name */ - DISPLAY_VERSION, /* version */ - "GNOME Keyring Plugin", /* summary */ - GNOMEKEYRING_DESCRIPTION, /* description */ - GNOMEKEYRING_AUTHOR, /* author */ - PURPLE_WEBSITE, /* homepage */ - gnomekeyring_load, /* load */ - gnomekeyring_unload, /* unload */ - NULL, /* destroy */ - NULL, /* ui_info */ - NULL, /* extra_info */ - NULL, /* prefs_info */ - NULL, /* actions */ - NULL, NULL, NULL, NULL /* padding */ -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(gnome_keyring, init_plugin, plugininfo) +PURPLE_PLUGIN_INIT(gnome_keyring, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/keyrings/internalkeyring.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/keyrings/internalkeyring.c Fri Jan 31 18:02:20 2014 +0530 @@ -28,7 +28,7 @@ #include "account.h" #include "debug.h" #include "keyring.h" -#include "plugin.h" +#include "plugins.h" #include "version.h" #include "ciphers/aescipher.h" @@ -38,8 +38,9 @@ #define INTKEYRING_NAME N_("Internal keyring") #define INTKEYRING_DESCRIPTION N_("This plugin provides the default password " \ "storage behaviour for libpurple.") -#define INTKEYRING_AUTHOR "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)" +#define INTKEYRING_AUTHORS { "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)",NULL } #define INTKEYRING_ID PURPLE_DEFAULT_KEYRING +#define INTKEYRING_DOMAIN (g_quark_from_static_string(INTKEYRING_ID)) #define INTKEYRING_VERIFY_STR "[verification-string]" #define INTKEYRING_PBKDF2_ITERATIONS 10000 @@ -956,9 +957,41 @@ return TRUE; } +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = INTKEYRING_AUTHORS; + + return purple_plugin_info_new( + "id", INTKEYRING_ID, + "name", INTKEYRING_NAME, + "version", DISPLAY_VERSION, + "category", N_("Keyring"), + "summary", "Internal Keyring Plugin", + "description", INTKEYRING_DESCRIPTION, + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL, + NULL + ); +} + static gboolean -intkeyring_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { + purple_prefs_add_none("/plugins/keyrings"); + purple_prefs_add_none("/plugins/keyrings/internal"); + purple_prefs_add_bool(INTKEYRING_PREFS "encrypt_passwords", FALSE); + purple_prefs_add_string(INTKEYRING_PREFS "encryption_method", + INTKEYRING_ENCRYPTION_METHOD); + purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_desired_iterations", + INTKEYRING_PBKDF2_ITERATIONS); + purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_iterations", + INTKEYRING_PBKDF2_ITERATIONS); + purple_prefs_add_string(INTKEYRING_PREFS "pbkdf2_salt", ""); + purple_prefs_add_string(INTKEYRING_PREFS "key_verifier", ""); + keyring_handler = purple_keyring_new(); purple_keyring_set_name(keyring_handler, _(INTKEYRING_NAME)); @@ -984,9 +1017,11 @@ } static gboolean -intkeyring_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { if (purple_keyring_get_inuse() == keyring_handler) { + g_set_error(error, INTKEYRING_DOMAIN, 0, "The keyring is currently " + "in use."); purple_debug_warning("keyring-internal", "keyring in use, cannot unload\n"); return FALSE; @@ -1008,47 +1043,4 @@ return TRUE; } -PurplePluginInfo plugininfo = -{ - PURPLE_PLUGIN_MAGIC, /* magic */ - PURPLE_MAJOR_VERSION, /* major_version */ - PURPLE_MINOR_VERSION, /* minor_version */ - PURPLE_PLUGIN_STANDARD, /* type */ - NULL, /* ui_requirement */ - PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ - INTKEYRING_ID, /* id */ - INTKEYRING_NAME, /* name */ - DISPLAY_VERSION, /* version */ - "Internal Keyring Plugin", /* summary */ - INTKEYRING_DESCRIPTION, /* description */ - INTKEYRING_AUTHOR, /* author */ - PURPLE_WEBSITE, /* homepage */ - intkeyring_load, /* load */ - intkeyring_unload, /* unload */ - NULL, /* destroy */ - NULL, /* ui_info */ - NULL, /* extra_info */ - NULL, /* prefs_info */ - NULL, /* actions */ - NULL, NULL, NULL, NULL /* padding */ -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - purple_prefs_add_none("/plugins/keyrings"); - purple_prefs_add_none("/plugins/keyrings/internal"); - purple_prefs_add_bool(INTKEYRING_PREFS "encrypt_passwords", FALSE); - purple_prefs_add_string(INTKEYRING_PREFS "encryption_method", - INTKEYRING_ENCRYPTION_METHOD); - purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_desired_iterations", - INTKEYRING_PBKDF2_ITERATIONS); - purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_iterations", - INTKEYRING_PBKDF2_ITERATIONS); - purple_prefs_add_string(INTKEYRING_PREFS "pbkdf2_salt", ""); - purple_prefs_add_string(INTKEYRING_PREFS "key_verifier", ""); -} - -PURPLE_INIT_PLUGIN(internal_keyring, init_plugin, plugininfo) +PURPLE_PLUGIN_INIT(internal_keyring, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/keyrings/kwallet.cpp Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/keyrings/kwallet.cpp Fri Jan 31 18:02:20 2014 +0530 @@ -29,7 +29,7 @@ #include "core.h" #include "debug.h" #include "keyring.h" -#include "plugin.h" +#include "plugins.h" #include "version.h" #include <QQueue> @@ -38,8 +38,9 @@ #define KWALLET_NAME N_("KWallet") #define KWALLET_DESCRIPTION N_("This plugin will store passwords in KWallet.") -#define KWALLET_AUTHOR "QuLogic (qulogic[at]pidgin.im)" +#define KWALLET_AUTHORS { "QuLogic (qulogic[at]pidgin.im)", NULL } #define KWALLET_ID "keyring-kwallet" +#define KWALLET_DOMAIN (g_quark_from_static_string(KWALLET_ID)) #define KWALLET_WALLET_NAME KWallet::Wallet::NetworkWallet() #define KWALLET_APP_NAME "Libpurple" @@ -488,8 +489,28 @@ return ui_name; } +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = KWALLET_AUTHORS; + + return purple_plugin_info_new( + "id", KWALLET_ID, + "name", KWALLET_NAME, + "version", DISPLAY_VERSION, + "category", N_("Keyring"), + "summary", "KWallet Keyring Plugin", + "description", KWALLET_DESCRIPTION, + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL, + NULL + ); +} + static gboolean -kwallet_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { if (!qCoreApp) { int argc = 0; @@ -498,6 +519,7 @@ } if (!kwallet_is_enabled()) { + g_set_error(error, KWALLET_DOMAIN, 0, "KWallet service is disabled."); purple_debug_info("keyring-kwallet", "KWallet service is disabled\n"); return FALSE; @@ -519,9 +541,11 @@ } static gboolean -kwallet_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { if (purple_keyring_get_inuse() == keyring_handler) { + g_set_error(error, KWALLET_DOMAIN, 0, "The keyring is currently " + "in use."); purple_debug_warning("keyring-kwallet", "keyring in use, cannot unload\n"); return FALSE; @@ -543,39 +567,7 @@ return TRUE; } -PurplePluginInfo plugininfo = -{ - PURPLE_PLUGIN_MAGIC, /* magic */ - PURPLE_MAJOR_VERSION, /* major_version */ - PURPLE_MINOR_VERSION, /* minor_version */ - PURPLE_PLUGIN_STANDARD, /* type */ - NULL, /* ui_requirement */ - PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ - KWALLET_ID, /* id */ - KWALLET_NAME, /* name */ - DISPLAY_VERSION, /* version */ - "KWallet Keyring Plugin", /* summary */ - KWALLET_DESCRIPTION, /* description */ - KWALLET_AUTHOR, /* author */ - PURPLE_WEBSITE, /* homepage */ - kwallet_load, /* load */ - kwallet_unload, /* unload */ - NULL, /* destroy */ - NULL, /* ui_info */ - NULL, /* extra_info */ - NULL, /* prefs_info */ - NULL, /* actions */ - NULL, NULL, NULL, NULL /* padding */ -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(kwallet_keyring, init_plugin, plugininfo) +PURPLE_PLUGIN_INIT(kwallet_keyring, plugin_query, plugin_load, plugin_unload); } /* extern "C" */
--- a/libpurple/plugins/keyrings/secretservice.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/keyrings/secretservice.c Fri Jan 31 18:02:20 2014 +0530 @@ -38,13 +38,14 @@ #include "account.h" #include "debug.h" #include "keyring.h" -#include "plugin.h" +#include "plugins.h" #include "version.h" #include <libsecret/secret.h> #define SECRETSERVICE_NAME N_("Secret Service") #define SECRETSERVICE_ID "keyring-libsecret" +#define SECRETSERVICE_DOMAIN (g_quark_from_static_string(SECRETSERVICE_ID)) static PurpleKeyring *keyring_handler = NULL; @@ -253,7 +254,7 @@ } static gboolean -ss_init(void) +ss_init(GError **error) { keyring_handler = purple_keyring_new(); @@ -281,57 +282,48 @@ /* Plugin interface */ /***********************************************/ -static gboolean -ss_load(PurplePlugin *plugin) +static PurplePluginInfo * +plugin_query(GError **error) { - return ss_init(); + const gchar * const authors[] = { + "Elliott Sales de Andrade (qulogic[at]pidgin.im)", + NULL + }; + + return purple_plugin_info_new( + "id", SECRETSERVICE_ID, + "name", SECRETSERVICE_NAME, + "version", DISPLAY_VERSION, + "category", N_("Keyring"), + "summary", "Secret Service Plugin", + "description", N_("This plugin will store passwords in Secret Service."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL, + NULL + ); } static gboolean -ss_unload(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { - if (purple_keyring_get_inuse() == keyring_handler) + return ss_init(error); +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + if (purple_keyring_get_inuse() == keyring_handler) { + g_set_error(error, SECRETSERVICE_DOMAIN, 0, "The keyring is currently " + "in use."); return FALSE; + } ss_uninit(); return TRUE; } -PurplePluginInfo plugininfo = -{ - PURPLE_PLUGIN_MAGIC, /* magic */ - PURPLE_MAJOR_VERSION, /* major_version */ - PURPLE_MINOR_VERSION, /* minor_version */ - PURPLE_PLUGIN_STANDARD, /* type */ - NULL, /* ui_requirement */ - PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ - SECRETSERVICE_ID, /* id */ - SECRETSERVICE_NAME, /* name */ - DISPLAY_VERSION, /* version */ - "Secret Service Plugin", /* summary */ - N_("This plugin will store passwords in Secret Service."), /* description */ - "Elliott Sales de Andrade (qulogic[at]pidgin.im)", /* author */ - PURPLE_WEBSITE, /* homepage */ - ss_load, /* load */ - ss_unload, /* unload */ - NULL, /* destroy */ - NULL, /* ui_info */ - NULL, /* extra_info */ - NULL, /* prefs_info */ - NULL, /* actions */ - NULL, /* padding... */ - NULL, - NULL, - NULL, -}; +PURPLE_PLUGIN_INIT(secret_service, plugin_query, plugin_load, plugin_unload); -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(secret_service, init_plugin, plugininfo) -
--- a/libpurple/plugins/keyrings/wincred.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/keyrings/wincred.c Fri Jan 31 18:02:20 2014 +0530 @@ -24,10 +24,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "internal.h" #include "debug.h" -#include "internal.h" #include "keyring.h" -#include "plugin.h" +#include "plugins.h" #include "version.h" #include <wincred.h> @@ -36,8 +36,9 @@ #define WINCRED_SUMMARY N_("Store passwords using Windows credentials") #define WINCRED_DESCRIPTION N_("This plugin stores passwords using Windows " \ "credentials.") -#define WINCRED_AUTHOR "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)" +#define WINCRED_AUTHORS {"Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)", NULL} #define WINCRED_ID "keyring-wincred" +#define WINCRED_DOMAIN (g_quark_from_static_string(WINCRED_ID)) #define WINCRED_MAX_TARGET_NAME 256 @@ -255,8 +256,28 @@ g_error_free(error); } +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = WINCRED_AUTHORS; + + return purple_plugin_info_new( + "id", WINCRED_ID, + "name", WINCRED_NAME, + "version", DISPLAY_VERSION, + "category", N_("Keyring"), + "summary", WINCRED_SUMMARY, + "description", WINCRED_DESCRIPTION, + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL, + NULL + ); +} + static gboolean -wincred_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { keyring_handler = purple_keyring_new(); @@ -271,9 +292,11 @@ } static gboolean -wincred_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { if (purple_keyring_get_inuse() == keyring_handler) { + g_set_error(error, WINCRED_DOMAIN, 0, "The keyring is currently " + "in use."); purple_debug_warning("keyring-wincred", "keyring in use, cannot unload\n"); return FALSE; @@ -286,36 +309,4 @@ return TRUE; } -PurplePluginInfo plugininfo = -{ - PURPLE_PLUGIN_MAGIC, /* magic */ - PURPLE_MAJOR_VERSION, /* major_version */ - PURPLE_MINOR_VERSION, /* minor_version */ - PURPLE_PLUGIN_STANDARD, /* type */ - NULL, /* ui_requirement */ - PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ - WINCRED_ID, /* id */ - WINCRED_NAME, /* name */ - DISPLAY_VERSION, /* version */ - WINCRED_SUMMARY, /* summary */ - WINCRED_DESCRIPTION, /* description */ - WINCRED_AUTHOR, /* author */ - PURPLE_WEBSITE, /* homepage */ - wincred_load, /* load */ - wincred_unload, /* unload */ - NULL, /* destroy */ - NULL, /* ui_info */ - NULL, /* extra_info */ - NULL, /* prefs_info */ - NULL, /* actions */ - NULL, NULL, NULL, NULL /* padding */ -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(wincred_keyring, init_plugin, plugininfo) +PURPLE_PLUGIN_INIT(wincred_keyring, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/log_reader.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/log_reader.c Fri Jan 31 18:02:20 2014 +0530 @@ -5,7 +5,7 @@ #include "debug.h" #include "glibcompat.h" #include "log.h" -#include "plugin.h" +#include "plugins.h" #include "pluginpref.h" #include "prefs.h" #include "stringref.h" @@ -67,9 +67,8 @@ { GList *list = NULL; const char *logdir; - PurplePlugin *plugin; - PurplePluginProtocolInfo *prpl_info; - char *prpl_name; + PurpleProtocol *protocol; + char *protocol_name; char *temp; char *path; GDir *dir; @@ -83,17 +82,13 @@ if (!logdir || !*logdir) return NULL; - plugin = purple_find_prpl(purple_account_get_protocol_id(account)); - if (!plugin) + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if (!protocol) return NULL; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - if (!prpl_info->list_icon) - return NULL; - - prpl_name = g_ascii_strup(prpl_info->list_icon(account, NULL), -1); - - temp = g_strdup_printf("%s.%s", prpl_name, purple_account_get_username(account)); + protocol_name = g_ascii_strup(purple_protocol_class_list_icon(protocol, account, NULL), -1); + + temp = g_strdup_printf("%s.%s", protocol_name, purple_account_get_username(account)); path = g_build_filename(logdir, temp, sn, NULL); g_free(temp); @@ -228,7 +223,7 @@ g_dir_close(dir); } - g_free(prpl_name); + g_free(protocol_name); g_free(path); return list; @@ -1206,9 +1201,8 @@ { GList *list = NULL; const char *logdir; - PurplePlugin *plugin; - PurplePluginProtocolInfo *prpl_info; - char *prpl_name; + PurpleProtocol *protocol; + char *protocol_name; const char *buddy_name; char *filename; char *path; @@ -1227,21 +1221,17 @@ if (!logdir || !*logdir) return NULL; - plugin = purple_find_prpl(purple_account_get_protocol_id(account)); - if (!plugin) + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if (!protocol) return NULL; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - if (!prpl_info->list_icon) - return NULL; - - prpl_name = g_ascii_strup(prpl_info->list_icon(account, NULL), -1); + protocol_name = g_ascii_strup(purple_protocol_class_list_icon(protocol, account, NULL), -1); buddy_name = purple_normalize(account, sn); filename = g_strdup_printf("%s.log", buddy_name); path = g_build_filename( - logdir, prpl_name, filename, NULL); + logdir, protocol_name, filename, NULL); purple_debug_info("Trillian log list", "Reading %s\n", path); /* FIXME: There's really no need to read the entire file at once. @@ -1255,7 +1245,7 @@ g_free(path); path = g_build_filename( - logdir, prpl_name, "Query", filename, NULL); + logdir, protocol_name, "Query", filename, NULL); purple_debug_info("Trillian log list", "Reading %s\n", path); if (!g_file_get_contents(path, &contents, &length, &error)) { if (error) @@ -1391,7 +1381,7 @@ } g_free(path); - g_free(prpl_name); + g_free(protocol_name); return g_list_reverse(list); } @@ -1757,8 +1747,7 @@ { GList *list = NULL; const char *logdir; - PurplePlugin *plugin; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; char *username; char *filename; char *path; @@ -1787,12 +1776,8 @@ if (!logdir || !*logdir) return NULL; - plugin = purple_find_prpl(purple_account_get_protocol_id(account)); - if (!plugin) - return NULL; - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - if (!prpl_info->list_icon) + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if (!protocol) return NULL; username = g_strdup(purple_normalize(account, purple_account_get_username(account))); @@ -2444,12 +2429,6 @@ * Plugin Code * *****************************************************************************/ -static void -init_plugin(PurplePlugin *plugin) -{ - -} - static void log_reader_init_prefs(void) { char *path; #ifdef _WIN32 @@ -2721,8 +2700,101 @@ g_free(path); } +static PurplePluginPrefFrame * +get_plugin_pref_frame(PurplePlugin *plugin) +{ + PurplePluginPrefFrame *frame; + PurplePluginPref *ppref; + + g_return_val_if_fail(plugin != NULL, FALSE); + + frame = purple_plugin_pref_frame_new(); + + + /* Add general preferences. */ + + ppref = purple_plugin_pref_new_with_label(_("General Log Reading Configuration")); + purple_plugin_pref_frame_add(frame, ppref); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/fast_sizes", _("Fast size calculations")); + purple_plugin_pref_frame_add(frame, ppref); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/use_name_heuristics", _("Use name heuristics")); + purple_plugin_pref_frame_add(frame, ppref); + + + /* Add Log Directory preferences. */ + + ppref = purple_plugin_pref_new_with_label(_("Log Directory")); + purple_plugin_pref_frame_add(frame, ppref); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/adium/log_directory", _("Adium")); + purple_plugin_pref_frame_add(frame, ppref); + +#if 0 + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/fire/log_directory", _("Fire")); + purple_plugin_pref_frame_add(frame, ppref); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/messenger_plus/log_directory", _("Messenger Plus!")); + purple_plugin_pref_frame_add(frame, ppref); +#endif + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/qip/log_directory", _("QIP")); + purple_plugin_pref_frame_add(frame, ppref); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/msn/log_directory", _("MSN Messenger")); + purple_plugin_pref_frame_add(frame, ppref); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/trillian/log_directory", _("Trillian")); + purple_plugin_pref_frame_add(frame, ppref); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/amsn/log_directory", _("aMSN")); + purple_plugin_pref_frame_add(frame, ppref); + + return frame; +} + +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Richard Laager <rlaager@pidgin.im>", + NULL + }; + + return purple_plugin_info_new( + "id", "core-log_reader", + "name", N_("Log Reader"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Includes other IM clients' logs in the log " + "viewer."), + "description", N_("When viewing logs, this plugin will include " + "logs from other IM clients. Currently, this " + "includes Adium, MSN Messenger, aMSN, and " + "Trillian.\n\n" + "WARNING: This plugin is still alpha code and " + "may crash frequently. Use it at your own " + "risk!"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { g_return_val_if_fail(plugin != NULL, FALSE); @@ -2819,7 +2891,7 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { g_return_val_if_fail(plugin != NULL, FALSE); @@ -2856,121 +2928,4 @@ return TRUE; } -static PurplePluginPrefFrame * -get_plugin_pref_frame(PurplePlugin *plugin) -{ - PurplePluginPrefFrame *frame; - PurplePluginPref *ppref; - - g_return_val_if_fail(plugin != NULL, FALSE); - - frame = purple_plugin_pref_frame_new(); - - - /* Add general preferences. */ - - ppref = purple_plugin_pref_new_with_label(_("General Log Reading Configuration")); - purple_plugin_pref_frame_add(frame, ppref); - - ppref = purple_plugin_pref_new_with_name_and_label( - "/plugins/core/log_reader/fast_sizes", _("Fast size calculations")); - purple_plugin_pref_frame_add(frame, ppref); - - ppref = purple_plugin_pref_new_with_name_and_label( - "/plugins/core/log_reader/use_name_heuristics", _("Use name heuristics")); - purple_plugin_pref_frame_add(frame, ppref); - - - /* Add Log Directory preferences. */ - - ppref = purple_plugin_pref_new_with_label(_("Log Directory")); - purple_plugin_pref_frame_add(frame, ppref); - - ppref = purple_plugin_pref_new_with_name_and_label( - "/plugins/core/log_reader/adium/log_directory", _("Adium")); - purple_plugin_pref_frame_add(frame, ppref); - -#if 0 - ppref = purple_plugin_pref_new_with_name_and_label( - "/plugins/core/log_reader/fire/log_directory", _("Fire")); - purple_plugin_pref_frame_add(frame, ppref); - - ppref = purple_plugin_pref_new_with_name_and_label( - "/plugins/core/log_reader/messenger_plus/log_directory", _("Messenger Plus!")); - purple_plugin_pref_frame_add(frame, ppref); -#endif - - ppref = purple_plugin_pref_new_with_name_and_label( - "/plugins/core/log_reader/qip/log_directory", _("QIP")); - purple_plugin_pref_frame_add(frame, ppref); - - ppref = purple_plugin_pref_new_with_name_and_label( - "/plugins/core/log_reader/msn/log_directory", _("MSN Messenger")); - purple_plugin_pref_frame_add(frame, ppref); - - ppref = purple_plugin_pref_new_with_name_and_label( - "/plugins/core/log_reader/trillian/log_directory", _("Trillian")); - purple_plugin_pref_frame_add(frame, ppref); - - ppref = purple_plugin_pref_new_with_name_and_label( - "/plugins/core/log_reader/amsn/log_directory", _("aMSN")); - purple_plugin_pref_frame_add(frame, ppref); - - return frame; -} - -static PurplePluginUiInfo prefs_info = { - get_plugin_pref_frame, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - "core-log_reader", /**< id */ - N_("Log Reader"), /**< name */ - DISPLAY_VERSION, /**< version */ - - /** summary */ - N_("Includes other IM clients' logs in the " - "log viewer."), - - /** description */ - N_("When viewing logs, this plugin will include " - "logs from other IM clients. Currently, this " - "includes Adium, MSN Messenger, aMSN, and " - "Trillian.\n\n" - "WARNING: This plugin is still alpha code and " - "may crash frequently. Use it at your own risk!"), - - "Richard Laager <rlaager@pidgin.im>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - NULL, /**< extra_info */ - &prefs_info, /**< prefs_info */ - NULL, /**< actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -PURPLE_INIT_PLUGIN(log_reader, init_plugin, info) +PURPLE_PLUGIN_INIT(log_reader, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/mono/loader/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/mono/loader/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -14,11 +14,12 @@ mono_la_LDFLAGS = -module -avoid-version -mono_la_LIBADD = $(MONO_LIBS) +mono_la_LIBADD = $(GPLUGIN_LIBS) $(MONO_LIBS) AM_CPPFLAGS = \ -I$(top_srcdir) \ -I$(top_srcdir)/libpurple \ $(DEBUG_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(PLUGIN_CFLAGS) \ $(MONO_CFLAGS)
--- a/libpurple/plugins/mono/loader/mono-helper.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/mono/loader/mono-helper.h Fri Jan 31 18:02:20 2014 +0530 @@ -7,7 +7,7 @@ #include <mono/metadata/assembly.h> #include <mono/metadata/debug-helpers.h> #include <mono/metadata/tokentype.h> -#include "plugin.h" +#include "plugins.h" #include "debug.h" typedef struct {
--- a/libpurple/plugins/mono/loader/mono.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/mono/loader/mono.c Fri Jan 31 18:02:20 2014 +0530 @@ -12,7 +12,7 @@ #include "internal.h" #include "debug.h" -#include "plugin.h" +#include "plugins.h" #include "version.h" #include "mono-helper.h" @@ -197,50 +197,47 @@ static PurplePluginLoaderInfo loader_info = { - NULL, probe_mono_plugin, load_mono_plugin, unload_mono_plugin, destroy_mono_plugin, - - /* padding */ - NULL, - NULL, - NULL, - NULL }; -static PurplePluginInfo info = +static GPluginPluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_LOADER, - NULL, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - MONO_PLUGIN_ID, - N_("Mono Plugin Loader"), - DISPLAY_VERSION, - N_("Loads .NET plugins with Mono."), - N_("Loads .NET plugins with Mono."), - "Eoin Coffey <ecoffey@simla.colostate.edu>", - PURPLE_WEBSITE, - NULL, - NULL, - plugin_destroy, - NULL, - &loader_info, - NULL, - NULL, + const gchar * const authors[] = { + "Eoin Coffey <ecoffey@simla.colostate.edu>", + NULL + }; - /* padding */ - NULL, - NULL, - NULL, - NULL -}; + return gplugin_plugin_info_new( + "id", MONO_PLUGIN_ID, + "name", N_("Mono Plugin Loader"), + "version", DISPLAY_VERSION, + "category", N_("Loader"), + "summary", N_("Loads .NET plugins with Mono."), + "description", N_("Loads .NET plugins with Mono."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "internal", TRUE, + "load-on-query", TRUE, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} static void init_plugin(PurplePlugin *plugin) { @@ -249,4 +246,4 @@ loader_info.exts = g_list_append(loader_info.exts, "dll"); } -PURPLE_INIT_PLUGIN(mono, init_plugin, info) +PURPLE_PLUGIN_INIT(mono, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/newline.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/newline.c Fri Jan 31 18:02:20 2014 +0530 @@ -22,7 +22,7 @@ #include <conversation.h> #include <debug.h> -#include <plugin.h> +#include <plugins.h> #include <signals.h> #include <util.h> #include <version.h> @@ -65,11 +65,40 @@ } +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Stu Tomlinson <stu@nosnilmot.com>", + NULL + }; + + return purple_plugin_info_new( + "id", "core-plugin_pack-newline", + "name", N_("New Line"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Prepends a newline to displayed message."), + "description", N_("Prepends a newline to messages so that the " + "rest of the message appears below the " + "username in the conversation window."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { void *conversation = purple_conversations_get_handle(); + purple_prefs_add_none("/plugins/core/newline"); + purple_prefs_add_bool("/plugins/core/newline/im", TRUE); + purple_prefs_add_bool("/plugins/core/newline/chat", TRUE); + purple_signal_connect(conversation, "writing-im-msg", plugin, PURPLE_CALLBACK(addnewline_msg_cb), NULL); purple_signal_connect(conversation, "writing-chat-msg", @@ -78,59 +107,10 @@ return TRUE; } -static PurplePluginUiInfo prefs_info = { - get_plugin_pref_frame, - NULL, - - /* Padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PURPLE_PLUGIN_MAGIC, /**< magic */ - PURPLE_MAJOR_VERSION, /**< major version */ - PURPLE_MINOR_VERSION, /**< minor version */ - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "core-plugin_pack-newline", /**< id */ - N_("New Line"), /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Prepends a newline to displayed message."), /**< summary */ - N_("Prepends a newline to messages so that the " - "rest of the message appears below the " - "username in the conversation window."), /**< description */ - "Stu Tomlinson <stu@nosnilmot.com>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - &prefs_info, /**< prefs_info */ - NULL, /**< actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) { - purple_prefs_add_none("/plugins/core/newline"); - purple_prefs_add_bool("/plugins/core/newline/im", TRUE); - purple_prefs_add_bool("/plugins/core/newline/chat", TRUE); + return TRUE; } -PURPLE_INIT_PLUGIN(newline, init_plugin, info) +PURPLE_PLUGIN_INIT(newline, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/notify_example.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/notify_example.c Fri Jan 31 18:02:20 2014 +0530 @@ -29,7 +29,7 @@ #include <purple.h> #define PLUGIN_ID "core-notifyexample" -#define PLUGIN_AUTHOR "John Bailey <rekkanoryo@cpw.pidgin.im>" +#define PLUGIN_AUTHORS { "John Bailey <rekkanoryo@cpw.pidgin.im>", NULL } /* The next four functions and the calls within them should cause dialog boxes to appear * when you select the plugin action from the Tools->Notify Example menu */ @@ -70,7 +70,7 @@ } static GList * -plugin_actions(PurplePlugin *plugin, gpointer context) +plugin_actions(PurplePlugin *plugin) { GList *actions = NULL; @@ -93,48 +93,37 @@ return g_list_reverse(actions); } +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = PLUGIN_AUTHORS; + + return purple_plugin_info_new( + "id", PLUGIN_ID, + "name", "Notify API Example", + "version", DISPLAY_VERSION, + "category", "Example", + "summary", "Notify API Example", + "description", "Notify API Example", + "authors", authors, + "website", "https://pidgin.im", + "abi-version", PURPLE_ABI_VERSION, + "actions-cb", plugin_actions, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { return TRUE; } -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, /* magic number */ - PURPLE_MAJOR_VERSION, /* purple major */ - PURPLE_MINOR_VERSION, /* purple minor */ - PURPLE_PLUGIN_STANDARD, /* plugin type */ - NULL, /* UI requirement */ - 0, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ - - PLUGIN_ID, /* id */ - "Notify API Example", /* name */ - DISPLAY_VERSION, /* version */ - "Notify API Example", /* summary */ - "Notify API Example", /* description */ - PLUGIN_AUTHOR, /* author */ - "https://pidgin.im", /* homepage */ - - plugin_load, /* load */ - NULL, /* unload */ - NULL, /* destroy */ - - NULL, /* ui info */ - NULL, /* extra info */ - NULL, /* prefs info */ - plugin_actions, /* actions */ - NULL, /* reserved */ - NULL, /* reserved */ - NULL, /* reserved */ - NULL /* reserved */ -}; - -static void -init_plugin(PurplePlugin *plugin) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { + return TRUE; } -PURPLE_INIT_PLUGIN(notifyexample, init_plugin, info) +PURPLE_PLUGIN_INIT(notifyexample, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/offlinemsg.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/offlinemsg.c Fri Jan 31 18:02:20 2014 +0530 @@ -21,10 +21,11 @@ #define PLUGIN_ID "core-plugin_pack-offlinemsg" #define PLUGIN_NAME N_("Offline Message Emulation") +#define PLUGIN_CATEGORY N_("Utility") #define PLUGIN_STATIC_NAME offlinemsg #define PLUGIN_SUMMARY N_("Save messages sent to an offline user as pounce.") #define PLUGIN_DESCRIPTION N_("Save messages sent to an offline user as pounce.") -#define PLUGIN_AUTHOR "Sadrul H Chowdhury <sadrul@users.sourceforge.net>" +#define PLUGIN_AUTHORS {"Sadrul H Chowdhury <sadrul@users.sourceforge.net>", NULL} /* Purple headers */ #include <version.h> @@ -172,20 +173,6 @@ } } -static gboolean -plugin_load(PurplePlugin *plugin) -{ - purple_signal_connect_priority(purple_conversations_get_handle(), "sending-im-msg", - plugin, PURPLE_CALLBACK(sending_msg_cb), plugin, PURPLE_SIGNAL_PRIORITY_HIGHEST); - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - return TRUE; -} - static PurplePluginPrefFrame * get_plugin_pref_frame(PurplePlugin *plugin) { @@ -204,57 +191,41 @@ return frame; } -static PurplePluginUiInfo prefs_info = { - get_plugin_pref_frame, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = +static PurplePluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, /* Magic */ - PURPLE_MAJOR_VERSION, /* Purple Major Version */ - PURPLE_MINOR_VERSION, /* Purple Minor Version */ - PURPLE_PLUGIN_STANDARD, /* plugin type */ - NULL, /* ui requirement */ - 0, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ + const gchar * const authors[] = PLUGIN_AUTHORS; - PLUGIN_ID, /* plugin id */ - PLUGIN_NAME, /* name */ - DISPLAY_VERSION, /* version */ - PLUGIN_SUMMARY, /* summary */ - PLUGIN_DESCRIPTION, /* description */ - PLUGIN_AUTHOR, /* author */ - PURPLE_WEBSITE, /* website */ - - plugin_load, /* load */ - plugin_unload, /* unload */ - NULL, /* destroy */ + return purple_plugin_info_new( + "id", PLUGIN_ID, + "name", PLUGIN_NAME, + "version", DISPLAY_VERSION, + "category", PLUGIN_CATEGORY, + "summary", PLUGIN_SUMMARY, + "description", PLUGIN_DESCRIPTION, + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} - NULL, /* ui_info */ - NULL, /* extra_info */ - &prefs_info, /* prefs_info */ - NULL, /* actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) { purple_prefs_add_none(PREF_PREFIX); purple_prefs_add_bool(PREF_ALWAYS, FALSE); + + purple_signal_connect_priority(purple_conversations_get_handle(), "sending-im-msg", + plugin, PURPLE_CALLBACK(sending_msg_cb), plugin, PURPLE_SIGNAL_PRIORITY_HIGHEST); + return TRUE; } -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +PURPLE_PLUGIN_INIT(PLUGIN_STATIC_NAME, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/one_time_password.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/one_time_password.c Fri Jan 31 18:02:20 2014 +0530 @@ -20,7 +20,7 @@ */ #include "internal.h" #include "debug.h" -#include "plugin.h" +#include "plugins.h" #include "version.h" #include "account.h" #include "accountopt.h" @@ -52,24 +52,51 @@ } } -static gboolean -plugin_load(PurplePlugin *plugin) +static PurplePluginInfo * +plugin_query(GError **error) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + const gchar * const authors[] = { + "Daniel Atallah <datallah@pidgin.im>", + NULL + }; + + return purple_plugin_info_new( + "id", PLUGIN_ID, + "name", N_("One Time Password Support"), + "version", DISPLAY_VERSION, + "category", N_("Security"), + "summary", N_("Enforce that passwords are used only once."), + "description", N_("Allows you to enforce on a per-account basis that " + "passwords not being saved are only used in a " + "single successful connection.\n" + "Note: The account password must not be saved for " + "this to work."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + PurpleProtocol *protocol; PurpleAccountOption *option; - GList *l; + GList *list, *l; + + list = purple_protocols_get_all(); /* Register protocol preference. */ - for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) { - prpl = (PurplePlugin *)l->data; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info != NULL && !(prpl_info->options & OPT_PROTO_NO_PASSWORD)) { + for (l = list; l != NULL; l = l->next) { + protocol = PURPLE_PROTOCOL(l->data); + if (protocol != NULL && !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD)) { option = purple_account_option_bool_new(_("One Time Password"), PREF_NAME, FALSE); - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); } } + g_list_free(list); /* Register callback. */ purple_signal_connect(purple_connections_get_handle(), "signed-on", @@ -79,23 +106,23 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleAccountOption *option; - GList *l, *options; + GList *list, *l, *options; + + list = purple_protocols_get_all(); /* Remove protocol preference. */ - for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) { - prpl = (PurplePlugin *)l->data; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info != NULL && !(prpl_info->options & OPT_PROTO_NO_PASSWORD)) { - options = prpl_info->protocol_options; + for (l = list; l != NULL; l = l->next) { + protocol = PURPLE_PROTOCOL(l->data); + if (protocol != NULL && !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD)) { + options = purple_protocol_get_protocol_options(protocol); while (options != NULL) { option = (PurpleAccountOption *) options->data; if (strcmp(PREF_NAME, purple_account_option_get_setting(option)) == 0) { - prpl_info->protocol_options = g_list_delete_link(prpl_info->protocol_options, options); + protocol->protocol_options = g_list_delete_link(protocol->protocol_options, options); purple_account_option_destroy(option); break; } @@ -103,49 +130,11 @@ } } } + g_list_free(list); /* Callback will be automagically unregistered */ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - PLUGIN_ID, /**< id */ - N_("One Time Password Support"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Enforce that passwords are used only once."), - /** description */ - N_("Allows you to enforce on a per-account basis that passwords not " - "being saved are only used in a single successful connection.\n" - "Note: The account password must not be saved for this to work."), - "Daniel Atallah <datallah@pidgin.im>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ - NULL, /**< reserved 1 */ - NULL, /**< reserved 2 */ - NULL, /**< reserved 3 */ - NULL /**< reserved 4 */ -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(one_time_password, init_plugin, info) +PURPLE_PLUGIN_INIT(one_time_password, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/perl/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -5,7 +5,7 @@ plugin_LTLIBRARIES = perl.la perl_la_LDFLAGS = -module -avoid-version -perl_la_LIBADD = $(GLIB_LIBS) $(PERL_LIBS) +perl_la_LIBADD = $(GLIB_LIBS) $(GPLUGIN_LIBS) $(PERL_LIBS) perl_la_SOURCES = \ perl.c \ perl-common.c \ @@ -167,6 +167,7 @@ -DLIBDIR=\"$(libdir)/purple-$(PURPLE_MAJOR_VERSION)\" \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(PLUGIN_CFLAGS) \ $(PERL_CFLAGS) \ -Wno-float-equal
--- a/libpurple/plugins/perl/common/BuddyIcon.xs Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/common/BuddyIcon.xs Fri Jan 31 18:02:20 2014 +0530 @@ -40,7 +40,7 @@ Purple::Buddy::Icon icon void -purple_buddy_icon_get_scale_size(spec, width, height) +purple_buddy_icon_spec_get_scaled_size(spec, width, height) Purple::Buddy::Icon::Spec spec int *width int *height
--- a/libpurple/plugins/perl/common/Cmds.xs Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/common/Cmds.xs Fri Jan 31 18:02:20 2014 +0530 @@ -17,7 +17,7 @@ const_iv(FAILED), const_iv(NOT_FOUND), const_iv(WRONG_ARGS), - const_iv(WRONG_PRPL), + const_iv(WRONG_PROTOCOL), const_iv(WRONG_TYPE), }; static const constiv ret_const_iv[] = { @@ -33,7 +33,7 @@ const_iv(VERY_LOW), const_iv(LOW), const_iv(DEFAULT), - const_iv(PRPL), + const_iv(PROTOCOL), const_iv(PLUGIN), const_iv(ALIAS), const_iv(HIGH), @@ -44,7 +44,7 @@ #define const_iv(name) {#name, (IV)PURPLE_CMD_FLAG_##name} const_iv(IM), const_iv(CHAT), - const_iv(PRPL_ONLY), + const_iv(PROTOCOL_ONLY), const_iv(ALLOW_WRONG_ARGS), }; @@ -85,19 +85,19 @@ g_list_free(ll); Purple::Cmd::Id -purple_cmd_register(plugin, command, args, priority, flag, prpl_id, func, helpstr, data = 0) +purple_cmd_register(plugin, command, args, priority, flag, protocol_id, func, helpstr, data = 0) Purple::Plugin plugin const gchar *command const gchar *args Purple::Cmd::Priority priority Purple::Cmd::Flag flag - const gchar *prpl_id + const gchar *protocol_id SV *func const gchar *helpstr SV *data CODE: RETVAL = purple_perl_cmd_register(plugin, command, args, priority, flag, - prpl_id, func, helpstr, data); + protocol_id, func, helpstr, data); OUTPUT: RETVAL
--- a/libpurple/plugins/perl/common/Prpl.xs Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/common/Prpl.xs Fri Jan 31 18:02:20 2014 +0530 @@ -1,29 +1,29 @@ #include "module.h" -MODULE = Purple::Prpl PACKAGE = Purple::Find PREFIX = purple_find_ +MODULE = Purple::Prpl PACKAGE = Purple::Find PREFIX = purple_protocols_ PROTOTYPES: ENABLE Purple::Plugin -purple_find_prpl(id) +purple_protocols_find(id) const char *id -MODULE = Purple::Prpl PACKAGE = Purple::Prpl PREFIX = purple_prpl_ +MODULE = Purple::Prpl PACKAGE = Purple::Prpl PREFIX = purple_protocol_ PROTOTYPES: ENABLE void -purple_prpl_change_account_status(account, old_status, new_status) +purple_protocol_change_account_status(account, old_status, new_status) Purple::Account account Purple::Status old_status Purple::Status new_status void -purple_prpl_get_statuses(account, presence) +purple_protocol_get_statuses(account, presence) Purple::Account account Purple::Presence presence PREINIT: GList *l, *ll; PPCODE: - ll = purple_prpl_get_statuses(account,presence); + ll = purple_protocol_get_statuses(account,presence); for (l = ll; l != NULL; l = l->next) { XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Status"))); } @@ -32,45 +32,44 @@ g_list_free(ll); void -purple_prpl_got_account_idle(account, idle, idle_time) +purple_protocol_got_account_idle(account, idle, idle_time) Purple::Account account gboolean idle time_t idle_time void -purple_prpl_got_account_login_time(account, login_time) +purple_protocol_got_account_login_time(account, login_time) Purple::Account account time_t login_time void -purple_prpl_got_user_idle(account, name, idle, idle_time) +purple_protocol_got_user_idle(account, name, idle, idle_time) Purple::Account account const char *name gboolean idle time_t idle_time void -purple_prpl_got_user_login_time(account, name, login_time) +purple_protocol_got_user_login_time(account, name, login_time) Purple::Account account const char *name time_t login_time int -purple_prpl_send_raw(gc, str) +purple_protocol_send_raw(gc, str) Purple::Connection gc const char *str PREINIT: - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; CODE: if (!gc) RETVAL = 0; else { - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - if (prpl_info && prpl_info->send_raw != NULL) { - RETVAL = prpl_info->send_raw(gc, str, strlen(str)); - } else { + protocol = purple_connection_get_protocol(gc); + if (protocol) + RETVAL = purple_protocol_iface_send_raw(protocol, gc, str, strlen(str)); + else RETVAL = 0; - } } OUTPUT: RETVAL
--- a/libpurple/plugins/perl/common/module.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/common/module.h Fri Jan 31 18:02:20 2014 +0530 @@ -54,12 +54,12 @@ #include "imgstore.h" #include "network.h" #include "notify.h" -#include "plugin.h" +#include "plugins.h" #include "pluginpref.h" #include "pounce.h" #include "prefs.h" #include "presence.h" -#include "prpl.h" +#include "protocol.h" #include "proxy.h" #include "request.h" #include "roomlist.h" @@ -200,7 +200,7 @@ typedef PurpleNotifyUserInfo * Purple__NotifyUserInfo; typedef PurpleNotifyUserInfoEntry * Purple__NotifyUserInfoEntry; -/* plugin.h */ +/* plugins.h */ typedef PurplePlugin * Purple__Plugin; typedef PurplePluginAction * Purple__Plugin__Action; typedef PurplePluginInfo * Purple__PluginInfo; @@ -230,10 +230,10 @@ typedef PurpleProxyInfo * Purple__ProxyInfo; typedef PurpleProxyType Purple__ProxyType; -/* prpl.h */ +/* protocol.h */ typedef PurpleBuddyIconSpec * Purple__Buddy__Icon__Spec; typedef PurpleIconScaleRules Purple__IconScaleRules; -typedef PurplePluginProtocolInfo * Purple__PluginProtocolInfo; +typedef PurpleProtocol * Purple__PluginProtocolInfo; typedef PurpleProtocolOptions Purple__ProtocolOptions; /* request.h */
--- a/libpurple/plugins/perl/perl-common.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/perl-common.h Fri Jan 31 18:02:20 2014 +0530 @@ -20,7 +20,7 @@ #ifdef _WIN32 #undef _WIN32DEP_H_ #endif -#include "plugin.h" +#include "plugins.h" #define is_hvref(o) \ ((o) && SvROK(o) && SvRV(o) && (SvTYPE(SvRV(o)) == SVt_PVHV))
--- a/libpurple/plugins/perl/perl-handlers.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/perl-handlers.c Fri Jan 31 18:02:20 2014 +0530 @@ -625,7 +625,7 @@ PurpleCmdId purple_perl_cmd_register(PurplePlugin *plugin, const gchar *command, const gchar *args, PurpleCmdPriority priority, - PurpleCmdFlag flag, const gchar *prpl_id, SV *callback, + PurpleCmdFlag flag, const gchar *protocol_id, SV *callback, const gchar *helpstr, SV *data) { PurplePerlCmdHandler *handler; @@ -633,7 +633,7 @@ handler = g_new0(PurplePerlCmdHandler, 1); handler->plugin = plugin; handler->cmd = g_strdup(command); - handler->prpl_id = g_strdup(prpl_id); + handler->protocol_id = g_strdup(protocol_id); if (callback != NULL && callback != &PL_sv_undef) handler->callback = newSVsv(callback); @@ -647,7 +647,7 @@ cmd_handlers = g_slist_append(cmd_handlers, handler); - handler->id = purple_cmd_register(command, args, priority, flag, prpl_id, + handler->id = purple_cmd_register(command, args, priority, flag, protocol_id, PURPLE_CMD_FUNC(perl_cmd_cb), helpstr, handler); @@ -667,7 +667,7 @@ SvREFCNT_dec(handler->data); g_free(handler->cmd); - g_free(handler->prpl_id); + g_free(handler->protocol_id); g_free(handler); }
--- a/libpurple/plugins/perl/perl-handlers.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/perl-handlers.h Fri Jan 31 18:02:20 2014 +0530 @@ -2,7 +2,7 @@ #define _PURPLE_PERL_HANDLERS_H_ #include "cmds.h" -#include "plugin.h" +#include "plugins.h" #include "prefs.h" #include "pluginpref.h" #ifdef PURPLE_GTKPERL @@ -15,7 +15,7 @@ PurpleCmdId id; SV *callback; SV *data; - gchar *prpl_id; + gchar *protocol_id; gchar *cmd; PurplePlugin *plugin; } PurplePerlCmdHandler; @@ -73,7 +73,7 @@ PurpleCmdId purple_perl_cmd_register(PurplePlugin *plugin, const gchar *cmd, const gchar *args, PurpleCmdPriority priority, - PurpleCmdFlag flag, const gchar *prpl_id, + PurpleCmdFlag flag, const gchar *protocol_id, SV *callback, const gchar *helpstr, SV *data); void purple_perl_cmd_unregister(PurpleCmdId id); void purple_perl_cmd_clear_for_plugin(PurplePlugin *plugin);
--- a/libpurple/plugins/perl/perl.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/perl.c Fri Jan 31 18:02:20 2014 +0530 @@ -90,7 +90,7 @@ #endif #include "internal.h" #include "debug.h" -#include "plugin.h" +#include "plugins.h" #include "signals.h" #include "version.h" @@ -649,70 +649,52 @@ } } +static PurplePluginLoaderInfo loader_info = +{ + probe_perl_plugin, /**< probe */ + load_perl_plugin, /**< load */ + unload_perl_plugin, /**< unload */ + destroy_perl_plugin, /**< destroy */ +}; + +static GPluginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Christian Hammond <chipx86@gnupdate.org>", + NULL + }; + + return gplugin_plugin_info_new( + "id", PERL_PLUGIN_ID, + "name", N_("Perl Plugin Loader"), + "version", DISPLAY_VERSION, + "category", N_("Loader"), + "summary", N_("Provides support for loading perl plugins."), + "description", N_("Provides support for loading perl plugins."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "internal", TRUE, + "load-on-query", TRUE, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { return TRUE; } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { perl_end(); return TRUE; } -static PurplePluginLoaderInfo loader_info = -{ - NULL, /**< exts */ - probe_perl_plugin, /**< probe */ - load_perl_plugin, /**< load */ - unload_perl_plugin, /**< unload */ - destroy_perl_plugin, /**< destroy */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_LOADER, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - PERL_PLUGIN_ID, /**< id */ - N_("Perl Plugin Loader"), /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Provides support for loading perl plugins."), /**< summary */ - N_("Provides support for loading perl plugins."), /**< description */ - "Christian Hammond <chipx86@gnupdate.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &loader_info, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - static void init_plugin(PurplePlugin *plugin) { @@ -738,4 +720,4 @@ g_module_open("perl.so", 0); } -PURPLE_INIT_PLUGIN(perl, init_plugin, info) +PURPLE_PLUGIN_INIT(perl, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/perl/scripts/account.pl Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/scripts/account.pl Fri Jan 31 18:02:20 2014 +0530 @@ -27,7 +27,7 @@ # We will create these on load then destroy them on unload my $TEST_NAME = "perlTestName"; - my $PROTOCOL_ID = "prpl-aim"; + my $PROTOCOL_ID = "aim"; sub plugin_init {
--- a/libpurple/plugins/perl/scripts/buddy_list.pl Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/scripts/buddy_list.pl Fri Jan 31 18:02:20 2014 +0530 @@ -24,7 +24,7 @@ my $TEST_GROUP = "UConn Buddies"; my $TEST_NAME = "johnhkelm"; my $TEST_ALIAS = "John Kelm"; - my $PROTOCOL_ID = "prpl-aim"; + my $PROTOCOL_ID = "aim"; sub plugin_init {
--- a/libpurple/plugins/perl/scripts/conversation.pl Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/scripts/conversation.pl Fri Jan 31 18:02:20 2014 +0530 @@ -30,7 +30,7 @@ my $TEST_GROUP = "UConn Buddies"; my $TEST_NAME = "johnhkelm"; my $TEST_ALIAS = "John Kelm"; - my $PROTOCOL_ID = "prpl-aim"; + my $PROTOCOL_ID = "aim"; sub plugin_init {
--- a/libpurple/plugins/perl/scripts/plugin_pref.pl Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/perl/scripts/plugin_pref.pl Fri Jan 31 18:02:20 2014 +0530 @@ -28,7 +28,7 @@ my $TEST_GROUP = "perlTestGroup"; my $TEST_NAME = "perlTestName"; my $TEST_ALIAS = "perlTestAlias"; - my $PROTOCOL_ID = "prpl-aim"; + my $PROTOCOL_ID = "aim"; sub foo { $frame = Purple::PluginPref::Frame->new();
--- a/libpurple/plugins/pluginpref_example.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/pluginpref_example.c Fri Jan 31 18:02:20 2014 +0530 @@ -100,55 +100,31 @@ return frame; } -static PurplePluginUiInfo prefs_info = { - get_plugin_pref_frame, - NULL, - - /* Padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = +static PurplePluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ + const gchar * const authors[] = { + "Gary Kramlich <amc_grim@users.sf.net>", + NULL + }; - "core-pluginpref_example", /**< id */ - "Pluginpref Example", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - "An example of how to use pluginprefs", - /** description */ - "An example of how to use pluginprefs", - "Gary Kramlich <amc_grim@users.sf.net>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ + return purple_plugin_info_new( + "id", "core-pluginpref_example", + "name", "Pluginpref Example", + "version", DISPLAY_VERSION, + "category", "Example", + "summary", "An example of how to use pluginprefs", + "description", "An example of how to use pluginprefs", + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} - NULL, /**< ui_info */ - NULL, /**< extra_info */ - &prefs_info, /**< prefs_info */ - NULL, /**< actions */ - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) { purple_prefs_add_none("/plugins/core/pluginpref_example"); purple_prefs_add_bool("/plugins/core/pluginpref_example/bool", TRUE); @@ -160,6 +136,14 @@ "max length string"); purple_prefs_add_string("/plugins/core/pluginpref_example/masked_string", "masked"); purple_prefs_add_string("/plugins/core/pluginpref_example/string_choice", "red"); + + return TRUE; } -PURPLE_INIT_PLUGIN(ppexample, init_plugin, info) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +PURPLE_PLUGIN_INIT(ppexample, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/psychic.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/psychic.c Fri Jan 31 18:02:20 2014 +0530 @@ -10,18 +10,19 @@ #include "status.h" #include "version.h" -#include "plugin.h" +#include "plugins.h" #include "pluginpref.h" #include "prefs.h" #define PLUGIN_ID "core-psychic" #define PLUGIN_NAME N_("Psychic Mode") +#define PLUGIN_CATEGORY N_("Utility") #define PLUGIN_SUMMARY N_("Psychic mode for incoming conversation") #define PLUGIN_DESC N_("Causes conversation windows to appear as other" \ " users begin to message you. This works for" \ " AIM, ICQ, XMPP, Sametime, and Yahoo!") -#define PLUGIN_AUTHOR "Christopher O'Brien <siege@preoccupied.net>" +#define PLUGIN_AUTHORS { "Christopher O'Brien <siege@preoccupied.net>", NULL } #define PREFS_BASE "/plugins/core/psychic" @@ -109,10 +110,37 @@ } +static PurplePluginInfo * +plugin_query(GError **error) { + + const gchar * const authors[] = PLUGIN_AUTHORS; + + return purple_plugin_info_new( + "id", PLUGIN_ID, + "name", PLUGIN_NAME, + "version", DISPLAY_VERSION, + "category", PLUGIN_CATEGORY, + "summary", PLUGIN_SUMMARY, + "description", PLUGIN_DESC, + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} + + static gboolean -plugin_load(PurplePlugin *plugin) { +plugin_load(PurplePlugin *plugin, GError **error) { void *convs_handle; + + purple_prefs_add_none(PREFS_BASE); + purple_prefs_add_bool(PREF_BUDDIES, FALSE); + purple_prefs_add_bool(PREF_NOTICE, TRUE); + purple_prefs_add_bool(PREF_STATUS, TRUE); + convs_handle = purple_conversations_get_handle(); purple_signal_connect(convs_handle, "buddy-typing", plugin, @@ -122,60 +150,11 @@ } -static PurplePluginUiInfo prefs_info = { - get_plugin_pref_frame, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PLUGIN_ID, /**< id */ - PLUGIN_NAME, /**< name */ - DISPLAY_VERSION, /**< version */ - PLUGIN_SUMMARY, /**< summary */ - PLUGIN_DESC, /**< description */ - PLUGIN_AUTHOR, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - &prefs_info, /**< prefs_info */ - NULL, /**< actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - - -static void -init_plugin(PurplePlugin *plugin) { - purple_prefs_add_none(PREFS_BASE); - purple_prefs_add_bool(PREF_BUDDIES, FALSE); - purple_prefs_add_bool(PREF_NOTICE, TRUE); - purple_prefs_add_bool(PREF_STATUS, TRUE); + return TRUE; } -PURPLE_INIT_PLUGIN(psychic, init_plugin, info) +PURPLE_PLUGIN_INIT(psychic, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/signals-test.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/signals-test.c Fri Jan 31 18:02:20 2014 +0530 @@ -676,21 +676,43 @@ purple_xmlnode_set_attrib(iq, "id", id); purple_xmlnode_set_attrib(iq, "type", "result"); - purple_signal_emit(purple_connection_get_prpl(pc), + purple_signal_emit(purple_connection_get_protocol(pc), "jabber-sending-xmlnode", pc, &iq); if (iq != NULL) purple_xmlnode_free(iq); } - /* Cookie monster eats IQ stanzas; the prpl shouldn't keep processing */ + /* Cookie monster eats IQ stanzas; the protocol shouldn't keep processing */ return TRUE; } /************************************************************************** * Plugin stuff **************************************************************************/ +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Christian Hammond <chipx86@gnupdate.org>", + NULL + }; + + return purple_plugin_info_new( + "id", SIGNAL_TEST_PLUGIN_ID, + "name", N_("Signals Test"), + "version", DISPLAY_VERSION, + "category", N_("Testing"), + "summary", N_("Test to see that all signals are working properly."), + "description", N_("Test to see that all signals are working properly."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { void *core_handle = purple_get_core(); void *blist_handle = purple_blist_get_handle(); @@ -700,7 +722,7 @@ void *ft_handle = purple_xfers_get_handle(); void *sound_handle = purple_sounds_get_handle(); void *notify_handle = purple_notify_get_handle(); - void *jabber_handle = purple_plugins_find_with_id("prpl-jabber"); + void *jabber_handle = purple_protocols_find("prpl-jabber"); /* Accounts subsystem signals */ purple_signal_connect(accounts_handle, "account-connecting", @@ -866,9 +888,9 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { - void *jabber_handle = purple_plugins_find_with_id("prpl-jabber"); + void *jabber_handle = purple_protocols_find("prpl-jabber"); purple_signals_disconnect_by_handle(plugin); @@ -884,45 +906,4 @@ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - SIGNAL_TEST_PLUGIN_ID, /**< id */ - N_("Signals Test"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Test to see that all signals are working properly."), - /** description */ - N_("Test to see that all signals are working properly."), - "Christian Hammond <chipx86@gnupdate.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - /* Padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(signalstest, init_plugin, info) +PURPLE_PLUGIN_INIT(signalstest, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/simple.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/simple.c Fri Jan 31 18:02:20 2014 +0530 @@ -1,13 +1,35 @@ #include "internal.h" #include "debug.h" -#include "plugin.h" +#include "plugins.h" #include "version.h" /** Plugin id : type-author-name (to guarantee uniqueness) */ #define SIMPLE_PLUGIN_ID "core-ewarmenhoven-simple" +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Eric Warmenhoven <eric@warmenhoven.org>", + NULL + }; + + return purple_plugin_info_new( + "id", SIMPLE_PLUGIN_ID, + "name", N_("Simple Plugin"), + "version", DISPLAY_VERSION, + "category", N_("Testing"), + "summary", N_("Tests to see that most things are working."), + "description", N_("Tests to see that most things are working."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { purple_debug(PURPLE_DEBUG_INFO, "simple", "simple plugin loaded.\n"); @@ -15,52 +37,11 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { purple_debug(PURPLE_DEBUG_INFO, "simple", "simple plugin unloaded.\n"); return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - SIMPLE_PLUGIN_ID, /**< id */ - N_("Simple Plugin"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Tests to see that most things are working."), - /** description */ - N_("Tests to see that most things are working."), - "Eric Warmenhoven <eric@warmenhoven.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - /* Padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(simple, init_plugin, info) +PURPLE_PLUGIN_INIT(simple, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/ssl/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/ssl/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -37,6 +37,7 @@ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(PLUGIN_CFLAGS) ssl_gnutls_la_CFLAGS = $(AM_CPPFLAGS) $(GNUTLS_CFLAGS)
--- a/libpurple/plugins/ssl/ssl-gnutls.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/ssl/ssl-gnutls.c Fri Jan 31 18:02:20 2014 +0530 @@ -22,7 +22,7 @@ #include "internal.h" #include "debug.h" #include "certificate.h" -#include "plugin.h" +#include "plugins.h" #include "sslconn.h" #include "version.h" #include "util.h" @@ -1280,8 +1280,31 @@ NULL }; +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Christian Hammond <chipx86@gnupdate.org>", + NULL + }; + + return purple_plugin_info_new( + "id", SSL_GNUTLS_PLUGIN_ID, + "name", N_("GNUTLS"), + "version", DISPLAY_VERSION, + "category", N_("SSL"), + "summary", N_("Provides SSL support through GNUTLS."), + "description", N_("Provides SSL support through GNUTLS."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { if(!purple_ssl_get_ops()) { purple_ssl_set_ops(&ssl_ops); @@ -1297,7 +1320,7 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { if(purple_ssl_get_ops() == &ssl_ops) { purple_ssl_set_ops(NULL); @@ -1308,46 +1331,4 @@ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - PURPLE_PLUGIN_FLAG_INVISIBLE, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - SSL_GNUTLS_PLUGIN_ID, /**< id */ - N_("GNUTLS"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Provides SSL support through GNUTLS."), - /** description */ - N_("Provides SSL support through GNUTLS."), - "Christian Hammond <chipx86@gnupdate.org>", - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(ssl_gnutls, init_plugin, info) +PURPLE_PLUGIN_INIT(ssl_gnutls, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/ssl/ssl-nss.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/ssl/ssl-nss.c Fri Jan 31 18:02:20 2014 +0530 @@ -22,7 +22,7 @@ #include "internal.h" #include "debug.h" #include "certificate.h" -#include "plugin.h" +#include "plugins.h" #include "sslconn.h" #include "util.h" #include "version.h" @@ -1004,9 +1004,31 @@ NULL }; +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Christian Hammond <chipx86@gnupdate.org>", + NULL + }; + + return purple_plugin_info_new( + "id", SSL_NSS_PLUGIN_ID, + "name", N_("NSS"), + "version", DISPLAY_VERSION, + "category", N_("SSL"), + "summary", N_("Provides SSL support through Mozilla NSS."), + "description", N_("Provides SSL support through Mozilla NSS."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL, + NULL + ); +} static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { if (!purple_ssl_get_ops()) { purple_ssl_set_ops(&ssl_ops); @@ -1022,7 +1044,7 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { if (purple_ssl_get_ops() == &ssl_ops) { purple_ssl_set_ops(NULL); @@ -1034,46 +1056,4 @@ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - PURPLE_PLUGIN_FLAG_INVISIBLE, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - SSL_NSS_PLUGIN_ID, /**< id */ - N_("NSS"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Provides SSL support through Mozilla NSS."), - /** description */ - N_("Provides SSL support through Mozilla NSS."), - "Christian Hammond <chipx86@gnupdate.org>", - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(ssl_nss, init_plugin, info) +PURPLE_PLUGIN_INIT(ssl_nss, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/ssl/ssl.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/ssl/ssl.c Fri Jan 31 18:02:20 2014 +0530 @@ -21,57 +21,93 @@ */ #include "internal.h" #include "debug.h" -#include "plugin.h" +#include "plugins.h" #include "sslconn.h" #include "version.h" -#define SSL_PLUGIN_ID "core-ssl" +#define SSL_PLUGIN_ID "core-ssl" +#define SSL_PLUGIN_DOMAIN (g_quark_from_static_string(SSL_PLUGIN_ID)) static PurplePlugin *ssl_plugin = NULL; static gboolean -probe_ssl_plugins(PurplePlugin *my_plugin) +probe_ssl_plugins(PurplePlugin *my_plugin, GError **error) { PurplePlugin *plugin; - GList *l; + PurplePluginInfo *info; + GList *plugins, *l; ssl_plugin = NULL; - for (l = purple_plugins_get_all(); l != NULL; l = l->next) + plugins = purple_plugins_find_all(); + + for (l = plugins; l != NULL; l = l->next) { - plugin = (PurplePlugin *)l->data; - + plugin = PURPLE_PLUGIN(l->data); if (plugin == my_plugin) continue; - if (plugin->info != NULL && plugin->info->id != NULL && - strncmp(plugin->info->id, "ssl-", 4) == 0) + info = purple_plugin_get_info(plugin); + + if (strncmp(purple_plugin_info_get_id(info), "ssl-", 4) == 0) { - if (purple_plugin_is_loaded(plugin) || purple_plugin_load(plugin)) + if (purple_plugin_load(plugin, NULL)) { ssl_plugin = plugin; - break; } } } - return (ssl_plugin != NULL); + g_list_free(plugins); + + if (ssl_plugin == NULL) { + g_set_error(error, SSL_PLUGIN_DOMAIN, 0, + "Could not load a plugin that implements SSL."); + return FALSE; + } else { + return TRUE; + } +} + +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Christian Hammond <chipx86@gnupdate.org>", + NULL + }; + + return purple_plugin_info_new( + "id", SSL_PLUGIN_ID, + "name", N_("SSL"), + "version", DISPLAY_VERSION, + "category", N_("SSL"), + "summary", N_("Provides a wrapper around SSL support libraries."), + "description", N_("Provides a wrapper around SSL support libraries."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); } static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { - return probe_ssl_plugins(plugin); + return probe_ssl_plugins(plugin, error); } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { if (ssl_plugin != NULL && g_list_find(purple_plugins_get_loaded(), ssl_plugin) != NULL) { - purple_plugin_unload(ssl_plugin); + if (!purple_plugin_unload(ssl_plugin, error)) + return FALSE; } ssl_plugin = NULL; @@ -79,46 +115,4 @@ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - PURPLE_PLUGIN_FLAG_INVISIBLE, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - SSL_PLUGIN_ID, /**< id */ - N_("SSL"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Provides a wrapper around SSL support libraries."), - /** description */ - N_("Provides a wrapper around SSL support libraries."), - "Christian Hammond <chipx86@gnupdate.org>", - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(ssl, init_plugin, info) +PURPLE_PLUGIN_INIT(ssl, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/statenotify.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/statenotify.c Fri Jan 31 18:02:20 2014 +0530 @@ -6,7 +6,7 @@ #include "signals.h" #include "version.h" -#include "plugin.h" +#include "plugins.h" #include "pluginpref.h" #include "prefs.h" @@ -116,11 +116,41 @@ return frame; } +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Christian Hammond <chipx86@gnupdate.org>", + NULL + }; + + return purple_plugin_info_new( + "id", STATENOTIFY_PLUGIN_ID, + "name", N_("Buddy State Notification"), + "version", DISPLAY_VERSION, + "category", N_("Notification"), + "summary", N_("Notifies in a conversation window when a " + "buddy goes or returns from away or idle."), + "description", N_("Notifies in a conversation window when a " + "buddy goes or returns from away or idle."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { void *blist_handle = purple_blist_get_handle(); + purple_prefs_add_none("/plugins/core/statenotify"); + purple_prefs_add_bool("/plugins/core/statenotify/notify_away", TRUE); + purple_prefs_add_bool("/plugins/core/statenotify/notify_idle", TRUE); + purple_prefs_add_bool("/plugins/core/statenotify/notify_signon", TRUE); + purple_signal_connect(blist_handle, "buddy-status-changed", plugin, PURPLE_CALLBACK(buddy_status_changed_cb), NULL); purple_signal_connect(blist_handle, "buddy-idle-changed", plugin, @@ -133,64 +163,10 @@ return TRUE; } -static PurplePluginUiInfo prefs_info = -{ - get_plugin_pref_frame, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - STATENOTIFY_PLUGIN_ID, /**< id */ - N_("Buddy State Notification"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Notifies in a conversation window when a buddy goes or returns from " - "away or idle."), - /** description */ - N_("Notifies in a conversation window when a buddy goes or returns from " - "away or idle."), - "Christian Hammond <chipx86@gnupdate.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - &prefs_info, /**< prefs_info */ - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - purple_prefs_add_none("/plugins/core/statenotify"); - purple_prefs_add_bool("/plugins/core/statenotify/notify_away", TRUE); - purple_prefs_add_bool("/plugins/core/statenotify/notify_idle", TRUE); - purple_prefs_add_bool("/plugins/core/statenotify/notify_signon", TRUE); + return TRUE; } -PURPLE_INIT_PLUGIN(statenotify, init_plugin, info) +PURPLE_PLUGIN_INIT(statenotify, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/plugins/tcl/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/tcl/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -7,7 +7,7 @@ tcl_la_SOURCES = tcl.c tcl_glib.c tcl_glib.h tcl_cmds.c tcl_signals.c tcl_purple.h \ tcl_ref.c tcl_cmd.c -tcl_la_LIBADD = $(GLIB_LIBS) $(TCL_LIBS) $(TK_LIBS) +tcl_la_LIBADD = $(GLIB_LIBS) $(GPLUGIN_LIBS) $(TCL_LIBS) $(TK_LIBS) EXTRA_DIST = signal-test.tcl Makefile.mingw @@ -17,6 +17,7 @@ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(PLUGIN_CFLAGS) \ $(TK_CFLAGS) \ $(TCL_CFLAGS)
--- a/libpurple/plugins/tcl/tcl.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/tcl/tcl.c Fri Jan 31 18:02:20 2014 +0530 @@ -38,7 +38,7 @@ #include "internal.h" #include "connection.h" -#include "plugin.h" +#include "plugins.h" #include "signals.h" #include "debug.h" #include "util.h" @@ -348,7 +348,39 @@ return; } -static gboolean tcl_load(PurplePlugin *plugin) +static PurplePluginLoaderInfo tcl_loader_info = +{ + tcl_probe_plugin, + tcl_load_plugin, + tcl_unload_plugin, + tcl_destroy_plugin, +}; + +static GPluginPluginInfo * +tcl_query(GError **error) +{ + const gchar * const authors[] = { + "Ethan Blanton <eblanton@cs.purdue.edu>", + NULL + }; + + return gplugin_plugin_info_new( + "id", "core-tcl", + "name", N_("Tcl Plugin Loader"), + "version", DISPLAY_VERSION, + "category", N_("Loader"), + "summary", N_("Provides support for loading Tcl plugins"), + "description", N_("Provides support for loading Tcl plugins"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "internal", TRUE, + "load-on-query", TRUE, + NULL + ); +} + +static gboolean tcl_load(PurplePlugin *plugin, GError **error) { if(!tcl_loaded) return FALSE; @@ -378,7 +410,7 @@ return TRUE; } -static gboolean tcl_unload(PurplePlugin *plugin) +static gboolean tcl_unload(PurplePlugin *plugin, GError **error) { g_hash_table_destroy(tcl_plugins); tcl_plugins = NULL; @@ -397,53 +429,6 @@ return TRUE; } -static PurplePluginLoaderInfo tcl_loader_info = -{ - NULL, - tcl_probe_plugin, - tcl_load_plugin, - tcl_unload_plugin, - tcl_destroy_plugin, - - /* pidgin */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo tcl_info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_LOADER, - NULL, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - "core-tcl", - N_("Tcl Plugin Loader"), - DISPLAY_VERSION, - N_("Provides support for loading Tcl plugins"), - N_("Provides support for loading Tcl plugins"), - "Ethan Blanton <eblanton@cs.purdue.edu>", - PURPLE_WEBSITE, - tcl_load, - tcl_unload, - NULL, - NULL, - &tcl_loader_info, - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - #ifdef _WIN32 typedef Tcl_Interp* (__cdecl* LPFNTCLCREATEINTERP)(void); typedef void (__cdecl* LPFNTKINIT)(Tcl_Interp*); @@ -515,4 +500,4 @@ tcl_loader_info.exts = g_list_append(tcl_loader_info.exts, "tcl"); } -PURPLE_INIT_PLUGIN(tcl, tcl_init_plugin, tcl_info) +PURPLE_PLUGIN_INIT(tcl, tcl_query, tcl_load, tcl_unload);
--- a/libpurple/plugins/tcl/tcl_cmd.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/tcl/tcl_cmd.c Fri Jan 31 18:02:20 2014 +0530 @@ -71,7 +71,7 @@ if ((id = purple_cmd_register(Tcl_GetString(handler->cmd), handler->args, handler->priority, - handler->flags, handler->prpl_id, + handler->flags, handler->protocol_id, PURPLE_CMD_FUNC(tcl_cmd_callback), handler->helpstr, (void *)handler)) == 0) return 0;
--- a/libpurple/plugins/tcl/tcl_cmds.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/tcl/tcl_cmds.c Fri Jan 31 18:02:20 2014 +0530 @@ -631,7 +631,7 @@ break; case CMD_CMD_REGISTER: if (objc != 9) { - Tcl_WrongNumArgs(interp, 2, objv, "cmd arglist priority flags prpl_id proc helpstr"); + Tcl_WrongNumArgs(interp, 2, objv, "cmd arglist priority flags protocol_id proc helpstr"); return TCL_ERROR; } handler = g_new0(struct tcl_cmd_handler, 1); @@ -648,7 +648,7 @@ g_free(handler); return error; } - handler->prpl_id = Tcl_GetString(objv[6]); + handler->protocol_id = Tcl_GetString(objv[6]); handler->proc = objv[7]; handler->helpstr = Tcl_GetString(objv[8]); handler->interp = interp;
--- a/libpurple/plugins/tcl/tcl_purple.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/plugins/tcl/tcl_purple.h Fri Jan 31 18:02:20 2014 +0530 @@ -27,7 +27,7 @@ #include "internal.h" #include "cmds.h" -#include "plugin.h" +#include "plugins.h" #include "stringref.h" struct tcl_signal_handler { @@ -55,7 +55,7 @@ const char *args; int priority; int flags; - const char *prpl_id; + const char *protocol_id; Tcl_Obj *proc; const char *helpstr;
--- a/libpurple/prefs.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/prefs.c Fri Jan 31 18:02:20 2014 +0530 @@ -1358,6 +1358,7 @@ purple_prefs_remove("/plugins/core/autorecon/hide_reconnecting_dialog"); purple_prefs_remove("/plugins/core/autorecon/restore_state"); purple_prefs_remove("/plugins/core/autorecon"); + purple_prefs_remove("/plugins/lopl"); /* Convert old sounds while_away pref to new 3-way pref. */ if (purple_prefs_exists("/purple/sound/while_away") && @@ -1388,7 +1389,6 @@ purple_prefs_add_none("/purple"); purple_prefs_add_none("/plugins"); purple_prefs_add_none("/plugins/core"); - purple_prefs_add_none("/plugins/lopl"); purple_prefs_add_none("/plugins/prpl"); /* Away */
--- a/libpurple/presence.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/presence.c Fri Jan 31 18:02:20 2014 +0530 @@ -558,8 +558,7 @@ { PurpleAccount *account; PurpleConnection *gc = NULL; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; gboolean idle = purple_presence_is_idle(presence); time_t idle_time = purple_presence_get_idle_time(presence); time_t current_time = time(NULL); @@ -590,14 +589,11 @@ gc = purple_account_get_connection(account); - if(gc) - prpl = purple_connection_get_prpl(gc); + if(PURPLE_CONNECTION_IS_CONNECTED(gc)) + protocol = purple_connection_get_protocol(gc); - if(PURPLE_CONNECTION_IS_CONNECTED(gc) && prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && prpl_info->set_idle) - prpl_info->set_idle(gc, (idle ? (current_time - idle_time) : 0)); + if (protocol) + purple_protocol_server_iface_set_idle(protocol, gc, (idle ? (current_time - idle_time) : 0)); } PurpleAccount * @@ -736,7 +732,7 @@ G_OBJECT_CLASS(presence_class)->constructed(object); PURPLE_PRESENCE_GET_PRIVATE(presence)->statuses = - purple_prpl_get_statuses(priv->account, presence); + purple_protocol_get_statuses(priv->account, presence); } /* Class initializer function */ @@ -933,7 +929,7 @@ account = purple_buddy_get_account(priv->buddy); PURPLE_PRESENCE_GET_PRIVATE(presence)->statuses = - purple_prpl_get_statuses(account, presence); + purple_protocol_get_statuses(account, presence); } /* Class initializer function */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocol.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,1097 @@ +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#include "dbus-maybe.h" +#include "protocol.h" + +static GObjectClass *parent_class; + +/************************************************************************** + * Protocol Object API + **************************************************************************/ +const char * +purple_protocol_get_id(const PurpleProtocol *protocol) +{ + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL); + + return protocol->id; +} + +const char * +purple_protocol_get_name(const PurpleProtocol *protocol) +{ + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL); + + return protocol->name; +} + +PurpleProtocolOptions +purple_protocol_get_options(const PurpleProtocol *protocol) +{ + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), 0); + + return protocol->options; +} + +GList * +purple_protocol_get_user_splits(const PurpleProtocol *protocol) +{ + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL); + + return protocol->user_splits; +} + +GList * +purple_protocol_get_protocol_options(const PurpleProtocol *protocol) +{ + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL); + + return protocol->protocol_options; +} + +PurpleBuddyIconSpec * +purple_protocol_get_icon_spec(const PurpleProtocol *protocol) +{ + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL); + + return protocol->icon_spec; +} + +PurpleWhiteboardOps * +purple_protocol_get_whiteboard_ops(const PurpleProtocol *protocol) +{ + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL); + + return protocol->whiteboard_ops; +} + +static void +user_splits_free(PurpleProtocol *protocol) +{ + g_return_if_fail(PURPLE_IS_PROTOCOL(protocol)); + + while (protocol->user_splits) { + PurpleAccountUserSplit *split = protocol->user_splits->data; + purple_account_user_split_destroy(split); + protocol->user_splits = g_list_delete_link(protocol->user_splits, + protocol->user_splits); + } +} + +static void +protocol_options_free(PurpleProtocol *protocol) +{ + g_return_if_fail(PURPLE_IS_PROTOCOL(protocol)); + + while (protocol->protocol_options) { + PurpleAccountOption *option = protocol->protocol_options->data; + purple_account_option_destroy(option); + protocol->protocol_options = + g_list_delete_link(protocol->protocol_options, + protocol->protocol_options); + } +} + +static void +icon_spec_free(PurpleProtocol *protocol) +{ + g_return_if_fail(PURPLE_IS_PROTOCOL(protocol)); + + g_free(protocol->icon_spec); + protocol->icon_spec = NULL; +} + +void +purple_protocol_override(PurpleProtocol *protocol, + PurpleProtocolOverrideFlags flags) +{ + g_return_if_fail(PURPLE_IS_PROTOCOL(protocol)); + + if (flags & PURPLE_PROTOCOL_OVERRIDE_USER_SPLITS) + user_splits_free(protocol); + if (flags & PURPLE_PROTOCOL_OVERRIDE_PROTOCOL_OPTIONS) + protocol_options_free(protocol); + if (flags & PURPLE_PROTOCOL_OVERRIDE_ICON_SPEC) + icon_spec_free(protocol); +} + +/************************************************************************** + * GObject stuff + **************************************************************************/ +static void +purple_protocol_init(GTypeInstance *instance, gpointer klass) +{ + PURPLE_DBUS_REGISTER_POINTER(PURPLE_PROTOCOL(instance), PurpleProtocol); +} + +static void +purple_protocol_finalize(GObject *object) +{ + PurpleProtocol *protocol = PURPLE_PROTOCOL(object); + GList *accounts, *l; + + accounts = purple_accounts_get_all_active(); + for (l = accounts; l != NULL; l = l->next) { + PurpleAccount *account = PURPLE_ACCOUNT(l->data); + if (purple_account_is_disconnected(account)) + continue; + + if (purple_strequal(protocol->id, + purple_account_get_protocol_id(account))) + purple_account_disconnect(account); + } + + g_list_free(accounts); + + purple_request_close_with_handle(protocol); + purple_notify_close_with_handle(protocol); + + purple_signals_disconnect_by_handle(protocol); + purple_signals_unregister_by_instance(protocol); + + purple_prefs_disconnect_by_handle(protocol); + + user_splits_free(protocol); + protocol_options_free(protocol); + icon_spec_free(protocol); + + PURPLE_DBUS_UNREGISTER_POINTER(protocol); + + parent_class->finalize(object); +} + +static void +purple_protocol_class_init(PurpleProtocolClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + obj_class->finalize = purple_protocol_finalize; +} + +GType +purple_protocol_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolClass), + .class_init = (GClassInitFunc)purple_protocol_class_init, + .instance_size = sizeof(PurpleProtocol), + .instance_init = (GInstanceInitFunc)purple_protocol_init, + }; + + type = g_type_register_static(G_TYPE_OBJECT, "PurpleProtocol", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + +/************************************************************************** + * Protocol Class API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolClass *klass = PURPLE_PROTOCOL_GET_CLASS(protocol); \ + g_return_if_fail(klass != NULL); \ + if (klass->funcname) \ + klass->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolClass *klass = PURPLE_PROTOCOL_GET_CLASS(protocol); \ + g_return_val_if_fail(klass != NULL, defaultreturn); \ + if (klass->funcname) \ + return klass->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +void +purple_protocol_class_login(PurpleProtocol *protocol, PurpleAccount *account) +{ + DEFINE_PROTOCOL_FUNC(protocol, login, account); +} + +void +purple_protocol_class_close(PurpleProtocol *protocol, PurpleConnection *gc) +{ + DEFINE_PROTOCOL_FUNC(protocol, close, gc); +} + +GList * +purple_protocol_class_status_types(PurpleProtocol *protocol, + PurpleAccount *account) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, status_types, account); +} + +const char * +purple_protocol_class_list_icon(PurpleProtocol *protocol, + PurpleAccount *account, PurpleBuddy *buddy) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, list_icon, account, buddy); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC + +/************************************************************************** + * Protocol Client Interface API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolClientIface *client_iface = \ + PURPLE_PROTOCOL_GET_CLIENT_IFACE(protocol); \ + if (client_iface && client_iface->funcname) \ + client_iface->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolClientIface *client_iface = \ + PURPLE_PROTOCOL_GET_CLIENT_IFACE(protocol); \ + if (client_iface && client_iface->funcname) \ + return client_iface->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +GType +purple_protocol_client_iface_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolClientIface), + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "PurpleProtocolClientIface", &info, 0); + } + return type; +} + +GList * +purple_protocol_client_iface_get_actions(PurpleProtocol *protocol, + PurpleConnection *gc) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_actions, gc); +} + +const char * +purple_protocol_client_iface_list_emblem(PurpleProtocol *protocol, + PurpleBuddy *buddy) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, list_emblem, buddy); +} + +char * +purple_protocol_client_iface_status_text(PurpleProtocol *protocol, + PurpleBuddy *buddy) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, status_text, buddy); +} + +void +purple_protocol_client_iface_tooltip_text(PurpleProtocol *protocol, + PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full) +{ + DEFINE_PROTOCOL_FUNC(protocol, tooltip_text, buddy, user_info, full); +} + +GList * +purple_protocol_client_iface_blist_node_menu(PurpleProtocol *protocol, + PurpleBlistNode *node) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, blist_node_menu, node); +} + +void +purple_protocol_client_iface_buddy_free(PurpleProtocol *protocol, + PurpleBuddy *buddy) +{ + DEFINE_PROTOCOL_FUNC(protocol, buddy_free, buddy); +} + +void +purple_protocol_client_iface_convo_closed(PurpleProtocol *protocol, + PurpleConnection *gc, const char *who) +{ + DEFINE_PROTOCOL_FUNC(protocol, convo_closed, gc, who); +} + +const char * +purple_protocol_client_iface_normalize(PurpleProtocol *protocol, + const PurpleAccount *account, const char *who) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, normalize, account, who); +} + +PurpleChat * +purple_protocol_client_iface_find_blist_chat(PurpleProtocol *protocol, + PurpleAccount *account, const char *name) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, find_blist_chat, account, + name); +} + +gboolean +purple_protocol_client_iface_offline_message(PurpleProtocol *protocol, + const PurpleBuddy *buddy) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, FALSE, offline_message, buddy); +} + +GHashTable * +purple_protocol_client_iface_get_account_text_table(PurpleProtocol *protocol, + PurpleAccount *account) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_account_text_table, + account); +} + +PurpleMood * +purple_protocol_client_iface_get_moods(PurpleProtocol *protocol, + PurpleAccount *account) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_moods, account); +} + +gssize +purple_protocol_client_iface_get_max_message_size(PurpleProtocol *protocol, + PurpleConversation *conv) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, get_max_message_size, conv); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC + +/************************************************************************** + * Protocol Server Interface API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolServerIface *server_iface = \ + PURPLE_PROTOCOL_GET_SERVER_IFACE(protocol); \ + if (server_iface && server_iface->funcname) \ + server_iface->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolServerIface *server_iface = \ + PURPLE_PROTOCOL_GET_SERVER_IFACE(protocol); \ + if (server_iface && server_iface->funcname) \ + return server_iface->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +GType +purple_protocol_server_iface_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolServerIface), + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "PurpleProtocolServerIface", &info, 0); + } + return type; +} + +void +purple_protocol_server_iface_register_user(PurpleProtocol *protocol, + PurpleAccount *account) +{ + DEFINE_PROTOCOL_FUNC(protocol, register_user, account); +} + +void +purple_protocol_server_iface_unregister_user(PurpleProtocol *protocol, + PurpleAccount *account, PurpleAccountUnregistrationCb cb, + void *user_data) +{ + DEFINE_PROTOCOL_FUNC(protocol, unregister_user, account, cb, user_data); +} + +void +purple_protocol_server_iface_set_info(PurpleProtocol *protocol, + PurpleConnection *gc, const char *info) +{ + DEFINE_PROTOCOL_FUNC(protocol, set_info, gc, info); +} + +void +purple_protocol_server_iface_get_info(PurpleProtocol *protocol, + PurpleConnection *gc, const char *who) +{ + DEFINE_PROTOCOL_FUNC(protocol, get_info, gc, who); +} + +void +purple_protocol_server_iface_set_status(PurpleProtocol *protocol, + PurpleAccount *account, PurpleStatus *status) +{ + DEFINE_PROTOCOL_FUNC(protocol, set_status, account, status); +} + +void +purple_protocol_server_iface_set_idle(PurpleProtocol *protocol, + PurpleConnection *gc, int idletime) +{ + DEFINE_PROTOCOL_FUNC(protocol, set_idle, gc, idletime); +} + +void +purple_protocol_server_iface_change_passwd(PurpleProtocol *protocol, + PurpleConnection *gc, const char *old_pass, const char *new_pass) +{ + DEFINE_PROTOCOL_FUNC(protocol, change_passwd, gc, old_pass, new_pass); +} + +void +purple_protocol_server_iface_add_buddy(PurpleProtocol *protocol, + PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, + const char *message) +{ + DEFINE_PROTOCOL_FUNC(protocol, add_buddy, gc, buddy, group, message); +} + +void +purple_protocol_server_iface_add_buddies(PurpleProtocol *protocol, + PurpleConnection *gc, GList *buddies, GList *groups, + const char *message) +{ + DEFINE_PROTOCOL_FUNC(protocol, add_buddies, gc, buddies, groups, message); +} + +void +purple_protocol_server_iface_remove_buddy(PurpleProtocol *protocol, + PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) +{ + DEFINE_PROTOCOL_FUNC(protocol, remove_buddy, gc, buddy, group); +} + +void +purple_protocol_server_iface_remove_buddies(PurpleProtocol *protocol, + PurpleConnection *gc, GList *buddies, GList *groups) +{ + DEFINE_PROTOCOL_FUNC(protocol, remove_buddies, gc, buddies, groups); +} + +void +purple_protocol_server_iface_keepalive(PurpleProtocol *protocol, + PurpleConnection *gc) +{ + DEFINE_PROTOCOL_FUNC(protocol, keepalive, gc); +} + +void +purple_protocol_server_iface_alias_buddy(PurpleProtocol *protocol, + PurpleConnection *gc, const char *who, const char *alias) +{ + DEFINE_PROTOCOL_FUNC(protocol, alias_buddy, gc, who, alias); +} + +void +purple_protocol_server_iface_group_buddy(PurpleProtocol *protocol, + PurpleConnection *gc, const char *who, const char *old_group, + const char *new_group) +{ + DEFINE_PROTOCOL_FUNC(protocol, group_buddy, gc, who, old_group, new_group); +} + +void +purple_protocol_server_iface_rename_group(PurpleProtocol *protocol, + PurpleConnection *gc, const char *old_name, PurpleGroup *group, + GList *moved_buddies) +{ + DEFINE_PROTOCOL_FUNC(protocol, rename_group, gc, old_name, group, + moved_buddies); +} + +void +purple_protocol_server_iface_set_buddy_icon(PurpleProtocol *protocol, + PurpleConnection *gc, PurpleStoredImage *img) +{ + DEFINE_PROTOCOL_FUNC(protocol, set_buddy_icon, gc, img); +} + +void +purple_protocol_server_iface_remove_group(PurpleProtocol *protocol, + PurpleConnection *gc, PurpleGroup *group) +{ + DEFINE_PROTOCOL_FUNC(protocol, remove_group, gc, group); +} + +int +purple_protocol_server_iface_send_raw(PurpleProtocol *protocol, + PurpleConnection *gc, const char *buf, int len) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, send_raw, gc, buf, len); +} + +void +purple_protocol_server_iface_set_public_alias(PurpleProtocol *protocol, + PurpleConnection *gc, const char *alias, + PurpleSetPublicAliasSuccessCallback success_cb, + PurpleSetPublicAliasFailureCallback failure_cb) +{ + DEFINE_PROTOCOL_FUNC(protocol, set_public_alias, gc, alias, success_cb, + failure_cb); +} + +void +purple_protocol_server_iface_get_public_alias(PurpleProtocol *protocol, + PurpleConnection *gc, PurpleGetPublicAliasSuccessCallback success_cb, + PurpleGetPublicAliasFailureCallback failure_cb) +{ + DEFINE_PROTOCOL_FUNC(protocol, get_public_alias, gc, success_cb, + failure_cb); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC + +/************************************************************************** + * Protocol IM Interface API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolIMIface *im_iface = \ + PURPLE_PROTOCOL_GET_IM_IFACE(protocol); \ + if (im_iface && im_iface->funcname) \ + im_iface->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolIMIface *im_iface = \ + PURPLE_PROTOCOL_GET_IM_IFACE(protocol); \ + if (im_iface && im_iface->funcname) \ + return im_iface->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +GType +purple_protocol_im_iface_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolIMIface), + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "PurpleProtocolIMIface", &info, 0); + } + return type; +} + +int +purple_protocol_im_iface_send(PurpleProtocol *protocol, PurpleConnection *gc, + const char *who, const char *message, PurpleMessageFlags flags) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, send, gc, who, message, + flags); +} + +unsigned int +purple_protocol_im_iface_send_typing(PurpleProtocol *protocol, + PurpleConnection *gc, const char *name, PurpleIMTypingState state) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, send_typing, gc, name, state); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC + +/************************************************************************** + * Protocol Chat Interface API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolChatIface *chat_iface = \ + PURPLE_PROTOCOL_GET_CHAT_IFACE(protocol); \ + if (chat_iface && chat_iface->funcname) \ + chat_iface->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolChatIface *chat_iface = \ + PURPLE_PROTOCOL_GET_CHAT_IFACE(protocol); \ + if (chat_iface && chat_iface->funcname) \ + return chat_iface->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +GType +purple_protocol_chat_iface_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolChatIface), + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "PurpleProtocolChatIface", &info, 0); + } + return type; +} + +GList * +purple_protocol_chat_iface_info(PurpleProtocol *protocol, PurpleConnection *gc) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, info, gc); +} + +GHashTable * +purple_protocol_chat_iface_info_defaults(PurpleProtocol *protocol, + PurpleConnection *gc, const char *chat_name) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, info_defaults, gc, + chat_name); +} + +void +purple_protocol_chat_iface_join(PurpleProtocol *protocol, PurpleConnection *gc, + GHashTable *components) +{ + DEFINE_PROTOCOL_FUNC(protocol, join, gc, components); +} + +void +purple_protocol_chat_iface_reject(PurpleProtocol *protocol, + PurpleConnection *gc, GHashTable *components) +{ + DEFINE_PROTOCOL_FUNC(protocol, reject, gc, components); +} + +char * +purple_protocol_chat_iface_get_name(PurpleProtocol *protocol, + GHashTable *components) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_name, components); +} + +void +purple_protocol_chat_iface_invite(PurpleProtocol *protocol, + PurpleConnection *gc, int id, const char *message, const char *who) +{ + DEFINE_PROTOCOL_FUNC(protocol, invite, gc, id, message, who); +} + +void +purple_protocol_chat_iface_leave(PurpleProtocol *protocol, PurpleConnection *gc, + int id) +{ + DEFINE_PROTOCOL_FUNC(protocol, leave, gc, id); +} + +void +purple_protocol_chat_iface_whisper(PurpleProtocol *protocol, + PurpleConnection *gc, int id, const char *who, const char *message) +{ + DEFINE_PROTOCOL_FUNC(protocol, whisper, gc, id, who, message); +} + +int +purple_protocol_chat_iface_send(PurpleProtocol *protocol, PurpleConnection *gc, + int id, const char *message, PurpleMessageFlags flags) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, send, gc, id, message, flags); +} + +char * +purple_protocol_chat_iface_get_user_real_name(PurpleProtocol *protocol, + PurpleConnection *gc, int id, const char *who) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_user_real_name, gc, id, + who); +} + +void +purple_protocol_chat_iface_set_topic(PurpleProtocol *protocol, + PurpleConnection *gc, int id, const char *topic) +{ + DEFINE_PROTOCOL_FUNC(protocol, set_topic, gc, id, topic); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC + +/************************************************************************** + * Protocol Privacy Interface API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolPrivacyIface *privacy_iface = \ + PURPLE_PROTOCOL_GET_PRIVACY_IFACE(protocol); \ + if (privacy_iface && privacy_iface->funcname) \ + privacy_iface->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolPrivacyIface *privacy_iface = \ + PURPLE_PROTOCOL_GET_PRIVACY_IFACE(protocol); \ + if (privacy_iface && privacy_iface->funcname) \ + return privacy_iface->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +GType +purple_protocol_privacy_iface_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolPrivacyIface), + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "PurpleProtocolPrivacyIface", &info, 0); + } + return type; +} + +void +purple_protocol_privacy_iface_add_permit(PurpleProtocol *protocol, + PurpleConnection *gc, const char *name) +{ + DEFINE_PROTOCOL_FUNC(protocol, add_permit, gc, name); +} + +void +purple_protocol_privacy_iface_add_deny(PurpleProtocol *protocol, + PurpleConnection *gc, const char *name) +{ + DEFINE_PROTOCOL_FUNC(protocol, add_deny, gc, name); +} + +void +purple_protocol_privacy_iface_rem_permit(PurpleProtocol *protocol, + PurpleConnection *gc, const char *name) +{ + DEFINE_PROTOCOL_FUNC(protocol, rem_permit, gc, name); +} + +void +purple_protocol_privacy_iface_rem_deny(PurpleProtocol *protocol, + PurpleConnection *gc, const char *name) +{ + DEFINE_PROTOCOL_FUNC(protocol, rem_deny, gc, name); +} + +void +purple_protocol_privacy_iface_set_permit_deny(PurpleProtocol *protocol, + PurpleConnection *gc) +{ + DEFINE_PROTOCOL_FUNC(protocol, set_permit_deny, gc); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC + +/************************************************************************** + * Protocol Xfer Interface API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolXferIface *xfer_iface = \ + PURPLE_PROTOCOL_GET_XFER_IFACE(protocol); \ + if (xfer_iface && xfer_iface->funcname) \ + xfer_iface->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolXferIface *xfer_iface = \ + PURPLE_PROTOCOL_GET_XFER_IFACE(protocol); \ + if (xfer_iface && xfer_iface->funcname) \ + return xfer_iface->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +GType +purple_protocol_xfer_iface_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolXferIface), + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "PurpleProtocolXferIface", &info, 0); + } + return type; +} + +gboolean +purple_protocol_xfer_iface_can_receive(PurpleProtocol *protocol, + PurpleConnection *gc, const char *who) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, FALSE, can_receive, gc, who); +} + +void +purple_protocol_xfer_iface_send(PurpleProtocol *protocol, PurpleConnection *gc, + const char *who, const char *filename) +{ + DEFINE_PROTOCOL_FUNC(protocol, send, gc, who, filename); +} + +PurpleXfer * +purple_protocol_xfer_iface_new_xfer(PurpleProtocol *protocol, + PurpleConnection *gc, const char *who) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, new_xfer, gc, who); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC + +/************************************************************************** + * Protocol Roomlist Interface API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolRoomlistIface *roomlist_iface = \ + PURPLE_PROTOCOL_GET_ROOMLIST_IFACE(protocol); \ + if (roomlist_iface && roomlist_iface->funcname) \ + roomlist_iface->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolRoomlistIface *roomlist_iface = \ + PURPLE_PROTOCOL_GET_ROOMLIST_IFACE(protocol); \ + if (roomlist_iface && roomlist_iface->funcname) \ + return roomlist_iface->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +GType +purple_protocol_roomlist_iface_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolRoomlistIface), + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "PurpleProtocolRoomlistIface", &info, 0); + } + return type; +} + +PurpleRoomlist * +purple_protocol_roomlist_iface_get_list(PurpleProtocol *protocol, + PurpleConnection *gc) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_list, gc); +} + +void +purple_protocol_roomlist_iface_cancel(PurpleProtocol *protocol, + PurpleRoomlist *list) +{ + DEFINE_PROTOCOL_FUNC(protocol, cancel, list); +} + +void +purple_protocol_roomlist_iface_expand_category(PurpleProtocol *protocol, + PurpleRoomlist *list, PurpleRoomlistRoom *category) +{ + DEFINE_PROTOCOL_FUNC(protocol, expand_category, list, category); +} + +char * +purple_protocol_roomlist_iface_room_serialize(PurpleProtocol *protocol, + PurpleRoomlistRoom *room) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, room_serialize, room); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC + +/************************************************************************** + * Protocol Attention Interface API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolAttentionIface *attention_iface = \ + PURPLE_PROTOCOL_GET_ATTENTION_IFACE(protocol); \ + if (attention_iface && attention_iface->funcname) \ + attention_iface->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolAttentionIface *attention_iface = \ + PURPLE_PROTOCOL_GET_ATTENTION_IFACE(protocol); \ + if (attention_iface && attention_iface->funcname) \ + return attention_iface->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +GType +purple_protocol_attention_iface_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolAttentionIface), + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "PurpleProtocolAttentionIface", &info, 0); + } + return type; +} + +gboolean +purple_protocol_attention_iface_send(PurpleProtocol *protocol, + PurpleConnection *gc, const char *username, guint type) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, FALSE, send, gc, username, type); +} + +GList * +purple_protocol_attention_iface_get_types(PurpleProtocol *protocol, + PurpleAccount *account) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_types, account); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC + +/************************************************************************** + * Protocol Media Interface API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolMediaIface *media_iface = \ + PURPLE_PROTOCOL_GET_MEDIA_IFACE(protocol); \ + if (media_iface && media_iface->funcname) \ + media_iface->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolMediaIface *media_iface = \ + PURPLE_PROTOCOL_GET_MEDIA_IFACE(protocol); \ + if (media_iface && media_iface->funcname) \ + return media_iface->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +GType +purple_protocol_media_iface_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolMediaIface), + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "PurpleProtocolMediaIface", &info, 0); + } + return type; +} + +gboolean +purple_protocol_media_iface_initiate_session(PurpleProtocol *protocol, + PurpleAccount *account, const char *who, PurpleMediaSessionType type) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, FALSE, initiate_session, account, + who, type); +} + +PurpleMediaCaps +purple_protocol_media_iface_get_caps(PurpleProtocol *protocol, + PurpleAccount *account, const char *who) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, get_caps, account, who); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC + +/************************************************************************** + * Protocol Factory Interface API + **************************************************************************/ +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \ + PurpleProtocolFactoryIface *factory_iface = \ + PURPLE_PROTOCOL_GET_FACTORY_IFACE(protocol); \ + if (factory_iface && factory_iface->funcname) \ + factory_iface->funcname(__VA_ARGS__); + +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \ + PurpleProtocolFactoryIface *factory_iface = \ + PURPLE_PROTOCOL_GET_FACTORY_IFACE(protocol); \ + if (factory_iface && factory_iface->funcname) \ + return factory_iface->funcname(__VA_ARGS__); \ + else \ + return defaultreturn; + +GType +purple_protocol_factory_iface_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurpleProtocolFactoryIface), + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "PurpleProtocolFactoryIface", &info, 0); + } + return type; +} + +PurpleConnection * +purple_protocol_factory_iface_connection_new(PurpleProtocol *protocol, + PurpleAccount *account, const char *password) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, connection_new, protocol, + account, password); +} + +PurpleRoomlist * +purple_protocol_factory_iface_roomlist_new(PurpleProtocol *protocol, + PurpleAccount *account) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, roomlist_new, account); +} + +PurpleWhiteboard * +purple_protocol_factory_iface_whiteboard_new(PurpleProtocol *protocol, + PurpleAccount *account, const char *who, int state) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, whiteboard_new, account, + who, state); +} + +PurpleXfer * +purple_protocol_factory_iface_xfer_new(PurpleProtocol *protocol, + PurpleAccount *account, PurpleXferType type, const char *who) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, xfer_new, account, type, + who); +} + +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN +#undef DEFINE_PROTOCOL_FUNC
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocol.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,1253 @@ +/** + * @file protocol.h Protocol object and interfaces API + * @ingroup core + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_PROTOCOL_H_ +#define _PURPLE_PROTOCOL_H_ + +#define PURPLE_TYPE_PROTOCOL (purple_protocol_get_type()) +#define PURPLE_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_PROTOCOL, PurpleProtocol)) +#define PURPLE_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_PROTOCOL, PurpleProtocolClass)) +#define PURPLE_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL)) +#define PURPLE_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_PROTOCOL)) +#define PURPLE_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_PROTOCOL, PurpleProtocolClass)) + +/** @copydoc _PurpleProtocol */ +typedef struct _PurpleProtocol PurpleProtocol; +/** @copydoc _PurpleProtocolClass */ +typedef struct _PurpleProtocolClass PurpleProtocolClass; + +#include "account.h" +#include "accountopt.h" +#include "buddyicon.h" +#include "buddylist.h" +#include "connection.h" +#include "conversations.h" +#include "debug.h" +#include "xfer.h" +#include "imgstore.h" +#include "media.h" +#include "notify.h" +#include "plugins.h" +#include "roomlist.h" +#include "status.h" +#include "whiteboard.h" + +/** + * Flags to indicate what base protocol's data a derived protocol wants to + * override. + * + * @see purple_protocol_override() + */ +typedef enum /*< flags >*/ +{ + PURPLE_PROTOCOL_OVERRIDE_USER_SPLITS = 1 << 1, + PURPLE_PROTOCOL_OVERRIDE_PROTOCOL_OPTIONS = 1 << 2, + PURPLE_PROTOCOL_OVERRIDE_ICON_SPEC = 1 << 3, +} PurpleProtocolOverrideFlags; + +/** + * Represents an instance of a protocol registered with the protocols + * subsystem. Protocols must initialize the members to appropriate values. + */ +struct _PurpleProtocol +{ + GObject gparent; + + const char *id; /**< Protocol ID */ + const char *name; /**< Translated name of the protocol */ + + PurpleProtocolOptions options; /**< Protocol options */ + + GList *user_splits; /**< A GList of PurpleAccountUserSplit */ + GList *protocol_options; /**< A GList of PurpleAccountOption */ + + PurpleBuddyIconSpec *icon_spec; /**< The icon spec. */ + + PurpleWhiteboardOps *whiteboard_ops; /**< Whiteboard operations */ + + /*< private >*/ + void (*_purple_reserved1)(void); + void (*_purple_reserved2)(void); + void (*_purple_reserved3)(void); + void (*_purple_reserved4)(void); +}; + +/** + * The base class for all protocols. + * + * All protocol types must implement the methods in this class. + */ +struct _PurpleProtocolClass +{ + GObjectClass parent_class; + + /** + * Log in to the server. + */ + void (*login)(PurpleAccount *); + + /** + * Close connection with the server. + */ + void (*close)(PurpleConnection *); + + /** + * Returns a list of #PurpleStatusType which exist for this account; + * and must add at least the offline and online states. + */ + GList *(*status_types)(PurpleAccount *account); + + /** + * Returns the base icon name for the given buddy and account. + * If buddy is NULL and the account is non-NULL, it will return the + * name to use for the account's icon. If both are NULL, it will + * return the name to use for the protocol's icon. + */ + const char *(*list_icon)(PurpleAccount *account, PurpleBuddy *buddy); + + /*< private >*/ + void (*_purple_reserved1)(void); + void (*_purple_reserved2)(void); + void (*_purple_reserved3)(void); + void (*_purple_reserved4)(void); +}; + +#define PURPLE_TYPE_PROTOCOL_CLIENT_IFACE (purple_protocol_client_iface_get_type()) +#define PURPLE_PROTOCOL_HAS_CLIENT_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_CLIENT_IFACE)) +#define PURPLE_PROTOCOL_GET_CLIENT_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, \ + PurpleProtocolClientIface)) + +/** @copydoc _PurpleProtocolClientIface */ +typedef struct _PurpleProtocolClientIface PurpleProtocolClientIface; + +/** + * The protocol client interface. + * + * This interface provides a gateway between purple and the protocol. + */ +struct _PurpleProtocolClientIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + /** + * Returns the actions the protocol can perform. These will show up in the + * Accounts menu, under a submenu with the name of the account. + */ + GList *(*get_actions)(PurpleConnection *); + + /** + * Fills the four char**'s with string identifiers for "emblems" + * that the UI will interpret and display as relevant + */ + const char *(*list_emblem)(PurpleBuddy *buddy); + + /** + * Gets a short string representing this buddy's status. This will + * be shown on the buddy list. + */ + char *(*status_text)(PurpleBuddy *buddy); + + /** + * Allows the protocol to add text to a buddy's tooltip. + */ + void (*tooltip_text)(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, + gboolean full); + + /** + * Returns a list of #PurpleMenuAction structs, which represent extra + * actions to be shown in (for example) the right-click menu for @a + * node. + */ + GList *(*blist_node_menu)(PurpleBlistNode *node); + + void (*buddy_free)(PurpleBuddy *); + + void (*convo_closed)(PurpleConnection *, const char *who); + + /** + * Convert the username @a who to its canonical form. Also checks for + * validity. + * + * For example, AIM treats "fOo BaR" and "foobar" as the same user; this + * function should return the same normalized string for both of those. + * On the other hand, both of these are invalid for protocols with + * number-based usernames, so function should return NULL in such case. + * + * @param account The account the username is related to. Can + * be NULL. + * @param who The username to convert. + * @return Normalized username, or NULL, if it's invalid. + */ + const char *(*normalize)(const PurpleAccount *account, const char *who); + + PurpleChat *(*find_blist_chat)(PurpleAccount *account, const char *name); + + /** Checks whether offline messages to @a buddy are supported. + * @return @c TRUE if @a buddy can be sent messages while they are + * offline, or @c FALSE if not. + */ + gboolean (*offline_message)(const PurpleBuddy *buddy); + + /** This allows protocols to specify additional strings to be used for + * various purposes. The idea is to stuff a bunch of strings in this hash + * table instead of expanding the struct for every addition. This hash + * table is allocated every call and MUST be unrefed by the caller. + * + * @param account The account to specify. This can be NULL. + * @return The protocol's string hash table. The hash table should be + * destroyed by the caller when it's no longer needed. + */ + GHashTable *(*get_account_text_table)(PurpleAccount *account); + + /** + * Returns an array of "PurpleMood"s, with the last one having + * "mood" set to @c NULL. + */ + PurpleMood *(*get_moods)(PurpleAccount *account); + + /** + * Gets the maximum message size in bytes for the conversation. + * + * It may depend on connection-specific or conversation-specific + * variables, like channel or buddy's name length. + * + * This value is intended for plaintext message, the exact value may be + * lower because of: + * - used newlines (some protocols count them as more than one byte), + * - formatting, + * - used special characters. + * + * @param conv The conversation to query, or NULL to get safe minimum + * for the protocol. + * + * @return Maximum message size, 0 if unspecified, -1 for infinite. + */ + gssize (*get_max_message_size)(PurpleConversation *conv); +}; + +#define PURPLE_TYPE_PROTOCOL_SERVER_IFACE (purple_protocol_server_iface_get_type()) +#define PURPLE_PROTOCOL_HAS_SERVER_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_SERVER_IFACE)) +#define PURPLE_PROTOCOL_GET_SERVER_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_SERVER_IFACE, \ + PurpleProtocolServerIface)) + +/** @copydoc _PurpleProtocolServerIface */ +typedef struct _PurpleProtocolServerIface PurpleProtocolServerIface; + +/** + * The protocol server interface. + * + * This interface provides a gateway between purple and the protocol's server. + */ +struct _PurpleProtocolServerIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + /** new user registration */ + void (*register_user)(PurpleAccount *); + + /** Remove the user from the server. The account can either be + * connected or disconnected. After the removal is finished, the + * connection will stay open and has to be closed! + */ + void (*unregister_user)(PurpleAccount *, PurpleAccountUnregistrationCb cb, + void *user_data); + + void (*set_info)(PurpleConnection *, const char *info); + + /** + * Should arrange for purple_notify_userinfo() to be called with + * @a who's user info. + */ + void (*get_info)(PurpleConnection *, const char *who); + void (*set_status)(PurpleAccount *account, PurpleStatus *status); + + void (*set_idle)(PurpleConnection *, int idletime); + void (*change_passwd)(PurpleConnection *, const char *old_pass, + const char *new_pass); + + /** + * Add a buddy to a group on the server. + * + * This protocol function may be called in situations in which the buddy is + * already in the specified group. If the protocol supports + * authorization and the user is not already authorized to see the + * status of \a buddy, \a add_buddy should request authorization. + * + * If authorization is required, then use the supplied invite message. + */ + void (*add_buddy)(PurpleConnection *pc, PurpleBuddy *buddy, + PurpleGroup *group, const char *message); + void (*add_buddies)(PurpleConnection *pc, GList *buddies, GList *groups, + const char *message); + void (*remove_buddy)(PurpleConnection *, PurpleBuddy *buddy, + PurpleGroup *group); + void (*remove_buddies)(PurpleConnection *, GList *buddies, GList *groups); + + /** If implemented, this will be called regularly for this protocol's + * active connections. You'd want to do this if you need to repeatedly + * send some kind of keepalive packet to the server to avoid being + * disconnected. ("Regularly" is defined by + * <code>KEEPALIVE_INTERVAL</code> in <tt>libpurple/connection.c</tt>.) + */ + void (*keepalive)(PurpleConnection *); + + /** save/store buddy's alias on server list/roster */ + void (*alias_buddy)(PurpleConnection *, const char *who, + const char *alias); + + /** change a buddy's group on a server list/roster */ + void (*group_buddy)(PurpleConnection *, const char *who, + const char *old_group, const char *new_group); + + /** rename a group on a server list/roster */ + void (*rename_group)(PurpleConnection *, const char *old_name, + PurpleGroup *group, GList *moved_buddies); + + /** + * Set the buddy icon for the given connection to @a img. The protocol + * does NOT own a reference to @a img; if it needs one, it must + * #purple_imgstore_ref(@a img) itself. + */ + void (*set_buddy_icon)(PurpleConnection *, PurpleStoredImage *img); + + void (*remove_group)(PurpleConnection *gc, PurpleGroup *group); + + /** For use in plugins that may understand the underlying protocol */ + int (*send_raw)(PurpleConnection *gc, const char *buf, int len); + + /** + * Set the user's "friendly name" (or alias or nickname or + * whatever term you want to call it) on the server. The + * protocol should call success_cb or failure_cb + * *asynchronously* (if it knows immediately that the set will fail, + * call one of the callbacks from an idle/0-second timeout) depending + * on if the nickname is set successfully. + * + * @param gc The connection for which to set an alias + * @param alias The new server-side alias/nickname for this account, + * or NULL to unset the alias/nickname (or return it to + * a protocol-specific "default"). + * @param success_cb Callback to be called if the public alias is set + * @param failure_cb Callback to be called if setting the public alias + * fails + * @see purple_account_set_public_alias + */ + void (*set_public_alias)(PurpleConnection *gc, const char *alias, + PurpleSetPublicAliasSuccessCallback success_cb, + PurpleSetPublicAliasFailureCallback failure_cb); + + /** + * Retrieve the user's "friendly name" as set on the server. + * The protocol should call success_cb or failure_cb + * *asynchronously* (even if it knows immediately that the get will fail, + * call one of the callbacks from an idle/0-second timeout) depending + * on if the nickname is retrieved. + * + * @param gc The connection for which to retireve the alias + * @param success_cb Callback to be called with the retrieved alias + * @param failure_cb Callback to be called if the protocol is unable to + * retrieve the alias + * @see purple_account_get_public_alias + */ + void (*get_public_alias)(PurpleConnection *gc, + PurpleGetPublicAliasSuccessCallback success_cb, + PurpleGetPublicAliasFailureCallback failure_cb); +}; + +#define PURPLE_TYPE_PROTOCOL_IM_IFACE (purple_protocol_im_iface_get_type()) +#define PURPLE_PROTOCOL_HAS_IM_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_IM_IFACE)) +#define PURPLE_PROTOCOL_GET_IM_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_IM_IFACE, \ + PurpleProtocolIMIface)) + +/** @copydoc _PurpleProtocolIMIface */ +typedef struct _PurpleProtocolIMIface PurpleProtocolIMIface; + +/** + * The protocol IM interface. + * + * This interface provides callbacks needed by protocols that implement IMs. + */ +struct _PurpleProtocolIMIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + /** + * This protocol function should return a positive value on success. + * If the message is too big to be sent, return -E2BIG. If + * the account is not connected, return -ENOTCONN. If the + * protocol is unable to send the message for another reason, return + * some other negative value. You can use one of the valid + * errno values, or just big something. If the message should + * not be echoed to the conversation window, return 0. + */ + int (*send)(PurpleConnection *, const char *who, + const char *message, + PurpleMessageFlags flags); + + /** + * @return If this protocol requires the PURPLE_IM_TYPING message to + * be sent repeatedly to signify that the user is still + * typing, then the protocol should return the number of + * seconds to wait before sending a subsequent notification. + * Otherwise the protocol should return 0. + */ + unsigned int (*send_typing)(PurpleConnection *, const char *name, + PurpleIMTypingState state); +}; + +#define PURPLE_TYPE_PROTOCOL_CHAT_IFACE (purple_protocol_chat_iface_get_type()) +#define PURPLE_PROTOCOL_HAS_CHAT_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_CHAT_IFACE)) +#define PURPLE_PROTOCOL_GET_CHAT_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_CHAT_IFACE, \ + PurpleProtocolChatIface)) + +/** @copydoc _PurpleProtocolChatIface */ +typedef struct _PurpleProtocolChatIface PurpleProtocolChatIface; + +/** + * The protocol chat interface. + * + * This interface provides callbacks needed by protocols that implement chats. + */ +struct _PurpleProtocolChatIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + /** + * Returns a list of #PurpleProtocolChatEntry structs, which represent + * information required by the protocol to join a chat. libpurple will + * call join_chat along with the information filled by the user. + * + * @return A list of #PurpleProtocolChatEntry structs + */ + GList *(*info)(PurpleConnection *); + + /** + * Returns a hashtable which maps #PurpleProtocolChatEntry struct + * identifiers to default options as strings based on chat_name. The + * resulting hashtable should be created with + * g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);. Use + * #get_chat_name if you instead need to extract a chat name from a + * hashtable. + * + * @param chat_name The chat name to be turned into components + * @return Hashtable containing the information extracted from chat_name + */ + GHashTable *(*info_defaults)(PurpleConnection *, + const char *chat_name); + + /** + * Called when the user requests joining a chat. Should arrange for + * #serv_got_joined_chat to be called. + * + * @param components A hashtable containing information required to + * join the chat as described by the entries returned + * by #chat_info. It may also be called when accepting + * an invitation, in which case this matches the + * data parameter passed to #serv_got_chat_invite. + */ + void (*join)(PurpleConnection *, GHashTable *components); + + /** + * Called when the user refuses a chat invitation. + * + * @param components A hashtable containing information required to + * join the chat as passed to #serv_got_chat_invite. + */ + void (*reject)(PurpleConnection *, GHashTable *components); + + /** + * Returns a chat name based on the information in components. Use + * #chat_info_defaults if you instead need to generate a hashtable + * from a chat name. + * + * @param components A hashtable containing information about the chat. + */ + char *(*get_name)(GHashTable *components); + + /** + * Invite a user to join a chat. + * + * @param id The id of the chat to invite the user to. + * @param message A message displayed to the user when the invitation + * is received. + * @param who The name of the user to send the invation to. + */ + void (*invite)(PurpleConnection *, int id, + const char *message, const char *who); + /** + * Called when the user requests leaving a chat. + * + * @param id The id of the chat to leave + */ + void (*leave)(PurpleConnection *, int id); + + /** + * Send a whisper to a user in a chat. + * + * @param id The id of the chat. + * @param who The name of the user to send the whisper to. + * @param message The message of the whisper. + */ + void (*whisper)(PurpleConnection *, int id, + const char *who, const char *message); + + /** + * Send a message to a chat. + * This protocol function should return a positive value on success. + * If the message is too big to be sent, return -E2BIG. If + * the account is not connected, return -ENOTCONN. If the + * protocol is unable to send the message for another reason, return + * some other negative value. You can use one of the valid + * errno values, or just big something. + * + * @param id The id of the chat to send the message to. + * @param message The message to send to the chat. + * @param flags A bitwise OR of #PurpleMessageFlags representing + * message flags. + * @return A positive number or 0 in case of success, + * a negative error number in case of failure. + */ + int (*send)(PurpleConnection *, int id, const char *message, + PurpleMessageFlags flags); + + /** Gets the real name of a participant in a chat. For example, on + * XMPP this turns a chat room nick <tt>foo</tt> into + * <tt>room\@server/foo</tt> + * @param gc the connection on which the room is. + * @param id the ID of the chat room. + * @param who the nickname of the chat participant. + * @return the real name of the participant. This string must be + * freed by the caller. + */ + char *(*get_user_real_name)(PurpleConnection *gc, int id, const char *who); + + void (*set_topic)(PurpleConnection *gc, int id, const char *topic); +}; + +#define PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE (purple_protocol_privacy_iface_get_type()) +#define PURPLE_PROTOCOL_HAS_PRIVACY_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE)) +#define PURPLE_PROTOCOL_GET_PRIVACY_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, \ + PurpleProtocolPrivacyIface)) + +/** @copydoc _PurpleProtocolPrivacyIface */ +typedef struct _PurpleProtocolPrivacyIface PurpleProtocolPrivacyIface; + +/** + * The protocol privacy interface. + * + * This interface provides privacy callbacks such as to permit/deny users. + */ +struct _PurpleProtocolPrivacyIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + void (*add_permit)(PurpleConnection *, const char *name); + void (*add_deny)(PurpleConnection *, const char *name); + void (*rem_permit)(PurpleConnection *, const char *name); + void (*rem_deny)(PurpleConnection *, const char *name); + void (*set_permit_deny)(PurpleConnection *); +}; + +#define PURPLE_TYPE_PROTOCOL_XFER_IFACE (purple_protocol_xfer_iface_get_type()) +#define PURPLE_PROTOCOL_HAS_XFER_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_XFER_IFACE)) +#define PURPLE_PROTOCOL_GET_XFER_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_XFER_IFACE, \ + PurpleProtocolXferIface)) + +/** @copydoc _PurpleProtocolXferIface */ +typedef struct _PurpleProtocolXferIface PurpleProtocolXferIface; + +/** + * The protocol file transfer interface. + * + * This interface provides file transfer callbacks for the protocol. + */ +struct _PurpleProtocolXferIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + gboolean (*can_receive)(PurpleConnection *, const char *who); + void (*send)(PurpleConnection *, const char *who, + const char *filename); + PurpleXfer *(*new_xfer)(PurpleConnection *, const char *who); +}; + +#define PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE (purple_protocol_roomlist_iface_get_type()) +#define PURPLE_PROTOCOL_HAS_ROOMLIST_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE)) +#define PURPLE_PROTOCOL_GET_ROOMLIST_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE, \ + PurpleProtocolRoomlistIface)) + +/** @copydoc _PurpleProtocolRoomlistIface */ +typedef struct _PurpleProtocolRoomlistIface PurpleProtocolRoomlistIface; + +/** + * The protocol roomlist interface. + * + * This interface provides callbacks for room listing. + */ +struct _PurpleProtocolRoomlistIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + PurpleRoomlist *(*get_list)(PurpleConnection *gc); + void (*cancel)(PurpleRoomlist *list); + void (*expand_category)(PurpleRoomlist *list, + PurpleRoomlistRoom *category); + /* room list serialize */ + char *(*room_serialize)(PurpleRoomlistRoom *room); +}; + +#define PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE (purple_protocol_attention_iface_get_type()) +#define PURPLE_PROTOCOL_HAS_ATTENTION_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE)) +#define PURPLE_PROTOCOL_GET_ATTENTION_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE, \ + PurpleProtocolAttentionIface)) + +/** @copydoc _PurpleProtocolAttentionIface */ +typedef struct _PurpleProtocolAttentionIface PurpleProtocolAttentionIface; + +/** + * The protocol attention interface. + * + * This interface provides attention API for sending and receiving + * zaps/nudges/buzzes etc. + */ +struct _PurpleProtocolAttentionIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + gboolean (*send)(PurpleConnection *gc, const char *username, + guint type); + GList *(*get_types)(PurpleAccount *acct); +}; + +#define PURPLE_TYPE_PROTOCOL_MEDIA_IFACE (purple_protocol_media_iface_get_type()) +#define PURPLE_PROTOCOL_HAS_MEDIA_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_MEDIA_IFACE)) +#define PURPLE_PROTOCOL_GET_MEDIA_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_MEDIA_IFACE, \ + PurpleProtocolMediaIface)) + +/** @copydoc _PurpleProtocolMediaIface */ +typedef struct _PurpleProtocolMediaIface PurpleProtocolMediaIface; + +/** + * The protocol media interface. + * + * This interface provides callbacks for media sessions on the protocol. + */ +struct _PurpleProtocolMediaIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + /** + * Initiate a media session with the given contact. + * + * @param account The account to initiate the media session on. + * @param who The remote user to initiate the session with. + * @param type The type of media session to initiate. + * @return TRUE if the call succeeded else FALSE. (Doesn't imply the media + * session or stream will be successfully created) + */ + gboolean (*initiate_session)(PurpleAccount *account, const char *who, + PurpleMediaSessionType type); + + /** + * Checks to see if the given contact supports the given type of media + * session. + * + * @param account The account the contact is on. + * @param who The remote user to check for media capability with. + * @return The media caps the contact supports. + */ + PurpleMediaCaps (*get_caps)(PurpleAccount *account, + const char *who); +}; + +#define PURPLE_TYPE_PROTOCOL_FACTORY_IFACE (purple_protocol_factory_iface_get_type()) +#define PURPLE_PROTOCOL_HAS_FACTORY_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_FACTORY_IFACE)) +#define PURPLE_PROTOCOL_GET_FACTORY_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_FACTORY_IFACE, \ + PurpleProtocolFactoryIface)) + +/** @copydoc _PurpleProtocolFactoryIface */ +typedef struct _PurpleProtocolFactoryIface PurpleProtocolFactoryIface; + +/** + * The protocol factory interface. + * + * This interface provides callbacks for construction of protocol-specific + * subclasses of some purple objects. + */ +struct _PurpleProtocolFactoryIface +{ + /*< private >*/ + GTypeInterface parent_iface; + + /** + * Creates a new protocol-specific connection object that inherits + * PurpleConnection. + */ + PurpleConnection *(*connection_new)(PurpleProtocol *protocol, + PurpleAccount *account, + const char *password); + + /** + * Creates a new protocol-specific room list object that inherits + * PurpleRoomlist. + */ + PurpleRoomlist *(*roomlist_new)(PurpleAccount *account); + + /** + * Creates a new protocol-specific whiteboard object that inherits + * PurpleWhiteboard. + */ + PurpleWhiteboard *(*whiteboard_new)(PurpleAccount *account, + const char *who, int state); + + /** + * Creates a new protocol-specific file transfer object that inherits + * PurpleXfer. + */ + PurpleXfer *(*xfer_new)(PurpleAccount *account, PurpleXferType type, + const char *who); +}; + +/** + * Returns TRUE if a protocol implements a function in an interface, + * FALSE otherwise. + */ +#define PURPLE_PROTOCOL_IMPLEMENTS(protocol, IFACE, func) \ + (PURPLE_PROTOCOL_HAS_##IFACE(protocol) && \ + PURPLE_PROTOCOL_GET_##IFACE(protocol)->func != NULL) + +G_BEGIN_DECLS + +/**************************************************************************/ +/** @name Protocol Object API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for #PurpleProtocol. + */ +GType purple_protocol_get_type(void); + +/** + * Returns the ID of a protocol. + * + * @param protocol The protocol. + * + * @return The ID of the protocol. + */ +const char *purple_protocol_get_id(const PurpleProtocol *protocol); + +/** + * Returns the translated name of a protocol. + * + * @param protocol The protocol. + * + * @return The translated name of the protocol. + */ +const char *purple_protocol_get_name(const PurpleProtocol *protocol); + +/** + * Returns the options of a protocol. + * + * @param protocol The protocol. + * + * @return The options of the protocol. + */ +PurpleProtocolOptions purple_protocol_get_options(const PurpleProtocol *protocol); + +/** + * Returns the user splits of a protocol. + * + * @param protocol The protocol. + * + * @return The user splits of the protocol. + */ +GList *purple_protocol_get_user_splits(const PurpleProtocol *protocol); + +/** + * Returns the protocol options of a protocol. + * + * @param protocol The protocol. + * + * @return The protocol options of the protocol. + */ +GList *purple_protocol_get_protocol_options(const PurpleProtocol *protocol); + +/** + * Returns the icon spec of a protocol. + * + * @param protocol The protocol. + * + * @return The icon spec of the protocol. + */ +PurpleBuddyIconSpec *purple_protocol_get_icon_spec(const PurpleProtocol *protocol); + +/** + * Returns the whiteboard ops of a protocol. + * + * @param protocol The protocol. + * + * @return The whiteboard ops of the protocol. + */ +PurpleWhiteboardOps *purple_protocol_get_whiteboard_ops(const PurpleProtocol *protocol); + +/** + * Lets derived protocol types override the base type's instance data, such as + * protocol options, user splits, icon spec, etc. + * This function is called in the *_init() function of your derived protocol, + * to delete the parent type's data so you can define your own. + * + * @param protocol The protocol instance. + * @param flags What instance data to delete. + */ +void purple_protocol_override(PurpleProtocol *protocol, + PurpleProtocolOverrideFlags flags); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Class API */ +/**************************************************************************/ +/*@{*/ + +/** @copydoc _PurpleProtocolClass::login */ +void purple_protocol_class_login(PurpleProtocol *, PurpleAccount *); + +/** @copydoc _PurpleProtocolClass::close */ +void purple_protocol_class_close(PurpleProtocol *, PurpleConnection *); + +/** @copydoc _PurpleProtocolClass::status_types */ +GList *purple_protocol_class_status_types(PurpleProtocol *, + PurpleAccount *account); + +/** @copydoc _PurpleProtocolClass::list_icon */ +const char *purple_protocol_class_list_icon(PurpleProtocol *, + PurpleAccount *account, PurpleBuddy *buddy); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Client Interface API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the protocol client interface. + */ +GType purple_protocol_client_iface_get_type(void); + +/** @copydoc _PurpleProtocolClientInterface::get_actions */ +GList *purple_protocol_client_iface_get_actions(PurpleProtocol *, + PurpleConnection *); + +/** @copydoc _PurpleProtocolClientInterface::list_emblem */ +const char *purple_protocol_client_iface_list_emblem(PurpleProtocol *, + PurpleBuddy *buddy); + +/** @copydoc _PurpleProtocolClientInterface::status_text */ +char *purple_protocol_client_iface_status_text(PurpleProtocol *, + PurpleBuddy *buddy); + +/** @copydoc _PurpleProtocolClientInterface::tooltip_text */ +void purple_protocol_client_iface_tooltip_text(PurpleProtocol *, + PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full); + +/** @copydoc _PurpleProtocolClientInterface::blist_node_menu */ +GList *purple_protocol_client_iface_blist_node_menu(PurpleProtocol *, + PurpleBlistNode *node); + +/** @copydoc _PurpleProtocolClientInterface::buddy_free */ +void purple_protocol_client_iface_buddy_free(PurpleProtocol *, PurpleBuddy *); + +/** @copydoc _PurpleProtocolClientInterface::convo_closed */ +void purple_protocol_client_iface_convo_closed(PurpleProtocol *, + PurpleConnection *, const char *who); + +/** @copydoc _PurpleProtocolClientInterface::normalize */ +const char *purple_protocol_client_iface_normalize(PurpleProtocol *, + const PurpleAccount *account, const char *who); + +/** @copydoc _PurpleProtocolClientInterface::find_blist_chat */ +PurpleChat *purple_protocol_client_iface_find_blist_chat(PurpleProtocol *, + PurpleAccount *account, const char *name); + +/** @copydoc _PurpleProtocolClientInterface::offline_message */ +gboolean purple_protocol_client_iface_offline_message(PurpleProtocol *, + const PurpleBuddy *buddy); + +/** @copydoc _PurpleProtocolClientInterface::get_account_text_table */ +GHashTable *purple_protocol_client_iface_get_account_text_table(PurpleProtocol *, + PurpleAccount *account); + +/** @copydoc _PurpleProtocolClientInterface::get_moods */ +PurpleMood *purple_protocol_client_iface_get_moods(PurpleProtocol *, + PurpleAccount *account); + +/** @copydoc _PurpleProtocolClientInterface::get_max_message_size */ +gssize purple_protocol_client_iface_get_max_message_size(PurpleProtocol *, + PurpleConversation *conv); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Server Interface API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the protocol server interface. + */ +GType purple_protocol_server_iface_get_type(void); + +/** @copydoc _PurpleProtocolServerInterface::register_user */ +void purple_protocol_server_iface_register_user(PurpleProtocol *, + PurpleAccount *); + +/** @copydoc _PurpleProtocolServerInterface::unregister_user */ +void purple_protocol_server_iface_unregister_user(PurpleProtocol *, + PurpleAccount *, PurpleAccountUnregistrationCb cb, void *user_data); + +/** @copydoc _PurpleProtocolServerInterface::set_info */ +void purple_protocol_server_iface_set_info(PurpleProtocol *, PurpleConnection *, + const char *info); + +/** @copydoc _PurpleProtocolServerInterface::get_info */ +void purple_protocol_server_iface_get_info(PurpleProtocol *, PurpleConnection *, + const char *who); + +/** @copydoc _PurpleProtocolServerInterface::set_status */ +void purple_protocol_server_iface_set_status(PurpleProtocol *, + PurpleAccount *account, PurpleStatus *status); + +/** @copydoc _PurpleProtocolServerInterface::set_idle */ +void purple_protocol_server_iface_set_idle(PurpleProtocol *, PurpleConnection *, + int idletime); + +/** @copydoc _PurpleProtocolServerInterface::change_passwd */ +void purple_protocol_server_iface_change_passwd(PurpleProtocol *, + PurpleConnection *, const char *old_pass, const char *new_pass); + +/** @copydoc _PurpleProtocolServerInterface::add_buddy */ +void purple_protocol_server_iface_add_buddy(PurpleProtocol *, + PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, + const char *message); + +/** @copydoc _PurpleProtocolServerInterface::add_buddies */ +void purple_protocol_server_iface_add_buddies(PurpleProtocol *, + PurpleConnection *pc, GList *buddies, GList *groups, + const char *message); + +/** @copydoc _PurpleProtocolServerInterface::remove_buddy */ +void purple_protocol_server_iface_remove_buddy(PurpleProtocol *, + PurpleConnection *, PurpleBuddy *buddy, PurpleGroup *group); + +/** @copydoc _PurpleProtocolServerInterface::remove_buddies */ +void purple_protocol_server_iface_remove_buddies(PurpleProtocol *, + PurpleConnection *, GList *buddies, GList *groups); + +/** @copydoc _PurpleProtocolServerInterface::keepalive */ +void purple_protocol_server_iface_keepalive(PurpleProtocol *, + PurpleConnection *); + +/** @copydoc _PurpleProtocolServerInterface::alias_buddy */ +void purple_protocol_server_iface_alias_buddy(PurpleProtocol *, + PurpleConnection *, const char *who, const char *alias); + +/** @copydoc _PurpleProtocolServerInterface::group_buddy */ +void purple_protocol_server_iface_group_buddy(PurpleProtocol *, + PurpleConnection *, const char *who, const char *old_group, + const char *new_group); + +/** @copydoc _PurpleProtocolServerInterface::rename_group */ +void purple_protocol_server_iface_rename_group(PurpleProtocol *, + PurpleConnection *, const char *old_name, PurpleGroup *group, + GList *moved_buddies); + +/** @copydoc _PurpleProtocolServerInterface::set_buddy_icon */ +void purple_protocol_server_iface_set_buddy_icon(PurpleProtocol *, + PurpleConnection *, PurpleStoredImage *img); + +/** @copydoc _PurpleProtocolServerInterface::remove_group */ +void purple_protocol_server_iface_remove_group(PurpleProtocol *, + PurpleConnection *gc, PurpleGroup *group); + +/** @copydoc _PurpleProtocolServerInterface::send_raw */ +int purple_protocol_server_iface_send_raw(PurpleProtocol *, + PurpleConnection *gc, const char *buf, int len); + +/** @copydoc _PurpleProtocolServerInterface::set_public_alias */ +void purple_protocol_server_iface_set_public_alias(PurpleProtocol *, + PurpleConnection *gc, const char *alias, + PurpleSetPublicAliasSuccessCallback success_cb, + PurpleSetPublicAliasFailureCallback failure_cb); + +/** @copydoc _PurpleProtocolServerInterface::get_public_alias */ +void purple_protocol_server_iface_get_public_alias(PurpleProtocol *, + PurpleConnection *gc, PurpleGetPublicAliasSuccessCallback success_cb, + PurpleGetPublicAliasFailureCallback failure_cb); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol IM Interface API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the protocol IM interface. + */ +GType purple_protocol_im_iface_get_type(void); + +/** @copydoc _PurpleProtocolIMInterface::send */ +int purple_protocol_im_iface_send(PurpleProtocol *, PurpleConnection *, + const char *who, const char *message, PurpleMessageFlags flags); + +/** @copydoc _PurpleProtocolIMInterface::send_typing */ +unsigned int purple_protocol_im_iface_send_typing(PurpleProtocol *, + PurpleConnection *, const char *name, PurpleIMTypingState state); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Chat Interface API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the protocol chat interface. + */ +GType purple_protocol_chat_iface_get_type(void); + +/** @copydoc _PurpleProtocolChatInterface::info */ +GList *purple_protocol_chat_iface_info(PurpleProtocol *, + PurpleConnection *); + +/** @copydoc _PurpleProtocolChatInterface::info_defaults */ +GHashTable *purple_protocol_chat_iface_info_defaults(PurpleProtocol *, + PurpleConnection *, const char *chat_name); + +/** @copydoc _PurpleProtocolChatInterface::join */ +void purple_protocol_chat_iface_join(PurpleProtocol *, PurpleConnection *, + GHashTable *components); + +/** @copydoc _PurpleProtocolChatInterface::reject */ +void purple_protocol_chat_iface_reject(PurpleProtocol *, + PurpleConnection *, GHashTable *components); + +/** @copydoc _PurpleProtocolChatInterface::get_name */ +char *purple_protocol_chat_iface_get_name(PurpleProtocol *, + GHashTable *components); + +/** @copydoc _PurpleProtocolChatInterface::invite */ +void purple_protocol_chat_iface_invite(PurpleProtocol *, + PurpleConnection *, int id, const char *message, const char *who); + +/** @copydoc _PurpleProtocolChatInterface::leave */ +void purple_protocol_chat_iface_leave(PurpleProtocol *, PurpleConnection *, + int id); + +/** @copydoc _PurpleProtocolChatInterface::whisper */ +void purple_protocol_chat_iface_whisper(PurpleProtocol *, + PurpleConnection *, int id, const char *who, const char *message); + +/** @copydoc _PurpleProtocolChatInterface::send */ +int purple_protocol_chat_iface_send(PurpleProtocol *, PurpleConnection *, + int id, const char *message, PurpleMessageFlags flags); + +/** @copydoc _PurpleProtocolChatInterface::get_user_real_name */ +char *purple_protocol_chat_iface_get_user_real_name(PurpleProtocol *, + PurpleConnection *gc, int id, const char *who); + +/** @copydoc _PurpleProtocolChatInterface::set_topic */ +void purple_protocol_chat_iface_set_topic(PurpleProtocol *, + PurpleConnection *gc, int id, const char *topic); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Privacy Interface API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the protocol privacy interface. + */ +GType purple_protocol_privacy_iface_get_type(void); + +/** @copydoc _PurpleProtocolPrivacyInterface::add_permit */ +void purple_protocol_privacy_iface_add_permit(PurpleProtocol *, + PurpleConnection *, const char *name); + +/** @copydoc _PurpleProtocolPrivacyInterface::add_deny */ +void purple_protocol_privacy_iface_add_deny(PurpleProtocol *, + PurpleConnection *, const char *name); + +/** @copydoc _PurpleProtocolPrivacyInterface::rem_permit */ +void purple_protocol_privacy_iface_rem_permit(PurpleProtocol *, + PurpleConnection *, const char *name); + +/** @copydoc _PurpleProtocolPrivacyInterface::rem_deny */ +void purple_protocol_privacy_iface_rem_deny(PurpleProtocol *, + PurpleConnection *, const char *name); + +/** @copydoc _PurpleProtocolPrivacyInterface::set_permit_deny */ +void purple_protocol_privacy_iface_set_permit_deny(PurpleProtocol *, + PurpleConnection *); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Xfer Interface API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the protocol xfer interface. + */ +GType purple_protocol_xfer_iface_get_type(void); + +/** @copydoc _PurpleProtocolXferInterface::can_receive */ +gboolean purple_protocol_xfer_iface_can_receive(PurpleProtocol *, + PurpleConnection *, const char *who); + +/** @copydoc _PurpleProtocolXferInterface::send */ +void purple_protocol_xfer_iface_send(PurpleProtocol *, PurpleConnection *, + const char *who, const char *filename); + +/** @copydoc _PurpleProtocolXferInterface::new_xfer */ +PurpleXfer *purple_protocol_xfer_iface_new_xfer(PurpleProtocol *, + PurpleConnection *, const char *who); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Roomlist Interface API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the protocol roomlist interface. + */ +GType purple_protocol_roomlist_iface_get_type(void); + +/** @copydoc _PurpleProtocolRoomlistInterface::get_list */ +PurpleRoomlist *purple_protocol_roomlist_iface_get_list(PurpleProtocol *, + PurpleConnection *gc); + +/** @copydoc _PurpleProtocolRoomlistInterface::cancel */ +void purple_protocol_roomlist_iface_cancel(PurpleProtocol *, + PurpleRoomlist *list); + +/** @copydoc _PurpleProtocolRoomlistInterface::expand_category */ +void purple_protocol_roomlist_iface_expand_category(PurpleProtocol *, + PurpleRoomlist *list, PurpleRoomlistRoom *category); + +/** @copydoc _PurpleProtocolRoomlistInterface::room_serialize */ +char *purple_protocol_roomlist_iface_room_serialize(PurpleProtocol *, + PurpleRoomlistRoom *room); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Attention Interface API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the protocol attention interface. + */ +GType purple_protocol_attention_iface_get_type(void); + +/** @copydoc _PurpleProtocolAttentionInterface::send */ +gboolean purple_protocol_attention_iface_send(PurpleProtocol *, + PurpleConnection *gc, const char *username, guint type); + +/** @copydoc _PurpleProtocolAttentionInterface::get_types */ +GList *purple_protocol_attention_iface_get_types(PurpleProtocol *, + PurpleAccount *acct); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Media Interface API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the protocol media interface. + */ +GType purple_protocol_media_iface_get_type(void); + +/** @copydoc _PurpleProtocolMediaInterface::initiate_session */ +gboolean purple_protocol_media_iface_initiate_session(PurpleProtocol *, + PurpleAccount *account, const char *who, PurpleMediaSessionType type); + +/** @copydoc _PurpleProtocolMediaInterface::get_caps */ +PurpleMediaCaps purple_protocol_media_iface_get_caps(PurpleProtocol *, + PurpleAccount *account, const char *who); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Factory Interface API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the protocol factory interface. + */ +GType purple_protocol_factory_iface_get_type(void); + +/** @copydoc _PurpleProtocolFactoryInterface::connection_new */ +PurpleConnection *purple_protocol_factory_iface_connection_new(PurpleProtocol *, + PurpleAccount *account, const char *password); + +/** @copydoc _PurpleProtocolFactoryInterface::roomlist_new */ +PurpleRoomlist *purple_protocol_factory_iface_roomlist_new(PurpleProtocol *, + PurpleAccount *account); + +/** @copydoc _PurpleProtocolFactoryInterface::whiteboard_new */ +PurpleWhiteboard *purple_protocol_factory_iface_whiteboard_new(PurpleProtocol *, + PurpleAccount *account, const char *who, int state); + +/** @copydoc _PurpleProtocolFactoryInterface::xfer_new */ +PurpleXfer *purple_protocol_factory_iface_xfer_new(PurpleProtocol *, + PurpleAccount *account, PurpleXferType type, const char *who); + +/*@}*/ + +G_END_DECLS + +#endif /* _PROTOCOL_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,917 @@ +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#include "internal.h" +#include "accountopt.h" +#include "conversation.h" +#include "debug.h" +#include "network.h" +#include "notify.h" +#include "protocol.h" +#include "request.h" +#include "util.h" + +static GHashTable *protocols = NULL; + +/**************************************************************************/ +/** @name Attention Type API */ +/**************************************************************************/ + +struct _PurpleAttentionType +{ + const char *name; /**< Shown in GUI elements */ + const char *incoming_description; /**< Shown when sent */ + const char *outgoing_description; /**< Shown when receied */ + const char *icon_name; /**< Icon to display (optional) */ + const char *unlocalized_name; /**< Unlocalized name for UIs needing it */ +}; + + +PurpleAttentionType * +purple_attention_type_new(const char *ulname, const char *name, + const char *inc_desc, const char *out_desc) +{ + PurpleAttentionType *attn = g_new0(PurpleAttentionType, 1); + + purple_attention_type_set_name(attn, name); + purple_attention_type_set_incoming_desc(attn, inc_desc); + purple_attention_type_set_outgoing_desc(attn, out_desc); + purple_attention_type_set_unlocalized_name(attn, ulname); + + return attn; +} + + +void +purple_attention_type_set_name(PurpleAttentionType *type, const char *name) +{ + g_return_if_fail(type != NULL); + + type->name = name; +} + +void +purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc) +{ + g_return_if_fail(type != NULL); + + type->incoming_description = desc; +} + +void +purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc) +{ + g_return_if_fail(type != NULL); + + type->outgoing_description = desc; +} + +void +purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name) +{ + g_return_if_fail(type != NULL); + + type->icon_name = name; +} + +void +purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname) +{ + g_return_if_fail(type != NULL); + + type->unlocalized_name = ulname; +} + +const char * +purple_attention_type_get_name(const PurpleAttentionType *type) +{ + g_return_val_if_fail(type != NULL, NULL); + + return type->name; +} + +const char * +purple_attention_type_get_incoming_desc(const PurpleAttentionType *type) +{ + g_return_val_if_fail(type != NULL, NULL); + + return type->incoming_description; +} + +const char * +purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type) +{ + g_return_val_if_fail(type != NULL, NULL); + + return type->outgoing_description; +} + +const char * +purple_attention_type_get_icon_name(const PurpleAttentionType *type) +{ + g_return_val_if_fail(type != NULL, NULL); + + if(type->icon_name == NULL || *(type->icon_name) == '\0') + return NULL; + + return type->icon_name; +} + +const char * +purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type) +{ + g_return_val_if_fail(type != NULL, NULL); + + return type->unlocalized_name; +} + +/************************************************************************** + * GBoxed code for PurpleAttentionType + **************************************************************************/ + +static PurpleAttentionType * +purple_attention_type_copy(PurpleAttentionType *attn) +{ + PurpleAttentionType *attn_copy; + + g_return_val_if_fail(attn != NULL, NULL); + + attn_copy = g_new(PurpleAttentionType, 1); + *attn_copy = *attn; + + return attn_copy; +} + +GType +purple_attention_type_get_type(void) +{ + static GType type = 0; + + if (type == 0) { + type = g_boxed_type_register_static("PurpleAttentionType", + (GBoxedCopyFunc)purple_attention_type_copy, + (GBoxedFreeFunc)g_free); + } + + return type; +} + +/************************************************************************** + * GBoxed code for PurpleProtocolChatEntry + **************************************************************************/ + +static PurpleProtocolChatEntry * +purple_protocol_chat_entry_copy(PurpleProtocolChatEntry *pce) +{ + PurpleProtocolChatEntry *pce_copy; + + g_return_val_if_fail(pce != NULL, NULL); + + pce_copy = g_new(PurpleProtocolChatEntry, 1); + *pce_copy = *pce; + + return pce_copy; +} + +GType +purple_protocol_chat_entry_get_type(void) +{ + static GType type = 0; + + if (type == 0) { + type = g_boxed_type_register_static("PurpleProtocolChatEntry", + (GBoxedCopyFunc)purple_protocol_chat_entry_copy, + (GBoxedFreeFunc)g_free); + } + + return type; +} + +/**************************************************************************/ +/** @name Protocol API */ +/**************************************************************************/ +void +purple_protocol_got_account_idle(PurpleAccount *account, gboolean idle, + time_t idle_time) +{ + g_return_if_fail(account != NULL); + g_return_if_fail(purple_account_is_connected(account)); + + purple_presence_set_idle(purple_account_get_presence(account), + idle, idle_time); +} + +void +purple_protocol_got_account_login_time(PurpleAccount *account, time_t login_time) +{ + PurplePresence *presence; + + g_return_if_fail(account != NULL); + g_return_if_fail(purple_account_is_connected(account)); + + if (login_time == 0) + login_time = time(NULL); + + presence = purple_account_get_presence(account); + + purple_presence_set_login_time(presence, login_time); +} + +void +purple_protocol_got_account_status(PurpleAccount *account, const char *status_id, ...) +{ + PurplePresence *presence; + PurpleStatus *status; + va_list args; + + g_return_if_fail(account != NULL); + g_return_if_fail(status_id != NULL); + g_return_if_fail(purple_account_is_connected(account)); + + presence = purple_account_get_presence(account); + status = purple_presence_get_status(presence, status_id); + + g_return_if_fail(status != NULL); + + va_start(args, status_id); + purple_status_set_active_with_attrs(status, TRUE, args); + va_end(args); +} + +void +purple_protocol_got_account_actions(PurpleAccount *account) +{ + + g_return_if_fail(account != NULL); + g_return_if_fail(purple_account_is_connected(account)); + + purple_signal_emit(purple_accounts_get_handle(), "account-actions-changed", + account); +} + +void +purple_protocol_got_user_idle(PurpleAccount *account, const char *name, + gboolean idle, time_t idle_time) +{ + PurplePresence *presence; + GSList *list; + + g_return_if_fail(account != NULL); + g_return_if_fail(name != NULL); + g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account)); + + if ((list = purple_blist_find_buddies(account, name)) == NULL) + return; + + while (list) { + presence = purple_buddy_get_presence(list->data); + list = g_slist_delete_link(list, list); + purple_presence_set_idle(presence, idle, idle_time); + } +} + +void +purple_protocol_got_user_login_time(PurpleAccount *account, const char *name, + time_t login_time) +{ + GSList *list; + PurplePresence *presence; + + g_return_if_fail(account != NULL); + g_return_if_fail(name != NULL); + + if ((list = purple_blist_find_buddies(account, name)) == NULL) + return; + + if (login_time == 0) + login_time = time(NULL); + + while (list) { + PurpleBuddy *buddy = list->data; + presence = purple_buddy_get_presence(buddy); + list = g_slist_delete_link(list, list); + + if (purple_presence_get_login_time(presence) != login_time) + { + purple_presence_set_login_time(presence, login_time); + + purple_signal_emit(purple_blist_get_handle(), "buddy-got-login-time", buddy); + } + } +} + +void +purple_protocol_got_user_status(PurpleAccount *account, const char *name, + const char *status_id, ...) +{ + GSList *list, *l; + PurpleBuddy *buddy; + PurplePresence *presence; + PurpleStatus *status; + PurpleStatus *old_status; + va_list args; + + g_return_if_fail(account != NULL); + g_return_if_fail(name != NULL); + g_return_if_fail(status_id != NULL); + g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account)); + + if((list = purple_blist_find_buddies(account, name)) == NULL) + return; + + for(l = list; l != NULL; l = l->next) { + buddy = l->data; + + presence = purple_buddy_get_presence(buddy); + status = purple_presence_get_status(presence, status_id); + + if(NULL == status) + /* + * TODO: This should never happen, right? We should call + * g_warning() or something. + */ + continue; + + old_status = purple_presence_get_active_status(presence); + + va_start(args, status_id); + purple_status_set_active_with_attrs(status, TRUE, args); + va_end(args); + + purple_buddy_update_status(buddy, old_status); + } + + g_slist_free(list); + + /* The buddy is no longer online, they are therefore by definition not + * still typing to us. */ + if (!purple_status_is_online(status)) { + serv_got_typing_stopped(purple_account_get_connection(account), name); + purple_protocol_got_media_caps(account, name); + } +} + +void purple_protocol_got_user_status_deactive(PurpleAccount *account, const char *name, + const char *status_id) +{ + GSList *list, *l; + PurpleBuddy *buddy; + PurplePresence *presence; + PurpleStatus *status; + + g_return_if_fail(account != NULL); + g_return_if_fail(name != NULL); + g_return_if_fail(status_id != NULL); + g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account)); + + if((list = purple_blist_find_buddies(account, name)) == NULL) + return; + + for(l = list; l != NULL; l = l->next) { + buddy = l->data; + + presence = purple_buddy_get_presence(buddy); + status = purple_presence_get_status(presence, status_id); + + if(NULL == status) + continue; + + if (purple_status_is_active(status)) { + purple_status_set_active(status, FALSE); + purple_buddy_update_status(buddy, status); + } + } + + g_slist_free(list); +} + +static void +do_protocol_change_account_status(PurpleAccount *account, + PurpleStatus *old_status, PurpleStatus *new_status) +{ + PurpleProtocol *protocol; + + if (purple_status_is_online(new_status) && + purple_account_is_disconnected(account) && + purple_network_is_available()) + { + purple_account_connect(account); + return; + } + + if (!purple_status_is_online(new_status)) + { + if (!purple_account_is_disconnected(account)) + purple_account_disconnect(account); + /* Clear out the unsaved password if we switch to offline status */ + if (!purple_account_get_remember_password(account)) + purple_account_set_password(account, NULL, NULL, NULL); + + return; + } + + if (purple_account_is_connecting(account)) + /* + * We don't need to call the set_status protocol function because + * the protocol will take care of setting its status during the + * connection process. + */ + return; + + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + + if (protocol == NULL) + return; + + if (!purple_account_is_disconnected(account)) + purple_protocol_server_iface_set_status(protocol, account, new_status); +} + +void +purple_protocol_change_account_status(PurpleAccount *account, + PurpleStatus *old_status, PurpleStatus *new_status) +{ + g_return_if_fail(account != NULL); + g_return_if_fail(new_status != NULL); + g_return_if_fail(!purple_status_is_exclusive(new_status) || old_status != NULL); + + do_protocol_change_account_status(account, old_status, new_status); + + purple_signal_emit(purple_accounts_get_handle(), "account-status-changed", + account, old_status, new_status); +} + +GList * +purple_protocol_get_statuses(PurpleAccount *account, PurplePresence *presence) +{ + GList *statuses = NULL; + GList *l; + PurpleStatus *status; + + g_return_val_if_fail(account != NULL, NULL); + g_return_val_if_fail(presence != NULL, NULL); + + for (l = purple_account_get_status_types(account); l != NULL; l = l->next) + { + status = purple_status_new((PurpleStatusType *)l->data, presence); + statuses = g_list_prepend(statuses, status); + } + + statuses = g_list_reverse(statuses); + + return statuses; +} + +static void +purple_protocol_attention(PurpleConversation *conv, const char *who, + guint type, PurpleMessageFlags flags, time_t mtime) +{ + PurpleAccount *account = purple_conversation_get_account(conv); + purple_signal_emit(purple_conversations_get_handle(), + flags == PURPLE_MESSAGE_SEND ? "sent-attention" : "got-attention", + account, who, conv, type); +} + +void +purple_protocol_send_attention(PurpleConnection *gc, const char *who, guint type_code) +{ + PurpleAttentionType *attn; + PurpleMessageFlags flags; + PurpleProtocol *protocol; + PurpleIMConversation *im; + PurpleBuddy *buddy; + const char *alias; + gchar *description; + time_t mtime; + + g_return_if_fail(gc != NULL); + g_return_if_fail(who != NULL); + + protocol = purple_protocols_find(purple_account_get_protocol_id(purple_connection_get_account(gc))); + g_return_if_fail(PURPLE_PROTOCOL_IMPLEMENTS(protocol, ATTENTION_IFACE, send)); + + mtime = time(NULL); + + attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code); + + if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who)) != NULL) + alias = purple_buddy_get_contact_alias(buddy); + else + alias = who; + + if (attn && purple_attention_type_get_outgoing_desc(attn)) { + description = g_strdup_printf(purple_attention_type_get_outgoing_desc(attn), alias); + } else { + description = g_strdup_printf(_("Requesting %s's attention..."), alias); + } + + flags = PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM; + + purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n", + description, who); + + if (!purple_protocol_attention_iface_send(protocol, gc, who, type_code)) + return; + + im = purple_im_conversation_new(purple_connection_get_account(gc), who); + purple_conversation_write_message(PURPLE_CONVERSATION(im), NULL, description, flags, mtime); + purple_protocol_attention(PURPLE_CONVERSATION(im), who, type_code, PURPLE_MESSAGE_SEND, time(NULL)); + + g_free(description); +} + +static void +got_attention(PurpleConnection *gc, int id, const char *who, guint type_code) +{ + PurpleMessageFlags flags; + PurpleAttentionType *attn; + PurpleBuddy *buddy; + const char *alias; + gchar *description; + time_t mtime; + + mtime = time(NULL); + + attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code); + + /* PURPLE_MESSAGE_NOTIFY is for attention messages. */ + flags = PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_RECV; + + /* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display + * it next to the attention command. And if it is null, display a generic icon. */ + + if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who)) != NULL) + alias = purple_buddy_get_contact_alias(buddy); + else + alias = who; + + if (attn && purple_attention_type_get_incoming_desc(attn)) { + description = g_strdup_printf(purple_attention_type_get_incoming_desc(attn), alias); + } else { + description = g_strdup_printf(_("%s has requested your attention!"), alias); + } + + purple_debug_info("server", "got_attention: got '%s' from %s\n", + description, who); + + if (id == -1) + serv_got_im(gc, who, description, flags, mtime); + else + serv_got_chat_in(gc, id, who, flags, description, mtime); + + /* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */ + + g_free(description); +} + +void +purple_protocol_got_attention(PurpleConnection *gc, const char *who, guint type_code) +{ + PurpleConversation *conv = NULL; + PurpleAccount *account = purple_connection_get_account(gc); + + got_attention(gc, -1, who, type_code); + conv = + purple_conversations_find_with_account(who, account); + if (conv) + purple_protocol_attention(conv, who, type_code, PURPLE_MESSAGE_RECV, + time(NULL)); +} + +void +purple_protocol_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code) +{ + got_attention(gc, id, who, type_code); +} + +gboolean +purple_protocol_initiate_media(PurpleAccount *account, + const char *who, + PurpleMediaSessionType type) +{ +#ifdef USE_VV + PurpleConnection *gc = NULL; + PurpleProtocol *protocol = NULL; + + if (account) + gc = purple_account_get_connection(account); + if (gc) + protocol = purple_connection_get_protocol(gc); + + if (protocol) { + /* should check that the protocol supports this media type here? */ + return purple_protocol_media_iface_initiate_session(protocol, account, who, type); + } else +#endif + return FALSE; +} + +PurpleMediaCaps +purple_protocol_get_media_caps(PurpleAccount *account, const char *who) +{ +#ifdef USE_VV + PurpleConnection *gc = NULL; + PurpleProtocol *protocol = NULL; + + if (account) + gc = purple_account_get_connection(account); + if (gc) + protocol = purple_connection_get_protocol(gc); + + if (protocol) + return purple_protocol_media_iface_get_caps(protocol, account, who); +#endif + return PURPLE_MEDIA_CAPS_NONE; +} + +void +purple_protocol_got_media_caps(PurpleAccount *account, const char *name) +{ +#ifdef USE_VV + GSList *list; + + g_return_if_fail(account != NULL); + g_return_if_fail(name != NULL); + + if ((list = purple_blist_find_buddies(account, name)) == NULL) + return; + + while (list) { + PurpleBuddy *buddy = list->data; + PurpleMediaCaps oldcaps = purple_buddy_get_media_caps(buddy); + PurpleMediaCaps newcaps = 0; + const gchar *bname = purple_buddy_get_name(buddy); + list = g_slist_delete_link(list, list); + + + newcaps = purple_protocol_get_media_caps(account, bname); + purple_buddy_set_media_caps(buddy, newcaps); + + if (oldcaps == newcaps) + continue; + + purple_signal_emit(purple_blist_get_handle(), + "buddy-caps-changed", buddy, + newcaps, oldcaps); + } +#endif +} + +gssize +purple_protocol_get_max_message_size(PurpleProtocol *protocol) +{ + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), 0); + + return purple_protocol_client_iface_get_max_message_size(protocol, NULL); +} + +/**************************************************************************/ +/** @name Protocol Action API */ +/**************************************************************************/ + +PurpleProtocolAction * +purple_protocol_action_new(const char* label, + PurpleProtocolActionCallback callback) +{ + PurpleProtocolAction *action; + + g_return_val_if_fail(label != NULL && callback != NULL, NULL); + + action = g_new0(PurpleProtocolAction, 1); + + action->label = g_strdup(label); + action->callback = callback; + + return action; +} + +void +purple_protocol_action_free(PurpleProtocolAction *action) +{ + g_return_if_fail(action != NULL); + + g_free(action->label); + g_free(action); +} + +/************************************************************************** + * GBoxed code for PurpleProtocolAction + **************************************************************************/ + +static PurpleProtocolAction * +purple_protocol_action_copy(PurpleProtocolAction *action) +{ + g_return_val_if_fail(action != NULL, NULL); + + return purple_protocol_action_new(action->label, action->callback); +} + +GType +purple_protocol_action_get_type(void) +{ + static GType type = 0; + + if (type == 0) { + type = g_boxed_type_register_static("PurpleProtocolAction", + (GBoxedCopyFunc)purple_protocol_action_copy, + (GBoxedFreeFunc)purple_protocol_action_free); + } + + return type; +} + +/************************************************************************** + * Protocols API + **************************************************************************/ +/** + * Negative if a before b, 0 if equal, positive if a after b. + */ +static gint +compare_protocol(PurpleProtocol *a, PurpleProtocol *b) +{ + const gchar *aname = purple_protocol_get_name(a); + const gchar *bname = purple_protocol_get_name(b); + + if (aname) { + if (bname) + return strcmp(aname, bname); + else + return -1; + } else { + if (bname) + return 1; + else + return 0; + } +} + +PurpleProtocol * +purple_protocols_find(const char *id) +{ + g_return_val_if_fail(protocols != NULL && id != NULL, NULL); + + return g_hash_table_lookup(protocols, id); +} + +PurpleProtocol * +purple_protocols_add(GType protocol_type, GError **error) +{ + PurpleProtocol *protocol; + PurpleProtocolClass *klass; + + if (protocol_type == G_TYPE_INVALID) { + g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0, + _("Protocol type is not registered")); + return NULL; + } + + if (!g_type_is_a(protocol_type, PURPLE_TYPE_PROTOCOL)) { + g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0, + _("Protocol type does not inherit PurpleProtocol")); + return NULL; + } + + if (G_TYPE_IS_ABSTRACT(protocol_type)) { + g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0, + _("Protocol type is abstract")); + return NULL; + } + + protocol = g_object_new(protocol_type, NULL); + klass = PURPLE_PROTOCOL_GET_CLASS(protocol); + + if (!protocol) { + g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0, + _("Could not create protocol instance")); + return NULL; + } + + if (!purple_protocol_get_id(protocol)) { + g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0, + _("Protocol does not provide an ID")); + + g_object_unref(protocol); + return NULL; + } + + if (purple_protocols_find(purple_protocol_get_id(protocol))) { + g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0, + _("A protocol with the ID %s is already added."), + purple_protocol_get_id(protocol)); + + g_object_unref(protocol); + return NULL; + } + + /* Make sure the protocol implements the required functions */ + if (!klass->login || !klass->close || + !klass->status_types || !klass->list_icon ) + { + g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0, + _("Protocol %s does not implement all the functions in " + "PurpleProtocolClass"), purple_protocol_get_id(protocol)); + + g_object_unref(protocol); + return NULL; + } + + g_hash_table_insert(protocols, g_strdup(purple_protocol_get_id(protocol)), + protocol); + + purple_debug_info("protocols", "Added protocol %s\n", + purple_protocol_get_id(protocol)); + + purple_signal_emit(purple_protocols_get_handle(), "protocol-added", + protocol); + return protocol; +} + +gboolean purple_protocols_remove(PurpleProtocol *protocol, GError **error) +{ + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), FALSE); + g_return_val_if_fail(purple_protocol_get_id(protocol) != NULL, FALSE); + + if (purple_protocols_find(purple_protocol_get_id(protocol)) == NULL) { + g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0, + _("Protocol %s is not added."), + purple_protocol_get_id(protocol)); + + return FALSE; + } + + purple_debug_info("protocols", "Removing protocol %s\n", + purple_protocol_get_id(protocol)); + + purple_signal_emit(purple_protocols_get_handle(), "protocol-removed", + protocol); + + g_hash_table_remove(protocols, purple_protocol_get_id(protocol)); + return TRUE; +} + +GList * +purple_protocols_get_all(void) +{ + GList *ret = NULL; + PurpleProtocol *protocol; + GHashTableIter iter; + + g_hash_table_iter_init(&iter, protocols); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&protocol)) + ret = g_list_insert_sorted(ret, protocol, (GCompareFunc)compare_protocol); + + return ret; +} + +/************************************************************************** + * Protocols Subsystem API + **************************************************************************/ +void +purple_protocols_init(void) +{ + void *handle = purple_protocols_get_handle(); + + protocols = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)g_object_unref); + + purple_signal_register(handle, "protocol-added", + purple_marshal_VOID__POINTER, + G_TYPE_NONE, 1, PURPLE_TYPE_PROTOCOL); + purple_signal_register(handle, "protocol-removed", + purple_marshal_VOID__POINTER, + G_TYPE_NONE, 1, PURPLE_TYPE_PROTOCOL); +} + +void * +purple_protocols_get_handle(void) +{ + static int handle; + + return &handle; +} + +void +purple_protocols_uninit(void) +{ + g_hash_table_destroy(protocols); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,640 @@ +/** + * @file protocols.h Protocols API + * @ingroup core + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_PROTOCOLS_H_ +#define _PURPLE_PROTOCOLS_H_ + +#define PURPLE_PROTOCOLS_DOMAIN (g_quark_from_static_string("protocols")) + +#define PURPLE_TYPE_PROTOCOL_ACTION (purple_protocol_action_get_type()) + +/** @copydoc _PurpleProtocolAction */ +typedef struct _PurpleProtocolAction PurpleProtocolAction; +typedef void (*PurpleProtocolActionCallback)(PurpleProtocolAction *); + +#define PURPLE_TYPE_ATTENTION_TYPE (purple_attention_type_get_type()) + +/** Represents "nudges" and "buzzes" that you may send to a buddy to attract + * their attention (or vice-versa). + */ +typedef struct _PurpleAttentionType PurpleAttentionType; + +/**************************************************************************/ +/** @name Basic Protocol Information */ +/**************************************************************************/ + +typedef enum /*< flags >*/ +{ + PURPLE_ICON_SCALE_DISPLAY = 0x01, /**< We scale the icon when we display it */ + PURPLE_ICON_SCALE_SEND = 0x02 /**< We scale the icon before we send it to the server */ +} PurpleIconScaleRules; + +/** + * Represents an entry containing information that must be supplied by the + * user when joining a chat. + */ +typedef struct _PurpleProtocolChatEntry PurpleProtocolChatEntry; + +/** + * Protocol options + * + * These should all be stuff that some protocols can do and others can't. + */ +typedef enum /*< flags >*/ +{ + /** + * User names are unique to a chat and are not shared between rooms. + * + * XMPP lets you choose what name you want in chats, so it shouldn't + * be pulling the aliases from the buddy list for the chat list; + * it gets annoying. + */ + OPT_PROTO_UNIQUE_CHATNAME = 0x00000004, + + /** + * Chat rooms have topics. + * + * IRC and XMPP support this. + */ + OPT_PROTO_CHAT_TOPIC = 0x00000008, + + /** + * Don't require passwords for sign-in. + * + * Zephyr doesn't require passwords, so there's no + * need for a password prompt. + */ + OPT_PROTO_NO_PASSWORD = 0x00000010, + + /** + * Notify on new mail. + * + * MSN and Yahoo notify you when you have new mail. + */ + OPT_PROTO_MAIL_CHECK = 0x00000020, + + /** + * Images in IMs. + * + * Oscar lets you send images in direct IMs. + */ + OPT_PROTO_IM_IMAGE = 0x00000040, + + /** + * Allow passwords to be optional. + * + * Passwords in IRC are optional, and are needed for certain + * functionality. + */ + OPT_PROTO_PASSWORD_OPTIONAL = 0x00000080, + + /** + * Allows font size to be specified in sane point size + * + * Probably just XMPP and Y!M + */ + OPT_PROTO_USE_POINTSIZE = 0x00000100, + + /** + * Set the Register button active even when the username has not + * been specified. + * + * Gadu-Gadu doesn't need a username to register new account (because + * usernames are assigned by the server). + */ + OPT_PROTO_REGISTER_NOSCREENNAME = 0x00000200, + + /** + * Indicates that slash commands are native to this protocol. + * Used as a hint that unknown commands should not be sent as messages. + */ + OPT_PROTO_SLASH_COMMANDS_NATIVE = 0x00000400, + + /** + * Indicates that this protocol supports sending a user-supplied message + * along with an invitation. + */ + OPT_PROTO_INVITE_MESSAGE = 0x00000800, + + /** + * Indicates that this protocol supports sending a user-supplied message + * along with an authorization acceptance. + */ + OPT_PROTO_AUTHORIZATION_GRANTED_MESSAGE = 0x00001000, + + /** + * Indicates that this protocol supports sending a user-supplied message + * along with an authorization denial. + */ + OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE = 0x00002000 + +} PurpleProtocolOptions; + +#include "media.h" +#include "protocol.h" +#include "status.h" + +#define PURPLE_TYPE_PROTOCOL_CHAT_ENTRY (purple_protocol_chat_entry_get_type()) + +/** @copydoc PurpleProtocolChatEntry */ +struct _PurpleProtocolChatEntry { + const char *label; /**< User-friendly name of the entry */ + const char *identifier; /**< Used by the protocol to identify the option */ + gboolean required; /**< True if it's required */ + gboolean is_int; /**< True if the entry expects an integer */ + int min; /**< Minimum value in case of integer */ + int max; /**< Maximum value in case of integer */ + gboolean secret; /**< True if the entry is secret (password) */ +}; + +/** + * Represents an action that the protocol can perform. This shows up in the + * Accounts menu, under a submenu with the name of the account. + */ +struct _PurpleProtocolAction { + char *label; + PurpleProtocolActionCallback callback; + PurpleConnection *connection; + gpointer user_data; +}; + +G_BEGIN_DECLS + +/**************************************************************************/ +/** @name Attention Type API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the #PurpleAttentionType boxed structure. + */ +GType purple_attention_type_get_type(void); + +/** + * Creates a new #PurpleAttentionType object and sets its mandatory parameters. + * + * @param ulname A non-localized string that can be used by UIs in need of such + * non-localized strings. This should be the same as @a name, + * without localization. + * @param name A localized string that the UI may display for the event. This + * should be the same string as @a ulname, with localization. + * @param inc_desc A localized description shown when the event is received. + * @param out_desc A localized description shown when the event is sent. + * + * @return A pointer to the new object. + */ +PurpleAttentionType *purple_attention_type_new(const char *ulname, const char *name, + const char *inc_desc, const char *out_desc); + +/** + * Sets the displayed name of the attention-demanding event. + * + * @param type The attention type. + * @param name The localized name that will be displayed by UIs. This should be + * the same string given as the unlocalized name, but with + * localization. + */ +void purple_attention_type_set_name(PurpleAttentionType *type, const char *name); + +/** + * Sets the description of the attention-demanding event shown in conversations + * when the event is received. + * + * @param type The attention type. + * @param desc The localized description for incoming events. + */ +void purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc); + +/** + * Sets the description of the attention-demanding event shown in conversations + * when the event is sent. + * + * @param type The attention type. + * @param desc The localized description for outgoing events. + */ +void purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc); + +/** + * Sets the name of the icon to display for the attention event; this is optional. + * + * @param type The attention type. + * @param name The icon's name. + * @note Icons are optional for attention events. + */ +void purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name); + +/** + * Sets the unlocalized name of the attention event; some UIs may need this, + * thus it is required. + * + * @param type The attention type. + * @param ulname The unlocalized name. This should be the same string given as + * the localized name, but without localization. + */ +void purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname); + +/** + * Get the attention type's name as displayed by the UI. + * + * @param type The attention type. + * + * @return The name. + */ +const char *purple_attention_type_get_name(const PurpleAttentionType *type); + +/** + * Get the attention type's description shown when the event is received. + * + * @param type The attention type. + * @return The description. + */ +const char *purple_attention_type_get_incoming_desc(const PurpleAttentionType *type); + +/** + * Get the attention type's description shown when the event is sent. + * + * @param type The attention type. + * @return The description. + */ +const char *purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type); + +/** + * Get the attention type's icon name. + * + * @param type The attention type. + * @return The icon name or @c NULL if unset/empty. + * @note Icons are optional for attention events. + */ +const char *purple_attention_type_get_icon_name(const PurpleAttentionType *type); + +/** + * Get the attention type's unlocalized name; this is useful for some UIs. + * + * @param type The attention type + * @return The unlocalized name. + */ +const char *purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Action API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the #PurpleProtocolAction boxed structure. + */ +GType purple_protocol_action_get_type(void); + +/** + * Allocates and returns a new PurpleProtocolAction. Use this to add actions in + * a list in the get_actions function of the protocol. + * + * @param label The description of the action to show to the user. + * @param callback The callback to call when the user selects this action. + */ +PurpleProtocolAction *purple_protocol_action_new(const char* label, + PurpleProtocolActionCallback callback); + +/** + * Frees a PurpleProtocolAction + * + * @param action The PurpleProtocolAction to free. + */ +void purple_protocol_action_free(PurpleProtocolAction *action); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol Chat Entry API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns the GType for the #PurpleProtocolChatEntry boxed structure. + */ +GType purple_protocol_chat_entry_get_type(void); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocol API */ +/**************************************************************************/ +/*@{*/ + +/** + * Notifies Purple that our account's idle state and time have changed. + * + * This is meant to be called from protocols. + * + * @param account The account. + * @param idle The user's idle state. + * @param idle_time The user's idle time. + */ +void purple_protocol_got_account_idle(PurpleAccount *account, gboolean idle, + time_t idle_time); + +/** + * Notifies Purple of our account's log-in time. + * + * This is meant to be called from protocols. + * + * @param account The account the user is on. + * @param login_time The user's log-in time. + */ +void purple_protocol_got_account_login_time(PurpleAccount *account, + time_t login_time); + +/** + * Notifies Purple that our account's status has changed. + * + * This is meant to be called from protocols. + * + * @param account The account the user is on. + * @param status_id The status ID. + * @param ... A NULL-terminated list of attribute IDs and values, + * beginning with the value for @a attr_id. + */ +void purple_protocol_got_account_status(PurpleAccount *account, + const char *status_id, ...) + G_GNUC_NULL_TERMINATED; + +/** + * Notifies Purple that our account's actions have changed. This is only + * called after the initial connection. Emits the account-actions-changed + * signal. + * + * This is meant to be called from protocols. + * + * @param account The account. + * + * @see account-actions-changed + */ +void purple_protocol_got_account_actions(PurpleAccount *account); + +/** + * Notifies Purple that a buddy's idle state and time have changed. + * + * This is meant to be called from protocols. + * + * @param account The account the user is on. + * @param name The name of the buddy. + * @param idle The user's idle state. + * @param idle_time The user's idle time. This is the time at + * which the user became idle, in seconds since + * the epoch. If the protocol does not know this value + * then it should pass 0. + */ +void purple_protocol_got_user_idle(PurpleAccount *account, const char *name, + gboolean idle, time_t idle_time); + +/** + * Notifies Purple of a buddy's log-in time. + * + * This is meant to be called from protocols. + * + * @param account The account the user is on. + * @param name The name of the buddy. + * @param login_time The user's log-in time. + */ +void purple_protocol_got_user_login_time(PurpleAccount *account, + const char *name, time_t login_time); + +/** + * Notifies Purple that a buddy's status has been activated. + * + * This is meant to be called from protocols. + * + * @param account The account the user is on. + * @param name The name of the buddy. + * @param status_id The status ID. + * @param ... A NULL-terminated list of attribute IDs and values, + * beginning with the value for @a attr_id. + */ +void purple_protocol_got_user_status(PurpleAccount *account, const char *name, + const char *status_id, ...) + G_GNUC_NULL_TERMINATED; + +/** + * Notifies libpurple that a buddy's status has been deactivated + * + * This is meant to be called from protocols. + * + * @param account The account the user is on. + * @param name The name of the buddy. + * @param status_id The status ID. + */ +void purple_protocol_got_user_status_deactive(PurpleAccount *account, + const char *name, + const char *status_id); + +/** + * Informs the server that our account's status changed. + * + * @param account The account the user is on. + * @param old_status The previous status. + * @param new_status The status that was activated, or deactivated + * (in the case of independent statuses). + */ +void purple_protocol_change_account_status(PurpleAccount *account, + PurpleStatus *old_status, + PurpleStatus *new_status); + +/** + * Retrieves the list of stock status types from a protocol. + * + * @param account The account the user is on. + * @param presence The presence for which we're going to get statuses + * + * @return List of statuses + */ +GList *purple_protocol_get_statuses(PurpleAccount *account, + PurplePresence *presence); + +/** + * Send an attention request message. + * + * @param gc The connection to send the message on. + * @param who Whose attention to request. + * @param type_code An index into the protocol's attention_types list + * determining the type of the attention request command to + * send. 0 if protocol only defines one (for example, Yahoo and + * MSN), but some protocols define more (MySpaceIM). + * + * Note that you can't send arbitrary PurpleAttentionType's, because there is + * only a fixed set of attention commands. + */ +void purple_protocol_send_attention(PurpleConnection *gc, const char *who, + guint type_code); + +/** + * Process an incoming attention message. + * + * @param gc The connection that received the attention message. + * @param who Who requested your attention. + * @param type_code An index into the protocol's attention_types list + * determining the type of the attention request command to + * send. + */ +void purple_protocol_got_attention(PurpleConnection *gc, const char *who, + guint type_code); + +/** + * Process an incoming attention message in a chat. + * + * @param gc The connection that received the attention message. + * @param id The chat id. + * @param who Who requested your attention. + * @param type_code An index into the protocol's attention_types list + * determining the type of the attention request command to + * send. + */ +void purple_protocol_got_attention_in_chat(PurpleConnection *gc, int id, + const char *who, guint type_code); + +/** + * Determines if the contact supports the given media session type. + * + * @param account The account the user is on. + * @param who The name of the contact to check capabilities for. + * + * @return The media caps the contact supports. + */ +PurpleMediaCaps purple_protocol_get_media_caps(PurpleAccount *account, + const char *who); + +/** + * Initiates a media session with the given contact. + * + * @param account The account the user is on. + * @param who The name of the contact to start a session with. + * @param type The type of media session to start. + * + * @return TRUE if the call succeeded else FALSE. (Doesn't imply the media + * session or stream will be successfully created) + */ +gboolean purple_protocol_initiate_media(PurpleAccount *account, + const char *who, + PurpleMediaSessionType type); + +/** + * Signals that the protocol received capabilities for the given contact. + * + * This function is intended to be used only by protocols. + * + * @param account The account the user is on. + * @param who The name of the contact for which capabilities have been received. + */ +void purple_protocol_got_media_caps(PurpleAccount *account, const char *who); + +/** + * Gets the safe maximum message size in bytes for the protocol. + * + * @see PurpleProtocol#get_max_message_size + * + * @param protocol The protocol to query. + * + * @return Maximum message size, 0 if unspecified, -1 for infinite. + */ +gssize +purple_protocol_get_max_message_size(PurpleProtocol *protocol); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocols API */ +/**************************************************************************/ +/*@{*/ + +/** + * Finds a protocol by ID. + * + * @param id The protocol's ID. + */ +PurpleProtocol *purple_protocols_find(const char *id); + +/** + * Adds a protocol to the list of protocols. + * + * @param protocol_type The type of the protocol to add. + * @param error Return location for a #GError or @c NULL. If provided, this + * will be set to the reason if adding fails. + * + * @return The protocol instance if the protocol was added, else @c NULL. + */ +PurpleProtocol *purple_protocols_add(GType protocol_type, GError **error); + +/** + * Removes a protocol from the list of protocols. This will disconnect all + * connected accounts using this protocol, and free the protocol's user splits + * and protocol options. + * + * @param protocol The protocol to remove. + * @param error Return location for a #GError or @c NULL. If provided, this + * will be set to the reason if removing fails. + * + * @return TRUE if the protocol was removed, else FALSE. + */ +gboolean purple_protocols_remove(PurpleProtocol *protocol, GError **error); + +/** + * Returns a list of all loaded protocols. + * + * @return A list of all loaded protocols. The list is owned by the caller, and + * must be g_list_free()d to avoid leaking the nodes. + */ +GList *purple_protocols_get_all(void); + +/*@}*/ + +/**************************************************************************/ +/** @name Protocols Subsytem API */ +/**************************************************************************/ +/*@{*/ + +/** + * Initializes the protocols subsystem. + */ +void purple_protocols_init(void); + +/** + * Returns the protocols subsystem handle. + * + * @return The protocols subsystem handle. + */ +void *purple_protocols_get_handle(void); + +/** + * Uninitializes the protocols subsystem. + */ +void purple_protocols_uninit(void); + +/*@}*/ + +G_END_DECLS + +#endif /* _PROTOCOLS_H_ */
--- a/libpurple/protocols/bonjour/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/bonjour/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -48,6 +48,7 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS) \ $(LIBXML_CFLAGS) \ - $(AVAHI_CFLAGS) \ No newline at end of file + $(AVAHI_CFLAGS)
--- a/libpurple/protocols/bonjour/bonjour.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/bonjour/bonjour.c Fri Jan 31 18:02:20 2014 +0530 @@ -35,6 +35,7 @@ #include "account.h" #include "accountopt.h" #include "debug.h" +#include "plugins.h" #include "util.h" #include "version.h" @@ -44,6 +45,8 @@ #include "buddy.h" #include "bonjour_ft.h" +static PurpleProtocol *my_protocol = NULL; + static char *default_firstname; static char *default_lastname; @@ -474,138 +477,12 @@ return -1; /* 5MB successfully tested. */ } -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - /* These shouldn't happen here because they are allocated in _init() */ - - g_free(default_firstname); - g_free(default_lastname); - - return TRUE; -} - -static PurplePlugin *my_protocol = NULL; - -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_NO_PASSWORD, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png,gif,jpeg", 0, 0, 96, 96, 65535, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ - bonjour_list_icon, /* list_icon */ - NULL, /* list_emblem */ - bonjour_status_text, /* status_text */ - bonjour_tooltip_text, /* tooltip_text */ - bonjour_status_types, /* status_types */ - NULL, /* blist_node_menu */ - NULL, /* chat_info */ - NULL, /* chat_info_defaults */ - bonjour_login, /* login */ - bonjour_close, /* close */ - bonjour_send_im, /* send_im */ - NULL, /* set_info */ - NULL, /* send_typing */ - NULL, /* get_info */ - bonjour_set_status, /* set_status */ - NULL, /* set_idle */ - NULL, /* change_passwd */ - bonjour_fake_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - bonjour_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* rem_permit */ - NULL, /* rem_deny */ - NULL, /* set_permit_deny */ - NULL, /* join_chat */ - NULL, /* reject_chat */ - NULL, /* get_chat_name */ - NULL, /* chat_invite */ - NULL, /* chat_leave */ - NULL, /* chat_whisper */ - NULL, /* chat_send */ - NULL, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* alias_buddy */ - bonjour_group_buddy, /* group_buddy */ - bonjour_rename_group, /* rename_group */ - NULL, /* buddy_free */ - bonjour_convo_closed, /* convo_closed */ - NULL, /* normalize */ - bonjour_set_buddy_icon, /* set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - bonjour_can_receive_file, /* can_receive_file */ - bonjour_send_file, /* send_file */ - bonjour_new_xfer, /* new_xfer */ - NULL, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - bonjour_get_max_message_size /* get_max_message_size */ -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-bonjour", /**< id */ - "Bonjour", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Bonjour Protocol Plugin"), - /** description */ - N_("Bonjour Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - #ifdef WIN32 static gboolean _set_default_name_cb(gpointer data) { gchar *fullname = data; const char *splitpoint; - GList *tmp = prpl_info.protocol_options; + GList *tmp = protocol.protocol_options; PurpleAccountOption *option; if (!fullname) { @@ -753,32 +630,138 @@ } static void -init_plugin(PurplePlugin *plugin) +bonjour_protocol_init(PurpleProtocol *protocol) { PurpleAccountOption *option; - initialize_default_account_values(); + protocol->id = "prpl-bonjour"; + protocol->name = "Bonjour"; + protocol->options = OPT_PROTO_NO_PASSWORD; + protocol->icon_spec = purple_buddy_icon_spec_new("png,gif,jpeg", + 0, 0, 96, 96, 65535, + PURPLE_ICON_SCALE_DISPLAY); /* Creating the options for the protocol */ option = purple_account_option_int_new(_("Local Port"), "port", BONJOUR_DEFAULT_PORT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("First name"), "first", default_firstname); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Last name"), "last", default_lastname); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Email"), "email", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("AIM Account"), "AIM", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("XMPP Account"), "jid", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); +} + +static void +bonjour_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = bonjour_login; + klass->close = bonjour_close; + klass->status_types = bonjour_status_types; + klass->list_icon = bonjour_list_icon; +} + +static void +bonjour_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->status_text = bonjour_status_text; + client_iface->tooltip_text = bonjour_tooltip_text; + client_iface->convo_closed = bonjour_convo_closed; + client_iface->get_max_message_size = bonjour_get_max_message_size; +} - my_protocol = plugin; +static void +bonjour_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->add_buddy = bonjour_fake_add_buddy; + server_iface->remove_buddy = bonjour_remove_buddy; + server_iface->group_buddy = bonjour_group_buddy; + server_iface->rename_group = bonjour_rename_group; + server_iface->set_buddy_icon = bonjour_set_buddy_icon; + server_iface->set_status = bonjour_set_status; +} + +static void +bonjour_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = bonjour_send_im; +} + +static void +bonjour_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->can_receive = bonjour_can_receive_file; + xfer_iface->send = bonjour_send_file; + xfer_iface->new_xfer = bonjour_new_xfer; } -PURPLE_INIT_PLUGIN(bonjour, init_plugin, info); +PURPLE_DEFINE_TYPE_EXTENDED( + BonjourProtocol, bonjour_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + bonjour_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + bonjour_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + bonjour_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + bonjour_protocol_xfer_iface_init) +); + +static PurplePluginInfo * +plugin_query(GError **error) +{ + return purple_plugin_info_new( + "id", "prpl-bonjour", + "name", "Bonjour Protocol", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("Bonjour Protocol Plugin"), + "description", N_("Bonjour Protocol Plugin"), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + bonjour_protocol_register_type(plugin); + + my_protocol = purple_protocols_add(BONJOUR_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; + + initialize_default_account_values(); + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + + g_free(default_firstname); + g_free(default_lastname); + + return TRUE; +} + +PURPLE_PLUGIN_INIT(bonjour, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/protocols/bonjour/bonjour.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/bonjour/bonjour.h Fri Jan 31 18:02:20 2014 +0530 @@ -26,8 +26,10 @@ #ifndef _BONJOUR_H_ #define _BONJOUR_H_ +#include "internal.h" +#include "protocol.h" + #include "mdns_common.h" -#include "internal.h" #include "jabber.h" #define BONJOUR_GROUP_NAME _("Bonjour") @@ -40,6 +42,23 @@ #define BONJOUR_DEFAULT_PORT 5298 +#define BONJOUR_TYPE_PROTOCOL (bonjour_protocol_get_type()) +#define BONJOUR_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BONJOUR_TYPE_PROTOCOL, BonjourProtocol)) +#define BONJOUR_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), BONJOUR_TYPE_PROTOCOL, BonjourProtocolClass)) +#define BONJOUR_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BONJOUR_TYPE_PROTOCOL)) +#define BONJOUR_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BONJOUR_TYPE_PROTOCOL)) +#define BONJOUR_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), BONJOUR_TYPE_PROTOCOL, BonjourProtocolClass)) + +typedef struct _BonjourProtocol +{ + PurpleProtocol parent; +} BonjourProtocol; + +typedef struct _BonjourProtocolClass +{ + PurpleProtocolClass parent_class; +} BonjourProtocolClass; + typedef struct _BonjourData { BonjourDnsSd *dns_sd_data; @@ -49,6 +68,11 @@ } BonjourData; /** + * Returns the GType for the BonjourProtocol object. + */ +G_MODULE_EXPORT GType bonjour_protocol_get_type(void); + +/** * This will always be username@machinename */ const char *bonjour_get_jid(PurpleAccount *account);
--- a/libpurple/protocols/bonjour/buddy.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/bonjour/buddy.c Fri Jan 31 18:02:20 2014 +0530 @@ -179,12 +179,12 @@ /* Set the user's status */ if (bonjour_buddy->msg != NULL) - purple_prpl_got_user_status(account, name, status_id, + purple_protocol_got_user_status(account, name, status_id, "message", bonjour_buddy->msg, NULL); else - purple_prpl_got_user_status(account, name, status_id, NULL); + purple_protocol_got_user_status(account, name, status_id, NULL); - purple_prpl_got_user_idle(account, name, FALSE, 0); + purple_protocol_got_user_idle(account, name, FALSE, 0); /* TODO: Because we don't save Bonjour buddies in blist.xml, * we will always have to look up the buddy icon at login time. @@ -211,7 +211,7 @@ purple_account_remove_buddy(purple_buddy_get_account(pb), pb, NULL); purple_blist_remove_buddy(pb); } else { - purple_prpl_got_user_status(purple_buddy_get_account(pb), + purple_protocol_got_user_status(purple_buddy_get_account(pb), purple_buddy_get_name(pb), "offline", NULL); bonjour_buddy_delete(purple_buddy_get_protocol_data(pb)); purple_buddy_set_protocol_data(pb, NULL);
--- a/libpurple/protocols/gg/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/gg/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -135,5 +135,6 @@ $(INTGG_CFLAGS) \ $(GLIB_CFLAGS) \ $(JSON_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS) \ $(GADU_EXTRA)
--- a/libpurple/protocols/gg/edisc.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/gg/edisc.c Fri Jan 31 18:02:20 2014 +0530 @@ -667,7 +667,7 @@ g_return_if_fail(gc != NULL); g_return_if_fail(who != NULL); - /* Nothing interesting here, this code is common among prpls. + /* Nothing interesting here, this code is common among protocols. * See ggp_edisc_xfer_send_new. */ xfer = ggp_edisc_xfer_send_new(gc, who);
--- a/libpurple/protocols/gg/gg.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/gg/gg.c Fri Jan 31 18:02:20 2014 +0530 @@ -28,7 +28,7 @@ #include <internal.h> -#include "plugin.h" +#include "plugins.h" #include "version.h" #include "notify.h" #include "buddylist.h" @@ -57,6 +57,10 @@ #include "libgaduw.h" /* ---------------------------------------------------------------------- */ +static PurpleProtocol *my_protocol = NULL; +static PurpleAccountOption *ggp_server_option; + +/* ---------------------------------------------------------------------- */ ggp_buddy_data * ggp_buddy_get_data(PurpleBuddy *buddy) { @@ -174,19 +178,19 @@ /* */ -/* static void ggp_action_buddylist_save(PurplePluginAction *action) {{{ */ -static void ggp_action_buddylist_save(PurplePluginAction *action) +/* static void ggp_action_buddylist_save(PurpleProtocolAction *action) {{{ */ +static void ggp_action_buddylist_save(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *)action->context; + PurpleConnection *gc = action->connection; purple_request_file(action, _("Save buddylist..."), NULL, TRUE, G_CALLBACK(ggp_callback_buddylist_save_ok), NULL, purple_request_cpar_from_connection(gc), gc); } -static void ggp_action_buddylist_load(PurplePluginAction *action) +static void ggp_action_buddylist_load(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *)action->context; + PurpleConnection *gc = action->connection; purple_request_file(action, _("Load buddylist from file..."), NULL, FALSE, G_CALLBACK(ggp_callback_buddylist_load_ok), NULL, @@ -608,7 +612,7 @@ } /* ---------------------------------------------------------------------- */ -/* ----- PurplePluginProtocolInfo ----------------------------------------- */ +/* ----- PurpleProtocol ----------------------------------------- */ /* ---------------------------------------------------------------------- */ static const char *ggp_list_icon(PurpleAccount *account, PurpleBuddy *buddy) @@ -839,7 +843,7 @@ if (state == PURPLE_IM_TYPING) dummy_length = (int)g_random_int(); - else // PURPLE_NOT_TYPING + else // PURPLE_IM_NOT_TYPING dummy_length = 0; gg_typing_notification( @@ -890,56 +894,56 @@ } } -static void ggp_action_multilogon(PurplePluginAction *action) +static void ggp_action_multilogon(PurpleProtocolAction *action) { - ggp_multilogon_dialog((PurpleConnection *)action->context); + ggp_multilogon_dialog(action->connection); } -static void ggp_action_status_broadcasting(PurplePluginAction *action) +static void ggp_action_status_broadcasting(PurpleProtocolAction *action) { - ggp_status_broadcasting_dialog((PurpleConnection *)action->context); + ggp_status_broadcasting_dialog(action->connection); } -static void ggp_action_search(PurplePluginAction *action) +static void ggp_action_search(PurpleProtocolAction *action) { - ggp_pubdir_search((PurpleConnection *)action->context, NULL); + ggp_pubdir_search(action->connection, NULL); } -static void ggp_action_set_info(PurplePluginAction *action) +static void ggp_action_set_info(PurpleProtocolAction *action) { - ggp_pubdir_set_info((PurpleConnection *)action->context); + ggp_pubdir_set_info(action->connection); } -static GList *ggp_actions(PurplePlugin *plugin, gpointer context) +static GList *ggp_get_actions(PurpleConnection *gc) { GList *m = NULL; - PurplePluginAction *act; + PurpleProtocolAction *act; - act = purple_plugin_action_new(_("Show other sessions"), + act = purple_protocol_action_new(_("Show other sessions"), ggp_action_multilogon); m = g_list_append(m, act); - act = purple_plugin_action_new(_("Show status only for buddies"), + act = purple_protocol_action_new(_("Show status only for buddies"), ggp_action_status_broadcasting); m = g_list_append(m, act); m = g_list_append(m, NULL); - act = purple_plugin_action_new(_("Find buddies..."), + act = purple_protocol_action_new(_("Find buddies..."), ggp_action_search); m = g_list_append(m, act); - act = purple_plugin_action_new(_("Set User Info"), + act = purple_protocol_action_new(_("Set User Info"), ggp_action_set_info); m = g_list_append(m, act); m = g_list_append(m, NULL); - act = purple_plugin_action_new(_("Save buddylist to file..."), + act = purple_protocol_action_new(_("Save buddylist to file..."), ggp_action_buddylist_save); m = g_list_append(m, act); - act = purple_plugin_action_new(_("Load buddylist from file..."), + act = purple_protocol_action_new(_("Load buddylist from file..."), ggp_action_buddylist_load); m = g_list_append(m, act); @@ -978,145 +982,24 @@ return 1200; /* no more than 1232 */ } -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_IM_IMAGE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png", 1, 1, 200, 200, 0, PURPLE_ICON_SCALE_DISPLAY | PURPLE_ICON_SCALE_SEND}, /* icon_spec */ - ggp_list_icon, /* list_icon */ - ggp_list_emblem, /* list_emblem */ - ggp_status_buddy_text, /* status_text */ - ggp_tooltip_text, /* tooltip_text */ - ggp_status_types, /* status_types */ - NULL, /* blist_node_menu */ -#if GGP_ENABLE_GG11 - ggp_chat_info, /* chat_info */ - ggp_chat_info_defaults, /* chat_info_defaults */ -#else - NULL, NULL, -#endif - ggp_login, /* login */ - ggp_close, /* close */ - ggp_message_send_im, /* send_im */ - NULL, /* set_info */ - ggp_send_typing, /* send_typing */ - ggp_pubdir_get_info_prpl, /* get_info */ - ggp_status_set_purplestatus, /* set_away */ - NULL, /* set_idle */ - NULL, /* change_passwd */ - ggp_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - ggp_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - ggp_add_deny, /* add_deny */ - NULL, /* rem_permit */ - ggp_rem_deny, /* rem_deny */ - NULL, /* set_permit_deny */ -#if GGP_ENABLE_GG11 - ggp_chat_join, /* join_chat */ - NULL, /* TODO */ /* reject_chat */ - ggp_chat_get_name, /* get_chat_name */ - ggp_chat_invite, /* chat_invite */ - ggp_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - ggp_chat_send, /* chat_send */ -#else - NULL, NULL, NULL, NULL, NULL, NULL, NULL, -#endif - ggp_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - ggp_roster_alias_buddy, /* alias_buddy */ - ggp_roster_group_buddy, /* group_buddy */ - ggp_roster_rename_group, /* rename_group */ - ggp_buddy_free, /* buddy_free */ - NULL, /* convo_closed */ - ggp_normalize, /* normalize */ - ggp_avatar_own_set, /* set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ -#if GGP_ENABLE_GG11 - ggp_chat_roomlist_get_list, /* roomlist_get_list */ -#else - NULL, -#endif - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - ggp_edisc_xfer_can_receive_file,/* can_receive_file */ - ggp_edisc_xfer_send_file, /* send_file */ - ggp_edisc_xfer_send_new, /* new_xfer */ - ggp_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - ggp_get_account_text_table, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* can_do_media */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - ggp_get_max_message_size /* get_max_message_size */ -}; - -static gboolean ggp_load(PurplePlugin *plugin); -static gboolean ggp_unload(PurplePlugin *plugin); - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, /* magic */ - PURPLE_MAJOR_VERSION, /* major_version */ - PURPLE_MINOR_VERSION, /* minor_version */ - PURPLE_PLUGIN_PROTOCOL, /* plugin type */ - NULL, /* ui_requirement */ - 0, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ - - "prpl-gg", /* id */ - "Gadu-Gadu", /* name */ - DISPLAY_VERSION, /* version */ - - N_("Gadu-Gadu Protocol Plugin"), /* summary */ - N_("Polish popular IM"), /* description */ - "boler@sourceforge.net", /* author */ - PURPLE_WEBSITE, /* homepage */ - - ggp_load, /* load */ - ggp_unload, /* unload */ - NULL, /* destroy */ - - NULL, /* ui_info */ - &prpl_info, /* extra_info */ - NULL, /* prefs_info */ - ggp_actions, /* actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurpleAccountOption *ggp_server_option; - -static void init_plugin(PurplePlugin *plugin) +static void +ggp_protocol_init(PurpleProtocol *protocol) { PurpleAccountOption *option; GList *encryption_options = NULL; GList *protocol_version = NULL; - purple_prefs_add_none("/plugins/prpl/gg"); + protocol->id = "prpl-gg"; + protocol->name = "Gadu-Gadu"; + protocol->options = OPT_PROTO_IM_IMAGE; + protocol->icon_spec = purple_buddy_icon_spec_new("png", + 1, 1, 200, 200, 0, + PURPLE_ICON_SCALE_DISPLAY | + PURPLE_ICON_SCALE_SEND); option = purple_account_option_string_new(_("GG server"), "gg_server", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); ggp_server_option = option; @@ -1134,7 +1017,7 @@ option = purple_account_option_list_new(_("Connection security"), "encryption", encryption_options); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); ADD_VALUE(protocol_version, _("Default"), "default"); @@ -1143,17 +1026,163 @@ option = purple_account_option_list_new(_("Protocol version"), "protocol_version", protocol_version); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Show links from strangers"), "show_links_from_strangers", 1); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); } -static gboolean ggp_load(PurplePlugin *plugin) +static void +ggp_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = ggp_login; + klass->close = ggp_close; + klass->status_types = ggp_status_types; + klass->list_icon = ggp_list_icon; +} + +static void +ggp_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = ggp_get_actions; + client_iface->list_emblem = ggp_list_emblem; + client_iface->status_text = ggp_status_buddy_text; + client_iface->tooltip_text = ggp_tooltip_text; + client_iface->buddy_free = ggp_buddy_free; + client_iface->normalize = ggp_normalize; + client_iface->offline_message = ggp_offline_message; + client_iface->get_account_text_table = ggp_get_account_text_table; + client_iface->get_max_message_size = ggp_get_max_message_size; +} + +static void +ggp_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->get_info = ggp_pubdir_get_info_protocol; + server_iface->set_status = ggp_status_set_purplestatus; + server_iface->add_buddy = ggp_add_buddy; + server_iface->remove_buddy = ggp_remove_buddy; + server_iface->keepalive = ggp_keepalive; + server_iface->alias_buddy = ggp_roster_alias_buddy; + server_iface->group_buddy = ggp_roster_group_buddy; + server_iface->rename_group = ggp_roster_rename_group; + server_iface->set_buddy_icon = ggp_avatar_own_set; +} + +static void +ggp_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = ggp_message_send_im; + im_iface->send_typing = ggp_send_typing; +} + +#if GGP_ENABLE_GG11 +static void +ggp_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->info = ggp_chat_info; + chat_iface->info_defaults = ggp_chat_info_defaults; + chat_iface->join = ggp_chat_join; + chat_iface->get_name = ggp_chat_get_name; + chat_iface->invite = ggp_chat_invite; + chat_iface->leave = ggp_chat_leave; + chat_iface->send = ggp_chat_send; + + chat_iface->reject = NULL; /* TODO */ +} + +static void +ggp_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface) +{ + roomlist_iface->get_list = ggp_chat_roomlist_get_list; +} +#endif + +static void +ggp_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) { + privacy_iface->add_deny = ggp_add_deny; + privacy_iface->rem_deny = ggp_rem_deny; +} + +static void +ggp_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->can_receive = ggp_edisc_xfer_can_receive_file; + xfer_iface->send = ggp_edisc_xfer_send_file; + xfer_iface->new_xfer = ggp_edisc_xfer_send_new; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + GGPProtocol, ggp_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + ggp_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + ggp_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + ggp_protocol_im_iface_init) +#if GGP_ENABLE_GG11 + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + ggp_protocol_chat_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE, + ggp_protocol_roomlist_iface_init) +#endif + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + ggp_protocol_privacy_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + ggp_protocol_xfer_iface_init) +); + +static gchar * +plugin_extra(PurplePlugin *plugin) +{ + return g_strdup_printf("Using libgadu version %s", gg_libgadu_version()); +} + +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "boler@sourceforge.net", + NULL + }; + + return purple_plugin_info_new( + "id", "prpl-gg", + "name", "Gadu-Gadu Protocol", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("Gadu-Gadu Protocol Plugin"), + "description", N_("Polish popular IM"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "extra-cb", plugin_extra, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + ggp_protocol_register_type(plugin); + + my_protocol = purple_protocols_add(GGP_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; + + purple_prefs_add_none("/plugins/prpl/gg"); + purple_debug_info("gg", "Loading Gadu-Gadu protocol plugin with " "libgadu %s...\n", gg_libgadu_version()); @@ -1166,16 +1195,20 @@ return TRUE; } -static gboolean ggp_unload(PurplePlugin *plugin) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { ggp_servconn_cleanup(); ggp_html_cleanup(); ggp_message_cleanup_global(); ggp_libgaduw_cleanup(); + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + return TRUE; } -PURPLE_INIT_PLUGIN(gg, init_plugin, info); +PURPLE_PLUGIN_INIT(gg, plugin_query, plugin_load, plugin_unload); /* vim: set ts=8 sts=0 sw=8 noet: */
--- a/libpurple/protocols/gg/gg.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/gg/gg.h Fri Jan 31 18:02:20 2014 +0530 @@ -47,6 +47,23 @@ #include "message-prpl.h" #include "edisc.h" +#define GGP_TYPE_PROTOCOL (ggp_protocol_get_type()) +#define GGP_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GGP_TYPE_PROTOCOL, GGPProtocol)) +#define GGP_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GGP_TYPE_PROTOCOL, GGPProtocolClass)) +#define GGP_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GGP_TYPE_PROTOCOL)) +#define GGP_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GGP_TYPE_PROTOCOL)) +#define GGP_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GGP_TYPE_PROTOCOL, GGPProtocolClass)) + +typedef struct _GGPProtocol +{ + PurpleProtocol parent; +} GGPProtocol; + +typedef struct _GGPProtocolClass +{ + PurpleProtocolClass parent_class; +} GGPProtocolClass; + typedef struct { struct gg_session *session; guint inpa; @@ -70,6 +87,8 @@ gboolean not_a_friend; } ggp_buddy_data; +G_MODULE_EXPORT GType ggp_protocol_get_type(void); + ggp_buddy_data * ggp_buddy_get_data(PurpleBuddy *buddy); const gchar * ggp_get_imtoken(PurpleConnection *gc);
--- a/libpurple/protocols/gg/image.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/gg/image.c Fri Jan 31 18:02:20 2014 +0530 @@ -182,7 +182,7 @@ g_path_get_basename() and purple_escape_filename() on it before passing it in. This is easy, but it's not clear if there might be other implications because this filename is used elsewhere within - this PRPL. */ + this protocol. */ stored_id = purple_imgstore_new_with_id( g_memdup(image_reply->image, image_reply->size), image_reply->size,
--- a/libpurple/protocols/gg/pubdir-prpl.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/gg/pubdir-prpl.c Fri Jan 31 18:02:20 2014 +0530 @@ -66,7 +66,7 @@ static void ggp_pubdir_got_data(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data); -static void ggp_pubdir_get_info_prpl_got(PurpleConnection *gc, +static void ggp_pubdir_get_info_protocol_got(PurpleConnection *gc, int records_count, const ggp_pubdir_record *records, int next_offset, void *_uin_p); @@ -351,19 +351,19 @@ ggp_pubdir_record_free(records, record_count); } -void ggp_pubdir_get_info_prpl(PurpleConnection *gc, const char *name) +void ggp_pubdir_get_info_protocol(PurpleConnection *gc, const char *name) { uin_t uin = ggp_str_to_uin(name); uin_t *uin_p = g_new0(uin_t, 1); *uin_p = uin; - purple_debug_info("gg", "ggp_pubdir_get_info_prpl: %u\n", uin); + purple_debug_info("gg", "ggp_pubdir_get_info_protocol: %u\n", uin); - ggp_pubdir_get_info(gc, uin, ggp_pubdir_get_info_prpl_got, uin_p); + ggp_pubdir_get_info(gc, uin, ggp_pubdir_get_info_protocol_got, uin_p); } -static void ggp_pubdir_get_info_prpl_got(PurpleConnection *gc, +static void ggp_pubdir_get_info_protocol_got(PurpleConnection *gc, int records_count, const ggp_pubdir_record *records, int next_offset, void *_uin_p) { @@ -376,7 +376,7 @@ if (records_count < 1) { - purple_debug_error("gg", "ggp_pubdir_get_info_prpl_got: " + purple_debug_error("gg", "ggp_pubdir_get_info_protocol_got: " "couldn't get info for %u\n", uin); purple_notify_user_info_add_pair_plaintext(info, NULL, _("Cannot get user information")); @@ -386,7 +386,7 @@ return; } - purple_debug_info("gg", "ggp_pubdir_get_info_prpl_got: %u\n", uin); + purple_debug_info("gg", "ggp_pubdir_get_info_protocol_got: %u\n", uin); g_assert(uin == record->uin); g_assert(records_count == 1); @@ -778,7 +778,7 @@ static void ggp_pubdir_search_results_info(PurpleConnection *gc, GList *row, gpointer _form) { - ggp_pubdir_get_info_prpl(gc, g_list_nth_data(row, 0)); + ggp_pubdir_get_info_protocol(gc, g_list_nth_data(row, 0)); } static void ggp_pubdir_search_results_new(PurpleConnection *gc, GList *row,
--- a/libpurple/protocols/gg/pubdir-prpl.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/gg/pubdir-prpl.h Fri Jan 31 18:02:20 2014 +0530 @@ -27,8 +27,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#ifndef _GGP_PUBDIR_PRPL_H -#define _GGP_PUBDIR_PRPL_H +#ifndef _GGP_PUBDIR_PROTOCOL_H +#define _GGP_PUBDIR_PROTOCOL_H #include <internal.h> #include <libgadu.h> @@ -62,7 +62,7 @@ void ggp_pubdir_get_info(PurpleConnection *gc, uin_t uin, ggp_pubdir_request_cb cb, void *user_data); -void ggp_pubdir_get_info_prpl(PurpleConnection *gc, const char *name); +void ggp_pubdir_get_info_protocol(PurpleConnection *gc, const char *name); void ggp_pubdir_request_buddy_alias(PurpleConnection *gc, PurpleBuddy *buddy); void ggp_pubdir_search(PurpleConnection *gc, @@ -70,4 +70,4 @@ void ggp_pubdir_set_info(PurpleConnection *gc); -#endif /* _GGP_PUBDIR_PRPL_H */ +#endif /* _GGP_PUBDIR_PROTOCOL_H */
--- a/libpurple/protocols/gg/status.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/gg/status.c Fri Jan 31 18:02:20 2014 +0530 @@ -316,7 +316,7 @@ GG_STATUS_DESCR_MAXSIZE); } - purple_prpl_got_user_status(account, + purple_protocol_got_user_status(account, purple_account_get_username(account), purple_status_get_id(status), status_msg_gg ? "message" : NULL, status_msg_gg, NULL); @@ -454,12 +454,12 @@ if (status_message) { - purple_prpl_got_user_status(account, ggp_uin_to_str(uin), + purple_protocol_got_user_status(account, ggp_uin_to_str(uin), purple_status, "message", status_message, NULL); } else { - purple_prpl_got_user_status(account, ggp_uin_to_str(uin), + purple_protocol_got_user_status(account, ggp_uin_to_str(uin), purple_status, NULL); }
--- a/libpurple/protocols/irc/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/irc/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -35,4 +35,5 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/irc/irc.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/irc/irc.c Fri Jan 31 18:02:20 2014 +0530 @@ -30,8 +30,8 @@ #include "conversation.h" #include "debug.h" #include "notify.h" -#include "prpl.h" -#include "plugin.h" +#include "protocol.h" +#include "plugins.h" #include "util.h" #include "version.h" @@ -43,7 +43,7 @@ static const char *irc_blist_icon(PurpleAccount *a, PurpleBuddy *b); static GList *irc_status_types(PurpleAccount *account); -static GList *irc_actions(PurplePlugin *plugin, gpointer context); +static GList *irc_get_actions(PurpleConnection *gc); /* static GList *irc_chat_info(PurpleConnection *gc); */ static void irc_login(PurpleAccount *account); static void irc_login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond); @@ -60,11 +60,11 @@ static gboolean irc_nick_equal(const char *nick1, const char *nick2); static void irc_buddy_free(struct irc_buddy *ib); -PurplePlugin *_irc_plugin = NULL; +PurpleProtocol *_irc_protocol = NULL; -static void irc_view_motd(PurplePluginAction *action) +static void irc_view_motd(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; struct irc_conn *irc; char *title; @@ -159,7 +159,7 @@ int ret; char *tosend= g_strdup(buf); - purple_signal_emit(_irc_plugin, "irc-sending-text", purple_account_get_connection(irc->account), &tosend); + purple_signal_emit(_irc_protocol, "irc-sending-text", purple_account_get_connection(irc->account), &tosend); if (tosend == NULL) return 0; @@ -286,12 +286,12 @@ return types; } -static GList *irc_actions(PurplePlugin *plugin, gpointer context) +static GList *irc_get_actions(PurpleConnection *gc) { GList *list = NULL; - PurplePluginAction *act = NULL; + PurpleProtocolAction *act = NULL; - act = purple_plugin_action_new(_("View MOTD"), irc_view_motd); + act = purple_protocol_action_new(_("View MOTD"), irc_view_motd); list = g_list_append(list, act); return list; @@ -300,15 +300,15 @@ static GList *irc_chat_join_info(PurpleConnection *gc) { GList *m = NULL; - struct proto_chat_entry *pce; + PurpleProtocolChatEntry *pce; - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Channel:"); pce->identifier = "channel"; pce->required = TRUE; m = g_list_append(m, pce); - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Password:"); pce->identifier = "password"; pce->secret = TRUE; @@ -616,7 +616,7 @@ ib = g_hash_table_lookup(irc->buddies, bname); if (ib != NULL) { ib->ref++; - purple_prpl_got_user_status(irc->account, bname, + purple_protocol_got_user_status(irc->account, bname, ib->online ? "available" : "offline", NULL); } else { ib = g_new0(struct irc_buddy, 1); @@ -925,179 +925,190 @@ return 417; } -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL | - OPT_PROTO_SLASH_COMMANDS_NATIVE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - NO_BUDDY_ICONS, /* icon_spec */ - irc_blist_icon, /* list_icon */ - NULL, /* list_emblems */ - NULL, /* status_text */ - NULL, /* tooltip_text */ - irc_status_types, /* away_states */ - NULL, /* blist_node_menu */ - irc_chat_join_info, /* chat_info */ - irc_chat_info_defaults, /* chat_info_defaults */ - irc_login, /* login */ - irc_close, /* close */ - irc_im_send, /* send_im */ - NULL, /* set_info */ - NULL, /* send_typing */ - irc_get_info, /* get_info */ - irc_set_status, /* set_status */ - NULL, /* set_idle */ - NULL, /* change_passwd */ - irc_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - irc_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* rem_permit */ - NULL, /* rem_deny */ - NULL, /* set_permit_deny */ - irc_chat_join, /* join_chat */ - NULL, /* reject_chat */ - irc_get_chat_name, /* get_chat_name */ - irc_chat_invite, /* chat_invite */ - irc_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - irc_chat_send, /* chat_send */ - irc_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - NULL, /* buddy_free */ - NULL, /* convo_closed */ - purple_normalize_nocase, /* normalize */ - NULL, /* set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* get_cb_real_name */ - irc_chat_set_topic, /* set_chat_topic */ - NULL, /* find_blist_chat */ - irc_roomlist_get_list, /* roomlist_get_list */ - irc_roomlist_cancel, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - irc_dccsend_send_file, /* send_file */ - irc_dccsend_new_xfer, /* new_xfer */ - NULL, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - irc_send_raw, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - irc_get_max_message_size /* get_max_message_size */ -}; - -static gboolean load_plugin (PurplePlugin *plugin) { - - purple_signal_register(plugin, "irc-sending-text", - purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, - PURPLE_TYPE_CONNECTION, - G_TYPE_POINTER); /* pointer to a string */ - purple_signal_register(plugin, "irc-receiving-text", - purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, - PURPLE_TYPE_CONNECTION, - G_TYPE_POINTER); /* pointer to a string */ - return TRUE; -} - - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-irc", /**< id */ - "IRC", /**< name */ - DISPLAY_VERSION, /**< version */ - N_("IRC Protocol Plugin"), /** summary */ - N_("The IRC Protocol Plugin that Sucks Less"), /** description */ - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - load_plugin, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - irc_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void _init_plugin(PurplePlugin *plugin) +static void +irc_protocol_init(PurpleProtocol *protocol) { PurpleAccountUserSplit *split; PurpleAccountOption *option; + protocol->id = "prpl-irc"; + protocol->name = "IRC"; + protocol->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL | + OPT_PROTO_SLASH_COMMANDS_NATIVE; + split = purple_account_user_split_new(_("Server"), IRC_DEFAULT_SERVER, '@'); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); + protocol->user_splits = g_list_append(protocol->user_splits, split); option = purple_account_option_int_new(_("Port"), "port", IRC_DEFAULT_PORT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Encodings"), "encoding", IRC_DEFAULT_CHARSET); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Auto-detect incoming UTF-8"), "autodetect_utf8", IRC_DEFAULT_AUTODETECT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Ident name"), "username", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Real name"), "realname", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); /* option = purple_account_option_string_new(_("Quit message"), "quitmsg", IRC_DEFAULT_QUIT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); */ option = purple_account_option_bool_new(_("Use SSL"), "ssl", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); #ifdef HAVE_CYRUS_SASL option = purple_account_option_bool_new(_("Authenticate with SASL"), "sasl", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new( _("Allow plaintext SASL auth over unencrypted connection"), "auth_plain_in_clear", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); #endif +} - _irc_plugin = plugin; +static void +irc_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = irc_login; + klass->close = irc_close; + klass->status_types = irc_status_types; + klass->list_icon = irc_blist_icon; +} + +static void +irc_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = irc_get_actions; + client_iface->normalize = purple_normalize_nocase; + client_iface->get_max_message_size = irc_get_max_message_size; +} + +static void +irc_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->set_status = irc_set_status; + server_iface->get_info = irc_get_info; + server_iface->add_buddy = irc_add_buddy; + server_iface->remove_buddy = irc_remove_buddy; + server_iface->keepalive = irc_keepalive; + server_iface->send_raw = irc_send_raw; +} + +static void +irc_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = irc_im_send; +} + +static void +irc_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->info = irc_chat_join_info; + chat_iface->info_defaults = irc_chat_info_defaults; + chat_iface->join = irc_chat_join; + chat_iface->get_name = irc_get_chat_name; + chat_iface->invite = irc_chat_invite; + chat_iface->leave = irc_chat_leave; + chat_iface->send = irc_chat_send; + chat_iface->set_topic = irc_chat_set_topic; +} + +static void +irc_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface) +{ + roomlist_iface->get_list = irc_roomlist_get_list; + roomlist_iface->cancel = irc_roomlist_cancel; +} + +static void +irc_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->send = irc_dccsend_send_file; + xfer_iface->new_xfer = irc_dccsend_new_xfer; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + IRCProtocol, irc_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + irc_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + irc_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + irc_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + irc_protocol_chat_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE, + irc_protocol_roomlist_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + irc_protocol_xfer_iface_init) +); + +static PurplePluginInfo * +plugin_query(GError **error) +{ + return purple_plugin_info_new( + "id", "prpl-irc", + "name", "IRC Protocol", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("IRC Protocol Plugin"), + "description", N_("The IRC Protocol Plugin that Sucks Less"), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + irc_protocol_register_type(plugin); + + _irc_protocol = purple_protocols_add(IRC_TYPE_PROTOCOL, error); + if (!_irc_protocol) + return FALSE; purple_prefs_remove("/plugins/prpl/irc/quitmsg"); purple_prefs_remove("/plugins/prpl/irc"); irc_register_commands(); + + purple_signal_register(_irc_protocol, "irc-sending-text", + purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, + PURPLE_TYPE_CONNECTION, + G_TYPE_POINTER); /* pointer to a string */ + purple_signal_register(_irc_protocol, "irc-receiving-text", + purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, + PURPLE_TYPE_CONNECTION, + G_TYPE_POINTER); /* pointer to a string */ + + return TRUE; } -PURPLE_INIT_PLUGIN(irc, _init_plugin, info); +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + irc_unregister_commands(); + + if (!purple_protocols_remove(_irc_protocol, error)) + return FALSE; + + return TRUE; +} + +PURPLE_PLUGIN_INIT(irc, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/protocols/irc/irc.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/irc/irc.h Fri Jan 31 18:02:20 2014 +0530 @@ -34,6 +34,13 @@ #include "roomlist.h" #include "sslconn.h" +#define IRC_TYPE_PROTOCOL (irc_protocol_get_type()) +#define IRC_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), IRC_TYPE_PROTOCOL, IRCProtocol)) +#define IRC_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), IRC_TYPE_PROTOCOL, IRCProtocolClass)) +#define IRC_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), IRC_TYPE_PROTOCOL)) +#define IRC_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), IRC_TYPE_PROTOCOL)) +#define IRC_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), IRC_TYPE_PROTOCOL, IRCProtocolClass)) + #define IRC_DEFAULT_SERVER "irc.freenode.net" #define IRC_DEFAULT_PORT 6667 #define IRC_DEFAULT_SSL_PORT 994 @@ -48,10 +55,19 @@ #define IRC_NAMES_FLAG "irc-namelist" - enum { IRC_USEROPT_SERVER, IRC_USEROPT_PORT, IRC_USEROPT_CHARSET }; enum irc_state { IRC_STATE_NEW, IRC_STATE_ESTABLISHED }; +typedef struct _IRCProtocol +{ + PurpleProtocol parent; +} IRCProtocol; + +typedef struct _IRCProtocolClass +{ + PurpleProtocolClass parent_class; +} IRCProtocolClass; + struct irc_conn { PurpleAccount *account; GHashTable *msgs; @@ -118,6 +134,8 @@ typedef int (*IRCCmdCallback) (struct irc_conn *irc, const char *cmd, const char *target, const char **args); +G_MODULE_EXPORT GType irc_protocol_get_type(void); + int irc_send(struct irc_conn *irc, const char *buf); int irc_send_len(struct irc_conn *irc, const char *buf, int len); gboolean irc_blist_timeout(struct irc_conn *irc); @@ -134,6 +152,7 @@ gboolean irc_ischannel(const char *string); void irc_register_commands(void); +void irc_unregister_commands(void); void irc_msg_table_build(struct irc_conn *irc); void irc_parse_msg(struct irc_conn *irc, char *input); char *irc_parse_ctcp(struct irc_conn *irc, const char *from, const char *to, const char *msg, int notice);
--- a/libpurple/protocols/irc/msgs.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/irc/msgs.c Fri Jan 31 18:02:20 2014 +0530 @@ -106,8 +106,8 @@ /* If we're away then set our away message */ status = purple_account_get_active_status(irc->account); if (purple_status_type_get_primitive(purple_status_get_status_type(status)) != PURPLE_STATUS_AVAILABLE) { - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - prpl_info->set_status(irc->account, status); + PurpleProtocol *protocol = purple_connection_get_protocol(gc); + purple_protocol_server_iface_set_status(protocol, irc->account, status); } /* this used to be in the core, but it's not now */ @@ -923,10 +923,10 @@ return; if (ib->online && !ib->new_online_status) { - purple_prpl_got_user_status(irc->account, name, "offline", NULL); + purple_protocol_got_user_status(irc->account, name, "offline", NULL); ib->online = FALSE; } else if (!ib->online && ib->new_online_status) { - purple_prpl_got_user_status(irc->account, name, "available", NULL); + purple_protocol_got_user_status(irc->account, name, "available", NULL); ib->online = TRUE; } }
--- a/libpurple/protocols/irc/parse.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/irc/parse.c Fri Jan 31 18:02:20 2014 +0530 @@ -34,6 +34,8 @@ #include <stdlib.h> #include <ctype.h> +static GSList *cmds = NULL; + static char *irc_send_convert(struct irc_conn *irc, const char *string); static char *irc_recv_convert(struct irc_conn *irc, const char *string); @@ -44,7 +46,7 @@ "orange", "yellow", "green", "teal", "cyan", "light blue", "pink", "grey", "light grey" }; -extern PurplePlugin *_irc_plugin; +extern PurpleProtocol *_irc_protocol; /*typedef void (*IRCMsgCallback)(struct irc_conn *irc, char *from, char *name, char **args);*/ static struct _irc_msg { @@ -201,12 +203,13 @@ static void irc_register_command(struct _irc_user_cmd *c) { + PurpleCmdId id; PurpleCmdFlag f; char args[10]; char *format; size_t i; - f = PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY + f = PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS; format = c->format; @@ -227,8 +230,9 @@ args[i] = '\0'; - purple_cmd_register(c->name, args, PURPLE_CMD_P_PRPL, f, "prpl-irc", + id = purple_cmd_register(c->name, args, PURPLE_CMD_P_PROTOCOL, f, "prpl-irc", irc_parse_purple_cmd, _(c->help), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); } void irc_register_commands(void) @@ -239,6 +243,15 @@ irc_register_command(c); } +void irc_unregister_commands(void) +{ + while (cmds) { + PurpleCmdId id = GPOINTER_TO_UINT(cmds->data); + purple_cmd_unregister(id); + cmds = g_slist_delete_link(cmds, cmds); + } +} + static char *irc_send_convert(struct irc_conn *irc, const char *string) { char *utf8; @@ -680,7 +693,7 @@ * TODO: It should be passed as an array of bytes and a length * instead of a null terminated string. */ - purple_signal_emit(_irc_plugin, "irc-receiving-text", gc, &input); + purple_signal_emit(_irc_protocol, "irc-receiving-text", gc, &input); if (!strncmp(input, "PING ", 5)) { msg = irc_format(irc, "vv", "PONG", input + 5); @@ -761,7 +774,7 @@ break; case '*': /* Ditto 'v' above; we're going to salvage this in case - * it leaks past the IRC prpl */ + * it leaks past the IRC protocol */ args[i] = purple_utf8_salvage(cur); cur = cur + strlen(cur); break;
--- a/libpurple/protocols/jabber/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -27,6 +27,8 @@ data.h \ disco.c \ disco.h \ + facebook.c \ + facebook.h \ google/gmail.c \ google/gmail.h \ google/google.c \ @@ -43,6 +45,8 @@ google/jingleinfo.h \ google/relay.c \ google/relay.h \ + gtalk.c \ + gtalk.h \ ibb.c \ ibb.h \ iq.c \ @@ -91,11 +95,13 @@ usertune.c \ usertune.h \ xdata.c \ - xdata.h + xdata.h \ + xmpp.c \ + xmpp.h AM_CFLAGS = $(st) -libxmpp_la_LDFLAGS = -module -avoid-version +libjabber_la_LDFLAGS = -module -avoid-version if USE_CYRUS_SASL JABBERSOURCES += auth_cyrus.c @@ -105,27 +111,18 @@ st = -DPURPLE_STATIC_PRPL noinst_LTLIBRARIES = libjabber.la -libjabber_la_SOURCES = $(JABBERSOURCES) libfacebook.c libgtalk.c libxmpp.c +libjabber_la_SOURCES = $(JABBERSOURCES) libjabber_la_CFLAGS = $(AM_CFLAGS) else st = -pkg_LTLIBRARIES = libjabber.la libfacebook.la libgtalk.la libxmpp.la +pkg_LTLIBRARIES = libjabber.la libjabber_la_SOURCES = $(JABBERSOURCES) libjabber_la_LIBADD = $(GLIB_LIBS) $(SASL_LIBS) $(LIBXML_LIBS) $(IDN_LIBS)\ $(FARSTREAM_LIBS) \ $(GSTREAMER_LIBS) -libfacebook_la_SOURCES = libfacebook.c -libfacebook_la_LIBADD = libjabber.la - -libgtalk_la_SOURCES = libgtalk.c -libgtalk_la_LIBADD = libjabber.la - -libxmpp_la_SOURCES = libxmpp.c -libxmpp_la_LIBADD = libjabber.la - endif AM_CPPFLAGS = \ @@ -133,6 +130,7 @@ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(IDN_CFLAGS) \ $(LIBXML_CFLAGS) \ $(FARSTREAM_CFLAGS) \
--- a/libpurple/protocols/jabber/Makefile.mingw Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/Makefile.mingw Fri Jan 31 18:02:20 2014 +0530 @@ -8,9 +8,6 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak TARGET = libjabber -FACEBOOK_TARGET = libfacebook -GTALK_TARGET = libgtalk -XMPP_TARGET = libxmpp TYPE = PLUGIN # Static or Plugin... @@ -65,6 +62,7 @@ chat.c \ data.c \ disco.c \ + facebook.c \ google/gmail.c \ google/google.c \ google/google_presence.c \ @@ -72,6 +70,7 @@ google/google_session.c \ google/jingleinfo.c \ google/relay.c \ + gtalk.c \ ibb.c \ iq.c \ jabber.c \ @@ -96,20 +95,12 @@ usernick.c \ usertune.c \ xdata.c \ + xmpp.c \ win32/posix.uname.c \ $(VV_SRC) OBJECTS = $(C_SRC:%.c=%.o) -FACEBOOK_C_SRC = libfacebook.c -FACEBOOK_OBJECTS = $(FACEBOOK_C_SRC:%.c=%.o) - -GTALK_C_SRC = libgtalk.c -GTALK_OBJECTS = $(GTALK_C_SRC:%.c=%.o) - -XMPP_C_SRC = libxmpp.c -XMPP_OBJECTS = $(XMPP_C_SRC:%.c=%.o) - ## ## LIBRARIES ## @@ -135,35 +126,20 @@ ## .PHONY: all install clean -all: $(TARGET).dll $(FACEBOOK_TARGET).dll $(GTALK_TARGET).dll $(XMPP_TARGET).dll +all: $(TARGET).dll install: all $(DLL_INSTALL_DIR) - cp $(FACEBOOK_TARGET).dll $(DLL_INSTALL_DIR) - cp $(GTALK_TARGET).dll $(DLL_INSTALL_DIR) - cp $(XMPP_TARGET).dll $(DLL_INSTALL_DIR) - cp $(TARGET).dll $(PURPLE_INSTALL_DIR) + cp $(TARGET).dll $(DLL_INSTALL_DIR) $(OBJECTS): $(PURPLE_CONFIG_H) -$(TARGET).dll $(TARGET).dll.a: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--out-implib,$(TARGET).dll.a -o $(TARGET).dll - -$(FACEBOOK_TARGET).dll: $(TARGET).dll.a $(FACEBOOK_OBJECTS) - $(CC) -shared $(FACEBOOK_OBJECTS) $(LIB_PATHS) $(LIBS) -ljabber $(DLL_LD_FLAGS) -o $(FACEBOOK_TARGET).dll - -$(GTALK_TARGET).dll: $(TARGET).dll.a $(GTALK_OBJECTS) - $(CC) -shared $(GTALK_OBJECTS) $(LIB_PATHS) $(LIBS) -ljabber $(DLL_LD_FLAGS) -o $(GTALK_TARGET).dll - -$(XMPP_TARGET).dll: $(TARGET).dll.a $(XMPP_OBJECTS) - $(CC) -shared $(XMPP_OBJECTS) $(LIB_PATHS) $(LIBS) -ljabber $(DLL_LD_FLAGS) -o $(XMPP_TARGET).dll +$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) + $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll ## ## CLEAN RULES ## clean: - rm -f $(OBJECTS) $(TARGET).dll $(TARGET).dll.a - rm -f $(FACEBOOK_OBJECTS) $(FACEBOOK_TARGET).dll - rm -f $(GTALK_OBJECTS) $(GTALK_TARGET).dll - rm -f $(XMPP_OBJECTS) $(XMPP_TARGET).dll + rm -f $(OBJECTS) $(TARGET).dll include $(PIDGIN_COMMON_TARGETS)
--- a/libpurple/protocols/jabber/adhoccommands.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/adhoccommands.c Fri Jan 31 18:02:20 2014 +0530 @@ -271,7 +271,7 @@ } if (js->state == JABBER_STREAM_CONNECTED) - purple_prpl_got_account_actions(purple_connection_get_account(js->gc)); + purple_protocol_got_account_actions(purple_connection_get_account(js->gc)); } static void @@ -320,10 +320,10 @@ jabber_iq_send(iq); } -static void jabber_adhoc_server_execute(PurplePluginAction *action) { +static void jabber_adhoc_server_execute(PurpleProtocolAction *action) { JabberAdHocCommands *cmd = action->user_data; if(cmd) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = (PurpleConnection *) action->connection; JabberStream *js = purple_connection_get_protocol_data(gc); jabber_adhoc_execute(js, cmd); @@ -344,7 +344,7 @@ for(riter = jbr->commands; riter; riter = g_list_next(riter)) { JabberAdHocCommands *cmd = riter->data; char *cmdname = g_strdup_printf("%s (%s)",cmd->name,jbr->name); - PurplePluginAction *act = purple_plugin_action_new(cmdname, jabber_adhoc_server_execute); + PurpleProtocolAction *act = purple_protocol_action_new(cmdname, jabber_adhoc_server_execute); act->user_data = cmd; *m = g_list_append(*m, act); g_free(cmdname); @@ -356,7 +356,7 @@ /* now add server commands */ for(cmdlst = js->commands; cmdlst; cmdlst = g_list_next(cmdlst)) { JabberAdHocCommands *cmd = cmdlst->data; - PurplePluginAction *act = purple_plugin_action_new(cmd->name, jabber_adhoc_server_execute); + PurpleProtocolAction *act = purple_protocol_action_new(cmd->name, jabber_adhoc_server_execute); act->user_data = cmd; *m = g_list_append(*m, act); }
--- a/libpurple/protocols/jabber/buddy.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/buddy.c Fri Jan 31 18:02:20 2014 +0530 @@ -23,7 +23,7 @@ #include "internal.h" #include "debug.h" #include "imgstore.h" -#include "prpl.h" +#include "protocol.h" #include "notify.h" #include "request.h" #include "util.h" @@ -624,9 +624,9 @@ * string (if any) into GSLists for the (multi-entry) edit dialog and * calls the set_vcard dialog. */ -void jabber_setup_set_info(PurplePluginAction *action) +void jabber_setup_set_info(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = (PurpleConnection *) action->connection; PurpleRequestFields *fields; PurpleRequestFieldGroup *group; PurpleRequestField *field; @@ -1361,7 +1361,7 @@ if (jbr == jabber_buddy_find_resource(jb, NULL)) { - purple_prpl_got_user_idle(purple_connection_get_account(js->gc), + purple_protocol_got_user_idle(purple_connection_get_account(js->gc), buddy_name, jbr->idle, jbr->idle); } } @@ -2273,9 +2273,9 @@ jabber_iq_send(iq); } -void jabber_user_search_begin(PurplePluginAction *action) +void jabber_user_search_begin(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = (PurpleConnection *) action->connection; JabberStream *js = purple_connection_get_protocol_data(gc); const char *def_val = purple_account_get_string(purple_connection_get_account(js->gc), "user_directory", ""); if(!*def_val && js->user_directories)
--- a/libpurple/protocols/jabber/buddy.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/buddy.h Fri Jan 31 18:02:20 2014 +0530 @@ -103,11 +103,11 @@ GList *jabber_blist_node_menu(PurpleBlistNode *node); void jabber_set_info(PurpleConnection *gc, const char *info); -void jabber_setup_set_info(PurplePluginAction *action); +void jabber_setup_set_info(PurpleProtocolAction *action); void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img); void jabber_user_search(JabberStream *js, const char *directory); -void jabber_user_search_begin(PurplePluginAction *); +void jabber_user_search_begin(PurpleProtocolAction *); void jabber_buddy_remove_all_pending_buddy_info_requests(JabberStream *js);
--- a/libpurple/protocols/jabber/caps.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/caps.c Fri Jan 31 18:02:20 2014 +0530 @@ -966,8 +966,8 @@ for (node = accounts; node; node = node->next) { PurpleAccount *account = node->data; - const char *prpl_id = purple_account_get_protocol_id(account); - if (g_str_equal("prpl-jabber", prpl_id) && purple_account_is_connected(account)) { + const char *protocol_id = purple_account_get_protocol_id(account); + if (g_str_equal("prpl-jabber", protocol_id) && purple_account_is_connected(account)) { PurpleConnection *gc = purple_account_get_connection(account); jabber_presence_send(purple_connection_get_protocol_data(gc), TRUE); }
--- a/libpurple/protocols/jabber/chat.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/chat.c Fri Jan 31 18:02:20 2014 +0530 @@ -22,7 +22,7 @@ */ #include "internal.h" #include "debug.h" -#include "prpl.h" /* for proto_chat_entry */ +#include "protocol.h" /* for PurpleProtocolChatEntry */ #include "notify.h" #include "request.h" #include "roomlist.h" @@ -38,27 +38,27 @@ GList *jabber_chat_info(PurpleConnection *gc) { GList *m = NULL; - struct proto_chat_entry *pce; + PurpleProtocolChatEntry *pce; - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Room:"); pce->identifier = "room"; pce->required = TRUE; m = g_list_append(m, pce); - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Server:"); pce->identifier = "server"; pce->required = TRUE; m = g_list_append(m, pce); - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Handle:"); pce->identifier = "handle"; pce->required = TRUE; m = g_list_append(m, pce); - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Password:"); pce->identifier = "password"; pce->secret = TRUE;
--- a/libpurple/protocols/jabber/chat.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/chat.h Fri Jan 31 18:02:20 2014 +0530 @@ -60,14 +60,14 @@ char *jabber_get_chat_name(GHashTable *data); /** - * in-prpl function for joining a chat room. Doesn't require sticking goop + * in-protocol function for joining a chat room. Doesn't require sticking goop * into a hash table. * * @param room The room to join. This MUST be normalized already. * @param server The server the room is on. This MUST be normalized already. * @param password The password (if required) to join the room. May be NULL. * @param data The chat hash table. May be NULL (it will be generated - * for current core<>prpl API interface.) + * for current core<>protocol API interface.) */ JabberChat *jabber_join_chat(JabberStream *js, const char *room, const char *server, const char *handle,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/facebook.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,177 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "internal.h" +#include "chat.h" +#include "core.h" +#include "plugins.h" + +#include "facebook.h" + +static const char * +facebook_list_icon(PurpleAccount *a, PurpleBuddy *b) +{ + return "facebook"; +} + +static void +facebook_protocol_init(PurpleProtocol *protocol) +{ + PurpleAccountUserSplit *split; + PurpleAccountOption *option; + GList *encryption_values = NULL; + + protocol->id = "prpl-facebook-xmpp"; + protocol->name = "Facebook (XMPP)"; + + /* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */ + split = purple_account_user_split_new(_("Domain"), "chat.facebook.com", '@'); + purple_account_user_split_set_reverse(split, FALSE); + protocol->user_splits = g_list_append(protocol->user_splits, split); + + split = purple_account_user_split_new(_("Resource"), "", '/'); + purple_account_user_split_set_reverse(split, FALSE); + protocol->user_splits = g_list_append(protocol->user_splits, split); + +#define ADD_VALUE(list, desc, v) { \ + PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \ + kvp->key = g_strdup((desc)); \ + kvp->value = g_strdup((v)); \ + list = g_list_prepend(list, kvp); \ +} + + ADD_VALUE(encryption_values, _("Require encryption"), "require_tls"); + ADD_VALUE(encryption_values, _("Use encryption if available"), "opportunistic_tls"); + ADD_VALUE(encryption_values, _("Use old-style SSL"), "old_ssl"); +#if 0 + ADD_VALUE(encryption_values, "None", "none"); +#endif + encryption_values = g_list_reverse(encryption_values); + +#undef ADD_VALUE + + option = purple_account_option_list_new(_("Connection security"), "connection_security", encryption_values); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_bool_new( + _("Allow plaintext auth over unencrypted streams"), + "auth_plain_in_clear", FALSE); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_int_new(_("Connect port"), "port", 5222); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_string_new(_("Connect server"), + "connect_server", NULL); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_string_new(_("BOSH URL"), + "bosh_url", NULL); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + /* this should probably be part of global smiley theme settings later on, + shared with MSN */ + option = purple_account_option_bool_new(_("Show Custom Smileys"), + "custom_smileys", TRUE); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); +} + +static void +facebook_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->list_icon = facebook_list_icon; +} + +static void +facebook_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_moods = NULL; +} + +static void +facebook_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->register_user = NULL; + server_iface->unregister_user = NULL; + server_iface->add_buddy = NULL; + server_iface->remove_buddy = NULL; + server_iface->alias_buddy = NULL; + server_iface->group_buddy = NULL; + server_iface->rename_group = NULL; +} + +static void +facebook_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) +{ + privacy_iface->add_deny = NULL; + privacy_iface->rem_deny = NULL; +} + +static void +facebook_protocol_attention_iface_init(PurpleProtocolAttentionIface *attention_iface) +{ + attention_iface->send = NULL; + attention_iface->get_types = NULL; +} + +static void +facebook_protocol_media_iface_init(PurpleProtocolMediaIface *media_iface) +{ + media_iface->initiate_session = NULL; + media_iface->get_caps = NULL; +} + +static void +facebook_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->can_receive = NULL; + xfer_iface->send = NULL; + xfer_iface->new_xfer = NULL; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + FacebookProtocol, facebook_protocol, JABBER_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + facebook_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + facebook_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + facebook_protocol_privacy_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE, + facebook_protocol_attention_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_MEDIA_IFACE, + facebook_protocol_media_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + facebook_protocol_xfer_iface_init) +);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/facebook.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,54 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#ifndef _FACEBOOK_H_ +#define _FACEBOOK_H_ + +#include "jabber.h" + +#define FACEBOOK_TYPE_PROTOCOL (facebook_protocol_get_type()) +#define FACEBOOK_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FACEBOOK_TYPE_PROTOCOL, FacebookProtocol)) +#define FACEBOOK_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FACEBOOK_TYPE_PROTOCOL, FacebookProtocolClass)) +#define FACEBOOK_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FACEBOOK_TYPE_PROTOCOL)) +#define FACEBOOK_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FACEBOOK_TYPE_PROTOCOL)) +#define FACEBOOK_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FACEBOOK_TYPE_PROTOCOL, FacebookProtocolClass)) + +typedef struct _FacebookProtocol +{ + JabberProtocol parent; +} FacebookProtocol; + +typedef struct _FacebookProtocolClass +{ + JabberProtocolClass parent_class; +} FacebookProtocolClass; + +/** + * Registers the FacebookProtocol type in the type system. + */ +void facebook_protocol_register_type(PurplePlugin *plugin); + +/** + * Returns the GType for the FacebookProtocol object. + */ +G_MODULE_EXPORT GType facebook_protocol_get_type(void); + +#endif /* _FACEBOOK_H_ */
--- a/libpurple/protocols/jabber/google/google_p2p.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/google/google_p2p.c Fri Jan 31 18:02:20 2014 +0530 @@ -123,28 +123,7 @@ return candidate; } -GType -jingle_google_p2p_get_type(void) -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = { - sizeof(JingleGoogleP2PClass), - NULL, - NULL, - (GClassInitFunc) jingle_google_p2p_class_init, - NULL, - NULL, - sizeof(JingleGoogleP2P), - 0, - (GInstanceInitFunc) jingle_google_p2p_init, - NULL - }; - type = g_type_register_static(JINGLE_TYPE_TRANSPORT, "JingleGoogleP2P", &info, 0); - } - return type; -} +PURPLE_DEFINE_TYPE(JingleGoogleP2P, jingle_google_p2p, JINGLE_TYPE_TRANSPORT); static void jingle_google_p2p_class_init(JingleGoogleP2PClass *klass)
--- a/libpurple/protocols/jabber/google/google_p2p.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/google/google_p2p.h Fri Jan 31 18:02:20 2014 +0530 @@ -89,7 +89,12 @@ * * @return The Google P2P class's GType. */ -GType jingle_google_p2p_get_type(void); +G_MODULE_EXPORT GType jingle_google_p2p_get_type(void); + +/** + * Registers the JingleGoogleP2P type in the type system. + */ +void jingle_google_p2p_register_type(PurplePlugin *plugin); JingleGoogleP2PCandidate *jingle_google_p2p_candidate_new(const gchar *id, guint generation, const gchar *address, guint port, guint preference,
--- a/libpurple/protocols/jabber/google/google_presence.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/google/google_presence.c Fri Jan 31 18:02:20 2014 +0530 @@ -27,12 +27,12 @@ if (!js->googletalk) return; if (jbr->status && purple_str_has_prefix(jbr->status, "♫ ")) { - purple_prpl_got_user_status(purple_connection_get_account(js->gc), user, "tune", + purple_protocol_got_user_status(purple_connection_get_account(js->gc), user, "tune", PURPLE_TUNE_TITLE, jbr->status + strlen("♫ "), NULL); g_free(jbr->status); jbr->status = NULL; } else { - purple_prpl_got_user_status_deactive(purple_connection_get_account(js->gc), user, "tune"); + purple_protocol_got_user_status_deactive(purple_connection_get_account(js->gc), user, "tune"); } }
--- a/libpurple/protocols/jabber/google/google_roster.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/google/google_roster.c Fri Jan 31 18:02:20 2014 +0530 @@ -156,7 +156,7 @@ } } - purple_prpl_got_user_status(account, who, "offline", NULL); + purple_protocol_got_user_status(account, who, "offline", NULL); } void jabber_google_roster_rem_deny(JabberStream *js, const char *who)
--- a/libpurple/protocols/jabber/google/google_session.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/google/google_session.c Fri Jan 31 18:02:20 2014 +0530 @@ -357,7 +357,7 @@ purple_connection_get_account(js->gc), "fsrtpconference", session->remote_jid, TRUE); - purple_media_set_prpl_data(session_data->media, session); + purple_media_set_protocol_data(session_data->media, session); g_signal_connect_swapped(G_OBJECT(session_data->media), "candidates-prepared", @@ -584,7 +584,7 @@ purple_connection_get_account(js->gc), "fsrtpconference", session->remote_jid, FALSE); - purple_media_set_prpl_data(session_data->media, session); + purple_media_set_protocol_data(session_data->media, session); g_signal_connect_swapped(G_OBJECT(session_data->media), "candidates-prepared", @@ -829,7 +829,7 @@ purple_connection_get_account(js->gc)); for (; iter; iter = g_list_delete_link(iter, iter)) { GoogleSession *gsession = - purple_media_get_prpl_data(iter->data); + purple_media_get_protocol_data(iter->data); if (google_session_id_equal(&(gsession->id), &id)) { session = gsession; break;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/gtalk.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,131 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "internal.h" +#include "chat.h" +#include "core.h" +#include "plugins.h" + +#include "gtalk.h" + +static const char * +gtalk_list_icon(PurpleAccount *a, PurpleBuddy *b) +{ + return "google-talk"; +} + +static void +gtalk_protocol_init(PurpleProtocol *protocol) +{ + PurpleAccountUserSplit *split; + PurpleAccountOption *option; + GList *encryption_values = NULL; + + protocol->id = "prpl-gtalk"; + protocol->name = "Google Talk (XMPP)"; + + /* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */ + split = purple_account_user_split_new(_("Domain"), "gmail.com", '@'); + purple_account_user_split_set_reverse(split, FALSE); + protocol->user_splits = g_list_append(protocol->user_splits, split); + + split = purple_account_user_split_new(_("Resource"), "", '/'); + purple_account_user_split_set_reverse(split, FALSE); + protocol->user_splits = g_list_append(protocol->user_splits, split); + +#define ADD_VALUE(list, desc, v) { \ + PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \ + kvp->key = g_strdup((desc)); \ + kvp->value = g_strdup((v)); \ + list = g_list_prepend(list, kvp); \ +} + + ADD_VALUE(encryption_values, _("Require encryption"), "require_tls"); + ADD_VALUE(encryption_values, _("Use encryption if available"), "opportunistic_tls"); + ADD_VALUE(encryption_values, _("Use old-style SSL"), "old_ssl"); +#if 0 + ADD_VALUE(encryption_values, "None", "none"); +#endif + encryption_values = g_list_reverse(encryption_values); + +#undef ADD_VALUE + + option = purple_account_option_list_new(_("Connection security"), "connection_security", encryption_values); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_bool_new( + _("Allow plaintext auth over unencrypted streams"), + "auth_plain_in_clear", FALSE); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_int_new(_("Connect port"), "port", 5222); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_string_new(_("Connect server"), + "connect_server", NULL); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_string_new(_("File transfer proxies"), + "ft_proxies", + /* TODO: Is this an acceptable default? + * Also, keep this in sync as they add more servers */ + JABBER_DEFAULT_FT_PROXIES); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_string_new(_("BOSH URL"), + "bosh_url", NULL); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + /* this should probably be part of global smiley theme settings later on, + shared with MSN */ + option = purple_account_option_bool_new(_("Show Custom Smileys"), + "custom_smileys", TRUE); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); +} + +static void +gtalk_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->list_icon = gtalk_list_icon; +} + +static void +gtalk_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + /* disable xmpp functions not available for gtalk */ + server_iface->register_user = NULL; + server_iface->unregister_user = NULL; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + GTalkProtocol, gtalk_protocol, JABBER_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + gtalk_protocol_server_iface_init) +);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/gtalk.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,54 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#ifndef _GTALK_H_ +#define _GTALK_H_ + +#include "jabber.h" + +#define GTALK_TYPE_PROTOCOL (gtalk_protocol_get_type()) +#define GTALK_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTALK_TYPE_PROTOCOL, GTalkProtocol)) +#define GTALK_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTALK_TYPE_PROTOCOL, GTalkProtocolClass)) +#define GTALK_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTALK_TYPE_PROTOCOL)) +#define GTALK_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTALK_TYPE_PROTOCOL)) +#define GTALK_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTALK_TYPE_PROTOCOL, GTalkProtocolClass)) + +typedef struct _GTalkProtocol +{ + JabberProtocol parent; +} GTalkProtocol; + +typedef struct _GTalkProtocolClass +{ + JabberProtocolClass parent_class; +} GTalkProtocolClass; + +/** + * Registers the GTalkProtocol type in the type system. + */ +void gtalk_protocol_register_type(PurplePlugin *plugin); + +/** + * Returns the GType for the GTalkProtocol object. + */ +G_MODULE_EXPORT GType gtalk_protocol_get_type(void); + +#endif /* _GTALK_H_ */
--- a/libpurple/protocols/jabber/iq.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/iq.c Fri Jan 31 18:02:20 2014 +0530 @@ -368,7 +368,7 @@ return; } - signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), + signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js->gc), "jabber-receiving-iq", js->gc, iq_type, id, from, packet)); if (signal_return) { jabber_id_free(from_id); @@ -410,7 +410,7 @@ g_free(key); if (signal_ref > 0) { - signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), "jabber-watched-iq", + signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js->gc), "jabber-watched-iq", js->gc, iq_type, id, from, child)); if (signal_return) { jabber_id_free(from_id);
--- a/libpurple/protocols/jabber/jabber.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jabber.c Fri Jan 31 18:02:20 2014 +0530 @@ -37,7 +37,7 @@ #include "notify.h" #include "pluginpref.h" #include "proxy.h" -#include "prpl.h" +#include "protocol.h" #include "request.h" #include "server.h" #include "status.h" @@ -52,6 +52,7 @@ #include "data.h" #include "disco.h" #include "google/google.h" +#include "google/google_p2p.h" #include "google/google_roster.h" #include "google/google_session.h" #include "ibb.h" @@ -68,8 +69,13 @@ #include "xdata.h" #include "pep.h" #include "adhoccommands.h" +#include "xmpp.h" +#include "gtalk.h" +#include "facebook.h" #include "jingle/jingle.h" +#include "jingle/iceudp.h" +#include "jingle/rawudp.h" #include "jingle/rtp.h" #define PING_TIMEOUT 60 @@ -81,7 +87,11 @@ GList *jabber_features = NULL; GList *jabber_identities = NULL; -static GHashTable *jabber_cmds = NULL; /* PurplePlugin * => GSList of ids */ +static PurpleProtocol *xmpp_protocol = NULL; +static PurpleProtocol *gtalk_protocol = NULL; +static PurpleProtocol *facebook_protocol = NULL; + +static GHashTable *jabber_cmds = NULL; /* PurpleProtocol * => GSList of ids */ static gint plugin_ref = 0; @@ -332,7 +342,7 @@ const char *name; const char *xmlns; - purple_signal_emit(purple_connection_get_prpl(js->gc), "jabber-receiving-xmlnode", js->gc, packet); + purple_signal_emit(purple_connection_get_protocol(js->gc), "jabber-receiving-xmlnode", js->gc, packet); /* if the signal leaves us with a null packet, we're done */ if(NULL == *packet) @@ -514,7 +524,7 @@ g_free(text); } - purple_signal_emit(purple_connection_get_prpl(gc), "jabber-sending-text", gc, &data); + purple_signal_emit(purple_connection_get_protocol(gc), "jabber-sending-text", gc, &data); if (data == NULL) return; @@ -571,7 +581,7 @@ do_jabber_send_raw(js, data, len); } -int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len) +int jabber_protocol_send_raw(PurpleConnection *gc, const char *buf, int len) { JabberStream *js = purple_connection_get_protocol_data(gc); @@ -614,7 +624,7 @@ void jabber_send(JabberStream *js, PurpleXmlNode *packet) { - purple_signal_emit(purple_connection_get_prpl(js->gc), "jabber-sending-xmlnode", js->gc, &packet); + purple_signal_emit(purple_connection_get_protocol(js->gc), "jabber-sending-xmlnode", js->gc, &packet); } static gboolean jabber_keepalive_timeout(PurpleConnection *gc) @@ -1587,7 +1597,7 @@ /* TODO: As Will pointed out in IRC, after being notified by the core to * shutdown, we should async. wait for the server to send us the stream * termination before destorying everything. That seems like it would require - * changing the semantics of prpl->close(), so it's a good idea for 3.0.0. + * changing the semantics of protocol's close(), so it's a good idea for 3.0.0. */ void jabber_close(PurpleConnection *gc) { @@ -2528,10 +2538,10 @@ jabber_iq_send(iq); } -static void jabber_password_change(PurplePluginAction *action) +static void jabber_password_change(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = (PurpleConnection *) action->connection; JabberStream *js = purple_connection_get_protocol_data(gc); PurpleRequestFields *fields; PurpleRequestFieldGroup *group; @@ -2560,28 +2570,27 @@ purple_request_cpar_from_connection(gc), js); } -GList *jabber_actions(PurplePlugin *plugin, gpointer context) +GList *jabber_get_actions(PurpleConnection *gc) { - PurpleConnection *gc = (PurpleConnection *) context; JabberStream *js = purple_connection_get_protocol_data(gc); GList *m = NULL; - PurplePluginAction *act; - - act = purple_plugin_action_new(_("Set User Info..."), + PurpleProtocolAction *act; + + act = purple_protocol_action_new(_("Set User Info..."), jabber_setup_set_info); m = g_list_append(m, act); /* if (js->protocol_options & CHANGE_PASSWORD) { */ - act = purple_plugin_action_new(_("Change Password..."), + act = purple_protocol_action_new(_("Change Password..."), jabber_password_change); m = g_list_append(m, act); /* } */ - act = purple_plugin_action_new(_("Search for Users..."), + act = purple_protocol_action_new(_("Search for Users..."), jabber_user_search_begin); m = g_list_append(m, act); - purple_debug_info("jabber", "jabber_actions: have pep: %s\n", js->pep?"YES":"NO"); + purple_debug_info("jabber", "jabber_get_actions: have pep: %s\n", js->pep?"YES":"NO"); if(js->pep) jabber_pep_init_actions(&m); @@ -3613,129 +3622,129 @@ } } -static void jabber_register_commands(PurplePlugin *plugin) +static void jabber_register_commands(PurpleProtocol *protocol) { GSList *commands = NULL; PurpleCmdId id; - id = purple_cmd_register("config", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + id = purple_cmd_register("config", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-jabber", jabber_cmd_chat_config, _("config: Configure a chat room."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("configure", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + id = purple_cmd_register("configure", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-jabber", jabber_cmd_chat_config, _("configure: Configure a chat room."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("nick", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + id = purple_cmd_register("nick", "s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-jabber", jabber_cmd_chat_nick, _("nick <new nickname>: Change your nickname."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("part", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + id = purple_cmd_register("part", "s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_part, _("part [message]: Leave the room."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("register", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + id = purple_cmd_register("register", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-jabber", jabber_cmd_chat_register, _("register: Register with a chat room."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); /* XXX: there needs to be a core /topic cmd, methinks */ - id = purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + id = purple_cmd_register("topic", "s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_topic, _("topic [new topic]: View or change the topic."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("ban", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + id = purple_cmd_register("ban", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_ban, _("ban <user> [reason]: Ban a user from the room."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("affiliate", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + id = purple_cmd_register("affiliate", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_affiliate, _("affiliate <owner|admin|member|outcast|none> [nick1] [nick2] ...: Get the users with an affiliation or set users' affiliation with the room."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("role", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + id = purple_cmd_register("role", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_role, _("role <moderator|participant|visitor|none> [nick1] [nick2] ...: Get the users with a role or set users' role with the room."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + id = purple_cmd_register("invite", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_invite, _("invite <user> [message]: Invite a user to the room."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + id = purple_cmd_register("join", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_join, _("join: <room[@server]> [password]: Join a chat."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("kick", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + id = purple_cmd_register("kick", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_kick, _("kick <user> [reason]: Kick a user from the room."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + id = purple_cmd_register("msg", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-jabber", jabber_cmd_chat_msg, _("msg <user> <message>: Send a private message to another user."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("ping", "w", PURPLE_CMD_P_PRPL, + id = purple_cmd_register("ping", "w", PURPLE_CMD_P_PROTOCOL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | - PURPLE_CMD_FLAG_PRPL_ONLY, + PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-jabber", jabber_cmd_ping, _("ping <jid>: Ping a user/component/server."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("buzz", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY | + id = purple_cmd_register("buzz", "w", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_buzz, _("buzz: Buzz a user to get their attention"), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - id = purple_cmd_register("mood", "ws", PURPLE_CMD_P_PRPL, + id = purple_cmd_register("mood", "ws", PURPLE_CMD_P_PROTOCOL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | - PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, + PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_mood, _("mood <mood> [text]: Set current user mood"), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - g_hash_table_insert(jabber_cmds, plugin, commands); + g_hash_table_insert(jabber_cmds, protocol, commands); } static void cmds_free_func(gpointer value) @@ -3747,11 +3756,12 @@ } } -static void jabber_unregister_commands(PurplePlugin *plugin) +static void jabber_unregister_commands(PurpleProtocol *protocol) { - g_hash_table_remove(jabber_cmds, plugin); + g_hash_table_remove(jabber_cmds, protocol); } +#if 0 /* IPC functions */ /** @@ -3799,6 +3809,71 @@ /* send presence with new caps info for all connected accounts */ jabber_caps_broadcast_change(); } +#endif + +static PurpleAccount *find_acct(const char *protocol, const char *acct_id) +{ + PurpleAccount *acct = NULL; + + /* If we have a specific acct, use it */ + if (acct_id) { + acct = purple_accounts_find(acct_id, protocol); + if (acct && !purple_account_is_connected(acct)) + acct = NULL; + } else { /* Otherwise find an active account for the protocol */ + GList *l = purple_accounts_get_all(); + while (l) { + if (!strcmp(protocol, purple_account_get_protocol_id(l->data)) + && purple_account_is_connected(l->data)) { + acct = l->data; + break; + } + l = l->next; + } + } + + return acct; +} + +static gboolean xmpp_uri_handler(const char *proto, const char *user, GHashTable *params) +{ + char *acct_id = params ? g_hash_table_lookup(params, "account") : NULL; + PurpleAccount *acct; + + if (g_ascii_strcasecmp(proto, "xmpp")) + return FALSE; + + acct = find_acct(proto, acct_id); + + if (!acct) + return FALSE; + + /* xmpp:romeo@montague.net?message;subject=Test%20Message;body=Here%27s%20a%20test%20message */ + /* params is NULL if the URI has no '?' (or anything after it) */ + if (!params || g_hash_table_lookup_extended(params, "message", NULL, NULL)) { + char *body = g_hash_table_lookup(params, "body"); + if (user && *user) { + PurpleIMConversation *im = + purple_im_conversation_new(acct, user); + purple_conversation_present(PURPLE_CONVERSATION(im)); + if (body && *body) + purple_conversation_send_confirm(PURPLE_CONVERSATION(im), body); + } + } else if (g_hash_table_lookup_extended(params, "roster", NULL, NULL)) { + char *name = g_hash_table_lookup(params, "name"); + if (user && *user) + purple_blist_request_add_buddy(acct, user, NULL, name); + } else if (g_hash_table_lookup_extended(params, "join", NULL, NULL)) { + PurpleConnection *gc = purple_account_get_connection(acct); + if (user && *user) { + GHashTable *params = jabber_chat_info_defaults(gc, user); + jabber_chat_join(gc, params); + } + return TRUE; + } + + return FALSE; +} static void jabber_do_init(void) @@ -3947,15 +4022,16 @@ jabber_cmds = NULL; } -void jabber_plugin_init(PurplePlugin *plugin) +static void jabber_init_protocol(PurpleProtocol *protocol) { ++plugin_ref; if (plugin_ref == 1) jabber_do_init(); - jabber_register_commands(plugin); - + jabber_register_commands(protocol); + +#if 0 /* IPC functions */ purple_plugin_ipc_register(plugin, "contact_has_feature", PURPLE_CALLBACK(jabber_ipc_contact_has_feature), purple_marshal_BOOLEAN__POINTER_POINTER_POINTER, @@ -3979,31 +4055,32 @@ G_TYPE_NONE, 2, G_TYPE_STRING, /* node */ G_TYPE_STRING); /* namespace */ - - purple_signal_register(plugin, "jabber-register-namespace-watcher", +#endif + + purple_signal_register(protocol, "jabber-register-namespace-watcher", purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, G_TYPE_STRING, /* node */ G_TYPE_STRING); /* namespace */ - purple_signal_register(plugin, "jabber-unregister-namespace-watcher", + purple_signal_register(protocol, "jabber-unregister-namespace-watcher", purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, G_TYPE_STRING, /* node */ G_TYPE_STRING); /* namespace */ - purple_signal_connect(plugin, "jabber-register-namespace-watcher", - plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL); - purple_signal_connect(plugin, "jabber-unregister-namespace-watcher", - plugin, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL); - - - purple_signal_register(plugin, "jabber-receiving-xmlnode", + purple_signal_connect(protocol, "jabber-register-namespace-watcher", + protocol, PURPLE_CALLBACK(jabber_iq_signal_register), NULL); + purple_signal_connect(protocol, "jabber-unregister-namespace-watcher", + protocol, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL); + + + purple_signal_register(protocol, "jabber-receiving-xmlnode", purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, PURPLE_TYPE_CONNECTION, G_TYPE_POINTER); /* pointer to a PurpleXmlNode* */ - purple_signal_register(plugin, "jabber-sending-xmlnode", + purple_signal_register(protocol, "jabber-sending-xmlnode", purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, PURPLE_TYPE_CONNECTION, G_TYPE_POINTER); /* pointer to a PurpleXmlNode* */ @@ -4012,16 +4089,16 @@ * Do not remove this or the plugin will fail. Completely. You have been * warned! */ - purple_signal_connect_priority(plugin, "jabber-sending-xmlnode", - plugin, PURPLE_CALLBACK(jabber_send_signal_cb), + purple_signal_connect_priority(protocol, "jabber-sending-xmlnode", + protocol, PURPLE_CALLBACK(jabber_send_signal_cb), NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST); - purple_signal_register(plugin, "jabber-sending-text", + purple_signal_register(protocol, "jabber-sending-text", purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, PURPLE_TYPE_CONNECTION, G_TYPE_POINTER); /* pointer to a string */ - purple_signal_register(plugin, "jabber-receiving-message", + purple_signal_register(protocol, "jabber-receiving-message", purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER, G_TYPE_BOOLEAN, 6, PURPLE_TYPE_CONNECTION, @@ -4031,7 +4108,7 @@ G_TYPE_STRING, /* to */ PURPLE_TYPE_XMLNODE); - purple_signal_register(plugin, "jabber-receiving-iq", + purple_signal_register(protocol, "jabber-receiving-iq", purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, G_TYPE_BOOLEAN, 5, PURPLE_TYPE_CONNECTION, @@ -4040,7 +4117,7 @@ G_TYPE_STRING, /* from */ PURPLE_TYPE_XMLNODE); - purple_signal_register(plugin, "jabber-watched-iq", + purple_signal_register(protocol, "jabber-watched-iq", purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, G_TYPE_BOOLEAN, 5, PURPLE_TYPE_CONNECTION, @@ -4049,7 +4126,7 @@ G_TYPE_STRING, /* from */ PURPLE_TYPE_XMLNODE); /* child */ - purple_signal_register(plugin, "jabber-receiving-presence", + purple_signal_register(protocol, "jabber-receiving-presence", purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER, G_TYPE_BOOLEAN, 4, PURPLE_TYPE_CONNECTION, @@ -4058,16 +4135,251 @@ PURPLE_TYPE_XMLNODE); } -void jabber_plugin_uninit(PurplePlugin *plugin) +static void jabber_uninit_protocol(PurpleProtocol *protocol) { g_return_if_fail(plugin_ref > 0); - purple_signals_unregister_by_instance(plugin); + purple_signals_unregister_by_instance(protocol); +#if 0 purple_plugin_ipc_unregister_all(plugin); - - jabber_unregister_commands(plugin); +#endif + jabber_unregister_commands(protocol); --plugin_ref; if (plugin_ref == 0) jabber_do_uninit(); } + +static void +jabber_protocol_init(PurpleProtocol *protocol) +{ + protocol->id = "prpl-jabber"; + protocol->name = "XMPP"; + protocol->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | + OPT_PROTO_MAIL_CHECK | +#ifdef HAVE_CYRUS_SASL + OPT_PROTO_PASSWORD_OPTIONAL | +#endif + OPT_PROTO_SLASH_COMMANDS_NATIVE; + + protocol->icon_spec = purple_buddy_icon_spec_new("png", + 32, 32, 96, 96, 0, + PURPLE_ICON_SCALE_SEND | + PURPLE_ICON_SCALE_DISPLAY); +} + +static void +jabber_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = jabber_login; + klass->close = jabber_close; + klass->status_types = jabber_status_types; + klass->list_icon = jabber_list_icon; +} + +static void +jabber_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = jabber_get_actions; + client_iface->list_emblem = jabber_list_emblem; + client_iface->status_text = jabber_status_text; + client_iface->tooltip_text = jabber_tooltip_text; + client_iface->blist_node_menu = jabber_blist_node_menu; + client_iface->convo_closed = jabber_convo_closed; + client_iface->normalize = jabber_normalize; + client_iface->find_blist_chat = jabber_find_blist_chat; + client_iface->offline_message = jabber_offline_message; + client_iface->get_moods = jabber_get_moods; +} + +static void +jabber_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->register_user = jabber_register_account; + server_iface->unregister_user = jabber_unregister_account; + server_iface->set_info = jabber_set_info; + server_iface->get_info = jabber_buddy_get_info; + server_iface->set_status = jabber_set_status; + server_iface->set_idle = jabber_idle_set; + server_iface->add_buddy = jabber_roster_add_buddy; + server_iface->remove_buddy = jabber_roster_remove_buddy; + server_iface->keepalive = jabber_keepalive; + server_iface->alias_buddy = jabber_roster_alias_change; + server_iface->group_buddy = jabber_roster_group_change; + server_iface->rename_group = jabber_roster_group_rename; + server_iface->set_buddy_icon = jabber_set_buddy_icon; + server_iface->send_raw = jabber_protocol_send_raw; +} + +static void +jabber_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = jabber_message_send_im; + im_iface->send_typing = jabber_send_typing; +} + +static void +jabber_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->info = jabber_chat_info; + chat_iface->info_defaults = jabber_chat_info_defaults; + chat_iface->join = jabber_chat_join; + chat_iface->get_name = jabber_get_chat_name; + chat_iface->invite = jabber_chat_invite; + chat_iface->leave = jabber_chat_leave; + chat_iface->send = jabber_message_send_chat; + chat_iface->get_user_real_name = jabber_chat_user_real_name; + chat_iface->set_topic = jabber_chat_set_topic; +} + +static void +jabber_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) +{ + privacy_iface->add_deny = jabber_add_deny; + privacy_iface->rem_deny = jabber_rem_deny; +} + +static void +jabber_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface) +{ + roomlist_iface->get_list = jabber_roomlist_get_list; + roomlist_iface->cancel = jabber_roomlist_cancel; + roomlist_iface->room_serialize = jabber_roomlist_room_serialize; +} + +static void +jabber_protocol_attention_iface_init(PurpleProtocolAttentionIface *attention_iface) +{ + attention_iface->send = jabber_send_attention; + attention_iface->get_types = jabber_attention_types; +} + +static void +jabber_protocol_media_iface_init(PurpleProtocolMediaIface *media_iface) +{ + media_iface->initiate_session = jabber_initiate_media; + media_iface->get_caps = jabber_get_media_caps; +} + +static void +jabber_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->can_receive = jabber_can_receive_file; + xfer_iface->send = jabber_si_xfer_send; + xfer_iface->new_xfer = jabber_si_new_xfer; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + JabberProtocol, jabber_protocol, PURPLE_TYPE_PROTOCOL, G_TYPE_FLAG_ABSTRACT, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + jabber_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + jabber_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + jabber_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + jabber_protocol_chat_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + jabber_protocol_privacy_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE, + jabber_protocol_roomlist_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE, + jabber_protocol_attention_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_MEDIA_IFACE, + jabber_protocol_media_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + jabber_protocol_xfer_iface_init) +); + +static PurplePluginInfo * +plugin_query(GError **error) +{ + return purple_plugin_info_new( + "id", "prpl-xmpp", + "name", "XMPP Protocols", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("XMPP, GTalk, and Facebook Protocols Plugin"), + "description", N_("XMPP, GTalk, and Facebook Protocols Plugin"), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + jingle_session_register_type(plugin); + + jingle_transport_register_type(plugin); + jingle_iceudp_register_type(plugin); + jingle_rawudp_register_type(plugin); + jingle_google_p2p_register_type(plugin); + + jingle_content_register_type(plugin); + jingle_rtp_register_type(plugin); + + jabber_protocol_register_type(plugin); + + facebook_protocol_register_type(plugin); + gtalk_protocol_register_type(plugin); + xmpp_protocol_register_type(plugin); + + xmpp_protocol = purple_protocols_add(XMPP_TYPE_PROTOCOL, error); + if (!xmpp_protocol) + return FALSE; + + gtalk_protocol = purple_protocols_add(GTALK_TYPE_PROTOCOL, error); + if (!gtalk_protocol) + return FALSE; + + facebook_protocol = purple_protocols_add(FACEBOOK_TYPE_PROTOCOL, error); + if (!facebook_protocol) + return FALSE; + + purple_signal_connect(purple_get_core(), "uri-handler", xmpp_protocol, + PURPLE_CALLBACK(xmpp_uri_handler), NULL); + purple_signal_connect(purple_get_core(), "uri-handler", gtalk_protocol, + PURPLE_CALLBACK(xmpp_uri_handler), NULL); + purple_signal_connect(purple_get_core(), "uri-handler", facebook_protocol, + PURPLE_CALLBACK(xmpp_uri_handler), NULL); + + jabber_init_protocol(xmpp_protocol); + jabber_init_protocol(gtalk_protocol); + jabber_init_protocol(facebook_protocol); + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + jabber_uninit_protocol(facebook_protocol); + jabber_uninit_protocol(gtalk_protocol); + jabber_uninit_protocol(xmpp_protocol); + + if (!purple_protocols_remove(facebook_protocol, error)) + return FALSE; + + if (!purple_protocols_remove(gtalk_protocol, error)) + return FALSE; + + if (!purple_protocols_remove(xmpp_protocol, error)) + return FALSE; + + return TRUE; +} + +PURPLE_PLUGIN_INIT(jabber, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/protocols/jabber/jabber.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jabber.h Fri Jan 31 18:02:20 2014 +0530 @@ -63,6 +63,7 @@ #include "http.h" #include "media.h" #include "mediamanager.h" +#include "protocol.h" #include "roomlist.h" #include "sslconn.h" @@ -81,6 +82,13 @@ #define CAPS0115_NODE "https://pidgin.im/" +#define JABBER_TYPE_PROTOCOL (jabber_protocol_get_type()) +#define JABBER_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), JABBER_TYPE_PROTOCOL, JabberProtocol)) +#define JABBER_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), JABBER_TYPE_PROTOCOL, JabberProtocolClass)) +#define JABBER_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), JABBER_TYPE_PROTOCOL)) +#define JABBER_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), JABBER_TYPE_PROTOCOL)) +#define JABBER_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), JABBER_TYPE_PROTOCOL, JabberProtocolClass)) + #define JABBER_DEFAULT_REQUIRE_TLS "require_starttls" #define JABBER_DEFAULT_FT_PROXIES "proxy.eu.jabber.org" @@ -97,6 +105,16 @@ JABBER_STREAM_CONNECTED } JabberStreamState; +typedef struct _JabberProtocol +{ + PurpleProtocol parent; +} JabberProtocol; + +typedef struct _JabberProtocolClass +{ + PurpleProtocolClass parent_class; +} JabberProtocolClass; + struct _JabberStream { int fd; @@ -310,6 +328,11 @@ */ extern GList *jabber_identities; +/** + * Returns the GType for the JabberProtocol object. + */ +G_MODULE_EXPORT GType jabber_protocol_get_type(void); + void jabber_stream_features_parse(JabberStream *js, PurpleXmlNode *packet); void jabber_process_packet(JabberStream *js, PurpleXmlNode **packet); void jabber_send(JabberStream *js, PurpleXmlNode *data); @@ -379,7 +402,7 @@ */ void jabber_stream_restart_inactivity_timer(JabberStream *js); -/** PRPL functions */ +/** Protocol functions */ const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b); const char* jabber_list_emblem(PurpleBuddy *b); char *jabber_status_text(PurpleBuddy *b); @@ -403,8 +426,8 @@ void jabber_convo_closed(PurpleConnection *gc, const char *who); PurpleChat *jabber_find_blist_chat(PurpleAccount *account, const char *name); gboolean jabber_offline_message(const PurpleBuddy *buddy); -int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len); -GList *jabber_actions(PurplePlugin *plugin, gpointer context); +int jabber_protocol_send_raw(PurpleConnection *gc, const char *buf, int len); +GList *jabber_get_actions(PurpleConnection *gc); gboolean jabber_audio_enabled(JabberStream *js, const char *unused); gboolean jabber_video_enabled(JabberStream *js, const char *unused); @@ -413,7 +436,4 @@ PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who); gboolean jabber_can_receive_file(PurpleConnection *gc, const gchar *who); -void jabber_plugin_init(PurplePlugin *plugin); -void jabber_plugin_uninit(PurplePlugin *plugin); - #endif /* PURPLE_JABBER_H_ */
--- a/libpurple/protocols/jabber/jingle/content.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/content.c Fri Jan 31 18:02:20 2014 +0530 @@ -68,28 +68,7 @@ static GObjectClass *parent_class = NULL; static GParamSpec *properties[PROP_LAST]; -GType -jingle_content_get_type() -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = { - sizeof(JingleContentClass), - NULL, - NULL, - (GClassInitFunc) jingle_content_class_init, - NULL, - NULL, - sizeof(JingleContent), - 0, - (GInstanceInitFunc) jingle_content_init, - NULL - }; - type = g_type_register_static(G_TYPE_OBJECT, "JingleContent", &info, 0); - } - return type; -} +PURPLE_DEFINE_TYPE(JingleContent, jingle_content, G_TYPE_OBJECT); static void jingle_content_class_init (JingleContentClass *klass)
--- a/libpurple/protocols/jabber/jingle/content.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/content.h Fri Jan 31 18:02:20 2014 +0530 @@ -73,7 +73,12 @@ * * @return The content class's GType. */ -GType jingle_content_get_type(void); +G_MODULE_EXPORT GType jingle_content_get_type(void); + +/** + * Registers the JingleContent type in the type system. + */ +void jingle_content_register_type(PurplePlugin *plugin); JingleContent *jingle_content_create(const gchar *type, const gchar *creator, const gchar *disposition, const gchar *name,
--- a/libpurple/protocols/jabber/jingle/iceudp.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/iceudp.c Fri Jan 31 18:02:20 2014 +0530 @@ -136,28 +136,7 @@ return candidate; } -GType -jingle_iceudp_get_type() -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = { - sizeof(JingleIceUdpClass), - NULL, - NULL, - (GClassInitFunc) jingle_iceudp_class_init, - NULL, - NULL, - sizeof(JingleIceUdp), - 0, - (GInstanceInitFunc) jingle_iceudp_init, - NULL - }; - type = g_type_register_static(JINGLE_TYPE_TRANSPORT, "JingleIceUdp", &info, 0); - } - return type; -} +PURPLE_DEFINE_TYPE(JingleIceUdp, jingle_iceudp, JINGLE_TYPE_TRANSPORT); static void jingle_iceudp_class_init (JingleIceUdpClass *klass)
--- a/libpurple/protocols/jabber/jingle/iceudp.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/iceudp.h Fri Jan 31 18:02:20 2014 +0530 @@ -94,7 +94,12 @@ * * @return The iceudp class's GType. */ -GType jingle_iceudp_get_type(void); +G_MODULE_EXPORT GType jingle_iceudp_get_type(void); + +/** + * Registers the JingleIceUdp type in the type system. + */ +void jingle_iceudp_register_type(PurplePlugin *plugin); JingleIceUdpCandidate *jingle_iceudp_candidate_new(const gchar *id, guint component, const gchar *foundation, guint generation,
--- a/libpurple/protocols/jabber/jingle/rawudp.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/rawudp.c Fri Jan 31 18:02:20 2014 +0530 @@ -107,28 +107,7 @@ return candidate; } -GType -jingle_rawudp_get_type() -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = { - sizeof(JingleRawUdpClass), - NULL, - NULL, - (GClassInitFunc) jingle_rawudp_class_init, - NULL, - NULL, - sizeof(JingleRawUdp), - 0, - (GInstanceInitFunc) jingle_rawudp_init, - NULL - }; - type = g_type_register_static(JINGLE_TYPE_TRANSPORT, "JingleRawUdp", &info, 0); - } - return type; -} +PURPLE_DEFINE_TYPE(JingleRawUdp, jingle_rawudp, JINGLE_TYPE_TRANSPORT); static void jingle_rawudp_class_init (JingleRawUdpClass *klass)
--- a/libpurple/protocols/jabber/jingle/rawudp.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/rawudp.h Fri Jan 31 18:02:20 2014 +0530 @@ -84,7 +84,12 @@ * * @return The rawudp class's GType. */ -GType jingle_rawudp_get_type(void); +G_MODULE_EXPORT GType jingle_rawudp_get_type(void); + +/** + * Registers the JingleRawUdp type in the type system. + */ +void jingle_rawudp_register_type(PurplePlugin *plugin); JingleRawUdpCandidate *jingle_rawudp_candidate_new(const gchar *id, guint generation, guint component, const gchar *ip, guint port);
--- a/libpurple/protocols/jabber/jingle/rtp.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/rtp.c Fri Jan 31 18:02:20 2014 +0530 @@ -76,28 +76,7 @@ static JingleContentClass *parent_class = NULL; static GParamSpec *properties[PROP_LAST]; -GType -jingle_rtp_get_type() -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = { - sizeof(JingleRtpClass), - NULL, - NULL, - (GClassInitFunc) jingle_rtp_class_init, - NULL, - NULL, - sizeof(JingleRtp), - 0, - (GInstanceInitFunc) jingle_rtp_init, - NULL - }; - type = g_type_register_static(JINGLE_TYPE_CONTENT, "JingleRtp", &info, 0); - } - return type; -} +PURPLE_DEFINE_TYPE(JingleRtp, jingle_rtp, JINGLE_TYPE_CONTENT); static void jingle_rtp_class_init (JingleRtpClass *klass) @@ -220,7 +199,7 @@ for (; iter; iter = g_list_delete_link(iter, iter)) { JingleSession *media_session = - purple_media_get_prpl_data(iter->data); + purple_media_get_protocol_data(iter->data); if (media_session == session) { media = iter->data; break; @@ -430,7 +409,7 @@ return NULL; } - purple_media_set_prpl_data(media, session); + purple_media_set_protocol_data(media, session); /* connect callbacks */ g_signal_connect(G_OBJECT(media), "candidates-prepared",
--- a/libpurple/protocols/jabber/jingle/rtp.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/rtp.h Fri Jan 31 18:02:20 2014 +0530 @@ -70,7 +70,12 @@ * * @return The rtp class's GType. */ -GType jingle_rtp_get_type(void); +G_MODULE_EXPORT GType jingle_rtp_get_type(void); + +/** + * Registers the JingleRtp type in the type system. + */ +void jingle_rtp_register_type(PurplePlugin *plugin); gchar *jingle_rtp_get_media_type(JingleContent *content); gchar *jingle_rtp_get_ssrc(JingleContent *content);
--- a/libpurple/protocols/jabber/jingle/session.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/session.c Fri Jan 31 18:02:20 2014 +0530 @@ -68,28 +68,7 @@ static GObjectClass *parent_class = NULL; static GParamSpec *properties[PROP_LAST]; -GType -jingle_session_get_type() -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = { - sizeof(JingleSessionClass), - NULL, - NULL, - (GClassInitFunc) jingle_session_class_init, - NULL, - NULL, - sizeof(JingleSession), - 0, - (GInstanceInitFunc) jingle_session_init, - NULL - }; - type = g_type_register_static(G_TYPE_OBJECT, "JingleSession", &info, 0); - } - return type; -} +PURPLE_DEFINE_TYPE(JingleSession, jingle_session, G_TYPE_OBJECT); static void jingle_session_class_init (JingleSessionClass *klass)
--- a/libpurple/protocols/jabber/jingle/session.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/session.h Fri Jan 31 18:02:20 2014 +0530 @@ -67,7 +67,12 @@ * * @return The session class's GType. */ -GType jingle_session_get_type(void); +G_MODULE_EXPORT GType jingle_session_get_type(void); + +/** + * Registers the JingleSession type in the type system. + */ +void jingle_session_register_type(PurplePlugin *plugin); JingleSession *jingle_session_create(JabberStream *js, const gchar *sid, const gchar *local_jid, const gchar *remote_jid,
--- a/libpurple/protocols/jabber/jingle/transport.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/transport.c Fri Jan 31 18:02:20 2014 +0530 @@ -53,28 +53,7 @@ PROP_0, }; -GType -jingle_transport_get_type() -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = { - sizeof(JingleTransportClass), - NULL, - NULL, - (GClassInitFunc) jingle_transport_class_init, - NULL, - NULL, - sizeof(JingleTransport), - 0, - (GInstanceInitFunc) jingle_transport_init, - NULL - }; - type = g_type_register_static(G_TYPE_OBJECT, "JingleTransport", &info, 0); - } - return type; -} +PURPLE_DEFINE_TYPE(JingleTransport, jingle_transport, G_TYPE_OBJECT); static void jingle_transport_class_init (JingleTransportClass *klass)
--- a/libpurple/protocols/jabber/jingle/transport.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/jingle/transport.h Fri Jan 31 18:02:20 2014 +0530 @@ -71,7 +71,12 @@ * * @return The transport class's GType. */ -GType jingle_transport_get_type(void); +G_MODULE_EXPORT GType jingle_transport_get_type(void); + +/** + * Registers the JingleTransport type in the type system. + */ +void jingle_transport_register_type(PurplePlugin *plugin); JingleTransport *jingle_transport_create(const gchar *type); const gchar *jingle_transport_get_transport_type(JingleTransport *transport);
--- a/libpurple/protocols/jabber/libfacebook.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,327 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -/* libfacebook is the Facebook XMPP protocol plugin. It is linked against - * libjabbercommon, which may be used to support other protocols (Bonjour) - * which may need to share code. - */ - -#include "internal.h" - -#include "accountopt.h" -#include "core.h" -#include "debug.h" -#include "version.h" - -#include "iq.h" -#include "jabber.h" -#include "chat.h" -#include "disco.h" -#include "message.h" -#include "roster.h" -#include "si.h" -#include "message.h" -#include "presence.h" -#include "google/google.h" -#include "pep.h" -#include "usermood.h" -#include "usertune.h" -#include "caps.h" -#include "data.h" -#include "ibb.h" - -static const char * -facebook_list_icon(PurpleAccount *a, PurpleBuddy *b) -{ - return "facebook"; -} - -static PurplePlugin *my_protocol = NULL; - -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_MAIL_CHECK | -#ifdef HAVE_CYRUS_SASL - OPT_PROTO_PASSWORD_OPTIONAL | -#endif - OPT_PROTO_SLASH_COMMANDS_NATIVE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ - facebook_list_icon, /* list_icon */ - jabber_list_emblem, /* list_emblems */ - jabber_status_text, /* status_text */ - jabber_tooltip_text, /* tooltip_text */ - jabber_status_types, /* status_types */ - jabber_blist_node_menu, /* blist_node_menu */ - jabber_chat_info, /* chat_info */ - jabber_chat_info_defaults, /* chat_info_defaults */ - jabber_login, /* login */ - jabber_close, /* close */ - jabber_message_send_im, /* send_im */ - jabber_set_info, /* set_info */ - jabber_send_typing, /* send_typing */ - jabber_buddy_get_info, /* get_info */ - jabber_set_status, /* set_status */ - jabber_idle_set, /* set_idle */ - NULL, /* change_passwd */ - NULL, /* add_buddy */ - NULL, /* add_buddies */ - NULL, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* rem_permit */ - NULL, /* rem_deny */ - NULL, /* set_permit_deny */ - jabber_chat_join, /* join_chat */ - NULL, /* reject_chat */ - jabber_get_chat_name, /* get_chat_name */ - jabber_chat_invite, /* chat_invite */ - jabber_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - jabber_message_send_chat, /* chat_send */ - jabber_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - NULL, /* buddy_free */ - jabber_convo_closed, /* convo_closed */ - jabber_normalize, /* normalize */ - jabber_set_buddy_icon, /* set_buddy_icon */ - NULL, /* remove_group */ - jabber_chat_user_real_name, /* get_cb_real_name */ - jabber_chat_set_topic, /* set_chat_topic */ - jabber_find_blist_chat, /* find_blist_chat */ - jabber_roomlist_get_list, /* roomlist_get_list */ - jabber_roomlist_cancel, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ - jabber_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - jabber_prpl_send_raw, /* send_raw */ - jabber_roomlist_room_serialize, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* attention_types */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL /* get_max_message_size */ -}; - -static gboolean load_plugin(PurplePlugin *plugin) -{ - jabber_plugin_init(plugin); - - return TRUE; -} - -static gboolean unload_plugin(PurplePlugin *plugin) -{ - jabber_plugin_uninit(plugin); - - return TRUE; -} - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-facebook-xmpp", /**< id */ - "Facebook (XMPP)", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Facebook XMPP Protocol Plugin"), - /** description */ - N_("Facebook XMPP Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - load_plugin, /**< load */ - unload_plugin, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - jabber_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurpleAccount *find_acct(const char *prpl, const char *acct_id) -{ - PurpleAccount *acct = NULL; - - /* If we have a specific acct, use it */ - if (acct_id) { - acct = purple_accounts_find(acct_id, prpl); - if (acct && !purple_account_is_connected(acct)) - acct = NULL; - } else { /* Otherwise find an active account for the protocol */ - GList *l = purple_accounts_get_all(); - while (l) { - if (!strcmp(prpl, purple_account_get_protocol_id(l->data)) - && purple_account_is_connected(l->data)) { - acct = l->data; - break; - } - l = l->next; - } - } - - return acct; -} - -static gboolean xmpp_uri_handler(const char *proto, const char *user, GHashTable *params) -{ - char *acct_id = params ? g_hash_table_lookup(params, "account") : NULL; - PurpleAccount *acct; - - if (g_ascii_strcasecmp(proto, "xmpp")) - return FALSE; - - acct = find_acct(purple_plugin_get_id(my_protocol), acct_id); - - if (!acct) - return FALSE; - - /* xmpp:romeo@montague.net?message;subject=Test%20Message;body=Here%27s%20a%20test%20message */ - /* params is NULL if the URI has no '?' (or anything after it) */ - if (!params || g_hash_table_lookup_extended(params, "message", NULL, NULL)) { - char *body = g_hash_table_lookup(params, "body"); - if (user && *user) { - PurpleIMConversation *im = - purple_im_conversation_new(acct, user); - purple_conversation_present(PURPLE_CONVERSATION(im)); - if (body && *body) - purple_conversation_send_confirm(PURPLE_CONVERSATION(im), body); - } - } else if (g_hash_table_lookup_extended(params, "roster", NULL, NULL)) { - char *name = g_hash_table_lookup(params, "name"); - if (user && *user) - purple_blist_request_add_buddy(acct, user, NULL, name); - } else if (g_hash_table_lookup_extended(params, "join", NULL, NULL)) { - PurpleConnection *gc = purple_account_get_connection(acct); - if (user && *user) { - GHashTable *params = jabber_chat_info_defaults(gc, user); - jabber_chat_join(gc, params); - } - return TRUE; - } - - return FALSE; -} - - -static void -init_plugin(PurplePlugin *plugin) -{ - PurpleAccountUserSplit *split; - PurpleAccountOption *option; - GList *encryption_values = NULL; - - /* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */ - split = purple_account_user_split_new(_("Domain"), "chat.facebook.com", '@'); - purple_account_user_split_set_reverse(split, FALSE); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); - - split = purple_account_user_split_new(_("Resource"), "", '/'); - purple_account_user_split_set_reverse(split, FALSE); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); - -#define ADD_VALUE(list, desc, v) { \ - PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \ - kvp->key = g_strdup((desc)); \ - kvp->value = g_strdup((v)); \ - list = g_list_prepend(list, kvp); \ -} - - ADD_VALUE(encryption_values, _("Use encryption if available"), "opportunistic_tls"); - ADD_VALUE(encryption_values, _("Require encryption"), "require_tls"); - ADD_VALUE(encryption_values, _("Use old-style SSL"), "old_ssl"); -#if 0 - ADD_VALUE(encryption_values, "None", "none"); -#endif - encryption_values = g_list_reverse(encryption_values); - -#undef ADD_VALUE - - option = purple_account_option_list_new(_("Connection security"), "connection_security", encryption_values); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_bool_new( - _("Allow plaintext auth over unencrypted streams"), - "auth_plain_in_clear", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_int_new(_("Connect port"), "port", 5222); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_string_new(_("Connect server"), - "connect_server", NULL); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_string_new(_("BOSH URL"), - "bosh_url", NULL); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - /* this should probably be part of global smiley theme settings later on, - shared with MSN */ - option = purple_account_option_bool_new(_("Show Custom Smileys"), - "custom_smileys", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - my_protocol = plugin; - - purple_signal_connect(purple_get_core(), "uri-handler", plugin, - PURPLE_CALLBACK(xmpp_uri_handler), NULL); -} - -PURPLE_INIT_PLUGIN(facebookxmpp, init_plugin, info); -
--- a/libpurple/protocols/jabber/libgtalk.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,335 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -/* libgtalk is the Google Talk XMPP protocol plugin. It is linked against - * libjabbercommon, which may be used to support other protocols (Bonjour) which - * may need to share code. - */ - -#include "internal.h" - -#include "accountopt.h" -#include "core.h" -#include "debug.h" -#include "version.h" - -#include "iq.h" -#include "jabber.h" -#include "chat.h" -#include "disco.h" -#include "message.h" -#include "roster.h" -#include "si.h" -#include "message.h" -#include "presence.h" -#include "google/google.h" -#include "pep.h" -#include "usermood.h" -#include "usertune.h" -#include "caps.h" -#include "data.h" -#include "ibb.h" - -static const char * -gtalk_list_icon(PurpleAccount *a, PurpleBuddy *b) -{ - return "google-talk"; -} - -static PurplePlugin *my_protocol = NULL; - -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_MAIL_CHECK | -#ifdef HAVE_CYRUS_SASL - OPT_PROTO_PASSWORD_OPTIONAL | -#endif - OPT_PROTO_SLASH_COMMANDS_NATIVE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ - gtalk_list_icon, /* list_icon */ - jabber_list_emblem, /* list_emblems */ - jabber_status_text, /* status_text */ - jabber_tooltip_text, /* tooltip_text */ - jabber_status_types, /* status_types */ - jabber_blist_node_menu, /* blist_node_menu */ - jabber_chat_info, /* chat_info */ - jabber_chat_info_defaults, /* chat_info_defaults */ - jabber_login, /* login */ - jabber_close, /* close */ - jabber_message_send_im, /* send_im */ - jabber_set_info, /* set_info */ - jabber_send_typing, /* send_typing */ - jabber_buddy_get_info, /* get_info */ - jabber_set_status, /* set_status */ - jabber_idle_set, /* set_idle */ - NULL, /* change_passwd */ - jabber_roster_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - jabber_roster_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - jabber_add_deny, /* add_deny */ - NULL, /* rem_permit */ - jabber_rem_deny, /* rem_deny */ - NULL, /* set_permit_deny */ - jabber_chat_join, /* join_chat */ - NULL, /* reject_chat */ - jabber_get_chat_name, /* get_chat_name */ - jabber_chat_invite, /* chat_invite */ - jabber_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - jabber_message_send_chat, /* chat_send */ - jabber_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - jabber_roster_alias_change, /* alias_buddy */ - jabber_roster_group_change, /* group_buddy */ - jabber_roster_group_rename, /* rename_group */ - NULL, /* buddy_free */ - jabber_convo_closed, /* convo_closed */ - jabber_normalize, /* normalize */ - jabber_set_buddy_icon, /* set_buddy_icon */ - NULL, /* remove_group */ - jabber_chat_user_real_name, /* get_cb_real_name */ - jabber_chat_set_topic, /* set_chat_topic */ - jabber_find_blist_chat, /* find_blist_chat */ - jabber_roomlist_get_list, /* roomlist_get_list */ - jabber_roomlist_cancel, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - jabber_can_receive_file, /* can_receive_file */ - jabber_si_xfer_send, /* send_file */ - jabber_si_new_xfer, /* new_xfer */ - jabber_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - jabber_prpl_send_raw, /* send_raw */ - jabber_roomlist_room_serialize, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - jabber_send_attention, /* send_attention */ - jabber_attention_types, /* attention_types */ - NULL, /* get_account_text_table */ - jabber_initiate_media, /* initiate_media */ - jabber_get_media_caps, /* get_media_caps */ - jabber_get_moods, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL /* get_max_message_size */ -}; - -static gboolean load_plugin(PurplePlugin *plugin) -{ - jabber_plugin_init(plugin); - - return TRUE; -} - -static gboolean unload_plugin(PurplePlugin *plugin) -{ - jabber_plugin_uninit(plugin); - - return TRUE; -} - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-gtalk", /**< id */ - "Google Talk (XMPP)", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Google Talk Protocol Plugin"), - /** description */ - N_("Google Talk Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - load_plugin, /**< load */ - unload_plugin, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - jabber_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurpleAccount *find_acct(const char *prpl, const char *acct_id) -{ - PurpleAccount *acct = NULL; - - /* If we have a specific acct, use it */ - if (acct_id) { - acct = purple_accounts_find(acct_id, prpl); - if (acct && !purple_account_is_connected(acct)) - acct = NULL; - } else { /* Otherwise find an active account for the protocol */ - GList *l = purple_accounts_get_all(); - while (l) { - if (!strcmp(prpl, purple_account_get_protocol_id(l->data)) - && purple_account_is_connected(l->data)) { - acct = l->data; - break; - } - l = l->next; - } - } - - return acct; -} - -static gboolean xmpp_uri_handler(const char *proto, const char *user, GHashTable *params) -{ - char *acct_id = params ? g_hash_table_lookup(params, "account") : NULL; - PurpleAccount *acct; - - if (g_ascii_strcasecmp(proto, "xmpp")) - return FALSE; - - acct = find_acct(purple_plugin_get_id(my_protocol), acct_id); - - if (!acct) - return FALSE; - - /* xmpp:romeo@montague.net?message;subject=Test%20Message;body=Here%27s%20a%20test%20message */ - /* params is NULL if the URI has no '?' (or anything after it) */ - if (!params || g_hash_table_lookup_extended(params, "message", NULL, NULL)) { - char *body = g_hash_table_lookup(params, "body"); - if (user && *user) { - PurpleIMConversation *im = - purple_im_conversation_new(acct, user); - purple_conversation_present(PURPLE_CONVERSATION(im)); - if (body && *body) - purple_conversation_send_confirm(PURPLE_CONVERSATION(im), body); - } - } else if (g_hash_table_lookup_extended(params, "roster", NULL, NULL)) { - char *name = g_hash_table_lookup(params, "name"); - if (user && *user) - purple_blist_request_add_buddy(acct, user, NULL, name); - } else if (g_hash_table_lookup_extended(params, "join", NULL, NULL)) { - PurpleConnection *gc = purple_account_get_connection(acct); - if (user && *user) { - GHashTable *params = jabber_chat_info_defaults(gc, user); - jabber_chat_join(gc, params); - } - return TRUE; - } - - return FALSE; -} - - -static void -init_plugin(PurplePlugin *plugin) -{ - PurpleAccountUserSplit *split; - PurpleAccountOption *option; - GList *encryption_values = NULL; - - /* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */ - split = purple_account_user_split_new(_("Domain"), "gmail.com", '@'); - purple_account_user_split_set_reverse(split, FALSE); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); - - split = purple_account_user_split_new(_("Resource"), "", '/'); - purple_account_user_split_set_reverse(split, FALSE); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); - -#define ADD_VALUE(list, desc, v) { \ - PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \ - kvp->key = g_strdup((desc)); \ - kvp->value = g_strdup((v)); \ - list = g_list_prepend(list, kvp); \ -} - - ADD_VALUE(encryption_values, _("Require encryption"), "require_tls"); - ADD_VALUE(encryption_values, _("Use encryption if available"), "opportunistic_tls"); - ADD_VALUE(encryption_values, _("Use old-style SSL"), "old_ssl"); -#if 0 - ADD_VALUE(encryption_values, "None", "none"); -#endif - encryption_values = g_list_reverse(encryption_values); - -#undef ADD_VALUE - - option = purple_account_option_list_new(_("Connection security"), "connection_security", encryption_values); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_bool_new( - _("Allow plaintext auth over unencrypted streams"), - "auth_plain_in_clear", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_int_new(_("Connect port"), "port", 5222); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_string_new(_("Connect server"), - "connect_server", NULL); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_string_new(_("File transfer proxies"), - "ft_proxies", - /* TODO: Is this an acceptable default? - * Also, keep this in sync as they add more servers */ - JABBER_DEFAULT_FT_PROXIES); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_string_new(_("BOSH URL"), - "bosh_url", NULL); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - /* this should probably be part of global smiley theme settings later on, - shared with MSN */ - option = purple_account_option_bool_new(_("Show Custom Smileys"), - "custom_smileys", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - my_protocol = plugin; - - purple_signal_connect(purple_get_core(), "uri-handler", plugin, - PURPLE_CALLBACK(xmpp_uri_handler), NULL); -} - -PURPLE_INIT_PLUGIN(gtalk, init_plugin, info); -
--- a/libpurple/protocols/jabber/libxmpp.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,331 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -/* libxmpp is the XMPP protocol plugin. It is linked against libjabbercommon, - * which may be used to support other protocols (Bonjour) which may need to - * share code. - */ - -#include "internal.h" - -#include "accountopt.h" -#include "core.h" -#include "debug.h" -#include "version.h" - -#include "iq.h" -#include "jabber.h" -#include "chat.h" -#include "disco.h" -#include "message.h" -#include "roster.h" -#include "si.h" -#include "message.h" -#include "presence.h" -#include "google/google.h" -#include "pep.h" -#include "usermood.h" -#include "usertune.h" -#include "caps.h" -#include "data.h" -#include "ibb.h" - -static PurplePlugin *my_protocol = NULL; - -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_MAIL_CHECK | -#ifdef HAVE_CYRUS_SASL - OPT_PROTO_PASSWORD_OPTIONAL | -#endif - OPT_PROTO_SLASH_COMMANDS_NATIVE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ - jabber_list_icon, /* list_icon */ - jabber_list_emblem, /* list_emblems */ - jabber_status_text, /* status_text */ - jabber_tooltip_text, /* tooltip_text */ - jabber_status_types, /* status_types */ - jabber_blist_node_menu, /* blist_node_menu */ - jabber_chat_info, /* chat_info */ - jabber_chat_info_defaults, /* chat_info_defaults */ - jabber_login, /* login */ - jabber_close, /* close */ - jabber_message_send_im, /* send_im */ - jabber_set_info, /* set_info */ - jabber_send_typing, /* send_typing */ - jabber_buddy_get_info, /* get_info */ - jabber_set_status, /* set_status */ - jabber_idle_set, /* set_idle */ - NULL, /* change_passwd */ - jabber_roster_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - jabber_roster_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - jabber_add_deny, /* add_deny */ - NULL, /* rem_permit */ - jabber_rem_deny, /* rem_deny */ - NULL, /* set_permit_deny */ - jabber_chat_join, /* join_chat */ - NULL, /* reject_chat */ - jabber_get_chat_name, /* get_chat_name */ - jabber_chat_invite, /* chat_invite */ - jabber_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - jabber_message_send_chat, /* chat_send */ - jabber_keepalive, /* keepalive */ - jabber_register_account, /* register_user */ - NULL, /* get_cb_info */ - jabber_roster_alias_change, /* alias_buddy */ - jabber_roster_group_change, /* group_buddy */ - jabber_roster_group_rename, /* rename_group */ - NULL, /* buddy_free */ - jabber_convo_closed, /* convo_closed */ - jabber_normalize, /* normalize */ - jabber_set_buddy_icon, /* set_buddy_icon */ - NULL, /* remove_group */ - jabber_chat_user_real_name, /* get_cb_real_name */ - jabber_chat_set_topic, /* set_chat_topic */ - jabber_find_blist_chat, /* find_blist_chat */ - jabber_roomlist_get_list, /* roomlist_get_list */ - jabber_roomlist_cancel, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - jabber_can_receive_file, /* can_receive_file */ - jabber_si_xfer_send, /* send_file */ - jabber_si_new_xfer, /* new_xfer */ - jabber_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - jabber_prpl_send_raw, /* send_raw */ - jabber_roomlist_room_serialize, /* roomlist_room_serialize */ - jabber_unregister_account, /* unregister_user */ - jabber_send_attention, /* send_attention */ - jabber_attention_types, /* attention_types */ - NULL, /* get_account_text_table */ - jabber_initiate_media, /* initiate_media */ - jabber_get_media_caps, /* get_media_caps */ - jabber_get_moods, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL /* get_max_message_size */ -}; - -static gboolean load_plugin(PurplePlugin *plugin) -{ - jabber_plugin_init(plugin); - - return TRUE; -} - -static gboolean unload_plugin(PurplePlugin *plugin) -{ - jabber_plugin_uninit(plugin); - - return TRUE; -} - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-jabber", /**< id */ - "XMPP", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("XMPP Protocol Plugin"), - /** description */ - N_("XMPP Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - load_plugin, /**< load */ - unload_plugin, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - jabber_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurpleAccount *find_acct(const char *prpl, const char *acct_id) -{ - PurpleAccount *acct = NULL; - - /* If we have a specific acct, use it */ - if (acct_id) { - acct = purple_accounts_find(acct_id, prpl); - if (acct && !purple_account_is_connected(acct)) - acct = NULL; - } else { /* Otherwise find an active account for the protocol */ - GList *l = purple_accounts_get_all(); - while (l) { - if (!strcmp(prpl, purple_account_get_protocol_id(l->data)) - && purple_account_is_connected(l->data)) { - acct = l->data; - break; - } - l = l->next; - } - } - - return acct; -} - -static gboolean xmpp_uri_handler(const char *proto, const char *user, GHashTable *params) -{ - char *acct_id = params ? g_hash_table_lookup(params, "account") : NULL; - PurpleAccount *acct; - - if (g_ascii_strcasecmp(proto, "xmpp")) - return FALSE; - - acct = find_acct(purple_plugin_get_id(my_protocol), acct_id); - - if (!acct) - return FALSE; - - /* xmpp:romeo@montague.net?message;subject=Test%20Message;body=Here%27s%20a%20test%20message */ - /* params is NULL if the URI has no '?' (or anything after it) */ - if (!params || g_hash_table_lookup_extended(params, "message", NULL, NULL)) { - char *body = g_hash_table_lookup(params, "body"); - if (user && *user) { - PurpleIMConversation *im = - purple_im_conversation_new(acct, user); - purple_conversation_present(PURPLE_CONVERSATION(im)); - if (body && *body) - purple_conversation_send_confirm(PURPLE_CONVERSATION(im), body); - } - } else if (g_hash_table_lookup_extended(params, "roster", NULL, NULL)) { - char *name = g_hash_table_lookup(params, "name"); - if (user && *user) - purple_blist_request_add_buddy(acct, user, NULL, name); - } else if (g_hash_table_lookup_extended(params, "join", NULL, NULL)) { - PurpleConnection *gc = purple_account_get_connection(acct); - if (user && *user) { - GHashTable *params = jabber_chat_info_defaults(gc, user); - jabber_chat_join(gc, params); - } - return TRUE; - } - - return FALSE; -} - - -static void -init_plugin(PurplePlugin *plugin) -{ - PurpleAccountUserSplit *split; - PurpleAccountOption *option; - GList *encryption_values = NULL; - - /* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */ - split = purple_account_user_split_new(_("Domain"), NULL, '@'); - purple_account_user_split_set_reverse(split, FALSE); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); - - split = purple_account_user_split_new(_("Resource"), "", '/'); - purple_account_user_split_set_reverse(split, FALSE); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); - -#define ADD_VALUE(list, desc, v) { \ - PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \ - kvp->key = g_strdup((desc)); \ - kvp->value = g_strdup((v)); \ - list = g_list_prepend(list, kvp); \ -} - - ADD_VALUE(encryption_values, _("Require encryption"), "require_tls"); - ADD_VALUE(encryption_values, _("Use encryption if available"), "opportunistic_tls"); - ADD_VALUE(encryption_values, _("Use old-style SSL"), "old_ssl"); -#if 0 - ADD_VALUE(encryption_values, "None", "none"); -#endif - encryption_values = g_list_reverse(encryption_values); - -#undef ADD_VALUE - - option = purple_account_option_list_new(_("Connection security"), "connection_security", encryption_values); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_bool_new( - _("Allow plaintext auth over unencrypted streams"), - "auth_plain_in_clear", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_int_new(_("Connect port"), "port", 5222); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_string_new(_("Connect server"), - "connect_server", NULL); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_string_new(_("File transfer proxies"), - "ft_proxies", - /* TODO: Is this an acceptable default? - * Also, keep this in sync as they add more servers */ - JABBER_DEFAULT_FT_PROXIES); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_string_new(_("BOSH URL"), - "bosh_url", NULL); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - /* this should probably be part of global smiley theme settings later on, - shared with MSN */ - option = purple_account_option_bool_new(_("Show Custom Smileys"), - "custom_smileys", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - my_protocol = plugin; - - purple_prefs_remove("/plugins/prpl/jabber"); - - purple_signal_connect(purple_get_core(), "uri-handler", plugin, - PURPLE_CALLBACK(xmpp_uri_handler), NULL); -} - - -PURPLE_INIT_PLUGIN(jabber, init_plugin, info);
--- a/libpurple/protocols/jabber/message.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/message.c Fri Jan 31 18:02:20 2014 +0530 @@ -313,7 +313,7 @@ return; /* Do not accept buzzes from unknown people */ /* xmpp only has 1 attention type, so index is 0 */ - purple_prpl_got_attention(jm->js->gc, jm->from, 0); + purple_protocol_got_attention(jm->js->gc, jm->from, 0); } /* used internally by the functions below */ @@ -510,7 +510,7 @@ to = purple_xmlnode_get_attrib(packet, "to"); type = purple_xmlnode_get_attrib(packet, "type"); - signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), + signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js->gc), "jabber-receiving-message", js->gc, type, id, from, to, packet)); if (signal_return) return;
--- a/libpurple/protocols/jabber/presence.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/presence.c Fri Jan 31 18:02:20 2014 +0530 @@ -133,14 +133,14 @@ if (purple_blist_find_buddy(account, username)) { jbr = jabber_buddy_find_resource(jb, NULL); if (jbr) { - purple_prpl_got_user_status(account, username, + purple_protocol_got_user_status(account, username, jabber_buddy_state_get_status_id(jbr->state), "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL); - purple_prpl_got_user_idle(account, username, jbr->idle, jbr->idle); + purple_protocol_got_user_idle(account, username, jbr->idle, jbr->idle); } else { - purple_prpl_got_user_status(account, username, "offline", + purple_protocol_got_user_status(account, username, "offline", msg ? "message" : NULL, msg, NULL); } @@ -502,7 +502,7 @@ jbr->caps.info = info; jbr->caps.exts = exts; - purple_prpl_got_media_caps( + purple_protocol_got_media_caps( purple_connection_get_account(userdata->js->gc), userdata->from); if (info == NULL) @@ -879,17 +879,17 @@ jbr = jabber_buddy_find_resource(presence->jb, NULL); if (jbr) { jabber_google_presence_incoming(js, buddy_name, jbr); - purple_prpl_got_user_status(account, buddy_name, + purple_protocol_got_user_status(account, buddy_name, jabber_buddy_state_get_status_id(jbr->state), "priority", jbr->priority, "message", jbr->status, NULL); - purple_prpl_got_user_idle(account, buddy_name, + purple_protocol_got_user_idle(account, buddy_name, jbr->idle, jbr->idle); if (presence->nickname) serv_got_alias(js->gc, buddy_name, presence->nickname); } else { - purple_prpl_got_user_status(account, buddy_name, + purple_protocol_got_user_status(account, buddy_name, jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE), presence->status ? "message" : NULL, presence->status, NULL); @@ -927,7 +927,7 @@ return; } - signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), + signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js->gc), "jabber-receiving-presence", js->gc, type, presence.from, packet)); if (signal_return) { goto out;
--- a/libpurple/protocols/jabber/roster.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/roster.c Fri Jan 31 18:02:20 2014 +0530 @@ -413,7 +413,7 @@ } else if(!jb || !(jb->subscription & JABBER_SUB_TO)) { jabber_presence_subscription_set(js, who, "subscribe"); } else if((jbr =jabber_buddy_find_resource(jb, NULL))) { - purple_prpl_got_user_status(purple_connection_get_account(gc), who, + purple_protocol_got_user_status(purple_connection_get_account(gc), who, jabber_buddy_state_get_status_id(jbr->state), "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL); }
--- a/libpurple/protocols/jabber/si.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/si.c Fri Jan 31 18:02:20 2014 +0530 @@ -1012,7 +1012,7 @@ purple_debug_info("jabber", "about to write %" G_GSIZE_FORMAT " bytes from IBB stream\n", size); purple_circular_buffer_append(jsx->ibb_buffer, data, size); - purple_xfer_prpl_ready(xfer); + purple_xfer_protocol_ready(xfer); } else { /* trying to write past size of file transfers negotiated size, reject transfer to protect against malicious behaviour */ @@ -1117,7 +1117,7 @@ purple_xfer_end(xfer); } else { /* send more... */ - purple_xfer_prpl_ready(xfer); + purple_xfer_protocol_ready(xfer); } } @@ -1128,7 +1128,7 @@ if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_OPENED) { purple_xfer_start(xfer, -1, NULL, 0); - purple_xfer_prpl_ready(xfer); + purple_xfer_protocol_ready(xfer); } else { /* error */ purple_xfer_end(xfer);
--- a/libpurple/protocols/jabber/useravatar.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/useravatar.c Fri Jan 31 18:02:20 2014 +0530 @@ -109,7 +109,7 @@ jabber_pep_publish(js, publish); } else { /* - * TODO: This is pretty gross. The Jabber PRPL really shouldn't + * TODO: This is pretty gross. The Jabber protocol really shouldn't * do voodoo to try to determine the image type, height * and width. */
--- a/libpurple/protocols/jabber/usermood.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/usermood.c Fri Jan 31 18:02:20 2014 +0530 @@ -167,12 +167,12 @@ break; } if (newmood != NULL) { - purple_prpl_got_user_status(purple_connection_get_account(js->gc), from, "mood", + purple_protocol_got_user_status(purple_connection_get_account(js->gc), from, "mood", PURPLE_MOOD_NAME, newmood, PURPLE_MOOD_COMMENT, moodtext, NULL); } else { - purple_prpl_got_user_status_deactive(purple_connection_get_account(js->gc), from, "mood"); + purple_protocol_got_user_status_deactive(purple_connection_get_account(js->gc), from, "mood"); } g_free(moodtext); }
--- a/libpurple/protocols/jabber/usernick.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/usernick.c Fri Jan 31 18:02:20 2014 +0530 @@ -85,8 +85,8 @@ g_free(oldnickname); } -static void do_nick_set_nick(PurplePluginAction *action) { - PurpleConnection *gc = action->context; +static void do_nick_set_nick(PurpleProtocolAction *action) { + PurpleConnection *gc = action->connection; JabberStream *js = purple_connection_get_protocol_data(gc); /* since the nickname might have been changed by another resource of this account, we always have to request the old one @@ -100,6 +100,6 @@ } void jabber_nick_init_action(GList **m) { - PurplePluginAction *act = purple_plugin_action_new(_("Set Nickname..."), do_nick_set_nick); + PurpleProtocolAction *act = purple_protocol_action_new(_("Set Nickname..."), do_nick_set_nick); *m = g_list_append(*m, act); }
--- a/libpurple/protocols/jabber/usertune.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/jabber/usertune.c Fri Jan 31 18:02:20 2014 +0530 @@ -92,7 +92,7 @@ } if (valid) { - purple_prpl_got_user_status(purple_connection_get_account(js->gc), from, "tune", + purple_protocol_got_user_status(purple_connection_get_account(js->gc), from, "tune", PURPLE_TUNE_ARTIST, tuneinfodata.artist, PURPLE_TUNE_TITLE, tuneinfodata.title, PURPLE_TUNE_ALBUM, tuneinfodata.album, @@ -100,7 +100,7 @@ PURPLE_TUNE_TIME, tuneinfodata.time, PURPLE_TUNE_URL, tuneinfodata.url, NULL); } else { - purple_prpl_got_user_status_deactive(purple_connection_get_account(js->gc), from, "tune"); + purple_protocol_got_user_status_deactive(purple_connection_get_account(js->gc), from, "tune"); } g_free(tuneinfodata.artist);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/xmpp.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,110 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "internal.h" +#include "chat.h" +#include "core.h" +#include "plugins.h" + +#include "xmpp.h" + +static void +xmpp_protocol_init(PurpleProtocol *protocol) +{ + PurpleAccountUserSplit *split; + PurpleAccountOption *option; + GList *encryption_values = NULL; + + /* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */ + split = purple_account_user_split_new(_("Domain"), NULL, '@'); + purple_account_user_split_set_reverse(split, FALSE); + protocol->user_splits = g_list_append(protocol->user_splits, split); + + split = purple_account_user_split_new(_("Resource"), "", '/'); + purple_account_user_split_set_reverse(split, FALSE); + protocol->user_splits = g_list_append(protocol->user_splits, split); + +#define ADD_VALUE(list, desc, v) { \ + PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \ + kvp->key = g_strdup((desc)); \ + kvp->value = g_strdup((v)); \ + list = g_list_prepend(list, kvp); \ +} + + ADD_VALUE(encryption_values, _("Require encryption"), "require_tls"); + ADD_VALUE(encryption_values, _("Use encryption if available"), "opportunistic_tls"); + ADD_VALUE(encryption_values, _("Use old-style SSL"), "old_ssl"); +#if 0 + ADD_VALUE(encryption_values, "None", "none"); +#endif + encryption_values = g_list_reverse(encryption_values); + +#undef ADD_VALUE + + option = purple_account_option_list_new(_("Connection security"), "connection_security", encryption_values); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_bool_new( + _("Allow plaintext auth over unencrypted streams"), + "auth_plain_in_clear", FALSE); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_int_new(_("Connect port"), "port", 5222); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_string_new(_("Connect server"), + "connect_server", NULL); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_string_new(_("File transfer proxies"), + "ft_proxies", + /* TODO: Is this an acceptable default? + * Also, keep this in sync as they add more servers */ + JABBER_DEFAULT_FT_PROXIES); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + option = purple_account_option_string_new(_("BOSH URL"), + "bosh_url", NULL); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + /* this should probably be part of global smiley theme settings later on, + shared with MSN */ + option = purple_account_option_bool_new(_("Show Custom Smileys"), + "custom_smileys", TRUE); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + + purple_prefs_remove("/plugins/prpl/jabber"); +} + +static void +xmpp_protocol_class_init(PurpleProtocolClass *klass) +{ +} + +PURPLE_DEFINE_TYPE(XMPPProtocol, xmpp_protocol, JABBER_TYPE_PROTOCOL);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/xmpp.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,54 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#ifndef _XMPP_H_ +#define _XMPP_H_ + +#include "jabber.h" + +#define XMPP_TYPE_PROTOCOL (xmpp_protocol_get_type()) +#define XMPP_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), XMPP_TYPE_PROTOCOL, XMPPProtocol)) +#define XMPP_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), XMPP_TYPE_PROTOCOL, XMPPProtocolClass)) +#define XMPP_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), XMPP_TYPE_PROTOCOL)) +#define XMPP_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), XMPP_TYPE_PROTOCOL)) +#define XMPP_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), XMPP_TYPE_PROTOCOL, XMPPProtocolClass)) + +typedef struct _XMPPProtocol +{ + JabberProtocol parent; +} XMPPProtocol; + +typedef struct _XMPPProtocolClass +{ + JabberProtocolClass parent_class; +} XMPPProtocolClass; + +/** + * Registers the XMPPProtocol type in the type system. + */ +void xmpp_protocol_register_type(PurplePlugin *plugin); + +/** + * Returns the GType for the XMPPProtocol object. + */ +G_MODULE_EXPORT GType xmpp_protocol_get_type(void); + +#endif /* _XMPP_H_ */
--- a/libpurple/protocols/msn/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/msn/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -99,4 +99,5 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/msn/contact.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/msn/contact.c Fri Jan 31 18:02:20 2014 +0530 @@ -853,8 +853,8 @@ if (mobile && user) { user->mobile = TRUE; - purple_prpl_got_user_status(session->account, user->passport, "mobile", NULL); - purple_prpl_got_user_status(session->account, user->passport, "available", NULL); + purple_protocol_got_user_status(session->account, user->passport, "mobile", NULL); + purple_protocol_got_user_status(session->account, user->passport, "available", NULL); } if (alias) purple_serv_got_private_alias(pc, passport, alias);
--- a/libpurple/protocols/msn/msg.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/msn/msg.c Fri Jan 31 18:02:20 2014 +0530 @@ -1038,12 +1038,12 @@ if (swboard->current_users > 1 || ((swboard->conv != NULL) && PURPLE_IS_CHAT_CONVERSATION(swboard->conv))) - purple_prpl_got_attention_in_chat(gc, swboard->chat_id, user, MSN_NUDGE); + purple_protocol_got_attention_in_chat(gc, swboard->chat_id, user, MSN_NUDGE); else - purple_prpl_got_attention(gc, user, MSN_NUDGE); + purple_protocol_got_attention(gc, user, MSN_NUDGE); } else { - purple_prpl_got_attention(gc, user, MSN_NUDGE); + purple_protocol_got_attention(gc, user, MSN_NUDGE); } } else if (!strcmp(id, "2")) {
--- a/libpurple/protocols/msn/msn.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/msn/msn.c Fri Jan 31 18:02:20 2014 +0530 @@ -33,7 +33,7 @@ #include "contact.h" #include "msg.h" #include "page.h" -#include "pluginpref.h" +#include "plugins.h" #include "prefs.h" #include "session.h" #include "smiley.h" @@ -41,7 +41,7 @@ #include "util.h" #include "cmds.h" #include "core.h" -#include "prpl.h" +#include "protocol.h" #include "msnutils.h" #include "version.h" @@ -96,6 +96,9 @@ MsnObject *obj; } MsnEmoticon; +static PurpleProtocol *my_protocol = NULL; +static GSList *cmds = NULL; + static const char * msn_normalize(const PurpleAccount *account, const char *str) { @@ -163,7 +166,7 @@ username = purple_conversation_get_name(conv); - purple_prpl_send_attention(gc, username, MSN_NUDGE); + purple_protocol_send_attention(gc, username, MSN_NUDGE); return PURPLE_CMD_RET_OK; } @@ -438,13 +441,13 @@ /* -- */ static void -msn_show_set_friendly_name(PurplePluginAction *action) +msn_show_set_friendly_name(PurpleProtocolAction *action) { PurpleConnection *gc; PurpleAccount *account; char *tmp; - gc = (PurpleConnection *) action->context; + gc = action->connection; account = purple_connection_get_account(gc); tmp = g_strdup_printf(_("Set friendly name for %s."), @@ -508,7 +511,7 @@ } static void -msn_show_locations(PurplePluginAction *action) +msn_show_locations(PurpleProtocolAction *action) { PurpleConnection *pc; PurpleAccount *account; @@ -520,7 +523,7 @@ GSList *l; MsnLocationData *data; - pc = (PurpleConnection *)action->context; + pc = action->connection; account = purple_connection_get_account(pc); session = purple_connection_get_protocol_data(pc); @@ -591,7 +594,7 @@ session->enable_mpop = TRUE; msn_annotate_contact(session, "Me", "MSN.IM.MPOP", "1", NULL); - purple_prpl_got_account_actions(purple_connection_get_account(pc)); + purple_protocol_got_account_actions(purple_connection_get_account(pc)); } static void @@ -621,15 +624,15 @@ g_free(user); } - purple_prpl_got_account_actions(account); + purple_protocol_got_account_actions(account); } static void -msn_show_set_mpop(PurplePluginAction *action) +msn_show_set_mpop(PurpleProtocolAction *action) { PurpleConnection *pc; - pc = (PurpleConnection *)action->context; + pc = action->connection; purple_request_action(pc, NULL, _("Allow multiple logins?"), _("Do you want to allow or disallow connecting from " @@ -643,12 +646,12 @@ } static void -msn_show_set_home_phone(PurplePluginAction *action) +msn_show_set_home_phone(PurpleProtocolAction *action) { PurpleConnection *gc; MsnSession *session; - gc = (PurpleConnection *) action->context; + gc = action->connection; session = purple_connection_get_protocol_data(gc); purple_request_input(gc, NULL, _("Set your home phone number."), NULL, @@ -660,12 +663,12 @@ } static void -msn_show_set_work_phone(PurplePluginAction *action) +msn_show_set_work_phone(PurpleProtocolAction *action) { PurpleConnection *gc; MsnSession *session; - gc = (PurpleConnection *) action->context; + gc = action->connection; session = purple_connection_get_protocol_data(gc); purple_request_input(gc, NULL, _("Set your work phone number."), NULL, @@ -677,12 +680,12 @@ } static void -msn_show_set_mobile_phone(PurplePluginAction *action) +msn_show_set_mobile_phone(PurpleProtocolAction *action) { PurpleConnection *gc; MsnSession *session; - gc = (PurpleConnection *) action->context; + gc = action->connection; session = purple_connection_get_protocol_data(gc); purple_request_input(gc, NULL, _("Set your mobile phone number."), NULL, @@ -694,11 +697,11 @@ } static void -msn_show_set_mobile_pages(PurplePluginAction *action) +msn_show_set_mobile_pages(PurpleProtocolAction *action) { PurpleConnection *gc; - gc = (PurpleConnection *) action->context; + gc = action->connection; purple_request_action(gc, NULL, _("Allow MSN Mobile pages?"), _("Do you want to allow or disallow people on " @@ -715,9 +718,9 @@ /* QuLogic: Disabled until confirmed correct. */ #if 0 static void -msn_show_blocked_text(PurplePluginAction *action) +msn_show_blocked_text(PurpleProtocolAction *action) { - PurpleConnection *pc = (PurpleConnection *) action->context; + PurpleConnection *pc = action->connection; MsnSession *session; char *title; @@ -739,12 +742,12 @@ #endif static void -msn_show_hotmail_inbox(PurplePluginAction *action) +msn_show_hotmail_inbox(PurpleProtocolAction *action) { PurpleConnection *gc; MsnSession *session; - gc = (PurpleConnection *) action->context; + gc = action->connection; session = purple_connection_get_protocol_data(gc); if (!session->passport_info.email_enabled) { @@ -988,7 +991,7 @@ status = purple_presence_get_active_status(presence); /* Official client says media takes precedence over message */ - /* I say message take precedence over media! Plus prpl-jabber agrees + /* I say message take precedence over media! Plus jabber agrees too */ msg = purple_status_get_attr_string(status, "message"); if (msg && *msg) @@ -1113,7 +1116,7 @@ /* XXX: This is being shown in non-full tooltips because the * XXX: blocked icon overlay isn't always accurate for MSN. * XXX: This can die as soon as purple_privacy_check() knows that - * XXX: this prpl always honors both the allow and deny lists. */ + * XXX: this protocol always honors both the allow and deny lists. */ /* While the above comment may be strictly correct (the privacy API needs * rewriteing), purple_privacy_check() is going to be more accurate at * indicating whether a particular buddy is going to be able to message @@ -1212,66 +1215,64 @@ } static GList * -msn_actions(PurplePlugin *plugin, gpointer context) +msn_get_actions(PurpleConnection *gc) { - PurpleConnection *gc; MsnSession *session; GList *m = NULL; - PurplePluginAction *act; - - gc = (PurpleConnection *) context; + PurpleProtocolAction *act; + session = purple_connection_get_protocol_data(gc); - act = purple_plugin_action_new(_("Set Friendly Name..."), + act = purple_protocol_action_new(_("Set Friendly Name..."), msn_show_set_friendly_name); m = g_list_append(m, act); m = g_list_append(m, NULL); if (session->enable_mpop) { - act = purple_plugin_action_new(_("View Locations..."), + act = purple_protocol_action_new(_("View Locations..."), msn_show_locations); m = g_list_append(m, act); m = g_list_append(m, NULL); } - act = purple_plugin_action_new(_("Set Home Phone Number..."), + act = purple_protocol_action_new(_("Set Home Phone Number..."), msn_show_set_home_phone); m = g_list_append(m, act); - act = purple_plugin_action_new(_("Set Work Phone Number..."), + act = purple_protocol_action_new(_("Set Work Phone Number..."), msn_show_set_work_phone); m = g_list_append(m, act); - act = purple_plugin_action_new(_("Set Mobile Phone Number..."), + act = purple_protocol_action_new(_("Set Mobile Phone Number..."), msn_show_set_mobile_phone); m = g_list_append(m, act); m = g_list_append(m, NULL); #if 0 - act = purple_plugin_action_new(_("Enable/Disable Mobile Devices..."), + act = purple_protocol_action_new(_("Enable/Disable Mobile Devices..."), msn_show_set_mobile_support); m = g_list_append(m, act); #endif - act = purple_plugin_action_new(_("Allow/Disallow Multiple Logins..."), + act = purple_protocol_action_new(_("Allow/Disallow Multiple Logins..."), msn_show_set_mpop); m = g_list_append(m, act); - act = purple_plugin_action_new(_("Allow/Disallow Mobile Pages..."), + act = purple_protocol_action_new(_("Allow/Disallow Mobile Pages..."), msn_show_set_mobile_pages); m = g_list_append(m, act); /* QuLogic: Disabled until confirmed correct. */ #if 0 m = g_list_append(m, NULL); - act = purple_plugin_action_new(_("View Blocked Text..."), + act = purple_protocol_action_new(_("View Blocked Text..."), msn_show_blocked_text); m = g_list_append(m, act); #endif m = g_list_append(m, NULL); - act = purple_plugin_action_new(_("Open Hotmail Inbox"), + act = purple_protocol_action_new(_("Open Hotmail Inbox"), msn_show_hotmail_inbox); m = g_list_append(m, act); @@ -2810,35 +2811,19 @@ PROFILE_URL, name)); } -static gboolean msn_load(PurplePlugin *plugin) -{ - msn_notification_init(); - msn_switchboard_init(); - - return TRUE; -} - -static gboolean msn_unload(PurplePlugin *plugin) -{ - msn_notification_end(); - msn_switchboard_end(); - - return TRUE; -} - -static PurpleAccount *find_acct(const char *prpl, const char *acct_id) +static PurpleAccount *find_acct(const char *protocol, const char *acct_id) { PurpleAccount *acct = NULL; /* If we have a specific acct, use it */ if (acct_id) { - acct = purple_accounts_find(acct_id, prpl); + acct = purple_accounts_find(acct_id, protocol); if (acct && !purple_account_is_connected(acct)) acct = NULL; } else { /* Otherwise find an active account for the protocol */ GList *l = purple_accounts_get_all(); while (l) { - if (!strcmp(prpl, purple_account_get_protocol_id(l->data)) + if (!strcmp(protocol, purple_account_get_protocol_id(l->data)) && purple_account_is_connected(l->data)) { acct = l->data; break; @@ -2897,165 +2882,222 @@ return 1525 - strlen(VERSION); } -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_MAIL_CHECK|OPT_PROTO_INVITE_MESSAGE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png,gif", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */ - msn_list_icon, /* list_icon */ - msn_list_emblems, /* list_emblems */ - msn_status_text, /* status_text */ - msn_tooltip_text, /* tooltip_text */ - msn_status_types, /* away_states */ - msn_blist_node_menu, /* blist_node_menu */ - NULL, /* chat_info */ - NULL, /* chat_info_defaults */ - msn_login, /* login */ - msn_close, /* close */ - msn_send_im, /* send_im */ - NULL, /* set_info */ - msn_send_typing, /* send_typing */ - msn_get_info, /* get_info */ - msn_set_status, /* set_away */ - msn_set_idle, /* set_idle */ - NULL, /* change_passwd */ - msn_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - msn_rem_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - msn_add_permit, /* add_permit */ - msn_add_deny, /* add_deny */ - msn_rem_permit, /* rem_permit */ - msn_rem_deny, /* rem_deny */ - msn_set_permit_deny, /* set_permit_deny */ - NULL, /* join_chat */ - NULL, /* reject chat invite */ - NULL, /* get_chat_name */ - msn_chat_invite, /* chat_invite */ - msn_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - msn_chat_send, /* chat_send */ - msn_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - msn_alias_buddy, /* alias_buddy */ - msn_group_buddy, /* group_buddy */ - msn_rename_group, /* rename_group */ - NULL, /* buddy_free */ - msn_convo_closed, /* convo_closed */ - msn_normalize, /* normalize */ - msn_set_buddy_icon, /* set_buddy_icon */ - msn_remove_group, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - msn_can_receive_file, /* can_receive_file */ - msn_send_file, /* send_file */ - msn_new_xfer, /* new_xfer */ - msn_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - msn_send_attention, /* send_attention */ - msn_attention_types, /* attention_types */ - msn_get_account_text_table, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - msn_set_public_alias, /* set_public_alias */ - msn_get_public_alias, /* get_public_alias */ - msn_get_max_message_size /* get_max_message_size */ -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-msn", /**< id */ - "MSN", /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Windows Live Messenger Protocol Plugin"), /**< summary */ - N_("Windows Live Messenger Protocol Plugin"), /**< description */ - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - msn_load, /**< load */ - msn_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - msn_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - static void -init_plugin(PurplePlugin *plugin) +msn_protocol_init(PurpleProtocol *protocol) { PurpleAccountOption *option; + protocol->id = "prpl-msn"; + protocol->name = "MSN"; + protocol->options = OPT_PROTO_MAIL_CHECK | OPT_PROTO_INVITE_MESSAGE; + protocol->icon_spec = purple_buddy_icon_spec_new("png,gif", + 0, 0, 96, 96, 0, + PURPLE_ICON_SCALE_SEND); + option = purple_account_option_string_new(_("Server"), "server", MSN_SERVER); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_int_new(_("Port"), "port", MSN_PORT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Use HTTP Method"), "http_method", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("HTTP Method Server"), "http_method_server", MSN_HTTPCONN_SERVER); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Show custom smileys"), "custom_smileys", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Allow direct connections"), "direct_connect", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Allow connecting from multiple locations"), "mpop", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + protocol->protocol_options = g_list_append(protocol->protocol_options, option); - - purple_cmd_register("nudge", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, +} + +static void +msn_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = msn_login; + klass->close = msn_close; + klass->status_types = msn_status_types; + klass->list_icon = msn_list_icon; +} + +static void +msn_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = msn_get_actions; + client_iface->list_emblem = msn_list_emblems; + client_iface->status_text = msn_status_text; + client_iface->tooltip_text = msn_tooltip_text; + client_iface->blist_node_menu = msn_blist_node_menu; + client_iface->convo_closed = msn_convo_closed; + client_iface->normalize = msn_normalize; + client_iface->offline_message = msn_offline_message; + client_iface->get_account_text_table = msn_get_account_text_table; + client_iface->get_max_message_size = msn_get_max_message_size; +} + +static void +msn_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->get_info = msn_get_info; + server_iface->set_status = msn_set_status; + server_iface->set_idle = msn_set_idle; + server_iface->add_buddy = msn_add_buddy; + server_iface->remove_buddy = msn_rem_buddy; + server_iface->keepalive = msn_keepalive; + server_iface->alias_buddy = msn_alias_buddy; + server_iface->group_buddy = msn_group_buddy; + server_iface->rename_group = msn_rename_group; + server_iface->set_buddy_icon = msn_set_buddy_icon; + server_iface->remove_group = msn_remove_group; + server_iface->set_public_alias = msn_set_public_alias; + server_iface->get_public_alias = msn_get_public_alias; +} + +static void +msn_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = msn_send_im; + im_iface->send_typing = msn_send_typing; +} + +static void +msn_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->invite = msn_chat_invite; + chat_iface->leave = msn_chat_leave; + chat_iface->send = msn_chat_send; +} + +static void +msn_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) +{ + privacy_iface->add_permit = msn_add_permit; + privacy_iface->add_deny = msn_add_deny; + privacy_iface->rem_permit = msn_rem_permit; + privacy_iface->rem_deny = msn_rem_deny; + privacy_iface->set_permit_deny = msn_set_permit_deny; +} + +static void +msn_protocol_attention_iface_init(PurpleProtocolAttentionIface *attention_iface) +{ + attention_iface->send = msn_send_attention; + attention_iface->get_types = msn_attention_types; +} + +static void +msn_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->can_receive = msn_can_receive_file; + xfer_iface->send = msn_send_file; + xfer_iface->new_xfer = msn_new_xfer; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + MsnProtocol, msn_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + msn_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + msn_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + msn_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + msn_protocol_chat_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + msn_protocol_privacy_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE, + msn_protocol_attention_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + msn_protocol_xfer_iface_init) +); + +static PurplePluginInfo * +plugin_query(GError **error) +{ + return purple_plugin_info_new( + "id", "prpl-msn", + "name", "MSN Protocol", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("Windows Live Messenger Protocol Plugin"), + "description", N_("Windows Live Messenger Protocol Plugin"), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + PurpleCmdId id; + + msn_protocol_register_type(plugin); + + my_protocol = purple_protocols_add(MSN_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; + + id = purple_cmd_register("nudge", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-msn", msn_cmd_nudge, _("nudge: nudge a user to get their attention"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); purple_prefs_remove("/plugins/prpl/msn"); - purple_signal_connect(purple_get_core(), "uri-handler", plugin, + msn_notification_init(); + msn_switchboard_init(); + + purple_signal_connect(purple_get_core(), "uri-handler", my_protocol, PURPLE_CALLBACK(msn_uri_handler), NULL); + + return TRUE; } -PURPLE_INIT_PLUGIN(msn, init_plugin, info); +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + msn_notification_end(); + msn_switchboard_end(); + + while (cmds) { + PurpleCmdId id = GPOINTER_TO_UINT(cmds->data); + purple_cmd_unregister(id); + cmds = g_slist_delete_link(cmds, cmds); + } + + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + + return TRUE; +} + +PURPLE_PLUGIN_INIT(msn, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/protocols/msn/msn.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/msn/msn.h Fri Jan 31 18:02:20 2014 +0530 @@ -24,6 +24,13 @@ #ifndef MSN_H #define MSN_H +#define MSN_TYPE_PROTOCOL (msn_protocol_get_type()) +#define MSN_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MSN_TYPE_PROTOCOL, MsnProtocol)) +#define MSN_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), MSN_TYPE_PROTOCOL, MsnProtocolClass)) +#define MSN_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), MSN_TYPE_PROTOCOL)) +#define MSN_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), MSN_TYPE_PROTOCOL)) +#define MSN_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), MSN_TYPE_PROTOCOL, MsnProtocolClass)) + typedef enum { MSN_CAP_VIA_MOBILE = 0x0000001, @@ -111,6 +118,8 @@ #define MSN_BUF_LEN 8192 +#define MSN_DOMAIN (g_quark_from_static_string("msn")) + /* Windows Live Messenger Server*/ #define MSN_SERVER "messenger.hotmail.com" #define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com" @@ -148,6 +157,18 @@ ((MSN_CLIENT_ID_VERSION << 24) | \ (MSN_CLIENT_ID_CAPABILITIES)) +typedef struct _MsnProtocol +{ + PurpleProtocol parent; +} MsnProtocol; + +typedef struct _MsnProtocolClass +{ + PurpleProtocolClass parent_class; +} MsnProtocolClass; + +G_MODULE_EXPORT GType msn_protocol_get_type(void); + void msn_set_public_alias(PurpleConnection *gc, const char *alias, PurpleSetPublicAliasSuccessCallback success_cb,
--- a/libpurple/protocols/msn/slplink.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/msn/slplink.c Fri Jan 31 18:02:20 2014 +0530 @@ -538,7 +538,7 @@ if (slpmsg->ft) { slpmsg->slpcall->u.incoming_data = g_byte_array_append(slpmsg->slpcall->u.incoming_data, (const guchar *)part->buffer, part->size); - purple_xfer_prpl_ready(slpmsg->slpcall->xfer); + purple_xfer_protocol_ready(slpmsg->slpcall->xfer); } else if (slpmsg->size && slpmsg->buffer) { guint64 offset = msn_p2p_info_get_offset(part->info);
--- a/libpurple/protocols/msn/slpmsg_part.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/msn/slpmsg_part.c Fri Jan 31 18:02:20 2014 +0530 @@ -190,7 +190,7 @@ if (slpmsg->slpcall->xfer && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED) { slpmsg->slpcall->xfer_msg = slpmsg; - purple_xfer_prpl_ready(slpmsg->slpcall->xfer); + purple_xfer_protocol_ready(slpmsg->slpcall->xfer); } else msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
--- a/libpurple/protocols/msn/soap.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/msn/soap.c Fri Jan 31 18:02:20 2014 +0530 @@ -23,6 +23,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "internal.h" + #include "soap.h" #include "debug.h"
--- a/libpurple/protocols/msn/user.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/msn/user.c Fri Jan 31 18:02:20 2014 +0530 @@ -130,34 +130,34 @@ offline = (user->status == NULL); if (!offline) { - purple_prpl_got_user_status(account, user->passport, user->status, + purple_protocol_got_user_status(account, user->passport, user->status, "message", user->statusline, NULL); } else { if (user->mobile) { - purple_prpl_got_user_status(account, user->passport, "mobile", NULL); - purple_prpl_got_user_status(account, user->passport, "available", NULL); + purple_protocol_got_user_status(account, user->passport, "mobile", NULL); + purple_protocol_got_user_status(account, user->passport, "available", NULL); } else { - purple_prpl_got_user_status(account, user->passport, "offline", NULL); + purple_protocol_got_user_status(account, user->passport, "offline", NULL); } } if (!offline || !user->mobile) { - purple_prpl_got_user_status_deactive(account, user->passport, "mobile"); + purple_protocol_got_user_status_deactive(account, user->passport, "mobile"); } if (!offline && user->extinfo && user->extinfo->media_type != CURRENT_MEDIA_UNKNOWN) { if (user->extinfo->media_type == CURRENT_MEDIA_MUSIC) { - purple_prpl_got_user_status(account, user->passport, "tune", + purple_protocol_got_user_status(account, user->passport, "tune", PURPLE_TUNE_ARTIST, user->extinfo->media_artist, PURPLE_TUNE_ALBUM, user->extinfo->media_album, PURPLE_TUNE_TITLE, user->extinfo->media_title, NULL); } else if (user->extinfo->media_type == CURRENT_MEDIA_GAMES) { - purple_prpl_got_user_status(account, user->passport, "tune", + purple_protocol_got_user_status(account, user->passport, "tune", "game", user->extinfo->media_title, NULL); } else if (user->extinfo->media_type == CURRENT_MEDIA_OFFICE) { - purple_prpl_got_user_status(account, user->passport, "tune", + purple_protocol_got_user_status(account, user->passport, "tune", "office", user->extinfo->media_title, NULL); } else { @@ -165,13 +165,13 @@ user->extinfo->media_type); } } else { - purple_prpl_got_user_status_deactive(account, user->passport, "tune"); + purple_protocol_got_user_status_deactive(account, user->passport, "tune"); } if (user->idle) - purple_prpl_got_user_idle(account, user->passport, TRUE, -1); + purple_protocol_got_user_idle(account, user->passport, TRUE, -1); else - purple_prpl_got_user_idle(account, user->passport, FALSE, 0); + purple_protocol_got_user_idle(account, user->passport, FALSE, 0); } void
--- a/libpurple/protocols/mxit/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -12,6 +12,8 @@ chunk.h \ cipher.c \ cipher.h \ + client.c \ + client.h \ filexfer.c \ filexfer.h \ formcmds.c \ @@ -26,8 +28,6 @@ mxit.h \ profile.c \ profile.h \ - protocol.c \ - protocol.h \ roster.c \ roster.h \ splashscreen.c \ @@ -60,4 +60,5 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/mxit/Makefile.mingw Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/Makefile.mingw Fri Jan 31 18:02:20 2014 +0530 @@ -41,6 +41,7 @@ aes.c \ chunk.c \ cipher.c \ + client.c \ filexfer.c \ formcmds.c \ login.c \ @@ -48,7 +49,6 @@ multimx.c \ mxit.c \ profile.c \ - protocol.c \ roster.c \ splashscreen.c \ voicevideo.c
--- a/libpurple/protocols/mxit/actions.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/actions.c Fri Jan 31 18:02:20 2014 +0530 @@ -27,7 +27,7 @@ #include "debug.h" #include "request.h" -#include "protocol.h" +#include "client.h" #include "mxit.h" #include "roster.h" #include "actions.h" @@ -207,9 +207,9 @@ * * @param action The action object */ -static void mxit_profile_action( PurplePluginAction* action ) +static void mxit_profile_action( PurpleProtocolAction* action ) { - PurpleConnection* gc = (PurpleConnection*) action->context; + PurpleConnection* gc = action->connection; struct MXitSession* session = purple_connection_get_protocol_data( gc ); struct MXitProfile* profile = session->profile; @@ -382,9 +382,9 @@ * * @param action The action object */ -static void mxit_change_pin_action( PurplePluginAction* action ) +static void mxit_change_pin_action( PurpleProtocolAction* action ) { - PurpleConnection* gc = (PurpleConnection*) action->context; + PurpleConnection* gc = action->connection; PurpleRequestFields* fields = NULL; PurpleRequestFieldGroup* group = NULL; @@ -417,9 +417,9 @@ * * @param action The action object */ -static void mxit_splash_action( PurplePluginAction* action ) +static void mxit_splash_action( PurpleProtocolAction* action ) { - PurpleConnection* gc = (PurpleConnection*) action->context; + PurpleConnection* gc = action->connection; struct MXitSession* session = purple_connection_get_protocol_data( gc ); if ( splash_current( session ) != NULL ) @@ -434,7 +434,7 @@ * * @param action The action object */ -static void mxit_about_action( PurplePluginAction* action ) +static void mxit_about_action( PurpleProtocolAction* action ) { char version[256]; @@ -454,9 +454,9 @@ * * @param action The action object */ -static void mxit_suggested_friends_action( PurplePluginAction* action ) +static void mxit_suggested_friends_action( PurpleProtocolAction* action ) { - PurpleConnection* gc = (PurpleConnection*) action->context; + PurpleConnection* gc = action->connection; struct MXitSession* session = purple_connection_get_protocol_data( gc ); const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_FULLNAME, CP_PROFILE_FIRSTNAME, @@ -489,9 +489,9 @@ * * @param action The action object */ -static void mxit_user_search_action( PurplePluginAction* action ) +static void mxit_user_search_action( PurpleProtocolAction* action ) { - PurpleConnection* gc = (PurpleConnection*) action->context; + PurpleConnection* gc = action->connection; purple_request_input( gc, _( "Search for user" ), _( "Search for a MXit contact" ), @@ -507,37 +507,36 @@ /*------------------------------------------------------------------------ * Associate actions with the MXit plugin. * - * @param plugin The MXit protocol plugin - * @param context The connection context (if available) + * @param gc The connection * @return The list of plugin actions */ -GList* mxit_actions( PurplePlugin* plugin, gpointer context ) +GList* mxit_get_actions( PurpleConnection *gc ) { - PurplePluginAction* action = NULL; + PurpleProtocolAction* action = NULL; GList* m = NULL; /* display / change profile */ - action = purple_plugin_action_new( _( "Change Profile..." ), mxit_profile_action ); + action = purple_protocol_action_new( _( "Change Profile..." ), mxit_profile_action ); m = g_list_append( m, action ); /* change PIN */ - action = purple_plugin_action_new( _( "Change PIN..." ), mxit_change_pin_action ); + action = purple_protocol_action_new( _( "Change PIN..." ), mxit_change_pin_action ); m = g_list_append( m, action ); /* suggested friends */ - action = purple_plugin_action_new( _( "Suggested friends..." ), mxit_suggested_friends_action ); + action = purple_protocol_action_new( _( "Suggested friends..." ), mxit_suggested_friends_action ); m = g_list_append( m, action ); /* search for contacts */ - action = purple_plugin_action_new( _( "Search for contacts..." ), mxit_user_search_action ); + action = purple_protocol_action_new( _( "Search for contacts..." ), mxit_user_search_action ); m = g_list_append( m, action ); /* display splash-screen */ - action = purple_plugin_action_new( _( "View Splash..." ), mxit_splash_action ); + action = purple_protocol_action_new( _( "View Splash..." ), mxit_splash_action ); m = g_list_append( m, action ); /* display plugin version */ - action = purple_plugin_action_new( _( "About..." ), mxit_about_action ); + action = purple_protocol_action_new( _( "About..." ), mxit_about_action ); m = g_list_append( m, action ); return m;
--- a/libpurple/protocols/mxit/actions.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/actions.h Fri Jan 31 18:02:20 2014 +0530 @@ -28,7 +28,7 @@ /* callbacks */ -GList* mxit_actions( PurplePlugin* plugin, gpointer context ); +GList* mxit_get_actions( PurpleConnection *gc ); #endif /* _MXIT_ACTIONS_H_ */
--- a/libpurple/protocols/mxit/chunk.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/chunk.c Fri Jan 31 18:02:20 2014 +0530 @@ -26,7 +26,7 @@ #include "internal.h" #include "debug.h" -#include "protocol.h" +#include "client.h" #include "mxit.h" #include "chunk.h" #include "filexfer.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/mxit/client.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,2953 @@ +/* + * MXit Protocol libPurple Plugin + * + * -- MXit client protocol implementation -- + * + * Pieter Loubser <libpurple@mxit.com> + * + * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd. + * <http://www.mxitlifestyle.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "internal.h" +#include "debug.h" +#include "version.h" + +#include "client.h" +#include "mxit.h" +#include "roster.h" +#include "chunk.h" +#include "filexfer.h" +#include "markup.h" +#include "multimx.h" +#include "splashscreen.h" +#include "login.h" +#include "formcmds.h" +#include "http.h" +#include "cipher.h" +#include "voicevideo.h" + + +#define MXIT_MS_OFFSET 3 + +/* configure the right record terminator char to use */ +#define CP_REC_TERM ( ( session->http ) ? CP_HTTP_REC_TERM : CP_SOCK_REC_TERM ) + + +/*------------------------------------------------------------------------ + * return the current timestamp in milliseconds + */ +gint64 mxit_now_milli( void ) +{ + GTimeVal now; + + g_get_current_time( &now ); + + return ( ( now.tv_sec * 1000 ) + ( now.tv_usec / 1000 ) ); +} + + +/*------------------------------------------------------------------------ + * Display a notification popup message to the user. + * + * @param type The type of notification: + * - info: PURPLE_NOTIFY_MSG_INFO + * - warning: PURPLE_NOTIFY_MSG_WARNING + * - error: PURPLE_NOTIFY_MSG_ERROR + * @param heading Heading text + * @param message Message text + */ +void mxit_popup( int type, const char* heading, const char* message ) +{ + /* (reference: "libpurple/notify.h") */ + purple_notify_message( NULL, type, _( MXIT_POPUP_WIN_NAME ), heading, message, NULL, NULL, NULL ); +} + + +/*------------------------------------------------------------------------ + * For compatibility with legacy clients, all usernames are sent from MXit with a domain + * appended. For MXit contacts, this domain is set to "@m". This function strips + * those fake domains. + * + * @param username The username of the contact + */ +void mxit_strip_domain( char* username ) +{ + if ( g_str_has_suffix( username, "@m" ) ) + username[ strlen( username ) - 2 ] = '\0'; +} + + +/*------------------------------------------------------------------------ + * Dump a byte buffer to the console for debugging purposes. + * + * @param buf The data + * @param len The data length + */ +void dump_bytes( struct MXitSession* session, const char* buf, int len ) +{ + char* msg = g_malloc0( len + 1 ); + int i; + + for ( i = 0; i < len; i++ ) { + char ch = buf[i]; + + if ( ch == CP_REC_TERM ) /* record terminator */ + msg[i] = '!'; + else if ( ch == CP_FLD_TERM ) /* field terminator */ + msg[i] = '^'; + else if ( ch == CP_PKT_TERM ) /* packet terminator */ + msg[i] = '@'; + else if ( ( ch < 0x20 ) || ( ch > 0x7E ) ) /* non-printable character */ + msg[i] = '_'; + else + msg[i] = ch; + } + + purple_debug_info( MXIT_PLUGIN_ID, "DUMP: '%s'\n", msg ); + + g_free( msg ); +} + + +/*------------------------------------------------------------------------ + * Determine if we have an active chat with a specific contact + * + * @param session The MXit session object + * @param who The contact name + * @return Return true if we have an active chat with the contact + */ +gboolean find_active_chat( const GList* chats, const char* who ) +{ + const GList* list = chats; + const char* chat = NULL; + + while ( list ) { + chat = (const char*) list->data; + + if ( strcmp( chat, who ) == 0 ) + return TRUE; + + list = g_list_next( list ); + } + + return FALSE; +} + + +/*======================================================================================================================== + * Low-level Packet transmission + */ + +/*------------------------------------------------------------------------ + * Remove next packet from transmission queue. + * + * @param session The MXit session object + * @return The next packet for transmission (or NULL) + */ +static struct tx_packet* pop_tx_packet( struct MXitSession* session ) +{ + struct tx_packet* packet = NULL; + + if ( session->queue.count > 0 ) { + /* dequeue the next packet */ + packet = session->queue.packets[session->queue.rd_i]; + session->queue.packets[session->queue.rd_i] = NULL; + session->queue.rd_i = ( session->queue.rd_i + 1 ) % MAX_QUEUE_SIZE; + session->queue.count--; + } + + return packet; +} + + +/*------------------------------------------------------------------------ + * Add packet to transmission queue. + * + * @param session The MXit session object + * @param packet The packet to transmit + * @return Return TRUE if packet was enqueue, or FALSE if queue is full. + */ +static gboolean push_tx_packet( struct MXitSession* session, struct tx_packet* packet ) +{ + if ( session->queue.count < MAX_QUEUE_SIZE ) { + /* enqueue packet */ + session->queue.packets[session->queue.wr_i] = packet; + session->queue.wr_i = ( session->queue.wr_i + 1 ) % MAX_QUEUE_SIZE; + session->queue.count++; + return TRUE; + } + else + return FALSE; /* queue is full */ +} + + +/*------------------------------------------------------------------------ + * Deallocate transmission packet. + * + * @param packet The packet to deallocate. + */ +static void free_tx_packet( struct tx_packet* packet ) +{ + g_free( packet->data ); + g_free( packet ); + packet = NULL; +} + + +/*------------------------------------------------------------------------ + * Flush all the packets from the tx queue and release the resources. + * + * @param session The MXit session object + */ +static void flush_queue( struct MXitSession* session ) +{ + struct tx_packet* packet; + + purple_debug_info( MXIT_PLUGIN_ID, "flushing the tx queue\n" ); + + while ( (packet = pop_tx_packet( session ) ) != NULL ) + free_tx_packet( packet ); +} + + +/*------------------------------------------------------------------------ + * TX Step 3: Write the packet data to the TCP connection. + * + * @param fd The file descriptor + * @param pktdata The packet data + * @param pktlen The length of the packet data + * @return Return -1 on error, otherwise 0 + */ +static int mxit_write_sock_packet( int fd, const char* pktdata, int pktlen ) +{ + int written; + int res; + + written = 0; + while ( written < pktlen ) { + res = write( fd, &pktdata[written], pktlen - written ); + if ( res <= 0 ) { + /* error on socket */ + if ( errno == EAGAIN ) + continue; + + purple_debug_error( MXIT_PLUGIN_ID, "Error while writing packet to MXit server (%i)\n", res ); + return -1; + } + written += res; + } + + return 0; +} + + +/** + * Callback called for handling a HTTP GET response + * + * @param http_conn http api object (see http.h) + * @param response http api object (see http.h) + * @param _session The MXit session object + */ +static void +mxit_cb_http_rx(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, + gpointer _session) +{ + struct MXitSession *session = _session; + const gchar *got_data; + size_t got_len; + + if (!purple_http_response_is_successful(response)) { + purple_debug_error(MXIT_PLUGIN_ID, "HTTP response error (%s)\n", + purple_http_response_get_error(response)); + return; + } + + /* convert the HTTP result */ + got_data = purple_http_response_get_data(response, &got_len); + memcpy(session->rx_dbuf, got_data, got_len); + session->rx_i = got_len; + + mxit_parse_packet(session); +} + + +/** + * TX Step 3: Write the packet data to the HTTP connection (GET style). + * + * @param session The MXit session object + * @param packet The packet data + */ +static void +mxit_write_http_get(struct MXitSession* session, struct tx_packet* packet) +{ + PurpleHttpRequest *req; + char *part = NULL; + + if (packet->datalen > 0) { + char *tmp; + + tmp = g_strndup(packet->data, packet->datalen); + part = g_strdup(purple_url_encode(tmp)); + g_free(tmp); + } + + req = purple_http_request_new(NULL); + purple_http_request_set_url_printf(req, "%s?%s%s", session->http_server, + purple_url_encode(packet->header), part ? part : ""); + purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT); + purple_http_connection_set_add(session->async_http_reqs, + purple_http_request(session->con, req, mxit_cb_http_rx, + session)); + purple_http_request_unref(req); + + g_free(part); +} + + +/** + * TX Step 3: Write the packet data to the HTTP connection (POST style). + * + * @param session The MXit session object + * @param packet The packet data + */ +static void +mxit_write_http_post(struct MXitSession* session, struct tx_packet* packet) +{ + PurpleHttpRequest *req; + + /* strip off the last '&' from the header */ + packet->header[packet->headerlen - 1] = '\0'; + packet->headerlen--; + + req = purple_http_request_new(NULL); + purple_http_request_set_url_printf(req, "%s?%s", session->http_server, + purple_url_encode(packet->header)); + purple_http_request_set_method(req, "POST"); + purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT); + purple_http_request_header_set(req, "Content-Type", + "application/octet-stream"); + purple_http_request_set_contents(req, packet->data + MXIT_MS_OFFSET, + packet->datalen - MXIT_MS_OFFSET); + purple_http_connection_set_add(session->async_http_reqs, + purple_http_request(session->con, req, mxit_cb_http_rx, + session)); + purple_http_request_unref(req); +} + + +/*------------------------------------------------------------------------ + * TX Step 2: Handle the transmission of the packet to the MXit server. + * + * @param session The MXit session object + * @param packet The packet to transmit + */ +static void mxit_send_packet( struct MXitSession* session, struct tx_packet* packet ) +{ + int res; + + if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) { + /* we are not connected so ignore all packets to be send */ + purple_debug_error( MXIT_PLUGIN_ID, "Dropping TX packet (we are not connected)\n" ); + return; + } + + purple_debug_info( MXIT_PLUGIN_ID, "Packet send CMD:%i (%i)\n", packet->cmd, packet->headerlen + packet->datalen ); +#ifdef DEBUG_PROTOCOL + dump_bytes( session, packet->header, packet->headerlen ); + dump_bytes( session, packet->data, packet->datalen ); +#endif + + if ( !session->http ) { + /* socket connection */ + char data[packet->datalen + packet->headerlen]; + int datalen; + + /* create raw data buffer */ + memcpy( data, packet->header, packet->headerlen ); + memcpy( data + packet->headerlen, packet->data, packet->datalen ); + datalen = packet->headerlen + packet->datalen; + + res = mxit_write_sock_packet( session->fd, data, datalen ); + if ( res < 0 ) { + /* we must have lost the connection, so terminate it so that we can reconnect */ + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "We have lost the connection to MXit. Please reconnect." ) ); + } + } + else { + /* http connection */ + + if ( packet->cmd == CP_CMD_MEDIA ) { + /* multimedia packets must be send with a HTTP POST */ + mxit_write_http_post( session, packet ); + } + else { + mxit_write_http_get( session, packet ); + } + } + + /* update the timestamp of the last-transmitted packet */ + session->last_tx = mxit_now_milli(); + + /* + * we need to remember that we are still waiting for the ACK from + * the server on this request + */ + session->outack = packet->cmd; + + /* free up the packet resources */ + free_tx_packet( packet ); +} + + +/*------------------------------------------------------------------------ + * TX Step 1: Create a new Tx packet and queue it for sending. + * + * @param session The MXit session object + * @param data The packet data (payload) + * @param datalen The length of the packet data + * @param cmd The MXit command for this packet + */ +static void mxit_queue_packet( struct MXitSession* session, const char* data, int datalen, int cmd ) +{ + struct tx_packet* packet; + char header[256]; + int hlen; + + /* create a packet for sending */ + packet = g_new0( struct tx_packet, 1 ); + packet->data = g_malloc0( datalen ); + packet->cmd = cmd; + packet->headerlen = 0; + + /* create generic packet header */ + hlen = g_snprintf( header, sizeof( header ), "id=%s%c", purple_account_get_username( session->acc ), CP_REC_TERM ); /* client mxitid */ + + if ( session->http ) { + /* http connection only */ + hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "s=" ); + if ( session->http_sesid > 0 ) { + hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "%u%c", session->http_sesid, CP_FLD_TERM ); /* http session id */ + } + session->http_seqno++; + hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "%u%c", session->http_seqno, CP_REC_TERM ); /* http request sequence id */ + } + + hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "cm=%i%c", cmd, CP_REC_TERM ); /* packet command */ + + if ( !session->http ) { + /* socket connection only */ + packet->headerlen = g_snprintf( packet->header, sizeof( packet->header ), "ln=%i%c", ( datalen + hlen ), CP_REC_TERM ); /* packet length */ + } + + /* copy the header to packet */ + memcpy( packet->header + packet->headerlen, header, hlen ); + packet->headerlen += hlen; + + /* copy payload to packet */ + if ( datalen > 0 ) + memcpy( packet->data, data, datalen ); + packet->datalen = datalen; + + + /* shortcut */ + if ( ( session->queue.count == 0 ) && ( session->outack == 0 ) ) { + /* the queue is empty and there are no outstanding acks so we can write it directly */ + mxit_send_packet( session, packet ); + } + else { + /* we need to queue this packet */ + + if ( ( packet->cmd == CP_CMD_PING ) || ( packet->cmd == CP_CMD_POLL ) ) { + /* we do NOT queue HTTP poll nor socket ping packets */ + free_tx_packet( packet ); + return; + } + + purple_debug_info( MXIT_PLUGIN_ID, "queueing packet for later sending cmd=%i\n", cmd ); + if ( !push_tx_packet( session, packet ) ) { + /* packet could not be queued for transmission */ + mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Message Send Error" ), _( "Unable to process your request at this time" ) ); + free_tx_packet( packet ); + } + } +} + + +/*------------------------------------------------------------------------ + * Manage the packet send queue (send next packet, timeout's, etc). + * + * @param session The MXit session object + */ +static void mxit_manage_queue( struct MXitSession* session ) +{ + struct tx_packet* packet = NULL; + gint64 now = mxit_now_milli(); + + if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) { + /* we are not connected, so ignore the queue */ + return; + } + else if ( session->outack > 0 ) { + /* we are still waiting for an outstanding ACK from the MXit server */ + if ( session->last_tx <= mxit_now_milli() - ( MXIT_ACK_TIMEOUT * 1000 ) ) { + /* ack timeout! so we close the connection here */ + purple_debug_info( MXIT_PLUGIN_ID, "mxit_manage_queue: Timeout awaiting ACK for command '%i'\n", session->outack ); + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Timeout while waiting for a response from the MXit server." ) ); + } + return; + } + + /* + * the mxit server has flood detection and it prevents you from sending messages to fast. + * this is a self defense mechanism, a very annoying feature. so the client must ensure that + * it does not send messages too fast otherwise mxit will ignore the user for 30 seconds. + * this is what we are trying to avoid here.. + */ + if ( session->q_fast_timer_id == 0 ) { + /* the fast timer has not been set yet */ + if ( session->last_tx > ( now - MXIT_TX_DELAY ) ) { + /* we need to wait a little before sending the next packet, so schedule a wakeup call */ + gint64 tdiff = now - ( session->last_tx ); + guint delay = ( MXIT_TX_DELAY - tdiff ) + 9; + if ( delay <= 0 ) + delay = MXIT_TX_DELAY; + session->q_fast_timer_id = purple_timeout_add( delay, mxit_manage_queue_fast, session ); + } + else { + /* get the next packet from the queue to send */ + packet = pop_tx_packet( session ); + if ( packet != NULL ) { + /* there was a packet waiting to be sent to the server, now is the time to do something about it */ + + /* send the packet to MXit server */ + mxit_send_packet( session, packet ); + } + } + } +} + + +/*------------------------------------------------------------------------ + * Slow callback to manage the packet send queue. + * + * @param session The MXit session object + */ +gboolean mxit_manage_queue_slow( gpointer user_data ) +{ + struct MXitSession* session = (struct MXitSession*) user_data; + + mxit_manage_queue( session ); + + /* continue running */ + return TRUE; +} + + +/*------------------------------------------------------------------------ + * Fast callback to manage the packet send queue. + * + * @param session The MXit session object + */ +gboolean mxit_manage_queue_fast( gpointer user_data ) +{ + struct MXitSession* session = (struct MXitSession*) user_data; + + session->q_fast_timer_id = 0; + mxit_manage_queue( session ); + + /* stop running */ + return FALSE; +} + + +/*------------------------------------------------------------------------ + * Callback to manage HTTP server polling (HTTP connections ONLY) + * + * @param session The MXit session object + */ +gboolean mxit_manage_polling( gpointer user_data ) +{ + struct MXitSession* session = (struct MXitSession*) user_data; + gboolean poll = FALSE; + gint64 now = mxit_now_milli(); + gint64 rxdiff; + + if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) { + /* we only poll if we are actually logged in */ + return TRUE; + } + + /* calculate the time differences */ + rxdiff = now - session->last_rx; + + if ( rxdiff < MXIT_HTTP_POLL_MIN ) { + /* we received some reply a few moments ago, so reset the poll interval */ + session->http_interval = MXIT_HTTP_POLL_MIN; + } + else if ( session->http_last_poll < ( now - session->http_interval ) ) { + /* time to poll again */ + poll = TRUE; + + /* back-off some more with the polling */ + session->http_interval = session->http_interval + ( session->http_interval / 2 ); + if ( session->http_interval > MXIT_HTTP_POLL_MAX ) + session->http_interval = MXIT_HTTP_POLL_MAX; + } + + /* debugging */ + //purple_debug_info( MXIT_PLUGIN_ID, "POLL TIMER: %i (%i)\n", session->http_interval, rxdiff ); + + if ( poll ) { + /* send poll request */ + session->http_last_poll = mxit_now_milli(); + mxit_send_poll( session ); + } + + return TRUE; +} + + +/*======================================================================================================================== + * Send MXit operations. + */ + +/*------------------------------------------------------------------------ + * Send a ping/keepalive packet to MXit server. + * + * @param session The MXit session object + */ +void mxit_send_ping( struct MXitSession* session ) +{ + /* queue packet for transmission */ + mxit_queue_packet( session, NULL, 0, CP_CMD_PING ); +} + + +/*------------------------------------------------------------------------ + * Send a poll request to the HTTP server (HTTP connections ONLY). + * + * @param session The MXit session object + */ +void mxit_send_poll( struct MXitSession* session ) +{ + /* queue packet for transmission */ + mxit_queue_packet( session, NULL, 0, CP_CMD_POLL ); +} + + +/*------------------------------------------------------------------------ + * Send a logout packet to the MXit server. + * + * @param session The MXit session object + */ +void mxit_send_logout( struct MXitSession* session ) +{ + /* queue packet for transmission */ + mxit_queue_packet( session, NULL, 0, CP_CMD_LOGOUT ); +} + + +/*------------------------------------------------------------------------ + * Send a register packet to the MXit server. + * + * @param session The MXit session object + */ +void mxit_send_register( struct MXitSession* session ) +{ + struct MXitProfile* profile = session->profile; + const char* locale; + char data[CP_MAX_PACKET]; + int datalen; + char* clientVersion; + unsigned int features = MXIT_CP_FEATURES; + + locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE ); + + /* Voice and Video supported */ + if ( mxit_audio_enabled() && mxit_video_enabled() ) + features |= ( MXIT_CF_VOICE | MXIT_CF_VIDEO ); + else if ( mxit_audio_enabled() ) + features |= MXIT_CF_VOICE; + + /* generate client version string (eg, P-2.7.10-Y-PURPLE) */ + clientVersion = g_strdup_printf( "%c-%i.%i.%i-%s-%s", MXIT_CP_DISTCODE, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CP_ARCH, MXIT_CP_PLATFORM ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%s%c%i%c%s%c" /* "ms"=password\1version\1maxreplyLen\1name\1 */ + "%s%c%i%c%s%c%s%c" /* dateOfBirth\1gender\1location\1capabilities\1 */ + "%s%c%i%c%s%c%s" /* dc\1features\1dialingcode\1locale */ + "%c%i%c%i", /* \1protocolVer\1lastRosterUpdate */ + session->encpwd, CP_FLD_TERM, clientVersion, CP_FLD_TERM, CP_MAX_FILESIZE, CP_FLD_TERM, profile->nickname, CP_FLD_TERM, + profile->birthday, CP_FLD_TERM, ( profile->male ) ? 1 : 0, CP_FLD_TERM, MXIT_DEFAULT_LOC, CP_FLD_TERM, MXIT_CP_CAP, CP_FLD_TERM, + session->distcode, CP_FLD_TERM, features, CP_FLD_TERM, session->dialcode, CP_FLD_TERM, locale, + CP_FLD_TERM, MXIT_CP_PROTO_VESION, CP_FLD_TERM, 0 + ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_REGISTER ); + + g_free( clientVersion ); +} + + +/*------------------------------------------------------------------------ + * Send a login packet to the MXit server. + * + * @param session The MXit session object + */ +void mxit_send_login( struct MXitSession* session ) +{ + const char* splashId; + const char* locale; + char data[CP_MAX_PACKET]; + int datalen; + char* clientVersion; + unsigned int features = MXIT_CP_FEATURES; + + locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE ); + + /* Voice and Video supported */ + if ( mxit_audio_enabled() && mxit_video_enabled() ) + features |= ( MXIT_CF_VOICE | MXIT_CF_VIDEO ); + else if ( mxit_audio_enabled() ) + features |= MXIT_CF_VOICE; + + /* generate client version string (eg, P-2.7.10-Y-PURPLE) */ + clientVersion = g_strdup_printf( "%c-%i.%i.%i-%s-%s", MXIT_CP_DISTCODE, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CP_ARCH, MXIT_CP_PLATFORM ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%s%c%i%c" /* "ms"=password\1version\1getContacts\1 */ + "%s%c%s%c%i%c" /* capabilities\1dc\1features\1 */ + "%s%c%s%c" /* dialingcode\1locale\1 */ + "%i%c%i%c%i", /* maxReplyLen\1protocolVer\1lastRosterUpdate */ + session->encpwd, CP_FLD_TERM, clientVersion, CP_FLD_TERM, 1, CP_FLD_TERM, + MXIT_CP_CAP, CP_FLD_TERM, session->distcode, CP_FLD_TERM, features, CP_FLD_TERM, + session->dialcode, CP_FLD_TERM, locale, CP_FLD_TERM, + CP_MAX_FILESIZE, CP_FLD_TERM, MXIT_CP_PROTO_VESION, CP_FLD_TERM, 0 + ); + + /* include "custom resource" information */ + splashId = splash_current( session ); + if ( splashId != NULL ) + datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%ccr=%s", CP_REC_TERM, splashId ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_LOGIN ); + + g_free( clientVersion ); +} + + +/*------------------------------------------------------------------------ + * Send a chat message packet to the MXit server. + * + * @param session The MXit session object + * @param to The username of the recipient + * @param msg The message text + */ +void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup, gboolean is_command ) +{ + char data[CP_MAX_PACKET]; + char* markuped_msg; + int datalen; + int msgtype = ( is_command ? CP_MSGTYPE_COMMAND : CP_MSGTYPE_NORMAL ); + + /* first we need to convert the markup from libPurple to MXit format */ + if ( parse_markup ) + markuped_msg = mxit_convert_markup_tx( msg, &msgtype ); + else + markuped_msg = g_strdup( msg ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%s%c%i%c%i", /* "ms"=jid\1msg\1type\1flags */ + to, CP_FLD_TERM, markuped_msg, CP_FLD_TERM, msgtype, CP_FLD_TERM, CP_MSG_MARKUP | CP_MSG_EMOTICON + ); + + /* free the resources */ + g_free( markuped_msg ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_TX_MSG ); +} + + +/*------------------------------------------------------------------------ + * Send a extended profile request packet to the MXit server. + * + * @param session The MXit session object + * @param username Username who's profile is being requested (NULL = our own) + * @param nr_attribs Number of attributes being requested + * @param attribute The names of the attributes + */ +void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] ) +{ + char data[CP_MAX_PACKET]; + int datalen; + unsigned int i; + + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%i", /* "ms="mxitid\1nr_attributes */ + ( username ? username : "" ), CP_FLD_TERM, nr_attrib + ); + + /* add attributes */ + for ( i = 0; i < nr_attrib; i++ ) + datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, attribute[i] ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_GET ); +} + + +/*------------------------------------------------------------------------ + * Send an update profile packet to the MXit server. + * + * @param session The MXit session object + * @param password The new password to be used for logging in (optional) + * @param nr_attrib The number of attributes + * @param attributes String containing the attribute-name, attribute-type and value (seperated by '\01') + */ +void mxit_send_extprofile_update( struct MXitSession* session, const char* password, unsigned int nr_attrib, const char* attributes ) +{ + char data[CP_MAX_PACKET]; + gchar** parts = NULL; + int datalen; + unsigned int i; + + if ( attributes ) + parts = g_strsplit( attributes, "\01", 1 + ( nr_attrib * 3 ) ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%i", /* "ms"=password\1nr_attibutes */ + ( password ) ? password : "", CP_FLD_TERM, nr_attrib + ); + + /* add attributes */ + for ( i = 1; i < nr_attrib * 3; i+=3 ) { + if ( parts == NULL || parts[i] == NULL || parts[i + 1] == NULL || parts[i + 2] == NULL ) { + purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile update attributes = '%s' - nbr=%u\n", attributes, nr_attrib ); + g_strfreev( parts ); + return; + } + datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, + "%c%s%c%s%c%s", /* \1name\1type\1value */ + CP_FLD_TERM, parts[i], CP_FLD_TERM, parts[i + 1], CP_FLD_TERM, parts[i + 2] ); + } + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_SET ); + + /* freeup the memory */ + g_strfreev( parts ); +} + + +/*------------------------------------------------------------------------ + * Send packet to request list of suggested friends. + * + * @param session The MXit session object + * @param max Maximum number of results to return + * @param nr_attribs Number of attributes being requested + * @param attribute The names of the attributes + */ +void mxit_send_suggest_friends( struct MXitSession* session, int max, unsigned int nr_attrib, const char* attribute[] ) +{ + char data[CP_MAX_PACKET]; + int datalen; + unsigned int i; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%i%c%s%c%i%c%i%c%i", /* inputType \1 input \1 maxSuggestions \1 startIndex \1 numAttributes \1 name0 \1 name1 ... \1 nameN */ + CP_SUGGEST_FRIENDS, CP_FLD_TERM, "", CP_FLD_TERM, max, CP_FLD_TERM, 0, CP_FLD_TERM, nr_attrib ); + + /* add attributes */ + for ( i = 0; i < nr_attrib; i++ ) + datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, attribute[i] ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_SUGGESTCONTACTS ); +} + + +/*------------------------------------------------------------------------ + * Send packet to perform a search for users. + * + * @param session The MXit session object + * @param max Maximum number of results to return + * @param text The search text + * @param nr_attribs Number of attributes being requested + * @param attribute The names of the attributes + */ +void mxit_send_suggest_search( struct MXitSession* session, int max, const char* text, unsigned int nr_attrib, const char* attribute[] ) +{ + char data[CP_MAX_PACKET]; + int datalen; + unsigned int i; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%i%c%s%c%i%c%i%c%i", /* inputType \1 input \1 maxSuggestions \1 startIndex \1 numAttributes \1 name0 \1 name1 ... \1 nameN */ + CP_SUGGEST_SEARCH, CP_FLD_TERM, text, CP_FLD_TERM, max, CP_FLD_TERM, 0, CP_FLD_TERM, nr_attrib ); + + /* add attributes */ + for ( i = 0; i < nr_attrib; i++ ) + datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, attribute[i] ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_SUGGESTCONTACTS ); +} + + +/*------------------------------------------------------------------------ + * Send a presence update packet to the MXit server. + * + * @param session The MXit session object + * @param presence The presence (as per MXit types) + * @param statusmsg The status message (can be NULL) + */ +void mxit_send_presence( struct MXitSession* session, int presence, const char* statusmsg ) +{ + char data[CP_MAX_PACKET]; + int datalen; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%i%c", /* "ms"=show\1status */ + presence, CP_FLD_TERM + ); + + /* append status message (if one is set) */ + if ( statusmsg ) + datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%s", statusmsg ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_STATUS ); +} + + +/*------------------------------------------------------------------------ + * Send a mood update packet to the MXit server. + * + * @param session The MXit session object + * @param mood The mood (as per MXit types) + */ +void mxit_send_mood( struct MXitSession* session, int mood ) +{ + char data[CP_MAX_PACKET]; + int datalen; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%i", /* "ms"=mood */ + mood + ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_MOOD ); +} + + +/*------------------------------------------------------------------------ + * Send an invite contact packet to the MXit server. + * + * @param session The MXit session object + * @param username The username of the contact being invited + * @param mxitid Indicates the username is a MXitId. + * @param alias Our alias for the contact + * @param groupname Group in which contact should be stored. + * @param message Invite message + */ +void mxit_send_invite( struct MXitSession* session, const char* username, gboolean mxitid, const char* alias, const char* groupname, const char* message ) +{ + char data[CP_MAX_PACKET]; + int datalen; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%s%c%s%c%i%c%s%c%i", /* "ms"=group \1 username \1 alias \1 type \1 msg \1 isuserid */ + groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias, + CP_FLD_TERM, MXIT_TYPE_MXIT, CP_FLD_TERM, + ( message ? message : "" ), CP_FLD_TERM, + ( mxitid ? 0 : 1 ) + ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_INVITE ); +} + + +/*------------------------------------------------------------------------ + * Send a remove contact packet to the MXit server. + * + * @param session The MXit session object + * @param username The username of the contact being removed + */ +void mxit_send_remove( struct MXitSession* session, const char* username ) +{ + char data[CP_MAX_PACKET]; + int datalen; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s", /* "ms"=username */ + username + ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_REMOVE ); +} + + +/*------------------------------------------------------------------------ + * Send an accept subscription (invite) packet to the MXit server. + * + * @param session The MXit session object + * @param username The username of the contact being accepted + * @param alias Our alias for the contact + */ +void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias ) +{ + char data[CP_MAX_PACKET]; + int datalen; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%s%c%s", /* "ms"=username\1group\1alias */ + username, CP_FLD_TERM, "", CP_FLD_TERM, alias + ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_ALLOW ); +} + + +/*------------------------------------------------------------------------ + * Send an deny subscription (invite) packet to the MXit server. + * + * @param session The MXit session object + * @param username The username of the contact being denied + * @param reason The message describing the reason for the rejection (can be NULL). + */ +void mxit_send_deny_sub( struct MXitSession* session, const char* username, const char* reason ) +{ + char data[CP_MAX_PACKET]; + int datalen; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s", /* "ms"=username */ + username + ); + + /* append reason (if one is set) */ + if ( reason ) + datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, reason ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_DENY ); +} + + +/*------------------------------------------------------------------------ + * Send an update contact packet to the MXit server. + * + * @param session The MXit session object + * @param username The username of the contact being denied + * @param alias Our alias for the contact + * @param groupname Group in which contact should be stored. + */ +void mxit_send_update_contact( struct MXitSession* session, const char* username, const char* alias, const char* groupname ) +{ + char data[CP_MAX_PACKET]; + int datalen; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%s%c%s", /* "ms"=groupname\1username\1alias */ + groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias + ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_UPDATE ); +} + + +/*------------------------------------------------------------------------ + * Send a splash-screen click event packet. + * + * @param session The MXit session object + * @param splashid The identifier of the splash-screen + */ +void mxit_send_splashclick( struct MXitSession* session, const char* splashid ) +{ + char data[CP_MAX_PACKET]; + int datalen; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s", /* "ms"=splashId */ + splashid + ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_SPLASHCLICK ); +} + + +/*------------------------------------------------------------------------ + * Send a message event packet. + * + * @param session The MXit session object + * @param to The username of the original sender (ie, recipient of the event) + * @param id The identifier of the event (received in message) + * @param event Identified the type of event + */ +void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event ) +{ + char data[CP_MAX_PACKET]; + int datalen; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_msgevent: to=%s id=%s event=%i\n", to, id, event ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%s%c%i", /* "ms"=contactAddress \1 id \1 event */ + to, CP_FLD_TERM, id, CP_FLD_TERM, event + ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_MSGEVENT ); +} + + +/*------------------------------------------------------------------------ + * Send packet to create a MultiMX room. + * + * @param session The MXit session object + * @param groupname Name of the room to create + * @param nr_usernames Number of users in initial invite + * @param usernames The usernames of the users in the initial invite + */ +void mxit_send_groupchat_create( struct MXitSession* session, const char* groupname, int nr_usernames, const char* usernames[] ) +{ + char data[CP_MAX_PACKET]; + int datalen; + int i; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%i", /* "ms"=roomname\1nr_jids\1jid0\1..\1jidN */ + groupname, CP_FLD_TERM, nr_usernames + ); + + /* add usernames */ + for ( i = 0; i < nr_usernames; i++ ) + datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, usernames[i] ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_CREATE ); +} + + +/*------------------------------------------------------------------------ + * Send packet to invite users to existing MultiMX room. + * + * @param session The MXit session object + * @param roomid The unique RoomID for the MultiMx room. + * @param nr_usernames Number of users being invited + * @param usernames The usernames of the users being invited + */ +void mxit_send_groupchat_invite( struct MXitSession* session, const char* roomid, int nr_usernames, const char* usernames[] ) +{ + char data[CP_MAX_PACKET]; + int datalen; + int i; + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), + "ms=%s%c%i", /* "ms"=roomid\1nr_jids\1jid0\1..\1jidN */ + roomid, CP_FLD_TERM, nr_usernames + ); + + /* add usernames */ + for ( i = 0; i < nr_usernames; i++ ) + datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, usernames[i] ); + + /* queue packet for transmission */ + mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_INVITE ); +} + + +/*------------------------------------------------------------------------ + * Send a "send file direct" multimedia packet. + * + * @param session The MXit session object + * @param username The username of the recipient + * @param filename The name of the file being sent + * @param buf The content of the file + * @param buflen The length of the file contents + */ +void mxit_send_file( struct MXitSession* session, const char* username, const char* filename, const unsigned char* buf, int buflen ) +{ + char data[CP_MAX_PACKET]; + int datalen = 0; + gchar* chunk; + int size; + + purple_debug_info( MXIT_PLUGIN_ID, "SENDING FILE '%s' of %i bytes to user '%s'\n", filename, buflen, username ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), "ms=" ); + + /* map chunk header over data buffer */ + chunk = &data[datalen]; + + size = mxit_chunk_create_senddirect( chunk_data( chunk ), username, filename, buf, buflen ); + if ( size < 0 ) { + purple_debug_error( MXIT_PLUGIN_ID, "Error creating senddirect chunk (%i)\n", size ); + return; + } + + set_chunk_type( chunk, CP_CHUNK_DIRECT_SND ); + set_chunk_length( chunk, size ); + datalen += MXIT_CHUNK_HEADER_SIZE + size; + + /* send the byte stream to the mxit server */ + mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); +} + + +/*------------------------------------------------------------------------ + * Send a "reject file" multimedia packet. + * + * @param session The MXit session object + * @param fileid A unique ID that identifies this file + */ +void mxit_send_file_reject( struct MXitSession* session, const char* fileid ) +{ + char data[CP_MAX_PACKET]; + int datalen = 0; + gchar* chunk; + int size; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_reject\n" ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), "ms=" ); + + /* map chunk header over data buffer */ + chunk = &data[datalen]; + + size = mxit_chunk_create_reject( chunk_data( chunk ), fileid ); + if ( size < 0 ) { + purple_debug_error( MXIT_PLUGIN_ID, "Error creating reject chunk (%i)\n", size ); + return; + } + + set_chunk_type( chunk, CP_CHUNK_REJECT ); + set_chunk_length( chunk, size ); + datalen += MXIT_CHUNK_HEADER_SIZE + size; + + /* send the byte stream to the mxit server */ + mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); +} + + +/*------------------------------------------------------------------------ + * Send a "get file" multimedia packet. + * + * @param session The MXit session object + * @param fileid A unique ID that identifies this file + * @param filesize The number of bytes to retrieve + * @param offset Offset in file at which to start retrieving + */ +void mxit_send_file_accept( struct MXitSession* session, const char* fileid, int filesize, int offset ) +{ + char data[CP_MAX_PACKET]; + int datalen = 0; + gchar* chunk; + int size; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_accept\n" ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), "ms=" ); + + /* map chunk header over data buffer */ + chunk = &data[datalen]; + + size = mxit_chunk_create_get( chunk_data(chunk), fileid, filesize, offset ); + if ( size < 0 ) { + purple_debug_error( MXIT_PLUGIN_ID, "Error creating getfile chunk (%i)\n", size ); + return; + } + + set_chunk_type( chunk, CP_CHUNK_GET ); + set_chunk_length( chunk, size ); + datalen += MXIT_CHUNK_HEADER_SIZE + size; + + /* send the byte stream to the mxit server */ + mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); +} + + +/*------------------------------------------------------------------------ + * Send a "received file" multimedia packet. + * + * @param session The MXit session object + * @param status The status of the file-transfer + */ +void mxit_send_file_received( struct MXitSession* session, const char* fileid, short status ) +{ + char data[CP_MAX_PACKET]; + int datalen = 0; + gchar* chunk; + int size; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_received\n" ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), "ms=" ); + + /* map chunk header over data buffer */ + chunk = &data[datalen]; + + size = mxit_chunk_create_received( chunk_data(chunk), fileid, status ); + if ( size < 0 ) { + purple_debug_error( MXIT_PLUGIN_ID, "Error creating received chunk (%i)\n", size ); + return; + } + + set_chunk_type( chunk, CP_CHUNK_RECEIVED ); + set_chunk_length( chunk, size ); + datalen += MXIT_CHUNK_HEADER_SIZE + size; + + /* send the byte stream to the mxit server */ + mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); +} + + +/*------------------------------------------------------------------------ + * Send a "set avatar" multimedia packet. + * + * @param session The MXit session object + * @param data The avatar data + * @param buflen The length of the avatar data + */ +void mxit_set_avatar( struct MXitSession* session, const unsigned char* avatar, int avatarlen ) +{ + char data[CP_MAX_PACKET]; + int datalen = 0; + gchar* chunk; + int size; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_set_avatar: %i bytes\n", avatarlen ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), "ms=" ); + + /* map chunk header over data buffer */ + chunk = &data[datalen]; + + size = mxit_chunk_create_set_avatar( chunk_data(chunk), avatar, avatarlen ); + if ( size < 0 ) { + purple_debug_error( MXIT_PLUGIN_ID, "Error creating set avatar chunk (%i)\n", size ); + return; + } + + set_chunk_type( chunk, CP_CHUNK_SET_AVATAR ); + set_chunk_length( chunk, size ); + datalen += MXIT_CHUNK_HEADER_SIZE + size; + + /* send the byte stream to the mxit server */ + mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); +} + + +/*------------------------------------------------------------------------ + * Send a "get avatar" multimedia packet. + * + * @param session The MXit session object + * @param mxitId The username who's avatar to request + * @param avatarId The id of the avatar image (as string) + * @param data The avatar data + * @param buflen The length of the avatar data + */ +void mxit_get_avatar( struct MXitSession* session, const char* mxitId, const char* avatarId ) +{ + char data[CP_MAX_PACKET]; + int datalen = 0; + gchar* chunk; + int size; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_get_avatar: %s\n", mxitId ); + + /* convert the packet to a byte stream */ + datalen = g_snprintf( data, sizeof( data ), "ms=" ); + + /* map chunk header over data buffer */ + chunk = &data[datalen]; + + size = mxit_chunk_create_get_avatar( chunk_data(chunk), mxitId, avatarId ); + if ( size < 0 ) { + purple_debug_error( MXIT_PLUGIN_ID, "Error creating get avatar chunk (%i)\n", size ); + return; + } + + set_chunk_type( chunk, CP_CHUNK_GET_AVATAR ); + set_chunk_length( chunk, size ); + datalen += MXIT_CHUNK_HEADER_SIZE + size; + + /* send the byte stream to the mxit server */ + mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); +} + + +/*------------------------------------------------------------------------ + * Process a login message packet. + * + * @param session The MXit session object + * @param records The packet's data records + * @param rcount The number of data records + */ +static void mxit_parse_cmd_login( struct MXitSession* session, struct record** records, int rcount ) +{ + PurpleStatus* status; + int presence; + const char* statusmsg; + const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_FULLNAME, + CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL, + CP_PROFILE_MOBILENR, CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME, CP_PROFILE_RELATIONSHIP, CP_PROFILE_FLAGS }; + + purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN ); + + /* we were not yet logged in so we need to complete the login sequence here */ + session->flags |= MXIT_FLAG_LOGGEDIN; + purple_connection_update_progress( session->con, _( "Successfully Logged In..." ), 3, 4 ); + purple_connection_set_state( session->con, PURPLE_CONNECTION_CONNECTED ); + + /* save extra info if this is a HTTP connection */ + if ( session->http ) { + /* save the http server to use for this session */ + g_strlcpy( session->http_server, records[1]->fields[3]->data, sizeof( session->http_server ) ); + + /* save the session id */ + session->http_sesid = atoi( records[0]->fields[0]->data ); + } + + /* extract UserId (from protocol 5.9) */ + if ( records[1]->fcount >= 9 ) + session->uid = g_strdup( records[1]->fields[8]->data ); + + /* extract VoIP server (from protocol 6.2) */ + if ( records[1]->fcount >= 11 ) + g_strlcpy( session->voip_server, records[1]->fields[10]->data, sizeof( session->voip_server ) ); + + /* display the current splash-screen */ + if ( splash_popup_enabled( session ) ) + splash_display( session ); + + /* update presence status */ + status = purple_account_get_active_status( session->acc ); + presence = mxit_convert_presence( purple_status_get_id( status ) ); + statusmsg = purple_status_get_attr_string( status, "message" ); + + if ( ( presence != MXIT_PRESENCE_ONLINE ) || ( statusmsg ) ) { + /* when logging into MXit, your default presence is online. but with the UI, one can change + * the presence to whatever. in the case where its changed to a different presence setting + * we need to send an update to the server, otherwise the user's presence will be out of + * sync between the UI and MXit. + */ + char* statusmsg1 = purple_markup_strip_html( statusmsg ); + char* statusmsg2 = g_strndup( statusmsg1, CP_MAX_STATUS_MSG ); + + mxit_send_presence( session, presence, statusmsg2 ); + + g_free( statusmsg1 ); + g_free( statusmsg2 ); + } + + /* retrieve our MXit profile */ + mxit_send_extprofile_request( session, NULL, ARRAY_SIZE( profilelist ), profilelist ); +} + + +/*------------------------------------------------------------------------ + * Process a received message packet. + * + * @param session The MXit session object + * @param records The packet's data records + * @param rcount The number of data records + */ +static void mxit_parse_cmd_message( struct MXitSession* session, struct record** records, int rcount ) +{ + struct RXMsgData* mx = NULL; + char* message = NULL; + char* sender = NULL; + int msglen = 0; + int msgflags = 0; + int msgtype = 0; + + if ( ( rcount == 1 ) || ( records[0]->fcount < 2 ) || ( records[1]->fcount == 0 ) || ( records[1]->fields[0]->len == 0 ) ) { + /* packet contains no message or an empty message */ + return; + } + + message = records[1]->fields[0]->data; + msglen = strlen( message ); + + /* strip off dummy domain */ + sender = records[0]->fields[0]->data; + mxit_strip_domain( sender ); + +#ifdef DEBUG_PROTOCOL + purple_debug_info( MXIT_PLUGIN_ID, "Message received from '%s'\n", sender ); +#endif + + /* decode message flags (if any) */ + if ( records[0]->fcount >= 5 ) + msgflags = atoi( records[0]->fields[4]->data ); + msgtype = atoi( records[0]->fields[2]->data ); + + if ( msgflags & CP_MSG_PWD_ENCRYPTED ) { + /* this is a password encrypted message. we do not currently support those so ignore it */ + PurpleBuddy* buddy; + const char* name; + char msg[128]; + + buddy = purple_blist_find_buddy( session->acc, sender ); + if ( buddy ) + name = purple_buddy_get_alias( buddy ); + else + name = sender; + g_snprintf( msg, sizeof( msg ), _( "%s sent you an encrypted message, but it is not supported on this client." ), name ); + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Error" ), msg ); + return; + } + else if ( msgflags & CP_MSG_TL_ENCRYPTED ) { + /* this is a transport-layer encrypted message. */ + message = mxit_decrypt_message( session, message ); + if ( !message ) { + /* could not be decrypted */ + serv_got_im( session->con, sender, _( "An encrypted message was received which could not be decrypted." ), PURPLE_MESSAGE_ERROR, time( NULL ) ); + return; + } + } + + if ( msgflags & CP_MSG_NOTIFY_DELIVERY ) { + /* delivery notification is requested */ + if ( records[0]->fcount >= 4 ) + mxit_send_msgevent( session, sender, records[0]->fields[3]->data, CP_MSGEVENT_DELIVERED ); + } + + /* create and initialise new markup struct */ + mx = g_new0( struct RXMsgData, 1 ); + mx->msg = g_string_sized_new( msglen ); + mx->session = session; + mx->from = g_strdup( sender ); + mx->timestamp = atoi( records[0]->fields[1]->data ); + mx->got_img = FALSE; + mx->chatid = -1; + mx->img_count = 0; + + /* update list of active chats */ + if ( !find_active_chat( session->active_chats, mx->from ) ) { + session->active_chats = g_list_append( session->active_chats, g_strdup( mx->from ) ); + } + + if ( is_multimx_contact( session, mx->from ) ) { + /* this is a MultiMx chatroom message */ + multimx_message_received( mx, message, msglen, msgtype, msgflags ); + } + else { + mxit_parse_markup( mx, message, msglen, msgtype, msgflags ); + } + + /* we are now done parsing the message */ + mx->converted = TRUE; + if ( mx->img_count == 0 ) { + /* we have all the data we need for this message to be displayed now. */ + mxit_show_message( mx ); + } + else { + /* this means there are still images outstanding for this message and + * still need to wait for them before we can display the message. + * so the image received callback function will eventually display + * the message. */ + } + + /* cleanup */ + if ( msgflags & CP_MSG_TL_ENCRYPTED ) + g_free( message ); +} + + +/*------------------------------------------------------------------------ + * Process a received subscription request packet. + * + * @param session The MXit session object + * @param records The packet's data records + * @param rcount The number of data records + */ +static void mxit_parse_cmd_new_sub( struct MXitSession* session, struct record** records, int rcount ) +{ + struct contact* contact; + struct record* rec; + int i; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_new_sub (%i recs)\n", rcount ); + + for ( i = 0; i < rcount; i++ ) { + rec = records[i]; + + if ( rec->fcount < 4 ) { + purple_debug_error( MXIT_PLUGIN_ID, "BAD SUBSCRIPTION RECORD! %i fields\n", rec->fcount ); + break; + } + + /* build up a new contact info struct */ + contact = g_new0( struct contact, 1 ); + + g_strlcpy( contact->username, rec->fields[0]->data, sizeof( contact->username ) ); + mxit_strip_domain( contact->username ); /* remove dummy domain */ + g_strlcpy( contact->alias, rec->fields[1]->data, sizeof( contact->alias ) ); + contact->type = atoi( rec->fields[2]->data ); + + if ( rec->fcount >= 5 ) { + /* there is a personal invite message attached */ + if ( ( rec->fields[4]->data ) && ( *rec->fields[4]->data ) ) + contact->msg = strdup( rec->fields[4]->data ); + } + + /* handle the subscription */ + if ( contact-> type == MXIT_TYPE_MULTIMX ) { /* subscription to a MultiMX room */ + char* creator = NULL; + + if ( rec->fcount >= 6 ) + creator = rec->fields[5]->data; + + multimx_invite( session, contact, creator ); + } + else + mxit_new_subscription( session, contact ); + } +} + + +/*------------------------------------------------------------------------ + * Parse the received presence value, and ensure that it is supported. + * + * @param value The received presence value. + * @return A valid presence value. + */ +static short mxit_parse_presence( const char* value ) +{ + short presence = atoi( value ); + + /* ensure that the presence value is valid */ + switch ( presence ) { + case MXIT_PRESENCE_OFFLINE : + case MXIT_PRESENCE_ONLINE : + case MXIT_PRESENCE_AWAY : + case MXIT_PRESENCE_DND : + return presence; + + default : + return MXIT_PRESENCE_ONLINE; + } +} + + +/*------------------------------------------------------------------------ + * Process a received contact update packet. + * + * @param session The MXit session object + * @param records The packet's data records + * @param rcount The number of data records + */ +static void mxit_parse_cmd_contact( struct MXitSession* session, struct record** records, int rcount ) +{ + struct contact* contact = NULL; + struct record* rec; + int i; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_contact (%i recs)\n", rcount ); + + for ( i = 0; i < rcount; i++ ) { + rec = records[i]; + + if ( rec->fcount < 6 ) { + purple_debug_error( MXIT_PLUGIN_ID, "BAD CONTACT RECORD! %i fields\n", rec->fcount ); + break; + } + + /* build up a new contact info struct */ + contact = g_new0( struct contact, 1 ); + + g_strlcpy( contact->groupname, rec->fields[0]->data, sizeof( contact->groupname ) ); + g_strlcpy( contact->username, rec->fields[1]->data, sizeof( contact->username ) ); + mxit_strip_domain( contact->username ); /* remove dummy domain */ + g_strlcpy( contact->alias, rec->fields[2]->data, sizeof( contact->alias ) ); + + contact->presence = mxit_parse_presence( rec->fields[3]->data ); + contact->type = atoi( rec->fields[4]->data ); + contact->mood = atoi( rec->fields[5]->data ); + + if ( rec->fcount > 6 ) { + /* added in protocol 5.9 - flags & subtype */ + contact->flags = atoi( rec->fields[6]->data ); + contact->subtype = rec->fields[7]->data[0]; + } + if ( rec->fcount > 8 ) { + /* added in protocol 6.0 - reject message */ + contact->msg = g_strdup( rec->fields[8]->data ); + } + + /* add the contact to the buddy list */ + if ( contact-> type == MXIT_TYPE_MULTIMX ) /* contact is a MultiMX room */ + multimx_created( session, contact ); + else + mxit_update_contact( session, contact ); + } + + if ( !( session->flags & MXIT_FLAG_FIRSTROSTER ) ) { + session->flags |= MXIT_FLAG_FIRSTROSTER; + mxit_update_blist( session ); + } +} + + +/*------------------------------------------------------------------------ + * Process a received presence update packet. + * + * @param session The MXit session object + * @param records The packet's data records + * @param rcount The number of data records + */ +static void mxit_parse_cmd_presence( struct MXitSession* session, struct record** records, int rcount ) +{ + int i; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_presence (%i recs)\n", rcount ); + + for ( i = 0; i < rcount; i++ ) { + struct record* rec = records[i]; + int flags = 0; + + if ( rec->fcount < 6 ) { + purple_debug_error( MXIT_PLUGIN_ID, "BAD PRESENCE RECORD! %i fields\n", rec->fcount ); + break; + } + + /* + * The format of the record is: + * contactAddressN \1 presenceN \1 moodN \1 customMoodN \1 statusMsgN \1 avatarIdN [ \1 flagsN ] + */ + mxit_strip_domain( rec->fields[0]->data ); /* contactAddress */ + + if ( rec->fcount >= 7 ) /* flags field is included */ + flags = atoi( rec->fields[6]->data ); + + mxit_update_buddy_presence( session, rec->fields[0]->data, mxit_parse_presence( rec->fields[1]->data ), atoi( rec->fields[2]->data ), + rec->fields[3]->data, rec->fields[4]->data, flags ); + mxit_update_buddy_avatar( session, rec->fields[0]->data, rec->fields[5]->data ); + } +} + + +/*------------------------------------------------------------------------ + * Process a received extended profile packet. + * + * @param session The MXit session object + * @param records The packet's data records + * @param rcount The number of data records + */ +static void mxit_parse_cmd_extprofile( struct MXitSession* session, struct record** records, int rcount ) +{ + const char* mxitId = records[0]->fields[0]->data; + struct MXitProfile* profile = NULL; + int count; + int i; + const char* avatarId = NULL; + char* statusMsg = NULL; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_extprofile: profile for '%s'\n", mxitId ); + + if ( ( records[0]->fields[0]->len == 0 ) || ( session->uid && ( strcmp( session->uid, records[0]->fields[0]->data ) == 0 ) ) ) { + /* No UserId or Our UserId provided, so this must be our own profile information */ + if ( session->profile == NULL ) + session->profile = g_new0( struct MXitProfile, 1 ); + profile = session->profile; + } + else { + /* is a buddy's profile */ + profile = g_new0( struct MXitProfile, 1 ); + } + + /* set the count for attributes */ + count = atoi( records[0]->fields[1]->data ); + + for ( i = 0; i < count; i++ ) { + char* fname; + char* fvalue; + char* fstatus; + int f = ( i * 3 ) + 2; + + fname = records[0]->fields[f]->data; /* field name */ + fvalue = records[0]->fields[f + 1]->data; /* field value */ + fstatus = records[0]->fields[f + 2]->data; /* field status */ + + /* first check the status on the returned attribute */ + if ( fstatus[0] != '0' ) { + /* error: attribute requested was NOT found */ + purple_debug_error( MXIT_PLUGIN_ID, "Bad profile status on attribute '%s' \n", fname ); + continue; + } + + if ( strcmp( CP_PROFILE_BIRTHDATE, fname ) == 0 ) { + /* birthdate */ + if ( records[0]->fields[f + 1]->len > 10 ) { + fvalue[10] = '\0'; + records[0]->fields[f + 1]->len = 10; + } + memcpy( profile->birthday, fvalue, records[0]->fields[f + 1]->len ); + } + else if ( strcmp( CP_PROFILE_GENDER, fname ) == 0 ) { + /* gender */ + profile->male = ( fvalue[0] == '1' ); + } + else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) { + /* nickname */ + g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) ); + } + else if ( strcmp( CP_PROFILE_STATUS, fname ) == 0 ) { + /* status message - just keep a reference to the value */ + statusMsg = g_markup_escape_text( fvalue, -1 ); + } + else if ( strcmp( CP_PROFILE_AVATAR, fname ) == 0 ) { + /* avatar id - just keep a reference to the value */ + avatarId = fvalue; + } + else if ( strcmp( CP_PROFILE_TITLE, fname ) == 0 ) { + /* title */ + g_strlcpy( profile->title, fvalue, sizeof( profile->title ) ); + } + else if ( strcmp( CP_PROFILE_FIRSTNAME, fname ) == 0 ) { + /* first name */ + g_strlcpy( profile->firstname, fvalue, sizeof( profile->firstname ) ); + } + else if ( strcmp( CP_PROFILE_LASTNAME, fname ) == 0 ) { + /* last name */ + g_strlcpy( profile->lastname, fvalue, sizeof( profile->lastname ) ); + } + else if ( strcmp( CP_PROFILE_EMAIL, fname ) == 0 ) { + /* email address */ + g_strlcpy( profile->email, fvalue, sizeof( profile->email ) ); + } + else if ( strcmp( CP_PROFILE_MOBILENR, fname ) == 0 ) { + /* mobile number */ + g_strlcpy( profile->mobilenr, fvalue, sizeof( profile->mobilenr ) ); + } + else if ( strcmp( CP_PROFILE_REGCOUNTRY, fname ) == 0 ) { + /* registered country */ + g_strlcpy( profile->regcountry, fvalue, sizeof( profile->regcountry ) ); + } + else if ( strcmp( CP_PROFILE_FLAGS, fname ) == 0 ) { + /* profile flags */ + profile->flags = g_ascii_strtoll( fvalue, NULL, 10 ); + } + else if ( strcmp( CP_PROFILE_LASTSEEN, fname ) == 0 ) { + /* last seen online */ + profile->lastonline = g_ascii_strtoll( fvalue, NULL, 10 ); + } + else if ( strcmp( CP_PROFILE_WHEREAMI, fname ) == 0 ) { + /* where am I */ + g_strlcpy( profile->whereami, fvalue, sizeof( profile->whereami ) ); + } + else if ( strcmp( CP_PROFILE_ABOUTME, fname ) == 0) { + /* about me */ + g_strlcpy( profile->aboutme, fvalue, sizeof( profile->aboutme ) ); + } + else if ( strcmp( CP_PROFILE_RELATIONSHIP, fname ) == 0) { + /* relatinship status */ + profile->relationship = strtol( fvalue, NULL, 10 ); + } + else { + /* invalid profile attribute */ + purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile attribute received '%s' \n", fname ); + } + } + + if ( profile != session->profile ) { + /* not our own profile */ + struct contact* contact = NULL; + + contact = get_mxit_invite_contact( session, mxitId ); + if ( contact ) { + /* this is an invite, so update its profile info */ + if ( ( statusMsg ) && ( *statusMsg ) ) { + /* update the status message */ + if ( contact->statusMsg ) + g_free( contact->statusMsg ); + contact->statusMsg = strdup( statusMsg ); + } + else + contact->statusMsg = NULL; + if ( contact->profile ) + g_free( contact->profile ); + contact->profile = profile; + if ( ( avatarId ) && ( *avatarId ) ) { + /* avatar must be requested for this invite before we can display it */ + mxit_get_avatar( session, mxitId, avatarId ); + if ( contact->avatarId ) + g_free( contact->avatarId ); + contact->avatarId = strdup( avatarId ); + } + else { + /* display what we have */ + contact->avatarId = NULL; + mxit_show_profile( session, mxitId, profile ); + } + } + else { + /* this is a contact */ + if ( avatarId ) + mxit_update_buddy_avatar( session, mxitId, avatarId ); + + if ( ( statusMsg ) && ( *statusMsg ) ) { + /* update the status message */ + PurpleBuddy* buddy = NULL; + + buddy = purple_blist_find_buddy( session->acc, mxitId ); + if ( buddy ) { + contact = purple_buddy_get_protocol_data( buddy ); + if ( contact ) { + if ( contact->statusMsg ) + g_free( contact->statusMsg ); + contact->statusMsg = strdup( statusMsg ); + } + } + } + + /* show the profile */ + mxit_show_profile( session, mxitId, profile ); + g_free( profile ); + } + } + + g_free( statusMsg ); +} + + +/*------------------------------------------------------------------------ + * Process a received suggest-contacts packet. + * + * @param session The MXit session object + * @param records The packet's data records + * @param rcount The number of data records + */ +static void mxit_parse_cmd_suggestcontacts( struct MXitSession* session, struct record** records, int rcount ) +{ + GList* entries = NULL; + int searchType; + int maxResults; + int count; + int i; + + /* + * searchType \1 numSuggestions \1 total \1 numAttributes \1 name0 \1 name1 \1 ... \1 nameN \0 + * userid \1 contactType \1 value0 \1 value1 ... valueN \0 + * ... + * userid \1 contactType \1 value0 \1 value1 ... valueN + */ + + /* the type of results */ + searchType = atoi( records[0]->fields[0]->data ); + + /* the maximum number of results */ + maxResults = atoi( records[0]->fields[2]->data ); + + /* set the count for attributes */ + count = atoi( records[0]->fields[3]->data ); + + for ( i = 1; i < rcount; i ++ ) { + struct record* rec = records[i]; + struct MXitProfile* profile = g_new0( struct MXitProfile, 1 ); + int j; + + g_strlcpy( profile->userid, rec->fields[0]->data, sizeof( profile->userid ) ); + // TODO: ContactType - User or Service + + for ( j = 0; j < count; j++ ) { + char* fname; + char* fvalue = ""; + + fname = records[0]->fields[4 + j]->data; /* field name */ + if ( records[i]->fcount > ( 2 + j ) ) + fvalue = records[i]->fields[2 + j]->data; /* field value */ + + purple_debug_info( MXIT_PLUGIN_ID, " %s: field='%s' value='%s'\n", profile->userid, fname, fvalue ); + + if ( strcmp( CP_PROFILE_BIRTHDATE, fname ) == 0 ) { + /* birthdate */ + g_strlcpy( profile->birthday, fvalue, sizeof( profile->birthday ) ); + } + else if ( strcmp( CP_PROFILE_FIRSTNAME, fname ) == 0 ) { + /* first name */ + g_strlcpy( profile->firstname, fvalue, sizeof( profile->firstname ) ); + } + else if ( strcmp( CP_PROFILE_LASTNAME, fname ) == 0 ) { + /* last name */ + g_strlcpy( profile->lastname, fvalue, sizeof( profile->lastname ) ); + } + else if ( strcmp( CP_PROFILE_GENDER, fname ) == 0 ) { + /* gender */ + profile->male = ( fvalue[0] == '1' ); + } + else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) { + /* nickname */ + g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) ); + } + else if ( strcmp( CP_PROFILE_WHEREAMI, fname ) == 0 ) { + /* where am I */ + g_strlcpy( profile->whereami, fvalue, sizeof( profile->whereami ) ); + } + /* ignore other attibutes */ + } + + entries = g_list_append( entries, profile ); + } + + /* display */ + mxit_show_search_results( session, searchType, maxResults, entries ); + + /* cleanup */ + g_list_foreach( entries, (GFunc)g_free, NULL ); +} + +/*------------------------------------------------------------------------ + * Process a received message event packet. + * + * @param session The MXit session object + * @param records The packet's data records + * @param rcount The number of data records + */ +static void mxit_parse_cmd_msgevent( struct MXitSession* session, struct record** records, int rcount ) +{ + int event; + + /* + * contactAddress \1 dateTime \1 id \1 event + */ + + /* strip off dummy domain */ + mxit_strip_domain( records[0]->fields[0]->data ); + + event = atoi( records[0]->fields[3]->data ); + + switch ( event ) { + case CP_MSGEVENT_TYPING : /* user is typing */ + case CP_MSGEVENT_ANGRY : /* user is typing angrily */ + serv_got_typing( session->con, records[0]->fields[0]->data, 0, PURPLE_IM_TYPING ); + break; + + case CP_MSGEVENT_STOPPED : /* user has stopped typing */ + serv_got_typing_stopped( session->con, records[0]->fields[0]->data ); + break; + + case CP_MSGEVENT_ERASING : /* user is erasing text */ + case CP_MSGEVENT_DELIVERED : /* message was delivered */ + case CP_MSGEVENT_DISPLAYED : /* message was viewed */ + /* these are currently not supported by libPurple */ + break; + + default: + purple_debug_error( MXIT_PLUGIN_ID, "Unknown message event received (%i)\n", event ); + } +} + + +/*------------------------------------------------------------------------ + * Return the length of a multimedia chunk + * + * @return The actual chunk data length in bytes + */ +static int get_chunk_len( const char* chunkdata ) +{ + int* sizeptr; + + sizeptr = (int*) &chunkdata[1]; /* we skip the first byte (type field) */ + + return ntohl( *sizeptr ); +} + + +/*------------------------------------------------------------------------ + * Process a received multimedia packet. + * + * @param session The MXit session object + * @param records The packet's data records + * @param rcount The number of data records + */ +static void mxit_parse_cmd_media( struct MXitSession* session, struct record** records, int rcount ) +{ + char type; + int size; + + type = records[0]->fields[0]->data[0]; + size = get_chunk_len( records[0]->fields[0]->data ); + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_media (%i records) (%i bytes)\n", rcount, size ); + + /* supported chunked data types */ + switch ( type ) { + case CP_CHUNK_CUSTOM : /* custom resource */ + { + struct cr_chunk chunk; + + /* decode the chunked data */ + memset( &chunk, 0, sizeof( struct cr_chunk ) ); + mxit_chunk_parse_cr( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); + + purple_debug_info( MXIT_PLUGIN_ID, "chunk info id=%s handle=%s op=%i\n", chunk.id, chunk.handle, chunk.operation ); + + /* this is a splash-screen operation */ + if ( strcmp( chunk.handle, HANDLE_SPLASH2 ) == 0 ) { + if ( chunk.operation == CR_OP_UPDATE ) { /* update the splash-screen */ + struct splash_chunk *splash = chunk.resources->data; // TODO: Fix - assuming 1st resource is splash + gboolean clickable = ( g_list_length( chunk.resources ) > 1 ); // TODO: Fix - if 2 resources, then is clickable + + if ( splash != NULL ) + splash_update( session, chunk.id, splash->data, splash->datalen, clickable ); + } + else if ( chunk.operation == CR_OP_REMOVE ) /* remove the splash-screen */ + splash_remove( session ); + } + + /* cleanup custom resources */ + g_list_foreach( chunk.resources, (GFunc)g_free, NULL ); + + } + break; + + case CP_CHUNK_OFFER : /* file offer */ + { + struct offerfile_chunk chunk; + + /* decode the chunked data */ + memset( &chunk, 0, sizeof( struct offerfile_chunk ) ); + mxit_chunk_parse_offer( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); + + /* process the offer */ + mxit_xfer_rx_offer( session, chunk.username, chunk.filename, chunk.filesize, chunk.fileid ); + } + break; + + case CP_CHUNK_GET : /* get file response */ + { + struct getfile_chunk chunk; + + /* decode the chunked data */ + memset( &chunk, 0, sizeof( struct getfile_chunk ) ); + mxit_chunk_parse_get( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); + + /* process the getfile */ + mxit_xfer_rx_file( session, chunk.fileid, chunk.data, chunk.length ); + } + break; + + case CP_CHUNK_GET_AVATAR : /* get avatars */ + { + struct getavatar_chunk chunk; + struct contact* contact = NULL; + + /* decode the chunked data */ + memset( &chunk, 0, sizeof( struct getavatar_chunk ) ); + mxit_chunk_parse_get_avatar( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); + + /* update avatar image */ + if ( chunk.data ) { + purple_debug_info( MXIT_PLUGIN_ID, "updating avatar for contact '%s'\n", chunk.mxitid ); + + contact = get_mxit_invite_contact( session, chunk.mxitid ); + if ( contact ) { + /* this is an invite (add image to the internal image store) */ + contact->imgid = purple_imgstore_new_with_id( g_memdup( chunk.data, chunk.length ), chunk.length, NULL ); + /* show the profile */ + mxit_show_profile( session, chunk.mxitid, contact->profile ); + } + else { + /* this is a contact's avatar, so update it */ + purple_buddy_icons_set_for_user( session->acc, chunk.mxitid, g_memdup( chunk.data, chunk.length ), chunk.length, chunk.avatarid ); + } + } + } + break; + + case CP_CHUNK_SET_AVATAR : + /* this is a reply packet to a set avatar request. no action is required */ + break; + + case CP_CHUNK_DIRECT_SND : + /* this is a ack for a file send. */ + { + struct sendfile_chunk chunk; + + memset( &chunk, 0, sizeof( struct sendfile_chunk ) ); + mxit_chunk_parse_sendfile( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); + + purple_debug_info( MXIT_PLUGIN_ID, "file-send send to '%s' [status=%i message='%s']\n", chunk.username, chunk.status, chunk.statusmsg ); + + if ( chunk.status != 0 ) /* not success */ + mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "File Send Failed" ), chunk.statusmsg ); + } + break; + + case CP_CHUNK_RECEIVED : + /* this is a ack for a file received. no action is required */ + break; + + default : + purple_debug_error( MXIT_PLUGIN_ID, "Unsupported chunked data packet type received (%i)\n", type ); + break; + } +} + + +/*------------------------------------------------------------------------ + * Handle a redirect sent from the MXit server. + * + * @param session The MXit session object + * @param url The redirect information + */ +static void mxit_perform_redirect( struct MXitSession* session, const char* url ) +{ + gchar** parts; + gchar** host; + int type; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_perform_redirect: %s\n", url ); + + /* tokenize the URL string */ + parts = g_strsplit( url, ";", 0 ); + + /* Part 1: protocol://host:port */ + host = g_strsplit( parts[0], ":", 4 ); + if ( strcmp( host[0], "socket" ) == 0 ) { + /* redirect to a MXit socket proxy */ + g_strlcpy( session->server, &host[1][2], sizeof( session->server ) ); + session->port = atoi( host[2] ); + } + else { + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Cannot perform redirect using the specified protocol" ) ); + goto redirect_fail; + } + + /* Part 2: type of redirect */ + type = atoi( parts[1] ); + if ( type == CP_REDIRECT_PERMANENT ) { + /* permanent redirect, so save new MXit server and port */ + purple_account_set_string( session->acc, MXIT_CONFIG_SERVER_ADDR, session->server ); + purple_account_set_int( session->acc, MXIT_CONFIG_SERVER_PORT, session->port ); + } + + /* Part 3: message (optional) */ + if ( parts[2] != NULL ) + purple_connection_notice( session->con, parts[2] ); + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_perform_redirect: %s redirect to %s:%i\n", + ( type == CP_REDIRECT_PERMANENT ) ? "Permanent" : "Temporary", session->server, session->port ); + + /* perform the re-connect to the new MXit server */ + mxit_reconnect( session ); + +redirect_fail: + g_strfreev( parts ); + g_strfreev( host ); +} + + +/*------------------------------------------------------------------------ + * Process a success response received from the MXit server. + * + * @param session The MXit session object + * @param packet The received packet + */ +static int process_success_response( struct MXitSession* session, struct rx_packet* packet ) +{ + /* ignore ping/poll packets */ + if ( ( packet->cmd != CP_CMD_PING ) && ( packet->cmd != CP_CMD_POLL ) ) + session->last_rx = mxit_now_milli(); + + /* + * when we pass the packet records to the next level for parsing + * we minus 3 records because 1) the first record is the packet + * type 2) packet reply status 3) the last record is bogus + */ + + /* packet command */ + switch ( packet->cmd ) { + + case CP_CMD_REGISTER : + /* fall through, when registeration successful, MXit will auto login */ + case CP_CMD_LOGIN : + /* login response */ + if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) { + mxit_parse_cmd_login( session, &packet->records[2], packet->rcount - 3 ); + } + break; + + case CP_CMD_LOGOUT : + /* logout response */ + session->flags &= ~MXIT_FLAG_LOGGEDIN; + purple_account_disconnect( session->acc ); + + /* note: + * we do not prompt the user here for a reconnect, because this could be the user + * logging in with his phone. so we just disconnect the account otherwise + * mxit will start to bounce between the phone and pidgin. also could be a valid + * disconnect selected by the user. + */ + return -1; + + case CP_CMD_CONTACT : + /* contact update */ + mxit_parse_cmd_contact( session, &packet->records[2], packet->rcount - 3 ); + break; + + case CP_CMD_PRESENCE : + /* presence update */ + mxit_parse_cmd_presence( session, &packet->records[2], packet->rcount - 3 ); + break; + + case CP_CMD_RX_MSG : + /* incoming message (no bogus record) */ + mxit_parse_cmd_message( session, &packet->records[2], packet->rcount - 2 ); + break; + + case CP_CMD_NEW_SUB : + /* new subscription request */ + mxit_parse_cmd_new_sub( session, &packet->records[2], packet->rcount - 3 ); + break; + + case CP_CMD_MEDIA : + /* multi-media message */ + mxit_parse_cmd_media( session, &packet->records[2], packet->rcount - 2 ); + break; + + case CP_CMD_EXTPROFILE_GET : + /* profile update */ + mxit_parse_cmd_extprofile( session, &packet->records[2], packet->rcount - 2 ); + break; + + case CP_CMD_SUGGESTCONTACTS : + /* suggest contacts */ + mxit_parse_cmd_suggestcontacts( session, &packet->records[2], packet->rcount - 2 ); + break; + + case CP_CMD_GOT_MSGEVENT : + /* received message event */ + mxit_parse_cmd_msgevent( session, &packet->records[2], packet->rcount - 2 ); + break; + + case CP_CMD_MOOD : + /* mood update */ + case CP_CMD_UPDATE : + /* update contact information */ + case CP_CMD_ALLOW : + /* allow subscription ack */ + case CP_CMD_DENY : + /* deny subscription ack */ + case CP_CMD_INVITE : + /* invite contact ack */ + case CP_CMD_REMOVE : + /* remove contact ack */ + case CP_CMD_TX_MSG : + /* outgoing message ack */ + case CP_CMD_STATUS : + /* presence update ack */ + case CP_CMD_GRPCHAT_CREATE : + /* create groupchat */ + case CP_CMD_GRPCHAT_INVITE : + /* groupchat invite */ + case CP_CMD_PING : + /* ping reply */ + case CP_CMD_POLL : + /* HTTP poll reply */ + case CP_CMD_EXTPROFILE_SET : + /* profile update */ + // TODO: Protocol 6.2 indicates status for each attribute, and current value. + case CP_CMD_SPLASHCLICK : + /* splash-screen clickthrough */ + case CP_CMD_MSGEVENT : + /* event message */ + break; + + default : + /* unknown packet */ + purple_debug_error( MXIT_PLUGIN_ID, "Received unknown client packet (cmd = %i)\n", packet->cmd ); + } + + return 0; +} + + +/*------------------------------------------------------------------------ + * Process an error response received from the MXit server. + * + * @param session The MXit session object + * @param packet The received packet + */ +static int process_error_response( struct MXitSession* session, struct rx_packet* packet ) +{ + char errmsg[256]; + const char* errdesc; + + /* set the error description to be shown to the user */ + if ( packet->errmsg ) + errdesc = packet->errmsg; + else + errdesc = _( "An internal MXit server error occurred." ); + + purple_debug_info( MXIT_PLUGIN_ID, "Error Reply %i:%s\n", packet->errcode, errdesc ); + + if ( packet->errcode == MXIT_ERRCODE_LOGGEDOUT ) { + /* we are not currently logged in, so we need to reconnect */ + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( errdesc ) ); + } + + /* packet command */ + switch ( packet->cmd ) { + + case CP_CMD_REGISTER : + case CP_CMD_LOGIN : + if ( packet->errcode == MXIT_ERRCODE_REDIRECT ) { + mxit_perform_redirect( session, packet->errmsg ); + return 0; + } + else { + g_snprintf( errmsg, sizeof( errmsg ), _( "Login error: %s (%i)" ), errdesc, packet->errcode ); + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errmsg ); + return -1; + } + case CP_CMD_LOGOUT : + g_snprintf( errmsg, sizeof( errmsg ), _( "Logout error: %s (%i)" ), errdesc, packet->errcode ); + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _( errmsg ) ); + return -1; + case CP_CMD_CONTACT : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Error" ), _( errdesc ) ); + break; + case CP_CMD_RX_MSG : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Error" ), _( errdesc ) ); + break; + case CP_CMD_TX_MSG : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Sending Error" ), _( errdesc ) ); + break; + case CP_CMD_STATUS : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Status Error" ), _( errdesc ) ); + break; + case CP_CMD_MOOD : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Mood Error" ), _( errdesc ) ); + break; + case CP_CMD_KICK : + /* + * the MXit server sends this packet if we were idle for too long. + * to stop the server from closing this connection we need to resend + * the login packet. + */ + mxit_send_login( session ); + break; + case CP_CMD_INVITE : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Invitation Error" ), _( errdesc ) ); + break; + case CP_CMD_REMOVE : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Removal Error" ), _( errdesc ) ); + break; + case CP_CMD_ALLOW : + case CP_CMD_DENY : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Subscription Error" ), _( errdesc ) ); + break; + case CP_CMD_UPDATE : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Update Error" ), _( errdesc ) ); + break; + case CP_CMD_MEDIA : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "File Transfer Error" ), _( errdesc ) ); + break; + case CP_CMD_GRPCHAT_CREATE : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Cannot create MultiMx room" ), _( errdesc ) ); + break; + case CP_CMD_GRPCHAT_INVITE : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "MultiMx Invitation Error" ), _( errdesc ) ); + break; + case CP_CMD_EXTPROFILE_GET : + case CP_CMD_EXTPROFILE_SET : + mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Profile Error" ), _( errdesc ) ); + break; + case CP_CMD_SPLASHCLICK : + case CP_CMD_MSGEVENT : + /* ignore error */ + break; + case CP_CMD_PING : + case CP_CMD_POLL : + break; + default : + mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Error" ), _( errdesc ) ); + break; + } + + return 0; +} + + +/*======================================================================================================================== + * Low-level Packet receive + */ + +#ifdef DEBUG_PROTOCOL +/*------------------------------------------------------------------------ + * Dump a received packet structure. + * + * @param p The received packet + */ +static void dump_packet( struct rx_packet* p ) +{ + struct record* r = NULL; + struct field* f = NULL; + int i; + int j; + + purple_debug_info( MXIT_PLUGIN_ID, "PACKET DUMP: (%i records)\n", p->rcount ); + + for ( i = 0; i < p->rcount; i++ ) { + r = p->records[i]; + purple_debug_info( MXIT_PLUGIN_ID, "RECORD: (%i fields)\n", r->fcount ); + + for ( j = 0; j < r->fcount; j++ ) { + f = r->fields[j]; + purple_debug_info( MXIT_PLUGIN_ID, "\tFIELD: (len=%i) '%s' \n", f->len, f->data ); + } + } +} +#endif + + +/*------------------------------------------------------------------------ + * Free up memory used by a packet structure. + * + * @param p The received packet + */ +static void free_rx_packet( struct rx_packet* p ) +{ + struct record* r = NULL; + struct field* f = NULL; + int i; + int j; + + for ( i = 0; i < p->rcount; i++ ) { + r = p->records[i]; + + for ( j = 0; j < r->fcount; j++ ) { + g_free( f ); + } + g_free( r->fields ); + g_free( r ); + } + g_free( p->records ); +} + + +/*------------------------------------------------------------------------ + * Add a new field to a record. + * + * @param r Parent record object + * @return The newly created field + */ +static struct field* add_field( struct record* r ) +{ + struct field* field; + + field = g_new0( struct field, 1 ); + + r->fields = g_realloc( r->fields, sizeof( struct field* ) * ( r->fcount + 1 ) ); + r->fields[r->fcount] = field; + r->fcount++; + + return field; +} + + +/*------------------------------------------------------------------------ + * Add a new record to a packet. + * + * @param p The packet object + * @return The newly created record + */ +static struct record* add_record( struct rx_packet* p ) +{ + struct record* rec; + + rec = g_new0( struct record, 1 ); + + p->records = g_realloc( p->records, sizeof( struct record* ) * ( p->rcount + 1 ) ); + p->records[p->rcount] = rec; + p->rcount++; + + return rec; +} + + +/*------------------------------------------------------------------------ + * Parse the received byte stream into a proper client protocol packet. + * + * @param session The MXit session object + * @return Success (0) or Failure (!0) + */ +int mxit_parse_packet( struct MXitSession* session ) +{ + struct rx_packet packet; + struct record* rec; + struct field* field; + gboolean pbreak; + unsigned int i; + int res = 0; + +#ifdef DEBUG_PROTOCOL + purple_debug_info( MXIT_PLUGIN_ID, "Received packet (%i bytes)\n", session->rx_i ); + dump_bytes( session, session->rx_dbuf, session->rx_i ); +#endif + + i = 0; + while ( i < session->rx_i ) { + + /* create first record and field */ + rec = NULL; + field = NULL; + memset( &packet, 0x00, sizeof( struct rx_packet ) ); + rec = add_record( &packet ); + pbreak = FALSE; + + /* break up the received packet into fields and records for easy parsing */ + while ( ( i < session->rx_i ) && ( !pbreak ) ) { + + switch ( session->rx_dbuf[i] ) { + case CP_SOCK_REC_TERM : + /* new record */ + if ( packet.rcount == 1 ) { + /* packet command */ + packet.cmd = atoi( packet.records[0]->fields[0]->data ); + } + else if ( packet.rcount == 2 ) { + /* special case: binary multimedia packets should not be parsed here */ + if ( packet.cmd == CP_CMD_MEDIA ) { + /* add the chunked to new record */ + rec = add_record( &packet ); + field = add_field( rec ); + field->data = &session->rx_dbuf[i + 1]; + field->len = session->rx_i - i; + /* now skip the binary data */ + res = get_chunk_len( field->data ); + /* determine if we have more packets */ + if ( res + 6 + i < session->rx_i ) { + /* we have more than one packet in this stream */ + i += res + 6; + pbreak = TRUE; + } + else { + i = session->rx_i; + } + } + } + else if ( !field ) { + field = add_field( rec ); + field->data = &session->rx_dbuf[i]; + } + session->rx_dbuf[i] = '\0'; + rec = add_record( &packet ); + field = NULL; + + break; + case CP_FLD_TERM : + /* new field */ + session->rx_dbuf[i] = '\0'; + if ( !field ) { + field = add_field( rec ); + field->data = &session->rx_dbuf[i]; + } + field = NULL; + break; + case CP_PKT_TERM : + /* packet is done! */ + session->rx_dbuf[i] = '\0'; + pbreak = TRUE; + break; + default : + /* skip non special characters */ + if ( !field ) { + field = add_field( rec ); + field->data = &session->rx_dbuf[i]; + } + field->len++; + break; + } + + i++; + } + + if ( packet.rcount < 2 ) { + /* bad packet */ + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Invalid packet received from MXit." ) ); + free_rx_packet( &packet ); + continue; + } + + session->rx_dbuf[session->rx_i] = '\0'; + packet.errcode = atoi( packet.records[1]->fields[0]->data ); + + purple_debug_info( MXIT_PLUGIN_ID, "Packet received CMD:%i (%i)\n", packet.cmd, packet.errcode ); +#ifdef DEBUG_PROTOCOL + /* debug */ + dump_packet( &packet ); +#endif + + /* reset the out ack */ + if ( session->outack == packet.cmd ) { + /* outstanding ack received from mxit server */ + session->outack = 0; + } + + /* check packet status */ + if ( packet.errcode != MXIT_ERRCODE_SUCCESS ) { + /* error reply! */ + if ( ( packet.records[1]->fcount > 1 ) && ( packet.records[1]->fields[1]->data ) ) + packet.errmsg = packet.records[1]->fields[1]->data; + else + packet.errmsg = NULL; + + res = process_error_response( session, &packet ); + } + else { + /* success reply! */ + res = process_success_response( session, &packet ); + } + + /* free up the packet resources */ + free_rx_packet( &packet ); + } + + if ( session->outack == 0 ) + mxit_manage_queue( session ); + + return res; +} + + +/*------------------------------------------------------------------------ + * Callback when data is received from the MXit server. + * + * @param user_data The MXit session object + * @param source The file-descriptor on which data was received + * @param cond Condition which caused the callback (PURPLE_INPUT_READ) + */ +void mxit_cb_rx( gpointer user_data, gint source, PurpleInputCondition cond ) +{ + struct MXitSession* session = (struct MXitSession*) user_data; + char ch; + int res; + int len; + + if ( session->rx_state == RX_STATE_RLEN ) { + /* we are reading in the packet length */ + len = read( session->fd, &ch, 1 ); + if ( len < 0 ) { + /* connection error */ + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x01)" ) ); + return; + } + else if ( len == 0 ) { + /* connection closed */ + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x02)" ) ); + return; + } + else { + /* byte read */ + if ( ch == CP_REC_TERM ) { + /* the end of the length record found */ + session->rx_lbuf[session->rx_i] = '\0'; + session->rx_res = atoi( &session->rx_lbuf[3] ); + if ( session->rx_res > CP_MAX_PACKET ) { + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x03)" ) ); + } + session->rx_state = RX_STATE_DATA; + session->rx_i = 0; + } + else { + /* still part of the packet length record */ + session->rx_lbuf[session->rx_i] = ch; + session->rx_i++; + if ( session->rx_i >= sizeof( session->rx_lbuf ) ) { + /* malformed packet length record (too long) */ + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x04)" ) ); + return; + } + } + } + } + else if ( session->rx_state == RX_STATE_DATA ) { + /* we are reading in the packet data */ + len = read( session->fd, &session->rx_dbuf[session->rx_i], session->rx_res ); + if ( len < 0 ) { + /* connection error */ + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x05)" ) ); + return; + } + else if ( len == 0 ) { + /* connection closed */ + purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x06)" ) ); + return; + } + else { + /* data read */ + session->rx_i += len; + session->rx_res -= len; + + if ( session->rx_res == 0 ) { + /* ok, so now we have read in the whole packet */ + session->rx_state = RX_STATE_PROC; + } + } + } + + if ( session->rx_state == RX_STATE_PROC ) { + /* we have a full packet, which we now need to process */ + res = mxit_parse_packet( session ); + + if ( res == 0 ) { + /* we are still logged in */ + session->rx_state = RX_STATE_RLEN; + session->rx_res = 0; + session->rx_i = 0; + } + } +} + + +/*------------------------------------------------------------------------ + * Log the user off MXit and close the connection + * + * @param session The MXit session object + */ +void mxit_close_connection( struct MXitSession* session ) +{ + purple_debug_info( MXIT_PLUGIN_ID, "mxit_close_connection\n" ); + + if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) { + /* we are already closed */ + return; + } + else if ( session->flags & MXIT_FLAG_LOGGEDIN ) { + /* we are currently logged in so we need to send a logout packet */ + if ( !session->http ) { + mxit_send_logout( session ); + } + session->flags &= ~MXIT_FLAG_LOGGEDIN; + } + session->flags &= ~MXIT_FLAG_CONNECTED; + + /* cancel all outstanding async calls */ + purple_http_connection_set_destroy(session->async_http_reqs); + session->async_http_reqs = NULL; + + /* remove the input cb function */ + if ( session->inpa ) { + purple_input_remove( session->inpa ); + session->inpa = 0; + } + + /* remove HTTP poll timer */ + if ( session->http_timer_id > 0 ) + purple_timeout_remove( session->http_timer_id ); + + /* remove slow queue manager timer */ + if ( session->q_slow_timer_id > 0 ) + purple_timeout_remove( session->q_slow_timer_id ); + + /* remove fast queue manager timer */ + if ( session->q_fast_timer_id > 0 ) + purple_timeout_remove( session->q_fast_timer_id ); + + /* remove all groupchat rooms */ + while ( session->rooms != NULL ) { + struct multimx* multimx = (struct multimx *) session->rooms->data; + + session->rooms = g_list_remove( session->rooms, multimx ); + + free( multimx ); + } + g_list_free( session->rooms ); + session->rooms = NULL; + + /* remove all rx chats names */ + while ( session->active_chats != NULL ) { + char* chat = (char*) session->active_chats->data; + + session->active_chats = g_list_remove( session->active_chats, chat ); + + g_free( chat ); + } + g_list_free( session->active_chats ); + session->active_chats = NULL; + + /* clear the internal invites */ + while ( session->invites != NULL ) { + struct contact* contact = (struct contact*) session->invites->data; + + session->invites = g_list_remove( session->invites, contact ); + + if ( contact->msg ) + g_free( contact->msg ); + if ( contact->statusMsg ) + g_free( contact->statusMsg ); + if ( contact->profile ) + g_free( contact->profile ); + g_free( contact ); + } + g_list_free( session->invites ); + session->invites = NULL; + + /* free profile information */ + if ( session->profile ) + free( session->profile ); + + /* free custom emoticons */ + mxit_free_emoticon_cache( session ); + + /* free allocated memory */ + if ( session->uid ) + g_free( session->uid ); + g_free( session->encpwd ); + session->encpwd = NULL; + + /* flush all the commands still in the queue */ + flush_queue( session ); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/mxit/client.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,352 @@ +/* + * MXit Protocol libPurple Plugin + * + * -- MXit client protocol implementation -- + * + * Pieter Loubser <libpurple@mxit.com> + * + * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd. + * <http://www.mxitlifestyle.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _MXIT_PROTO_H_ +#define _MXIT_PROTO_H_ + + +/* Client protocol constants */ +#define CP_SOCK_REC_TERM '\x00' /* socket record terminator */ +#define CP_HTTP_REC_TERM '\x26' /* http record terminator '&' */ +#define CP_FLD_TERM '\x01' /* field terminator */ +#define CP_PKT_TERM '\x02' /* packet terminator */ + + +#define CP_MAX_PACKET ( 1 * 1000 * 1000 ) /* maximum client protocol packet size (1 MB) */ +#define CP_MAX_FILESIZE ( CP_MAX_PACKET - 1000 ) /* maximum file size (reserve some space for packet headers) */ +#define MXIT_EMOTICON_SIZE 18 /* icon size for custom emoticons */ +#define CP_MAX_STATUS_MSG 250 /* maximum status message length (in characters) */ + +/* Avatars */ +#define MXIT_AVATAR_SIZE 96 /* default avatar image size 96x96 */ +#define MXIT_AVATAR_TYPE "PNG" /* request avatars in this file type (only a suggestion) */ +#define MXIT_AVATAR_BITDEPT 24 /* request avatars with this bit depth (only a suggestion) */ + +/* Protocol error codes */ +#define MXIT_ERRCODE_SUCCESS 0 +#define MXIT_ERRCODE_REDIRECT 16 +#define MXIT_ERRCODE_LOGGEDOUT 42 + +/* MXit client features */ +#define MXIT_CF_NONE 0x000000 +#define MXIT_CF_FORMS 0x000001 +#define MXIT_CF_FILE_TRANSFER 0x000002 +#define MXIT_CF_CAMERA 0x000004 +#define MXIT_CF_COMMANDS 0x000008 +#define MXIT_CF_SMS 0x000010 +#define MXIT_CF_FILE_ACCESS 0x000020 +#define MXIT_CF_MIDP2 0x000040 +#define MXIT_CF_SKINS 0x000080 +#define MXIT_CF_AUDIO 0x000100 +#define MXIT_CF_ENCRYPTION 0x000200 +#define MXIT_CF_VOICE_REC 0x000400 +#define MXIT_CF_VECTOR_GFX 0x000800 +#define MXIT_CF_IMAGES 0x001000 +#define MXIT_CF_MARKUP 0x002000 +#define MXIT_CF_VIBES 0x004000 +#define MXIT_CF_SELECT_CONTACT 0x008000 +#define MXIT_CF_CUSTOM_EMO 0x010000 +#define MXIT_CF_ALERT_PROFILES 0x020000 +#define MXIT_CF_EXT_MARKUP 0x040000 +#define MXIT_CF_PLAIN_PWD 0x080000 +#define MXIT_CF_NO_GATEWAYS 0x100000 +#define MXIT_CF_NO_AVATARS 0x200000 +#define MXIT_CF_GAMING 0x400000 +#define MXIT_CF_GAMING_UPDATE 0x800000 +#define MXIT_CF_VOICE 0x1000000 +#define MXIT_CF_VIDEO 0x2000000 +#define MXIT_CF_TOUCHSCREEN 0x4000000 +#define MXIT_CF_SVC_CONNECTION 0x8000000 +#define MXIT_CF_MXML 0x10000000 +#define MXIT_CF_TYPING_NOTIFY 0x20000000 + +/* Client features supported by this implementation */ +#define MXIT_CP_FEATURES ( MXIT_CF_FILE_TRANSFER | MXIT_CF_FILE_ACCESS | MXIT_CF_AUDIO | MXIT_CF_MARKUP | MXIT_CF_EXT_MARKUP | MXIT_CF_NO_GATEWAYS | MXIT_CF_IMAGES | MXIT_CF_COMMANDS | MXIT_CF_VIBES | MXIT_CF_MIDP2 | MXIT_CF_TYPING_NOTIFY ) + + +#define MXIT_PING_INTERVAL ( 5 * 60 ) /* ping the server after X seconds of being idle (5 minutes) */ +#define MXIT_ACK_TIMEOUT ( 30 ) /* timeout after waiting X seconds for an ack from the server (30 seconds) */ +#define MXIT_TX_DELAY ( 100 ) /* delay between sending consecutive packets (100 ms) */ + +/* MXit client version */ +#define MXIT_CP_DISTCODE 'P' /* client distribution code (magic, do not touch!) */ +#define MXIT_CP_ARCH "Y" /* client architecture series (Y not for Yoda but for PC-client) */ +#define MXIT_CLIENT_ID "LP" /* client ID as specified by MXit */ +#define MXIT_CP_PLATFORM "PURPLE" /* client platform */ +#define MXIT_CP_PROTO_VESION 63 /* client protocol version */ + +/* set operating system name */ +#if defined( __APPLE__ ) +#define MXIT_CP_OS "apple" +#elif defined( _WIN32 ) +#define MXIT_CP_OS "windows" +#elif defined( __linux__ ) +#define MXIT_CP_OS "linux" +#else +#define MXIT_CP_OS "unknown" +#endif + +/* Client capabilities */ +#define MXIT_CP_CAP "utf8=true;cid="MXIT_CLIENT_ID + +/* Client settings */ +#define MAX_QUEUE_SIZE ( 1 << 5 ) /* tx queue size (32 packets) */ +#define MXIT_POPUP_WIN_NAME "MXit Notification" /* popup window name */ +#define MXIT_DEFAULT_LOCALE "en" /* default locale setting */ +#define MXIT_DEFAULT_LOC "planetpurple" /* the default location for registration */ + +/* Client protocol commands */ +#define CP_CMD_LOGIN 0x0001 /* (1) login */ +#define CP_CMD_LOGOUT 0x0002 /* (2) logout */ +#define CP_CMD_CONTACT 0x0003 /* (3) get contacts */ +#define CP_CMD_UPDATE 0x0005 /* (5) update contact information */ +#define CP_CMD_INVITE 0x0006 /* (6) subscribe to new contact */ +#define CP_CMD_PRESENCE 0x0007 /* (7) get presence */ +#define CP_CMD_REMOVE 0x0008 /* (8) remove contact */ +#define CP_CMD_RX_MSG 0x0009 /* (9) get new messages */ +#define CP_CMD_TX_MSG 0x000A /* (10) send new message */ +#define CP_CMD_REGISTER 0x000B /* (11) register */ +//#define CP_CMD_PROFILE_SET 0x000C /* (12) set profile (DEPRECATED see CP_CMD_EXTPROFILE_SET) */ +#define CP_CMD_SUGGESTCONTACTS 0x000D /* (13) suggest contacts */ +#define CP_CMD_POLL 0x0011 /* (17) poll the HTTP server for an update */ +//#define CP_CMD_PROFILE_GET 0x001A /* (26) get profile (DEPRECATED see CP_CMD_EXTPROFILE_GET) */ +#define CP_CMD_MEDIA 0x001B /* (27) get multimedia message */ +#define CP_CMD_SPLASHCLICK 0x001F /* (31) splash-screen clickthrough */ +#define CP_CMD_STATUS 0x0020 /* (32) set shown presence & status */ +#define CP_CMD_MSGEVENT 0x0023 /* (35) Raise message event */ +#define CP_CMD_GOT_MSGEVENT 0x0024 /* (36) Get message event */ +#define CP_CMD_MOOD 0x0029 /* (41) set mood */ +#define CP_CMD_KICK 0x002B /* (43) login kick */ +#define CP_CMD_GRPCHAT_CREATE 0x002C /* (44) create new groupchat */ +#define CP_CMD_GRPCHAT_INVITE 0x002D /* (45) add new groupchat member */ +#define CP_CMD_NEW_SUB 0x0033 /* (51) get new subscription */ +#define CP_CMD_ALLOW 0x0034 /* (52) allow subscription */ +#define CP_CMD_DENY 0x0037 /* (55) deny subscription */ +#define CP_CMD_EXTPROFILE_GET 0x0039 /* (57) get extended profile */ +#define CP_CMD_EXTPROFILE_SET 0x003A /* (58) set extended profile */ +#define CP_CMD_PING 0x03E8 /* (1000) ping (keepalive) */ + +/* HTTP connection */ +#define MXIT_HTTP_POLL_MIN 7 /* minimum time between HTTP polls (seconds) */ +#define MXIT_HTTP_POLL_MAX ( 10 * 60 ) /* maximum time between HTTP polls (seconds) */ + +/* receiver states */ +#define RX_STATE_RLEN 0x01 /* reading packet length section */ +#define RX_STATE_DATA 0x02 /* reading packet data section */ +#define RX_STATE_PROC 0x03 /* process read data */ + +/* message flags */ +#define CP_MSG_NOTIFY_DELIVERY 0x0002 /* request delivery notification */ +#define CP_MSG_NOTIFY_READ 0x0004 /* request read notification */ +#define CP_MSG_PWD_ENCRYPTED 0x0010 /* message is password encrypted */ +#define CP_MSG_TL_ENCRYPTED 0x0020 /* message is transport encrypted */ +#define CP_MSG_RPLY_PWD_ENCRYPT 0x0040 /* reply should be password encrypted */ +#define CP_MSG_RPLY_TL_ENCRYPT 0x0080 /* reply should be transport encrypted */ +#define CP_MSG_MARKUP 0x0200 /* message may contain markup */ +#define CP_MSG_EMOTICON 0x0400 /* message may contain custom emoticons */ +#define CP_MSG_FAREWELL 0x0800 /* this is a farewell message */ + +/* redirect types */ +#define CP_REDIRECT_PERMANENT 1 /* permanent redirect */ +#define CP_REDIRECT_TEMPORARY 2 /* temporary redirect */ + +/* message tx types */ +#define CP_MSGTYPE_NORMAL 0x01 /* normal message */ +#define CP_MSGTYPE_CHAT 0x02 /* chat message */ +#define CP_MSGTYPE_HEADLINE 0x03 /* headline message */ +#define CP_MSGTYPE_ERROR 0x04 /* error message */ +#define CP_MSGTYPE_GROUPCHAT 0x05 /* groupchat message */ +#define CP_MSGTYPE_FORM 0x06 /* mxit custom form */ +#define CP_MSGTYPE_COMMAND 0x07 /* mxit command */ + +/* message event types */ +#define CP_MSGEVENT_DELIVERED 0x02 /* message was delivered */ +#define CP_MSGEVENT_DISPLAYED 0x04 /* message was viewed */ +#define CP_MSGEVENT_TYPING 0x10 /* user is typing */ +#define CP_MSGEVENT_STOPPED 0x20 /* user has stopped typing */ +#define CP_MSGEVENT_ANGRY 0x40 /* user is typing angrily */ +#define CP_MSGEVENT_ERASING 0x80 /* user is erasing text */ + +/* extended profile attribute fields */ +#define CP_PROFILE_BIRTHDATE "birthdate" /* Birthdate (String - ISO 8601 format) */ +#define CP_PROFILE_GENDER "gender" /* Gender (Boolean - 0=female, 1=male) */ +// #define CP_PROFILE_HIDENUMBER "hidenumber" /* Hide Number (Boolean - 0=false, 1=true) (DEPRECATED) */ +#define CP_PROFILE_FULLNAME "fullname" /* Fullname (UTF8 String) */ +#define CP_PROFILE_STATUS "statusmsg" /* Status Message (UTF8 String) */ +#define CP_PROFILE_PREVSTATUS "prevstatusmsgs" /* Previous Status Messages (UTF8 String) */ +#define CP_PROFILE_AVATAR "avatarid" /* Avatar ID (String) */ +#define CP_PROFILE_MODIFIED "lastmodified" /* Last-Modified timestamp */ +#define CP_PROFILE_TITLE "title" /* Title (UTF8 String) */ +#define CP_PROFILE_FIRSTNAME "firstname" /* First name (UTF8 String) */ +#define CP_PROFILE_LASTNAME "lastname" /* Last name (UTF8 String) */ +#define CP_PROFILE_EMAIL "email" /* Email address (UTF8 String) */ +#define CP_PROFILE_MOBILENR "mobilenumber" /* Mobile Number (UTF8 String) */ +#define CP_PROFILE_REGCOUNTRY "registeredcountry" /* Registered Country Code (UTF8 String) */ +#define CP_PROFILE_FLAGS "flags" /* Profile flags (Bitset) */ +#define CP_PROFILE_LASTSEEN "lastseen" /* Last-Online timestamp */ +#define CP_PROFILE_WHEREAMI "whereami" /* Where am I / Where I live */ +#define CP_PROFILE_ABOUTME "aboutme" /* About me */ +#define CP_PROFILE_RELATIONSHIP "relationship" /* Relationship Status */ + +/* extended profile field types */ +#define CP_PROFILE_TYPE_BOOL 0x02 /* boolean (0 or 1) */ +#define CP_PROFILE_TYPE_SHORT 0x04 /* short (16-bit) */ +#define CP_PROFILE_TYPE_INT 0x05 /* integer (32-bit) */ +#define CP_PROFILE_TYPE_LONG 0x06 /* long (64-bit) */ +#define CP_PROFILE_TYPE_UTF8 0x0A /* UTF8 string */ +#define CP_PROFILE_TYPE_DATE 0x0B /* date-time (ISO 8601 format) */ + +/* profile flags */ +#define CP_PROF_NOT_SEARCHABLE 0x02 /* user cannot be searched for */ +#define CP_PROF_NOT_SUGGESTABLE 0x08 /* user cannot be suggested as friend */ +#define CP_PROF_DOBLOCKED 0x40 /* date-of-birth cannot be changed */ + +/* suggestion types */ +#define CP_SUGGEST_ADDRESSBOOK 0 /* address book search */ +#define CP_SUGGEST_FRIENDS 1 /* suggested friends */ +#define CP_SUGGEST_SEARCH 2 /* free-text search */ +#define CP_SUGGEST_MXITID 3 /* MXitId search */ + +/* define this to enable protocol debugging (very verbose logging) */ +#define DEBUG_PROTOCOL + + +/* ======================================================================================= */ + +struct MXitSession; + +/*------------------------------------------*/ + +struct field { + char* data; + int len; +}; + +struct record { + struct field** fields; + int fcount; +}; + +struct rx_packet { + int cmd; + int errcode; + char* errmsg; + struct record** records; + int rcount; +}; + +struct tx_packet { + int cmd; + char header[256]; + int headerlen; + char* data; + int datalen; +}; + +/*------------------------------------------*/ + + +/* + * A received message data object + */ +struct RXMsgData { + struct MXitSession* session; /* MXit session object */ + char* from; /* the sender's name */ + time_t timestamp; /* time at which the message was sent */ + GString* msg; /* newly created message converted to libPurple formatting */ + gboolean got_img; /* flag to say if this message got any images/emoticons embedded */ + short img_count; /* the amount of images/emoticons still outstanding for the message */ + int chatid; /* multimx chatroom id */ + int flags; /* libPurple conversation flags */ + gboolean converted; /* true if the message has been completely parsed and converted to libPurple markup */ + gboolean processed; /* the message has been processed completely and should be freed up */ +}; + + + +/* + * The packet transmission queue. + */ +struct tx_queue { + struct tx_packet* packets[MAX_QUEUE_SIZE]; /* array of packet pointers */ + int count; /* number of packets queued */ + int rd_i; /* queue current read index (queue offset for reading a packet) */ + int wr_i; /* queue current write index (queue offset for adding new packet) */ +}; + + +/* ======================================================================================= */ + +void mxit_popup( int type, const char* heading, const char* message ); +void mxit_strip_domain( char* username ); +gboolean find_active_chat( const GList* chats, const char* who ); + +void mxit_cb_rx( gpointer data, gint source, PurpleInputCondition cond ); +gboolean mxit_manage_queue_slow( gpointer user_data ); +gboolean mxit_manage_queue_fast( gpointer user_data ); +gboolean mxit_manage_polling( gpointer user_data ); + +void mxit_send_register( struct MXitSession* session ); +void mxit_send_login( struct MXitSession* session ); +void mxit_send_logout( struct MXitSession* session ); +void mxit_send_ping( struct MXitSession* session ); +void mxit_send_poll( struct MXitSession* session ); + +void mxit_send_presence( struct MXitSession* session, int presence, const char* statusmsg ); +void mxit_send_mood( struct MXitSession* session, int mood ); +void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup, gboolean is_command ); + +void mxit_send_extprofile_update( struct MXitSession* session, const char* password, unsigned int nr_attrib, const char* attributes ); +void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] ); + +void mxit_send_suggest_friends( struct MXitSession* session, int max, unsigned int nr_attrib, const char* attribute[] ); +void mxit_send_suggest_search( struct MXitSession* session, int max, const char* text, unsigned int nr_attrib, const char* attribute[] ); + +void mxit_send_invite( struct MXitSession* session, const char* username, gboolean mxitid, const char* alias, const char* groupname, const char* message ); +void mxit_send_remove( struct MXitSession* session, const char* username ); +void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias ); +void mxit_send_deny_sub( struct MXitSession* session, const char* username, const char* reason ); +void mxit_send_update_contact( struct MXitSession* session, const char* username, const char* alias, const char* groupname ); +void mxit_send_splashclick( struct MXitSession* session, const char* splashid ); +void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event); + +void mxit_send_file( struct MXitSession* session, const char* username, const char* filename, const unsigned char* buf, int buflen ); +void mxit_send_file_reject( struct MXitSession* session, const char* fileid ); +void mxit_send_file_accept( struct MXitSession* session, const char* fileid, int filesize, int offset ); +void mxit_send_file_received( struct MXitSession* session, const char* fileid, short status ); +void mxit_set_avatar( struct MXitSession* session, const unsigned char* avatar, int avatarlen ); +void mxit_get_avatar( struct MXitSession* session, const char* mxitId, const char* avatarId ); + +void mxit_send_groupchat_create( struct MXitSession* session, const char* groupname, int nr_usernames, const char* usernames[] ); +void mxit_send_groupchat_invite( struct MXitSession* session, const char* roomid, int nr_usernames, const char* usernames[] ); + +int mxit_parse_packet( struct MXitSession* session ); +void dump_bytes( struct MXitSession* session, const char* buf, int len ); +void mxit_close_connection( struct MXitSession* session ); +gint64 mxit_now_milli( void ); + + +#endif /* _MXIT_PROTO_H_ */ +
--- a/libpurple/protocols/mxit/filexfer.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/filexfer.c Fri Jan 31 18:02:20 2014 +0530 @@ -26,7 +26,7 @@ #include "internal.h" #include "debug.h" -#include "protocol.h" +#include "client.h" #include "mxit.h" #include "chunk.h" #include "filexfer.h"
--- a/libpurple/protocols/mxit/formcmds.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/formcmds.c Fri Jan 31 18:02:20 2014 +0530 @@ -28,7 +28,7 @@ #include "debug.h" #include "http.h" -#include "protocol.h" +#include "client.h" #include "mxit.h" #include "markup.h" #include "formcmds.h"
--- a/libpurple/protocols/mxit/formcmds.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/formcmds.h Fri Jan 31 18:02:20 2014 +0530 @@ -26,7 +26,7 @@ #ifndef _MXIT_FORMCMDS_H_ #define _MXIT_FORMCMDS_H_ -#include "protocol.h" +#include "client.h" int mxit_parse_command(struct RXMsgData* mx, char* message);
--- a/libpurple/protocols/mxit/login.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/login.c Fri Jan 31 18:02:20 2014 +0530 @@ -29,7 +29,7 @@ #include "request.h" #include "version.h" -#include "protocol.h" +#include "client.h" #include "mxit.h" #include "cipher.h" #include "login.h"
--- a/libpurple/protocols/mxit/markup.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/markup.c Fri Jan 31 18:02:20 2014 +0530 @@ -27,7 +27,7 @@ #include "debug.h" #include "http.h" -#include "protocol.h" +#include "client.h" #include "mxit.h" #include "markup.h" #include "chunk.h"
--- a/libpurple/protocols/mxit/multimx.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/multimx.c Fri Jan 31 18:02:20 2014 +0530 @@ -26,7 +26,7 @@ #include "internal.h" #include "debug.h" -#include "protocol.h" +#include "client.h" #include "mxit.h" #include "multimx.h" #include "markup.h" @@ -434,10 +434,10 @@ GList* mxit_chat_info(PurpleConnection *gc) { GList *m = NULL; - struct proto_chat_entry *pce; + PurpleProtocolChatEntry *pce; /* Configuration option: Room Name */ - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _( "_Room Name:" ); pce->identifier = "room"; pce->required = TRUE;
--- a/libpurple/protocols/mxit/mxit.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/mxit.c Fri Jan 31 18:02:20 2014 +0530 @@ -26,10 +26,11 @@ #include "internal.h" #include "debug.h" #include "accountopt.h" +#include "plugins.h" #include "version.h" #include "mxit.h" -#include "protocol.h" +#include "client.h" #include "login.h" #include "roster.h" #include "chunk.h" @@ -47,6 +48,7 @@ static PurpleNotifyUiOps* mxit_nots_override_original; static PurpleNotifyUiOps mxit_nots_override; static int not_link_ref_count = 0; +static PurpleProtocol *my_protocol = NULL; /*------------------------------------------------------------------------ @@ -716,145 +718,214 @@ /*========================================================================================================================*/ -static PurplePluginProtocolInfo proto_info = { - sizeof( PurplePluginProtocolInfo ), /* struct_size */ - OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE | OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE, /* options */ - NULL, /* user_splits */ - NULL, /* protocol_options */ - { /* icon_spec */ +/*------------------------------------------------------------------------ + * Initializing the MXit protocol instance. + * + * @param protocol The MXit protocol + */ +static void +mxit_protocol_init( PurpleProtocol *protocol ) +{ + PurpleAccountOption *option; + + protocol->id = MXIT_PROTOCOL_ID; + protocol->name = MXIT_PROTOCOL_NAME; + protocol->options = OPT_PROTO_REGISTER_NOSCREENNAME | + OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_IM_IMAGE | + OPT_PROTO_INVITE_MESSAGE | + OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE; + protocol->icon_spec = purple_buddy_icon_spec_new( "png,jpeg,bmp", /* supported formats */ 32, 32, /* min width & height */ 800, 800, /* max width & height */ CP_MAX_FILESIZE, /* max filesize */ PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY /* scaling rules */ - }, - mxit_list_icon, /* list_icon */ - mxit_list_emblem, /* list_emblem */ - mxit_status_text, /* status_text */ - mxit_tooltip, /* tooltip_text */ - mxit_status_types, /* status types [roster.c] */ - mxit_blist_menu, /* blist_node_menu */ - mxit_chat_info, /* chat_info [multimx.c] */ - mxit_chat_info_defaults,/* chat_info_defaults */ - mxit_login, /* login [login.c] */ - mxit_close, /* close */ - mxit_send_im, /* send_im */ - NULL, /* set_info */ - mxit_send_typing, /* send_typing */ - mxit_get_info, /* get_info */ - mxit_set_status, /* set_status */ - NULL, /* set_idle */ - NULL, /* change_passwd */ - mxit_add_buddy, /* add_buddy [roster.c] */ - NULL, /* add_buddies */ - mxit_remove_buddy, /* remove_buddy [roster.c] */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* rem_permit */ - NULL, /* rem_deny */ - NULL, /* set_permit_deny */ - mxit_chat_join, /* join_chat [multimx.c] */ - mxit_chat_reject, /* reject chat invite [multimx.c] */ - mxit_chat_name, /* get_chat_name [multimx.c] */ - mxit_chat_invite, /* chat_invite [multimx.c] */ - mxit_chat_leave, /* chat_leave [multimx.c] */ - NULL, /* chat_whisper */ - mxit_chat_send, /* chat_send [multimx.c] */ - mxit_keepalive, /* keepalive */ - mxit_register, /* register_user */ - NULL, /* get_cb_info */ - mxit_buddy_alias, /* alias_buddy [roster.c] */ - mxit_buddy_group, /* group_buddy [roster.c] */ - mxit_rename_group, /* rename_group [roster.c] */ - mxit_free_buddy, /* buddy_free */ - NULL, /* convo_closed */ - NULL, /* normalize */ - mxit_set_buddy_icon, /* set_buddy_icon */ - NULL, /* remove_group */ // TODO: Add function to move all contacts out of this group (cmd=30 - remove group)? - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - mxit_xfer_enabled, /* can_receive_file [filexfer.c] */ - mxit_xfer_tx, /* send_file [filexfer.c */ - mxit_xfer_new, /* new_xfer [filexfer.c] */ - mxit_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* attention_types */ - mxit_get_text_table, /* get_account_text_table */ - mxit_media_initiate, /* initiate_media */ - mxit_media_caps, /* get_media_caps */ - mxit_get_moods, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL /* get_max_message_size */ -}; - - -static PurplePluginInfo plugin_info = { - PURPLE_PLUGIN_MAGIC, /* purple magic, this must always be PURPLE_PLUGIN_MAGIC */ - PURPLE_MAJOR_VERSION, /* libpurple version */ - PURPLE_MINOR_VERSION, /* libpurple version */ - PURPLE_PLUGIN_PROTOCOL, /* plugin type (connecting to another network) */ - NULL, /* UI requirement (NULL for core plugin) */ - 0, /* plugin flags (zero is default) */ - NULL, /* plugin dependencies (set this value to NULL no matter what) */ - PURPLE_PRIORITY_DEFAULT, /* libpurple priority */ - - MXIT_PLUGIN_ID, /* plugin id (must be unique) */ - MXIT_PLUGIN_NAME, /* plugin name (this will be displayed in the UI) */ - DISPLAY_VERSION, /* version of the plugin */ - - MXIT_PLUGIN_SUMMARY, /* short summary of the plugin */ - MXIT_PLUGIN_DESC, /* description of the plugin (can be long) */ - MXIT_PLUGIN_EMAIL, /* plugin author name and email address */ - MXIT_PLUGIN_WWW, /* plugin website (to find new versions and reporting of bugs) */ - - NULL, /* function pointer for loading the plugin */ - NULL, /* function pointer for unloading the plugin */ - NULL, /* function pointer for destroying the plugin */ - - NULL, /* pointer to an UI-specific struct */ - &proto_info, /* pointer to either a PurplePluginLoaderInfo or PurplePluginProtocolInfo struct */ - NULL, /* pointer to a PurplePluginUiInfo struct */ - mxit_actions, /* function pointer where you can define plugin-actions */ - - /* padding */ - NULL, /* pointer reserved for future use */ - NULL, /* pointer reserved for future use */ - NULL, /* pointer reserved for future use */ - NULL /* pointer reserved for future use */ -}; - - -/*------------------------------------------------------------------------ - * Initialising the MXit plugin. - * - * @param plugin The plugin object - */ -static void init_plugin( PurplePlugin* plugin ) -{ - PurpleAccountOption* option; + ); /* Configuration options */ /* WAP server (reference: "libpurple/accountopt.h") */ option = purple_account_option_string_new( _( "WAP Server" ), MXIT_CONFIG_WAPSERVER, DEFAULT_WAPSITE ); - proto_info.protocol_options = g_list_append( proto_info.protocol_options, option ); + protocol->protocol_options = g_list_append( protocol->protocol_options, option ); option = purple_account_option_bool_new( _( "Connect via HTTP" ), MXIT_CONFIG_USE_HTTP, FALSE ); - proto_info.protocol_options = g_list_append( proto_info.protocol_options, option ); + protocol->protocol_options = g_list_append( protocol->protocol_options, option ); option = purple_account_option_bool_new( _( "Enable splash-screen popup" ), MXIT_CONFIG_SPLASHPOPUP, FALSE ); - proto_info.protocol_options = g_list_append( proto_info.protocol_options, option ); + protocol->protocol_options = g_list_append( protocol->protocol_options, option ); +} + + +/*------------------------------------------------------------------------ + * Initializing the MXit class and interfaces. */ + + +static void +mxit_protocol_class_init( PurpleProtocolClass *klass ) +{ + klass->login = mxit_login; /* [login.c] */ + klass->close = mxit_close; + klass->status_types = mxit_status_types; /* [roster.c] */ + klass->list_icon = mxit_list_icon; +} + + +static void +mxit_protocol_client_iface_init( PurpleProtocolClientIface *client_iface ) +{ + client_iface->get_actions = mxit_get_actions; /* [actions.c] */ + client_iface->list_emblem = mxit_list_emblem; + client_iface->status_text = mxit_status_text; + client_iface->tooltip_text = mxit_tooltip; + client_iface->blist_node_menu = mxit_blist_menu; + client_iface->buddy_free = mxit_free_buddy; + client_iface->offline_message = mxit_offline_message; + client_iface->get_account_text_table = mxit_get_text_table; + client_iface->get_moods = mxit_get_moods; +} + + +static void +mxit_protocol_server_iface_init( PurpleProtocolServerIface *server_iface ) +{ + server_iface->register_user = mxit_register; + server_iface->get_info = mxit_get_info; + server_iface->set_status = mxit_set_status; + server_iface->add_buddy = mxit_add_buddy; /* [roster.c] */ + server_iface->remove_buddy = mxit_remove_buddy; /* [roster.c] */ + server_iface->keepalive = mxit_keepalive; + server_iface->alias_buddy = mxit_buddy_alias; /* [roster.c] */ + server_iface->group_buddy = mxit_buddy_group; /* [roster.c] */ + server_iface->rename_group = mxit_rename_group; /* [roster.c] */ + server_iface->set_buddy_icon = mxit_set_buddy_icon; + + /* TODO: Add function to move all contacts out of this group (cmd=30 - remove group)? */ + server_iface->remove_group = NULL; +} + + +static void +mxit_protocol_im_iface_init( PurpleProtocolIMIface *im_iface ) +{ + im_iface->send = mxit_send_im; + im_iface->send_typing = mxit_send_typing; +} + + +static void +mxit_protocol_chat_iface_init( PurpleProtocolChatIface *chat_iface ) +{ + chat_iface->info = mxit_chat_info; /* [multimx.c] */ + chat_iface->info_defaults = mxit_chat_info_defaults; + chat_iface->join = mxit_chat_join; /* [multimx.c] */ + chat_iface->reject = mxit_chat_reject; /* [multimx.c] */ + chat_iface->get_name = mxit_chat_name; /* [multimx.c] */ + chat_iface->invite = mxit_chat_invite; /* [multimx.c] */ + chat_iface->leave = mxit_chat_leave; /* [multimx.c] */ + chat_iface->send = mxit_chat_send; /* [multimx.c] */ +} + + +static void +mxit_protocol_media_iface_init( PurpleProtocolMediaIface *media_iface ) +{ + media_iface->initiate_session = mxit_media_initiate; + media_iface->get_caps = mxit_media_caps; } -PURPLE_INIT_PLUGIN( mxit, init_plugin, plugin_info ); + +static void +mxit_protocol_xfer_iface_init( PurpleProtocolXferIface *xfer_iface ) +{ + xfer_iface->can_receive = mxit_xfer_enabled; /* [filexfer.c] */ + xfer_iface->send = mxit_xfer_tx; /* [filexfer.c] */ + xfer_iface->new_xfer = mxit_xfer_new; /* [filexfer.c] */ +} + + +PURPLE_DEFINE_TYPE_EXTENDED( + MXitProtocol, mxit_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC( PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + mxit_protocol_client_iface_init ) + + PURPLE_IMPLEMENT_INTERFACE_STATIC( PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + mxit_protocol_server_iface_init ) + + PURPLE_IMPLEMENT_INTERFACE_STATIC( PURPLE_TYPE_PROTOCOL_IM_IFACE, + mxit_protocol_im_iface_init ) + + PURPLE_IMPLEMENT_INTERFACE_STATIC( PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + mxit_protocol_chat_iface_init ) + + PURPLE_IMPLEMENT_INTERFACE_STATIC( PURPLE_TYPE_PROTOCOL_MEDIA_IFACE, + mxit_protocol_media_iface_init ) + + PURPLE_IMPLEMENT_INTERFACE_STATIC( PURPLE_TYPE_PROTOCOL_XFER_IFACE, + mxit_protocol_xfer_iface_init ) +); + + +/*------------------------------------------------------------------------ + * Querying the MXit plugin. + * + * @param error Query error (if any) + */ +static PurplePluginInfo * +plugin_query( GError **error ) +{ + const gchar * const authors[] = MXIT_PLUGIN_AUTHORS; + return purple_plugin_info_new( + "id", MXIT_PLUGIN_ID, /* plugin id (must be unique) */ + "name", MXIT_PLUGIN_NAME, /* plugin name (this will be displayed in the UI) */ + "version", DISPLAY_VERSION, /* version of the plugin */ + "category", MXIT_PLUGIN_CATEGORY, /* category of the plugin */ + "summary", MXIT_PLUGIN_SUMMARY, /* short summary of the plugin */ + "description", MXIT_PLUGIN_DESC, /* description of the plugin (can be long) */ + "authors", authors, /* plugin authors' name and email addresses */ + "website", MXIT_PLUGIN_WWW, /* plugin website (to find new versions and reporting of bugs) */ + "abi-version", PURPLE_ABI_VERSION, /* ABI version required by the plugin */ + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + + +/*------------------------------------------------------------------------ + * Loading the MXit plugin. + * + * @param plugin The plugin object + * @param error Load error (if any) + */ +static gboolean +plugin_load( PurplePlugin *plugin, GError **error ) +{ + mxit_protocol_register_type(plugin); + + my_protocol = purple_protocols_add(MXIT_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; + + return TRUE; +} + + +/*------------------------------------------------------------------------ + * Unloading the MXit plugin. + * + * @param plugin The plugin object + * @param error Unload error (if any) + */ +static gboolean +plugin_unload( PurplePlugin *plugin, GError **error ) +{ + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + + return TRUE; +} + + +PURPLE_PLUGIN_INIT( mxit, plugin_query, plugin_load, plugin_unload );
--- a/libpurple/protocols/mxit/mxit.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/mxit.h Fri Jan 31 18:02:20 2014 +0530 @@ -57,14 +57,20 @@ #endif -#include "protocol.h" +#include "client.h" #include "profile.h" +/* Protocol details */ +#define MXIT_PROTOCOL_ID "prpl-loubserp-mxit" +#define MXIT_PROTOCOL_NAME "MXit" + + /* Plugin details */ -#define MXIT_PLUGIN_ID "prpl-loubserp-mxit" -#define MXIT_PLUGIN_NAME "MXit" -#define MXIT_PLUGIN_EMAIL "Pieter Loubser <libpurple@mxit.com>" +#define MXIT_PLUGIN_ID "prpl-mxit" +#define MXIT_PLUGIN_NAME "MXit Protocol" +#define MXIT_PLUGIN_CATEGORY "Protocol" +#define MXIT_PLUGIN_AUTHORS { "Pieter Loubser <libpurple@mxit.com>", NULL } #define MXIT_PLUGIN_WWW "http://www.mxit.com" #define MXIT_PLUGIN_SUMMARY "MXit Protocol Plugin" #define MXIT_PLUGIN_DESC "MXit" @@ -72,6 +78,15 @@ #define MXIT_HTTP_USERAGENT "libpurple-"DISPLAY_VERSION +/* protocol type macros */ +#define MXIT_TYPE_PROTOCOL (mxit_protocol_get_type()) +#define MXIT_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MXIT_TYPE_PROTOCOL, MXitProtocol)) +#define MXIT_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), MXIT_TYPE_PROTOCOL, MXitProtocolClass)) +#define MXIT_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), MXIT_TYPE_PROTOCOL)) +#define MXIT_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), MXIT_TYPE_PROTOCOL)) +#define MXIT_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), MXIT_TYPE_PROTOCOL, MXitProtocolClass)) + + /* default connection settings */ #define DEFAULT_SERVER "stream.mxit.co.za" #define DEFAULT_PORT 9119 @@ -124,6 +139,17 @@ #define ARRAY_SIZE( x ) ( sizeof( x ) / sizeof( x[0] ) ) +typedef struct _MXitProtocol +{ + PurpleProtocol parent; +} MXitProtocol; + +typedef struct _MXitProtocolClass +{ + PurpleProtocolClass parent_class; +} MXitProtocolClass; + + /* * data structure containing all MXit session information */ @@ -188,6 +214,7 @@ GHashTable* iimages; /* table which maps inline images (including emoticons) to purple's imgstore id's */ }; +G_MODULE_EXPORT GType mxit_protocol_get_type(void); char* mxit_status_text( PurpleBuddy* buddy ); void mxit_enable_signals( struct MXitSession* session );
--- a/libpurple/protocols/mxit/protocol.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2953 +0,0 @@ -/* - * MXit Protocol libPurple Plugin - * - * -- MXit client protocol implementation -- - * - * Pieter Loubser <libpurple@mxit.com> - * - * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd. - * <http://www.mxitlifestyle.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "internal.h" -#include "debug.h" -#include "version.h" - -#include "protocol.h" -#include "mxit.h" -#include "roster.h" -#include "chunk.h" -#include "filexfer.h" -#include "markup.h" -#include "multimx.h" -#include "splashscreen.h" -#include "login.h" -#include "formcmds.h" -#include "http.h" -#include "cipher.h" -#include "voicevideo.h" - - -#define MXIT_MS_OFFSET 3 - -/* configure the right record terminator char to use */ -#define CP_REC_TERM ( ( session->http ) ? CP_HTTP_REC_TERM : CP_SOCK_REC_TERM ) - - -/*------------------------------------------------------------------------ - * return the current timestamp in milliseconds - */ -gint64 mxit_now_milli( void ) -{ - GTimeVal now; - - g_get_current_time( &now ); - - return ( ( now.tv_sec * 1000 ) + ( now.tv_usec / 1000 ) ); -} - - -/*------------------------------------------------------------------------ - * Display a notification popup message to the user. - * - * @param type The type of notification: - * - info: PURPLE_NOTIFY_MSG_INFO - * - warning: PURPLE_NOTIFY_MSG_WARNING - * - error: PURPLE_NOTIFY_MSG_ERROR - * @param heading Heading text - * @param message Message text - */ -void mxit_popup( int type, const char* heading, const char* message ) -{ - /* (reference: "libpurple/notify.h") */ - purple_notify_message( NULL, type, _( MXIT_POPUP_WIN_NAME ), heading, message, NULL, NULL, NULL ); -} - - -/*------------------------------------------------------------------------ - * For compatibility with legacy clients, all usernames are sent from MXit with a domain - * appended. For MXit contacts, this domain is set to "@m". This function strips - * those fake domains. - * - * @param username The username of the contact - */ -void mxit_strip_domain( char* username ) -{ - if ( g_str_has_suffix( username, "@m" ) ) - username[ strlen( username ) - 2 ] = '\0'; -} - - -/*------------------------------------------------------------------------ - * Dump a byte buffer to the console for debugging purposes. - * - * @param buf The data - * @param len The data length - */ -void dump_bytes( struct MXitSession* session, const char* buf, int len ) -{ - char* msg = g_malloc0( len + 1 ); - int i; - - for ( i = 0; i < len; i++ ) { - char ch = buf[i]; - - if ( ch == CP_REC_TERM ) /* record terminator */ - msg[i] = '!'; - else if ( ch == CP_FLD_TERM ) /* field terminator */ - msg[i] = '^'; - else if ( ch == CP_PKT_TERM ) /* packet terminator */ - msg[i] = '@'; - else if ( ( ch < 0x20 ) || ( ch > 0x7E ) ) /* non-printable character */ - msg[i] = '_'; - else - msg[i] = ch; - } - - purple_debug_info( MXIT_PLUGIN_ID, "DUMP: '%s'\n", msg ); - - g_free( msg ); -} - - -/*------------------------------------------------------------------------ - * Determine if we have an active chat with a specific contact - * - * @param session The MXit session object - * @param who The contact name - * @return Return true if we have an active chat with the contact - */ -gboolean find_active_chat( const GList* chats, const char* who ) -{ - const GList* list = chats; - const char* chat = NULL; - - while ( list ) { - chat = (const char*) list->data; - - if ( strcmp( chat, who ) == 0 ) - return TRUE; - - list = g_list_next( list ); - } - - return FALSE; -} - - -/*======================================================================================================================== - * Low-level Packet transmission - */ - -/*------------------------------------------------------------------------ - * Remove next packet from transmission queue. - * - * @param session The MXit session object - * @return The next packet for transmission (or NULL) - */ -static struct tx_packet* pop_tx_packet( struct MXitSession* session ) -{ - struct tx_packet* packet = NULL; - - if ( session->queue.count > 0 ) { - /* dequeue the next packet */ - packet = session->queue.packets[session->queue.rd_i]; - session->queue.packets[session->queue.rd_i] = NULL; - session->queue.rd_i = ( session->queue.rd_i + 1 ) % MAX_QUEUE_SIZE; - session->queue.count--; - } - - return packet; -} - - -/*------------------------------------------------------------------------ - * Add packet to transmission queue. - * - * @param session The MXit session object - * @param packet The packet to transmit - * @return Return TRUE if packet was enqueue, or FALSE if queue is full. - */ -static gboolean push_tx_packet( struct MXitSession* session, struct tx_packet* packet ) -{ - if ( session->queue.count < MAX_QUEUE_SIZE ) { - /* enqueue packet */ - session->queue.packets[session->queue.wr_i] = packet; - session->queue.wr_i = ( session->queue.wr_i + 1 ) % MAX_QUEUE_SIZE; - session->queue.count++; - return TRUE; - } - else - return FALSE; /* queue is full */ -} - - -/*------------------------------------------------------------------------ - * Deallocate transmission packet. - * - * @param packet The packet to deallocate. - */ -static void free_tx_packet( struct tx_packet* packet ) -{ - g_free( packet->data ); - g_free( packet ); - packet = NULL; -} - - -/*------------------------------------------------------------------------ - * Flush all the packets from the tx queue and release the resources. - * - * @param session The MXit session object - */ -static void flush_queue( struct MXitSession* session ) -{ - struct tx_packet* packet; - - purple_debug_info( MXIT_PLUGIN_ID, "flushing the tx queue\n" ); - - while ( (packet = pop_tx_packet( session ) ) != NULL ) - free_tx_packet( packet ); -} - - -/*------------------------------------------------------------------------ - * TX Step 3: Write the packet data to the TCP connection. - * - * @param fd The file descriptor - * @param pktdata The packet data - * @param pktlen The length of the packet data - * @return Return -1 on error, otherwise 0 - */ -static int mxit_write_sock_packet( int fd, const char* pktdata, int pktlen ) -{ - int written; - int res; - - written = 0; - while ( written < pktlen ) { - res = write( fd, &pktdata[written], pktlen - written ); - if ( res <= 0 ) { - /* error on socket */ - if ( errno == EAGAIN ) - continue; - - purple_debug_error( MXIT_PLUGIN_ID, "Error while writing packet to MXit server (%i)\n", res ); - return -1; - } - written += res; - } - - return 0; -} - - -/** - * Callback called for handling a HTTP GET response - * - * @param http_conn http api object (see http.h) - * @param response http api object (see http.h) - * @param _session The MXit session object - */ -static void -mxit_cb_http_rx(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, - gpointer _session) -{ - struct MXitSession *session = _session; - const gchar *got_data; - size_t got_len; - - if (!purple_http_response_is_successful(response)) { - purple_debug_error(MXIT_PLUGIN_ID, "HTTP response error (%s)\n", - purple_http_response_get_error(response)); - return; - } - - /* convert the HTTP result */ - got_data = purple_http_response_get_data(response, &got_len); - memcpy(session->rx_dbuf, got_data, got_len); - session->rx_i = got_len; - - mxit_parse_packet(session); -} - - -/** - * TX Step 3: Write the packet data to the HTTP connection (GET style). - * - * @param session The MXit session object - * @param packet The packet data - */ -static void -mxit_write_http_get(struct MXitSession* session, struct tx_packet* packet) -{ - PurpleHttpRequest *req; - char *part = NULL; - - if (packet->datalen > 0) { - char *tmp; - - tmp = g_strndup(packet->data, packet->datalen); - part = g_strdup(purple_url_encode(tmp)); - g_free(tmp); - } - - req = purple_http_request_new(NULL); - purple_http_request_set_url_printf(req, "%s?%s%s", session->http_server, - purple_url_encode(packet->header), part ? part : ""); - purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT); - purple_http_connection_set_add(session->async_http_reqs, - purple_http_request(session->con, req, mxit_cb_http_rx, - session)); - purple_http_request_unref(req); - - g_free(part); -} - - -/** - * TX Step 3: Write the packet data to the HTTP connection (POST style). - * - * @param session The MXit session object - * @param packet The packet data - */ -static void -mxit_write_http_post(struct MXitSession* session, struct tx_packet* packet) -{ - PurpleHttpRequest *req; - - /* strip off the last '&' from the header */ - packet->header[packet->headerlen - 1] = '\0'; - packet->headerlen--; - - req = purple_http_request_new(NULL); - purple_http_request_set_url_printf(req, "%s?%s", session->http_server, - purple_url_encode(packet->header)); - purple_http_request_set_method(req, "POST"); - purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT); - purple_http_request_header_set(req, "Content-Type", - "application/octet-stream"); - purple_http_request_set_contents(req, packet->data + MXIT_MS_OFFSET, - packet->datalen - MXIT_MS_OFFSET); - purple_http_connection_set_add(session->async_http_reqs, - purple_http_request(session->con, req, mxit_cb_http_rx, - session)); - purple_http_request_unref(req); -} - - -/*------------------------------------------------------------------------ - * TX Step 2: Handle the transmission of the packet to the MXit server. - * - * @param session The MXit session object - * @param packet The packet to transmit - */ -static void mxit_send_packet( struct MXitSession* session, struct tx_packet* packet ) -{ - int res; - - if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) { - /* we are not connected so ignore all packets to be send */ - purple_debug_error( MXIT_PLUGIN_ID, "Dropping TX packet (we are not connected)\n" ); - return; - } - - purple_debug_info( MXIT_PLUGIN_ID, "Packet send CMD:%i (%i)\n", packet->cmd, packet->headerlen + packet->datalen ); -#ifdef DEBUG_PROTOCOL - dump_bytes( session, packet->header, packet->headerlen ); - dump_bytes( session, packet->data, packet->datalen ); -#endif - - if ( !session->http ) { - /* socket connection */ - char data[packet->datalen + packet->headerlen]; - int datalen; - - /* create raw data buffer */ - memcpy( data, packet->header, packet->headerlen ); - memcpy( data + packet->headerlen, packet->data, packet->datalen ); - datalen = packet->headerlen + packet->datalen; - - res = mxit_write_sock_packet( session->fd, data, datalen ); - if ( res < 0 ) { - /* we must have lost the connection, so terminate it so that we can reconnect */ - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "We have lost the connection to MXit. Please reconnect." ) ); - } - } - else { - /* http connection */ - - if ( packet->cmd == CP_CMD_MEDIA ) { - /* multimedia packets must be send with a HTTP POST */ - mxit_write_http_post( session, packet ); - } - else { - mxit_write_http_get( session, packet ); - } - } - - /* update the timestamp of the last-transmitted packet */ - session->last_tx = mxit_now_milli(); - - /* - * we need to remember that we are still waiting for the ACK from - * the server on this request - */ - session->outack = packet->cmd; - - /* free up the packet resources */ - free_tx_packet( packet ); -} - - -/*------------------------------------------------------------------------ - * TX Step 1: Create a new Tx packet and queue it for sending. - * - * @param session The MXit session object - * @param data The packet data (payload) - * @param datalen The length of the packet data - * @param cmd The MXit command for this packet - */ -static void mxit_queue_packet( struct MXitSession* session, const char* data, int datalen, int cmd ) -{ - struct tx_packet* packet; - char header[256]; - int hlen; - - /* create a packet for sending */ - packet = g_new0( struct tx_packet, 1 ); - packet->data = g_malloc0( datalen ); - packet->cmd = cmd; - packet->headerlen = 0; - - /* create generic packet header */ - hlen = g_snprintf( header, sizeof( header ), "id=%s%c", purple_account_get_username( session->acc ), CP_REC_TERM ); /* client mxitid */ - - if ( session->http ) { - /* http connection only */ - hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "s=" ); - if ( session->http_sesid > 0 ) { - hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "%u%c", session->http_sesid, CP_FLD_TERM ); /* http session id */ - } - session->http_seqno++; - hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "%u%c", session->http_seqno, CP_REC_TERM ); /* http request sequence id */ - } - - hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "cm=%i%c", cmd, CP_REC_TERM ); /* packet command */ - - if ( !session->http ) { - /* socket connection only */ - packet->headerlen = g_snprintf( packet->header, sizeof( packet->header ), "ln=%i%c", ( datalen + hlen ), CP_REC_TERM ); /* packet length */ - } - - /* copy the header to packet */ - memcpy( packet->header + packet->headerlen, header, hlen ); - packet->headerlen += hlen; - - /* copy payload to packet */ - if ( datalen > 0 ) - memcpy( packet->data, data, datalen ); - packet->datalen = datalen; - - - /* shortcut */ - if ( ( session->queue.count == 0 ) && ( session->outack == 0 ) ) { - /* the queue is empty and there are no outstanding acks so we can write it directly */ - mxit_send_packet( session, packet ); - } - else { - /* we need to queue this packet */ - - if ( ( packet->cmd == CP_CMD_PING ) || ( packet->cmd == CP_CMD_POLL ) ) { - /* we do NOT queue HTTP poll nor socket ping packets */ - free_tx_packet( packet ); - return; - } - - purple_debug_info( MXIT_PLUGIN_ID, "queueing packet for later sending cmd=%i\n", cmd ); - if ( !push_tx_packet( session, packet ) ) { - /* packet could not be queued for transmission */ - mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Message Send Error" ), _( "Unable to process your request at this time" ) ); - free_tx_packet( packet ); - } - } -} - - -/*------------------------------------------------------------------------ - * Manage the packet send queue (send next packet, timeout's, etc). - * - * @param session The MXit session object - */ -static void mxit_manage_queue( struct MXitSession* session ) -{ - struct tx_packet* packet = NULL; - gint64 now = mxit_now_milli(); - - if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) { - /* we are not connected, so ignore the queue */ - return; - } - else if ( session->outack > 0 ) { - /* we are still waiting for an outstanding ACK from the MXit server */ - if ( session->last_tx <= mxit_now_milli() - ( MXIT_ACK_TIMEOUT * 1000 ) ) { - /* ack timeout! so we close the connection here */ - purple_debug_info( MXIT_PLUGIN_ID, "mxit_manage_queue: Timeout awaiting ACK for command '%i'\n", session->outack ); - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Timeout while waiting for a response from the MXit server." ) ); - } - return; - } - - /* - * the mxit server has flood detection and it prevents you from sending messages to fast. - * this is a self defense mechanism, a very annoying feature. so the client must ensure that - * it does not send messages too fast otherwise mxit will ignore the user for 30 seconds. - * this is what we are trying to avoid here.. - */ - if ( session->q_fast_timer_id == 0 ) { - /* the fast timer has not been set yet */ - if ( session->last_tx > ( now - MXIT_TX_DELAY ) ) { - /* we need to wait a little before sending the next packet, so schedule a wakeup call */ - gint64 tdiff = now - ( session->last_tx ); - guint delay = ( MXIT_TX_DELAY - tdiff ) + 9; - if ( delay <= 0 ) - delay = MXIT_TX_DELAY; - session->q_fast_timer_id = purple_timeout_add( delay, mxit_manage_queue_fast, session ); - } - else { - /* get the next packet from the queue to send */ - packet = pop_tx_packet( session ); - if ( packet != NULL ) { - /* there was a packet waiting to be sent to the server, now is the time to do something about it */ - - /* send the packet to MXit server */ - mxit_send_packet( session, packet ); - } - } - } -} - - -/*------------------------------------------------------------------------ - * Slow callback to manage the packet send queue. - * - * @param session The MXit session object - */ -gboolean mxit_manage_queue_slow( gpointer user_data ) -{ - struct MXitSession* session = (struct MXitSession*) user_data; - - mxit_manage_queue( session ); - - /* continue running */ - return TRUE; -} - - -/*------------------------------------------------------------------------ - * Fast callback to manage the packet send queue. - * - * @param session The MXit session object - */ -gboolean mxit_manage_queue_fast( gpointer user_data ) -{ - struct MXitSession* session = (struct MXitSession*) user_data; - - session->q_fast_timer_id = 0; - mxit_manage_queue( session ); - - /* stop running */ - return FALSE; -} - - -/*------------------------------------------------------------------------ - * Callback to manage HTTP server polling (HTTP connections ONLY) - * - * @param session The MXit session object - */ -gboolean mxit_manage_polling( gpointer user_data ) -{ - struct MXitSession* session = (struct MXitSession*) user_data; - gboolean poll = FALSE; - gint64 now = mxit_now_milli(); - gint64 rxdiff; - - if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) { - /* we only poll if we are actually logged in */ - return TRUE; - } - - /* calculate the time differences */ - rxdiff = now - session->last_rx; - - if ( rxdiff < MXIT_HTTP_POLL_MIN ) { - /* we received some reply a few moments ago, so reset the poll interval */ - session->http_interval = MXIT_HTTP_POLL_MIN; - } - else if ( session->http_last_poll < ( now - session->http_interval ) ) { - /* time to poll again */ - poll = TRUE; - - /* back-off some more with the polling */ - session->http_interval = session->http_interval + ( session->http_interval / 2 ); - if ( session->http_interval > MXIT_HTTP_POLL_MAX ) - session->http_interval = MXIT_HTTP_POLL_MAX; - } - - /* debugging */ - //purple_debug_info( MXIT_PLUGIN_ID, "POLL TIMER: %i (%i)\n", session->http_interval, rxdiff ); - - if ( poll ) { - /* send poll request */ - session->http_last_poll = mxit_now_milli(); - mxit_send_poll( session ); - } - - return TRUE; -} - - -/*======================================================================================================================== - * Send MXit operations. - */ - -/*------------------------------------------------------------------------ - * Send a ping/keepalive packet to MXit server. - * - * @param session The MXit session object - */ -void mxit_send_ping( struct MXitSession* session ) -{ - /* queue packet for transmission */ - mxit_queue_packet( session, NULL, 0, CP_CMD_PING ); -} - - -/*------------------------------------------------------------------------ - * Send a poll request to the HTTP server (HTTP connections ONLY). - * - * @param session The MXit session object - */ -void mxit_send_poll( struct MXitSession* session ) -{ - /* queue packet for transmission */ - mxit_queue_packet( session, NULL, 0, CP_CMD_POLL ); -} - - -/*------------------------------------------------------------------------ - * Send a logout packet to the MXit server. - * - * @param session The MXit session object - */ -void mxit_send_logout( struct MXitSession* session ) -{ - /* queue packet for transmission */ - mxit_queue_packet( session, NULL, 0, CP_CMD_LOGOUT ); -} - - -/*------------------------------------------------------------------------ - * Send a register packet to the MXit server. - * - * @param session The MXit session object - */ -void mxit_send_register( struct MXitSession* session ) -{ - struct MXitProfile* profile = session->profile; - const char* locale; - char data[CP_MAX_PACKET]; - int datalen; - char* clientVersion; - unsigned int features = MXIT_CP_FEATURES; - - locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE ); - - /* Voice and Video supported */ - if ( mxit_audio_enabled() && mxit_video_enabled() ) - features |= ( MXIT_CF_VOICE | MXIT_CF_VIDEO ); - else if ( mxit_audio_enabled() ) - features |= MXIT_CF_VOICE; - - /* generate client version string (eg, P-2.7.10-Y-PURPLE) */ - clientVersion = g_strdup_printf( "%c-%i.%i.%i-%s-%s", MXIT_CP_DISTCODE, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CP_ARCH, MXIT_CP_PLATFORM ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%s%c%i%c%s%c" /* "ms"=password\1version\1maxreplyLen\1name\1 */ - "%s%c%i%c%s%c%s%c" /* dateOfBirth\1gender\1location\1capabilities\1 */ - "%s%c%i%c%s%c%s" /* dc\1features\1dialingcode\1locale */ - "%c%i%c%i", /* \1protocolVer\1lastRosterUpdate */ - session->encpwd, CP_FLD_TERM, clientVersion, CP_FLD_TERM, CP_MAX_FILESIZE, CP_FLD_TERM, profile->nickname, CP_FLD_TERM, - profile->birthday, CP_FLD_TERM, ( profile->male ) ? 1 : 0, CP_FLD_TERM, MXIT_DEFAULT_LOC, CP_FLD_TERM, MXIT_CP_CAP, CP_FLD_TERM, - session->distcode, CP_FLD_TERM, features, CP_FLD_TERM, session->dialcode, CP_FLD_TERM, locale, - CP_FLD_TERM, MXIT_CP_PROTO_VESION, CP_FLD_TERM, 0 - ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_REGISTER ); - - g_free( clientVersion ); -} - - -/*------------------------------------------------------------------------ - * Send a login packet to the MXit server. - * - * @param session The MXit session object - */ -void mxit_send_login( struct MXitSession* session ) -{ - const char* splashId; - const char* locale; - char data[CP_MAX_PACKET]; - int datalen; - char* clientVersion; - unsigned int features = MXIT_CP_FEATURES; - - locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE ); - - /* Voice and Video supported */ - if ( mxit_audio_enabled() && mxit_video_enabled() ) - features |= ( MXIT_CF_VOICE | MXIT_CF_VIDEO ); - else if ( mxit_audio_enabled() ) - features |= MXIT_CF_VOICE; - - /* generate client version string (eg, P-2.7.10-Y-PURPLE) */ - clientVersion = g_strdup_printf( "%c-%i.%i.%i-%s-%s", MXIT_CP_DISTCODE, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CP_ARCH, MXIT_CP_PLATFORM ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%s%c%i%c" /* "ms"=password\1version\1getContacts\1 */ - "%s%c%s%c%i%c" /* capabilities\1dc\1features\1 */ - "%s%c%s%c" /* dialingcode\1locale\1 */ - "%i%c%i%c%i", /* maxReplyLen\1protocolVer\1lastRosterUpdate */ - session->encpwd, CP_FLD_TERM, clientVersion, CP_FLD_TERM, 1, CP_FLD_TERM, - MXIT_CP_CAP, CP_FLD_TERM, session->distcode, CP_FLD_TERM, features, CP_FLD_TERM, - session->dialcode, CP_FLD_TERM, locale, CP_FLD_TERM, - CP_MAX_FILESIZE, CP_FLD_TERM, MXIT_CP_PROTO_VESION, CP_FLD_TERM, 0 - ); - - /* include "custom resource" information */ - splashId = splash_current( session ); - if ( splashId != NULL ) - datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%ccr=%s", CP_REC_TERM, splashId ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_LOGIN ); - - g_free( clientVersion ); -} - - -/*------------------------------------------------------------------------ - * Send a chat message packet to the MXit server. - * - * @param session The MXit session object - * @param to The username of the recipient - * @param msg The message text - */ -void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup, gboolean is_command ) -{ - char data[CP_MAX_PACKET]; - char* markuped_msg; - int datalen; - int msgtype = ( is_command ? CP_MSGTYPE_COMMAND : CP_MSGTYPE_NORMAL ); - - /* first we need to convert the markup from libPurple to MXit format */ - if ( parse_markup ) - markuped_msg = mxit_convert_markup_tx( msg, &msgtype ); - else - markuped_msg = g_strdup( msg ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%s%c%i%c%i", /* "ms"=jid\1msg\1type\1flags */ - to, CP_FLD_TERM, markuped_msg, CP_FLD_TERM, msgtype, CP_FLD_TERM, CP_MSG_MARKUP | CP_MSG_EMOTICON - ); - - /* free the resources */ - g_free( markuped_msg ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_TX_MSG ); -} - - -/*------------------------------------------------------------------------ - * Send a extended profile request packet to the MXit server. - * - * @param session The MXit session object - * @param username Username who's profile is being requested (NULL = our own) - * @param nr_attribs Number of attributes being requested - * @param attribute The names of the attributes - */ -void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] ) -{ - char data[CP_MAX_PACKET]; - int datalen; - unsigned int i; - - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%i", /* "ms="mxitid\1nr_attributes */ - ( username ? username : "" ), CP_FLD_TERM, nr_attrib - ); - - /* add attributes */ - for ( i = 0; i < nr_attrib; i++ ) - datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, attribute[i] ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_GET ); -} - - -/*------------------------------------------------------------------------ - * Send an update profile packet to the MXit server. - * - * @param session The MXit session object - * @param password The new password to be used for logging in (optional) - * @param nr_attrib The number of attributes - * @param attributes String containing the attribute-name, attribute-type and value (seperated by '\01') - */ -void mxit_send_extprofile_update( struct MXitSession* session, const char* password, unsigned int nr_attrib, const char* attributes ) -{ - char data[CP_MAX_PACKET]; - gchar** parts = NULL; - int datalen; - unsigned int i; - - if ( attributes ) - parts = g_strsplit( attributes, "\01", 1 + ( nr_attrib * 3 ) ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%i", /* "ms"=password\1nr_attibutes */ - ( password ) ? password : "", CP_FLD_TERM, nr_attrib - ); - - /* add attributes */ - for ( i = 1; i < nr_attrib * 3; i+=3 ) { - if ( parts == NULL || parts[i] == NULL || parts[i + 1] == NULL || parts[i + 2] == NULL ) { - purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile update attributes = '%s' - nbr=%u\n", attributes, nr_attrib ); - g_strfreev( parts ); - return; - } - datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, - "%c%s%c%s%c%s", /* \1name\1type\1value */ - CP_FLD_TERM, parts[i], CP_FLD_TERM, parts[i + 1], CP_FLD_TERM, parts[i + 2] ); - } - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_SET ); - - /* freeup the memory */ - g_strfreev( parts ); -} - - -/*------------------------------------------------------------------------ - * Send packet to request list of suggested friends. - * - * @param session The MXit session object - * @param max Maximum number of results to return - * @param nr_attribs Number of attributes being requested - * @param attribute The names of the attributes - */ -void mxit_send_suggest_friends( struct MXitSession* session, int max, unsigned int nr_attrib, const char* attribute[] ) -{ - char data[CP_MAX_PACKET]; - int datalen; - unsigned int i; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%i%c%s%c%i%c%i%c%i", /* inputType \1 input \1 maxSuggestions \1 startIndex \1 numAttributes \1 name0 \1 name1 ... \1 nameN */ - CP_SUGGEST_FRIENDS, CP_FLD_TERM, "", CP_FLD_TERM, max, CP_FLD_TERM, 0, CP_FLD_TERM, nr_attrib ); - - /* add attributes */ - for ( i = 0; i < nr_attrib; i++ ) - datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, attribute[i] ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_SUGGESTCONTACTS ); -} - - -/*------------------------------------------------------------------------ - * Send packet to perform a search for users. - * - * @param session The MXit session object - * @param max Maximum number of results to return - * @param text The search text - * @param nr_attribs Number of attributes being requested - * @param attribute The names of the attributes - */ -void mxit_send_suggest_search( struct MXitSession* session, int max, const char* text, unsigned int nr_attrib, const char* attribute[] ) -{ - char data[CP_MAX_PACKET]; - int datalen; - unsigned int i; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%i%c%s%c%i%c%i%c%i", /* inputType \1 input \1 maxSuggestions \1 startIndex \1 numAttributes \1 name0 \1 name1 ... \1 nameN */ - CP_SUGGEST_SEARCH, CP_FLD_TERM, text, CP_FLD_TERM, max, CP_FLD_TERM, 0, CP_FLD_TERM, nr_attrib ); - - /* add attributes */ - for ( i = 0; i < nr_attrib; i++ ) - datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, attribute[i] ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_SUGGESTCONTACTS ); -} - - -/*------------------------------------------------------------------------ - * Send a presence update packet to the MXit server. - * - * @param session The MXit session object - * @param presence The presence (as per MXit types) - * @param statusmsg The status message (can be NULL) - */ -void mxit_send_presence( struct MXitSession* session, int presence, const char* statusmsg ) -{ - char data[CP_MAX_PACKET]; - int datalen; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%i%c", /* "ms"=show\1status */ - presence, CP_FLD_TERM - ); - - /* append status message (if one is set) */ - if ( statusmsg ) - datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%s", statusmsg ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_STATUS ); -} - - -/*------------------------------------------------------------------------ - * Send a mood update packet to the MXit server. - * - * @param session The MXit session object - * @param mood The mood (as per MXit types) - */ -void mxit_send_mood( struct MXitSession* session, int mood ) -{ - char data[CP_MAX_PACKET]; - int datalen; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%i", /* "ms"=mood */ - mood - ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_MOOD ); -} - - -/*------------------------------------------------------------------------ - * Send an invite contact packet to the MXit server. - * - * @param session The MXit session object - * @param username The username of the contact being invited - * @param mxitid Indicates the username is a MXitId. - * @param alias Our alias for the contact - * @param groupname Group in which contact should be stored. - * @param message Invite message - */ -void mxit_send_invite( struct MXitSession* session, const char* username, gboolean mxitid, const char* alias, const char* groupname, const char* message ) -{ - char data[CP_MAX_PACKET]; - int datalen; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%s%c%s%c%i%c%s%c%i", /* "ms"=group \1 username \1 alias \1 type \1 msg \1 isuserid */ - groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias, - CP_FLD_TERM, MXIT_TYPE_MXIT, CP_FLD_TERM, - ( message ? message : "" ), CP_FLD_TERM, - ( mxitid ? 0 : 1 ) - ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_INVITE ); -} - - -/*------------------------------------------------------------------------ - * Send a remove contact packet to the MXit server. - * - * @param session The MXit session object - * @param username The username of the contact being removed - */ -void mxit_send_remove( struct MXitSession* session, const char* username ) -{ - char data[CP_MAX_PACKET]; - int datalen; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s", /* "ms"=username */ - username - ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_REMOVE ); -} - - -/*------------------------------------------------------------------------ - * Send an accept subscription (invite) packet to the MXit server. - * - * @param session The MXit session object - * @param username The username of the contact being accepted - * @param alias Our alias for the contact - */ -void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias ) -{ - char data[CP_MAX_PACKET]; - int datalen; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%s%c%s", /* "ms"=username\1group\1alias */ - username, CP_FLD_TERM, "", CP_FLD_TERM, alias - ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_ALLOW ); -} - - -/*------------------------------------------------------------------------ - * Send an deny subscription (invite) packet to the MXit server. - * - * @param session The MXit session object - * @param username The username of the contact being denied - * @param reason The message describing the reason for the rejection (can be NULL). - */ -void mxit_send_deny_sub( struct MXitSession* session, const char* username, const char* reason ) -{ - char data[CP_MAX_PACKET]; - int datalen; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s", /* "ms"=username */ - username - ); - - /* append reason (if one is set) */ - if ( reason ) - datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, reason ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_DENY ); -} - - -/*------------------------------------------------------------------------ - * Send an update contact packet to the MXit server. - * - * @param session The MXit session object - * @param username The username of the contact being denied - * @param alias Our alias for the contact - * @param groupname Group in which contact should be stored. - */ -void mxit_send_update_contact( struct MXitSession* session, const char* username, const char* alias, const char* groupname ) -{ - char data[CP_MAX_PACKET]; - int datalen; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%s%c%s", /* "ms"=groupname\1username\1alias */ - groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias - ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_UPDATE ); -} - - -/*------------------------------------------------------------------------ - * Send a splash-screen click event packet. - * - * @param session The MXit session object - * @param splashid The identifier of the splash-screen - */ -void mxit_send_splashclick( struct MXitSession* session, const char* splashid ) -{ - char data[CP_MAX_PACKET]; - int datalen; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s", /* "ms"=splashId */ - splashid - ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_SPLASHCLICK ); -} - - -/*------------------------------------------------------------------------ - * Send a message event packet. - * - * @param session The MXit session object - * @param to The username of the original sender (ie, recipient of the event) - * @param id The identifier of the event (received in message) - * @param event Identified the type of event - */ -void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event ) -{ - char data[CP_MAX_PACKET]; - int datalen; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_msgevent: to=%s id=%s event=%i\n", to, id, event ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%s%c%i", /* "ms"=contactAddress \1 id \1 event */ - to, CP_FLD_TERM, id, CP_FLD_TERM, event - ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_MSGEVENT ); -} - - -/*------------------------------------------------------------------------ - * Send packet to create a MultiMX room. - * - * @param session The MXit session object - * @param groupname Name of the room to create - * @param nr_usernames Number of users in initial invite - * @param usernames The usernames of the users in the initial invite - */ -void mxit_send_groupchat_create( struct MXitSession* session, const char* groupname, int nr_usernames, const char* usernames[] ) -{ - char data[CP_MAX_PACKET]; - int datalen; - int i; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%i", /* "ms"=roomname\1nr_jids\1jid0\1..\1jidN */ - groupname, CP_FLD_TERM, nr_usernames - ); - - /* add usernames */ - for ( i = 0; i < nr_usernames; i++ ) - datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, usernames[i] ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_CREATE ); -} - - -/*------------------------------------------------------------------------ - * Send packet to invite users to existing MultiMX room. - * - * @param session The MXit session object - * @param roomid The unique RoomID for the MultiMx room. - * @param nr_usernames Number of users being invited - * @param usernames The usernames of the users being invited - */ -void mxit_send_groupchat_invite( struct MXitSession* session, const char* roomid, int nr_usernames, const char* usernames[] ) -{ - char data[CP_MAX_PACKET]; - int datalen; - int i; - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), - "ms=%s%c%i", /* "ms"=roomid\1nr_jids\1jid0\1..\1jidN */ - roomid, CP_FLD_TERM, nr_usernames - ); - - /* add usernames */ - for ( i = 0; i < nr_usernames; i++ ) - datalen += g_snprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, usernames[i] ); - - /* queue packet for transmission */ - mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_INVITE ); -} - - -/*------------------------------------------------------------------------ - * Send a "send file direct" multimedia packet. - * - * @param session The MXit session object - * @param username The username of the recipient - * @param filename The name of the file being sent - * @param buf The content of the file - * @param buflen The length of the file contents - */ -void mxit_send_file( struct MXitSession* session, const char* username, const char* filename, const unsigned char* buf, int buflen ) -{ - char data[CP_MAX_PACKET]; - int datalen = 0; - gchar* chunk; - int size; - - purple_debug_info( MXIT_PLUGIN_ID, "SENDING FILE '%s' of %i bytes to user '%s'\n", filename, buflen, username ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), "ms=" ); - - /* map chunk header over data buffer */ - chunk = &data[datalen]; - - size = mxit_chunk_create_senddirect( chunk_data( chunk ), username, filename, buf, buflen ); - if ( size < 0 ) { - purple_debug_error( MXIT_PLUGIN_ID, "Error creating senddirect chunk (%i)\n", size ); - return; - } - - set_chunk_type( chunk, CP_CHUNK_DIRECT_SND ); - set_chunk_length( chunk, size ); - datalen += MXIT_CHUNK_HEADER_SIZE + size; - - /* send the byte stream to the mxit server */ - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); -} - - -/*------------------------------------------------------------------------ - * Send a "reject file" multimedia packet. - * - * @param session The MXit session object - * @param fileid A unique ID that identifies this file - */ -void mxit_send_file_reject( struct MXitSession* session, const char* fileid ) -{ - char data[CP_MAX_PACKET]; - int datalen = 0; - gchar* chunk; - int size; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_reject\n" ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), "ms=" ); - - /* map chunk header over data buffer */ - chunk = &data[datalen]; - - size = mxit_chunk_create_reject( chunk_data( chunk ), fileid ); - if ( size < 0 ) { - purple_debug_error( MXIT_PLUGIN_ID, "Error creating reject chunk (%i)\n", size ); - return; - } - - set_chunk_type( chunk, CP_CHUNK_REJECT ); - set_chunk_length( chunk, size ); - datalen += MXIT_CHUNK_HEADER_SIZE + size; - - /* send the byte stream to the mxit server */ - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); -} - - -/*------------------------------------------------------------------------ - * Send a "get file" multimedia packet. - * - * @param session The MXit session object - * @param fileid A unique ID that identifies this file - * @param filesize The number of bytes to retrieve - * @param offset Offset in file at which to start retrieving - */ -void mxit_send_file_accept( struct MXitSession* session, const char* fileid, int filesize, int offset ) -{ - char data[CP_MAX_PACKET]; - int datalen = 0; - gchar* chunk; - int size; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_accept\n" ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), "ms=" ); - - /* map chunk header over data buffer */ - chunk = &data[datalen]; - - size = mxit_chunk_create_get( chunk_data(chunk), fileid, filesize, offset ); - if ( size < 0 ) { - purple_debug_error( MXIT_PLUGIN_ID, "Error creating getfile chunk (%i)\n", size ); - return; - } - - set_chunk_type( chunk, CP_CHUNK_GET ); - set_chunk_length( chunk, size ); - datalen += MXIT_CHUNK_HEADER_SIZE + size; - - /* send the byte stream to the mxit server */ - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); -} - - -/*------------------------------------------------------------------------ - * Send a "received file" multimedia packet. - * - * @param session The MXit session object - * @param status The status of the file-transfer - */ -void mxit_send_file_received( struct MXitSession* session, const char* fileid, short status ) -{ - char data[CP_MAX_PACKET]; - int datalen = 0; - gchar* chunk; - int size; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_received\n" ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), "ms=" ); - - /* map chunk header over data buffer */ - chunk = &data[datalen]; - - size = mxit_chunk_create_received( chunk_data(chunk), fileid, status ); - if ( size < 0 ) { - purple_debug_error( MXIT_PLUGIN_ID, "Error creating received chunk (%i)\n", size ); - return; - } - - set_chunk_type( chunk, CP_CHUNK_RECEIVED ); - set_chunk_length( chunk, size ); - datalen += MXIT_CHUNK_HEADER_SIZE + size; - - /* send the byte stream to the mxit server */ - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); -} - - -/*------------------------------------------------------------------------ - * Send a "set avatar" multimedia packet. - * - * @param session The MXit session object - * @param data The avatar data - * @param buflen The length of the avatar data - */ -void mxit_set_avatar( struct MXitSession* session, const unsigned char* avatar, int avatarlen ) -{ - char data[CP_MAX_PACKET]; - int datalen = 0; - gchar* chunk; - int size; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_set_avatar: %i bytes\n", avatarlen ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), "ms=" ); - - /* map chunk header over data buffer */ - chunk = &data[datalen]; - - size = mxit_chunk_create_set_avatar( chunk_data(chunk), avatar, avatarlen ); - if ( size < 0 ) { - purple_debug_error( MXIT_PLUGIN_ID, "Error creating set avatar chunk (%i)\n", size ); - return; - } - - set_chunk_type( chunk, CP_CHUNK_SET_AVATAR ); - set_chunk_length( chunk, size ); - datalen += MXIT_CHUNK_HEADER_SIZE + size; - - /* send the byte stream to the mxit server */ - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); -} - - -/*------------------------------------------------------------------------ - * Send a "get avatar" multimedia packet. - * - * @param session The MXit session object - * @param mxitId The username who's avatar to request - * @param avatarId The id of the avatar image (as string) - * @param data The avatar data - * @param buflen The length of the avatar data - */ -void mxit_get_avatar( struct MXitSession* session, const char* mxitId, const char* avatarId ) -{ - char data[CP_MAX_PACKET]; - int datalen = 0; - gchar* chunk; - int size; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_get_avatar: %s\n", mxitId ); - - /* convert the packet to a byte stream */ - datalen = g_snprintf( data, sizeof( data ), "ms=" ); - - /* map chunk header over data buffer */ - chunk = &data[datalen]; - - size = mxit_chunk_create_get_avatar( chunk_data(chunk), mxitId, avatarId ); - if ( size < 0 ) { - purple_debug_error( MXIT_PLUGIN_ID, "Error creating get avatar chunk (%i)\n", size ); - return; - } - - set_chunk_type( chunk, CP_CHUNK_GET_AVATAR ); - set_chunk_length( chunk, size ); - datalen += MXIT_CHUNK_HEADER_SIZE + size; - - /* send the byte stream to the mxit server */ - mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); -} - - -/*------------------------------------------------------------------------ - * Process a login message packet. - * - * @param session The MXit session object - * @param records The packet's data records - * @param rcount The number of data records - */ -static void mxit_parse_cmd_login( struct MXitSession* session, struct record** records, int rcount ) -{ - PurpleStatus* status; - int presence; - const char* statusmsg; - const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_FULLNAME, - CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL, - CP_PROFILE_MOBILENR, CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME, CP_PROFILE_RELATIONSHIP, CP_PROFILE_FLAGS }; - - purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN ); - - /* we were not yet logged in so we need to complete the login sequence here */ - session->flags |= MXIT_FLAG_LOGGEDIN; - purple_connection_update_progress( session->con, _( "Successfully Logged In..." ), 3, 4 ); - purple_connection_set_state( session->con, PURPLE_CONNECTION_CONNECTED ); - - /* save extra info if this is a HTTP connection */ - if ( session->http ) { - /* save the http server to use for this session */ - g_strlcpy( session->http_server, records[1]->fields[3]->data, sizeof( session->http_server ) ); - - /* save the session id */ - session->http_sesid = atoi( records[0]->fields[0]->data ); - } - - /* extract UserId (from protocol 5.9) */ - if ( records[1]->fcount >= 9 ) - session->uid = g_strdup( records[1]->fields[8]->data ); - - /* extract VoIP server (from protocol 6.2) */ - if ( records[1]->fcount >= 11 ) - g_strlcpy( session->voip_server, records[1]->fields[10]->data, sizeof( session->voip_server ) ); - - /* display the current splash-screen */ - if ( splash_popup_enabled( session ) ) - splash_display( session ); - - /* update presence status */ - status = purple_account_get_active_status( session->acc ); - presence = mxit_convert_presence( purple_status_get_id( status ) ); - statusmsg = purple_status_get_attr_string( status, "message" ); - - if ( ( presence != MXIT_PRESENCE_ONLINE ) || ( statusmsg ) ) { - /* when logging into MXit, your default presence is online. but with the UI, one can change - * the presence to whatever. in the case where its changed to a different presence setting - * we need to send an update to the server, otherwise the user's presence will be out of - * sync between the UI and MXit. - */ - char* statusmsg1 = purple_markup_strip_html( statusmsg ); - char* statusmsg2 = g_strndup( statusmsg1, CP_MAX_STATUS_MSG ); - - mxit_send_presence( session, presence, statusmsg2 ); - - g_free( statusmsg1 ); - g_free( statusmsg2 ); - } - - /* retrieve our MXit profile */ - mxit_send_extprofile_request( session, NULL, ARRAY_SIZE( profilelist ), profilelist ); -} - - -/*------------------------------------------------------------------------ - * Process a received message packet. - * - * @param session The MXit session object - * @param records The packet's data records - * @param rcount The number of data records - */ -static void mxit_parse_cmd_message( struct MXitSession* session, struct record** records, int rcount ) -{ - struct RXMsgData* mx = NULL; - char* message = NULL; - char* sender = NULL; - int msglen = 0; - int msgflags = 0; - int msgtype = 0; - - if ( ( rcount == 1 ) || ( records[0]->fcount < 2 ) || ( records[1]->fcount == 0 ) || ( records[1]->fields[0]->len == 0 ) ) { - /* packet contains no message or an empty message */ - return; - } - - message = records[1]->fields[0]->data; - msglen = strlen( message ); - - /* strip off dummy domain */ - sender = records[0]->fields[0]->data; - mxit_strip_domain( sender ); - -#ifdef DEBUG_PROTOCOL - purple_debug_info( MXIT_PLUGIN_ID, "Message received from '%s'\n", sender ); -#endif - - /* decode message flags (if any) */ - if ( records[0]->fcount >= 5 ) - msgflags = atoi( records[0]->fields[4]->data ); - msgtype = atoi( records[0]->fields[2]->data ); - - if ( msgflags & CP_MSG_PWD_ENCRYPTED ) { - /* this is a password encrypted message. we do not currently support those so ignore it */ - PurpleBuddy* buddy; - const char* name; - char msg[128]; - - buddy = purple_blist_find_buddy( session->acc, sender ); - if ( buddy ) - name = purple_buddy_get_alias( buddy ); - else - name = sender; - g_snprintf( msg, sizeof( msg ), _( "%s sent you an encrypted message, but it is not supported on this client." ), name ); - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Error" ), msg ); - return; - } - else if ( msgflags & CP_MSG_TL_ENCRYPTED ) { - /* this is a transport-layer encrypted message. */ - message = mxit_decrypt_message( session, message ); - if ( !message ) { - /* could not be decrypted */ - serv_got_im( session->con, sender, _( "An encrypted message was received which could not be decrypted." ), PURPLE_MESSAGE_ERROR, time( NULL ) ); - return; - } - } - - if ( msgflags & CP_MSG_NOTIFY_DELIVERY ) { - /* delivery notification is requested */ - if ( records[0]->fcount >= 4 ) - mxit_send_msgevent( session, sender, records[0]->fields[3]->data, CP_MSGEVENT_DELIVERED ); - } - - /* create and initialise new markup struct */ - mx = g_new0( struct RXMsgData, 1 ); - mx->msg = g_string_sized_new( msglen ); - mx->session = session; - mx->from = g_strdup( sender ); - mx->timestamp = atoi( records[0]->fields[1]->data ); - mx->got_img = FALSE; - mx->chatid = -1; - mx->img_count = 0; - - /* update list of active chats */ - if ( !find_active_chat( session->active_chats, mx->from ) ) { - session->active_chats = g_list_append( session->active_chats, g_strdup( mx->from ) ); - } - - if ( is_multimx_contact( session, mx->from ) ) { - /* this is a MultiMx chatroom message */ - multimx_message_received( mx, message, msglen, msgtype, msgflags ); - } - else { - mxit_parse_markup( mx, message, msglen, msgtype, msgflags ); - } - - /* we are now done parsing the message */ - mx->converted = TRUE; - if ( mx->img_count == 0 ) { - /* we have all the data we need for this message to be displayed now. */ - mxit_show_message( mx ); - } - else { - /* this means there are still images outstanding for this message and - * still need to wait for them before we can display the message. - * so the image received callback function will eventually display - * the message. */ - } - - /* cleanup */ - if ( msgflags & CP_MSG_TL_ENCRYPTED ) - g_free( message ); -} - - -/*------------------------------------------------------------------------ - * Process a received subscription request packet. - * - * @param session The MXit session object - * @param records The packet's data records - * @param rcount The number of data records - */ -static void mxit_parse_cmd_new_sub( struct MXitSession* session, struct record** records, int rcount ) -{ - struct contact* contact; - struct record* rec; - int i; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_new_sub (%i recs)\n", rcount ); - - for ( i = 0; i < rcount; i++ ) { - rec = records[i]; - - if ( rec->fcount < 4 ) { - purple_debug_error( MXIT_PLUGIN_ID, "BAD SUBSCRIPTION RECORD! %i fields\n", rec->fcount ); - break; - } - - /* build up a new contact info struct */ - contact = g_new0( struct contact, 1 ); - - g_strlcpy( contact->username, rec->fields[0]->data, sizeof( contact->username ) ); - mxit_strip_domain( contact->username ); /* remove dummy domain */ - g_strlcpy( contact->alias, rec->fields[1]->data, sizeof( contact->alias ) ); - contact->type = atoi( rec->fields[2]->data ); - - if ( rec->fcount >= 5 ) { - /* there is a personal invite message attached */ - if ( ( rec->fields[4]->data ) && ( *rec->fields[4]->data ) ) - contact->msg = strdup( rec->fields[4]->data ); - } - - /* handle the subscription */ - if ( contact-> type == MXIT_TYPE_MULTIMX ) { /* subscription to a MultiMX room */ - char* creator = NULL; - - if ( rec->fcount >= 6 ) - creator = rec->fields[5]->data; - - multimx_invite( session, contact, creator ); - } - else - mxit_new_subscription( session, contact ); - } -} - - -/*------------------------------------------------------------------------ - * Parse the received presence value, and ensure that it is supported. - * - * @param value The received presence value. - * @return A valid presence value. - */ -static short mxit_parse_presence( const char* value ) -{ - short presence = atoi( value ); - - /* ensure that the presence value is valid */ - switch ( presence ) { - case MXIT_PRESENCE_OFFLINE : - case MXIT_PRESENCE_ONLINE : - case MXIT_PRESENCE_AWAY : - case MXIT_PRESENCE_DND : - return presence; - - default : - return MXIT_PRESENCE_ONLINE; - } -} - - -/*------------------------------------------------------------------------ - * Process a received contact update packet. - * - * @param session The MXit session object - * @param records The packet's data records - * @param rcount The number of data records - */ -static void mxit_parse_cmd_contact( struct MXitSession* session, struct record** records, int rcount ) -{ - struct contact* contact = NULL; - struct record* rec; - int i; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_contact (%i recs)\n", rcount ); - - for ( i = 0; i < rcount; i++ ) { - rec = records[i]; - - if ( rec->fcount < 6 ) { - purple_debug_error( MXIT_PLUGIN_ID, "BAD CONTACT RECORD! %i fields\n", rec->fcount ); - break; - } - - /* build up a new contact info struct */ - contact = g_new0( struct contact, 1 ); - - g_strlcpy( contact->groupname, rec->fields[0]->data, sizeof( contact->groupname ) ); - g_strlcpy( contact->username, rec->fields[1]->data, sizeof( contact->username ) ); - mxit_strip_domain( contact->username ); /* remove dummy domain */ - g_strlcpy( contact->alias, rec->fields[2]->data, sizeof( contact->alias ) ); - - contact->presence = mxit_parse_presence( rec->fields[3]->data ); - contact->type = atoi( rec->fields[4]->data ); - contact->mood = atoi( rec->fields[5]->data ); - - if ( rec->fcount > 6 ) { - /* added in protocol 5.9 - flags & subtype */ - contact->flags = atoi( rec->fields[6]->data ); - contact->subtype = rec->fields[7]->data[0]; - } - if ( rec->fcount > 8 ) { - /* added in protocol 6.0 - reject message */ - contact->msg = g_strdup( rec->fields[8]->data ); - } - - /* add the contact to the buddy list */ - if ( contact-> type == MXIT_TYPE_MULTIMX ) /* contact is a MultiMX room */ - multimx_created( session, contact ); - else - mxit_update_contact( session, contact ); - } - - if ( !( session->flags & MXIT_FLAG_FIRSTROSTER ) ) { - session->flags |= MXIT_FLAG_FIRSTROSTER; - mxit_update_blist( session ); - } -} - - -/*------------------------------------------------------------------------ - * Process a received presence update packet. - * - * @param session The MXit session object - * @param records The packet's data records - * @param rcount The number of data records - */ -static void mxit_parse_cmd_presence( struct MXitSession* session, struct record** records, int rcount ) -{ - int i; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_presence (%i recs)\n", rcount ); - - for ( i = 0; i < rcount; i++ ) { - struct record* rec = records[i]; - int flags = 0; - - if ( rec->fcount < 6 ) { - purple_debug_error( MXIT_PLUGIN_ID, "BAD PRESENCE RECORD! %i fields\n", rec->fcount ); - break; - } - - /* - * The format of the record is: - * contactAddressN \1 presenceN \1 moodN \1 customMoodN \1 statusMsgN \1 avatarIdN [ \1 flagsN ] - */ - mxit_strip_domain( rec->fields[0]->data ); /* contactAddress */ - - if ( rec->fcount >= 7 ) /* flags field is included */ - flags = atoi( rec->fields[6]->data ); - - mxit_update_buddy_presence( session, rec->fields[0]->data, mxit_parse_presence( rec->fields[1]->data ), atoi( rec->fields[2]->data ), - rec->fields[3]->data, rec->fields[4]->data, flags ); - mxit_update_buddy_avatar( session, rec->fields[0]->data, rec->fields[5]->data ); - } -} - - -/*------------------------------------------------------------------------ - * Process a received extended profile packet. - * - * @param session The MXit session object - * @param records The packet's data records - * @param rcount The number of data records - */ -static void mxit_parse_cmd_extprofile( struct MXitSession* session, struct record** records, int rcount ) -{ - const char* mxitId = records[0]->fields[0]->data; - struct MXitProfile* profile = NULL; - int count; - int i; - const char* avatarId = NULL; - char* statusMsg = NULL; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_extprofile: profile for '%s'\n", mxitId ); - - if ( ( records[0]->fields[0]->len == 0 ) || ( session->uid && ( strcmp( session->uid, records[0]->fields[0]->data ) == 0 ) ) ) { - /* No UserId or Our UserId provided, so this must be our own profile information */ - if ( session->profile == NULL ) - session->profile = g_new0( struct MXitProfile, 1 ); - profile = session->profile; - } - else { - /* is a buddy's profile */ - profile = g_new0( struct MXitProfile, 1 ); - } - - /* set the count for attributes */ - count = atoi( records[0]->fields[1]->data ); - - for ( i = 0; i < count; i++ ) { - char* fname; - char* fvalue; - char* fstatus; - int f = ( i * 3 ) + 2; - - fname = records[0]->fields[f]->data; /* field name */ - fvalue = records[0]->fields[f + 1]->data; /* field value */ - fstatus = records[0]->fields[f + 2]->data; /* field status */ - - /* first check the status on the returned attribute */ - if ( fstatus[0] != '0' ) { - /* error: attribute requested was NOT found */ - purple_debug_error( MXIT_PLUGIN_ID, "Bad profile status on attribute '%s' \n", fname ); - continue; - } - - if ( strcmp( CP_PROFILE_BIRTHDATE, fname ) == 0 ) { - /* birthdate */ - if ( records[0]->fields[f + 1]->len > 10 ) { - fvalue[10] = '\0'; - records[0]->fields[f + 1]->len = 10; - } - memcpy( profile->birthday, fvalue, records[0]->fields[f + 1]->len ); - } - else if ( strcmp( CP_PROFILE_GENDER, fname ) == 0 ) { - /* gender */ - profile->male = ( fvalue[0] == '1' ); - } - else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) { - /* nickname */ - g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) ); - } - else if ( strcmp( CP_PROFILE_STATUS, fname ) == 0 ) { - /* status message - just keep a reference to the value */ - statusMsg = g_markup_escape_text( fvalue, -1 ); - } - else if ( strcmp( CP_PROFILE_AVATAR, fname ) == 0 ) { - /* avatar id - just keep a reference to the value */ - avatarId = fvalue; - } - else if ( strcmp( CP_PROFILE_TITLE, fname ) == 0 ) { - /* title */ - g_strlcpy( profile->title, fvalue, sizeof( profile->title ) ); - } - else if ( strcmp( CP_PROFILE_FIRSTNAME, fname ) == 0 ) { - /* first name */ - g_strlcpy( profile->firstname, fvalue, sizeof( profile->firstname ) ); - } - else if ( strcmp( CP_PROFILE_LASTNAME, fname ) == 0 ) { - /* last name */ - g_strlcpy( profile->lastname, fvalue, sizeof( profile->lastname ) ); - } - else if ( strcmp( CP_PROFILE_EMAIL, fname ) == 0 ) { - /* email address */ - g_strlcpy( profile->email, fvalue, sizeof( profile->email ) ); - } - else if ( strcmp( CP_PROFILE_MOBILENR, fname ) == 0 ) { - /* mobile number */ - g_strlcpy( profile->mobilenr, fvalue, sizeof( profile->mobilenr ) ); - } - else if ( strcmp( CP_PROFILE_REGCOUNTRY, fname ) == 0 ) { - /* registered country */ - g_strlcpy( profile->regcountry, fvalue, sizeof( profile->regcountry ) ); - } - else if ( strcmp( CP_PROFILE_FLAGS, fname ) == 0 ) { - /* profile flags */ - profile->flags = g_ascii_strtoll( fvalue, NULL, 10 ); - } - else if ( strcmp( CP_PROFILE_LASTSEEN, fname ) == 0 ) { - /* last seen online */ - profile->lastonline = g_ascii_strtoll( fvalue, NULL, 10 ); - } - else if ( strcmp( CP_PROFILE_WHEREAMI, fname ) == 0 ) { - /* where am I */ - g_strlcpy( profile->whereami, fvalue, sizeof( profile->whereami ) ); - } - else if ( strcmp( CP_PROFILE_ABOUTME, fname ) == 0) { - /* about me */ - g_strlcpy( profile->aboutme, fvalue, sizeof( profile->aboutme ) ); - } - else if ( strcmp( CP_PROFILE_RELATIONSHIP, fname ) == 0) { - /* relatinship status */ - profile->relationship = strtol( fvalue, NULL, 10 ); - } - else { - /* invalid profile attribute */ - purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile attribute received '%s' \n", fname ); - } - } - - if ( profile != session->profile ) { - /* not our own profile */ - struct contact* contact = NULL; - - contact = get_mxit_invite_contact( session, mxitId ); - if ( contact ) { - /* this is an invite, so update its profile info */ - if ( ( statusMsg ) && ( *statusMsg ) ) { - /* update the status message */ - if ( contact->statusMsg ) - g_free( contact->statusMsg ); - contact->statusMsg = strdup( statusMsg ); - } - else - contact->statusMsg = NULL; - if ( contact->profile ) - g_free( contact->profile ); - contact->profile = profile; - if ( ( avatarId ) && ( *avatarId ) ) { - /* avatar must be requested for this invite before we can display it */ - mxit_get_avatar( session, mxitId, avatarId ); - if ( contact->avatarId ) - g_free( contact->avatarId ); - contact->avatarId = strdup( avatarId ); - } - else { - /* display what we have */ - contact->avatarId = NULL; - mxit_show_profile( session, mxitId, profile ); - } - } - else { - /* this is a contact */ - if ( avatarId ) - mxit_update_buddy_avatar( session, mxitId, avatarId ); - - if ( ( statusMsg ) && ( *statusMsg ) ) { - /* update the status message */ - PurpleBuddy* buddy = NULL; - - buddy = purple_blist_find_buddy( session->acc, mxitId ); - if ( buddy ) { - contact = purple_buddy_get_protocol_data( buddy ); - if ( contact ) { - if ( contact->statusMsg ) - g_free( contact->statusMsg ); - contact->statusMsg = strdup( statusMsg ); - } - } - } - - /* show the profile */ - mxit_show_profile( session, mxitId, profile ); - g_free( profile ); - } - } - - g_free( statusMsg ); -} - - -/*------------------------------------------------------------------------ - * Process a received suggest-contacts packet. - * - * @param session The MXit session object - * @param records The packet's data records - * @param rcount The number of data records - */ -static void mxit_parse_cmd_suggestcontacts( struct MXitSession* session, struct record** records, int rcount ) -{ - GList* entries = NULL; - int searchType; - int maxResults; - int count; - int i; - - /* - * searchType \1 numSuggestions \1 total \1 numAttributes \1 name0 \1 name1 \1 ... \1 nameN \0 - * userid \1 contactType \1 value0 \1 value1 ... valueN \0 - * ... - * userid \1 contactType \1 value0 \1 value1 ... valueN - */ - - /* the type of results */ - searchType = atoi( records[0]->fields[0]->data ); - - /* the maximum number of results */ - maxResults = atoi( records[0]->fields[2]->data ); - - /* set the count for attributes */ - count = atoi( records[0]->fields[3]->data ); - - for ( i = 1; i < rcount; i ++ ) { - struct record* rec = records[i]; - struct MXitProfile* profile = g_new0( struct MXitProfile, 1 ); - int j; - - g_strlcpy( profile->userid, rec->fields[0]->data, sizeof( profile->userid ) ); - // TODO: ContactType - User or Service - - for ( j = 0; j < count; j++ ) { - char* fname; - char* fvalue = ""; - - fname = records[0]->fields[4 + j]->data; /* field name */ - if ( records[i]->fcount > ( 2 + j ) ) - fvalue = records[i]->fields[2 + j]->data; /* field value */ - - purple_debug_info( MXIT_PLUGIN_ID, " %s: field='%s' value='%s'\n", profile->userid, fname, fvalue ); - - if ( strcmp( CP_PROFILE_BIRTHDATE, fname ) == 0 ) { - /* birthdate */ - g_strlcpy( profile->birthday, fvalue, sizeof( profile->birthday ) ); - } - else if ( strcmp( CP_PROFILE_FIRSTNAME, fname ) == 0 ) { - /* first name */ - g_strlcpy( profile->firstname, fvalue, sizeof( profile->firstname ) ); - } - else if ( strcmp( CP_PROFILE_LASTNAME, fname ) == 0 ) { - /* last name */ - g_strlcpy( profile->lastname, fvalue, sizeof( profile->lastname ) ); - } - else if ( strcmp( CP_PROFILE_GENDER, fname ) == 0 ) { - /* gender */ - profile->male = ( fvalue[0] == '1' ); - } - else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) { - /* nickname */ - g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) ); - } - else if ( strcmp( CP_PROFILE_WHEREAMI, fname ) == 0 ) { - /* where am I */ - g_strlcpy( profile->whereami, fvalue, sizeof( profile->whereami ) ); - } - /* ignore other attibutes */ - } - - entries = g_list_append( entries, profile ); - } - - /* display */ - mxit_show_search_results( session, searchType, maxResults, entries ); - - /* cleanup */ - g_list_foreach( entries, (GFunc)g_free, NULL ); -} - -/*------------------------------------------------------------------------ - * Process a received message event packet. - * - * @param session The MXit session object - * @param records The packet's data records - * @param rcount The number of data records - */ -static void mxit_parse_cmd_msgevent( struct MXitSession* session, struct record** records, int rcount ) -{ - int event; - - /* - * contactAddress \1 dateTime \1 id \1 event - */ - - /* strip off dummy domain */ - mxit_strip_domain( records[0]->fields[0]->data ); - - event = atoi( records[0]->fields[3]->data ); - - switch ( event ) { - case CP_MSGEVENT_TYPING : /* user is typing */ - case CP_MSGEVENT_ANGRY : /* user is typing angrily */ - serv_got_typing( session->con, records[0]->fields[0]->data, 0, PURPLE_IM_TYPING ); - break; - - case CP_MSGEVENT_STOPPED : /* user has stopped typing */ - serv_got_typing_stopped( session->con, records[0]->fields[0]->data ); - break; - - case CP_MSGEVENT_ERASING : /* user is erasing text */ - case CP_MSGEVENT_DELIVERED : /* message was delivered */ - case CP_MSGEVENT_DISPLAYED : /* message was viewed */ - /* these are currently not supported by libPurple */ - break; - - default: - purple_debug_error( MXIT_PLUGIN_ID, "Unknown message event received (%i)\n", event ); - } -} - - -/*------------------------------------------------------------------------ - * Return the length of a multimedia chunk - * - * @return The actual chunk data length in bytes - */ -static int get_chunk_len( const char* chunkdata ) -{ - int* sizeptr; - - sizeptr = (int*) &chunkdata[1]; /* we skip the first byte (type field) */ - - return ntohl( *sizeptr ); -} - - -/*------------------------------------------------------------------------ - * Process a received multimedia packet. - * - * @param session The MXit session object - * @param records The packet's data records - * @param rcount The number of data records - */ -static void mxit_parse_cmd_media( struct MXitSession* session, struct record** records, int rcount ) -{ - char type; - int size; - - type = records[0]->fields[0]->data[0]; - size = get_chunk_len( records[0]->fields[0]->data ); - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_media (%i records) (%i bytes)\n", rcount, size ); - - /* supported chunked data types */ - switch ( type ) { - case CP_CHUNK_CUSTOM : /* custom resource */ - { - struct cr_chunk chunk; - - /* decode the chunked data */ - memset( &chunk, 0, sizeof( struct cr_chunk ) ); - mxit_chunk_parse_cr( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); - - purple_debug_info( MXIT_PLUGIN_ID, "chunk info id=%s handle=%s op=%i\n", chunk.id, chunk.handle, chunk.operation ); - - /* this is a splash-screen operation */ - if ( strcmp( chunk.handle, HANDLE_SPLASH2 ) == 0 ) { - if ( chunk.operation == CR_OP_UPDATE ) { /* update the splash-screen */ - struct splash_chunk *splash = chunk.resources->data; // TODO: Fix - assuming 1st resource is splash - gboolean clickable = ( g_list_length( chunk.resources ) > 1 ); // TODO: Fix - if 2 resources, then is clickable - - if ( splash != NULL ) - splash_update( session, chunk.id, splash->data, splash->datalen, clickable ); - } - else if ( chunk.operation == CR_OP_REMOVE ) /* remove the splash-screen */ - splash_remove( session ); - } - - /* cleanup custom resources */ - g_list_foreach( chunk.resources, (GFunc)g_free, NULL ); - - } - break; - - case CP_CHUNK_OFFER : /* file offer */ - { - struct offerfile_chunk chunk; - - /* decode the chunked data */ - memset( &chunk, 0, sizeof( struct offerfile_chunk ) ); - mxit_chunk_parse_offer( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); - - /* process the offer */ - mxit_xfer_rx_offer( session, chunk.username, chunk.filename, chunk.filesize, chunk.fileid ); - } - break; - - case CP_CHUNK_GET : /* get file response */ - { - struct getfile_chunk chunk; - - /* decode the chunked data */ - memset( &chunk, 0, sizeof( struct getfile_chunk ) ); - mxit_chunk_parse_get( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); - - /* process the getfile */ - mxit_xfer_rx_file( session, chunk.fileid, chunk.data, chunk.length ); - } - break; - - case CP_CHUNK_GET_AVATAR : /* get avatars */ - { - struct getavatar_chunk chunk; - struct contact* contact = NULL; - - /* decode the chunked data */ - memset( &chunk, 0, sizeof( struct getavatar_chunk ) ); - mxit_chunk_parse_get_avatar( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); - - /* update avatar image */ - if ( chunk.data ) { - purple_debug_info( MXIT_PLUGIN_ID, "updating avatar for contact '%s'\n", chunk.mxitid ); - - contact = get_mxit_invite_contact( session, chunk.mxitid ); - if ( contact ) { - /* this is an invite (add image to the internal image store) */ - contact->imgid = purple_imgstore_new_with_id( g_memdup( chunk.data, chunk.length ), chunk.length, NULL ); - /* show the profile */ - mxit_show_profile( session, chunk.mxitid, contact->profile ); - } - else { - /* this is a contact's avatar, so update it */ - purple_buddy_icons_set_for_user( session->acc, chunk.mxitid, g_memdup( chunk.data, chunk.length ), chunk.length, chunk.avatarid ); - } - } - } - break; - - case CP_CHUNK_SET_AVATAR : - /* this is a reply packet to a set avatar request. no action is required */ - break; - - case CP_CHUNK_DIRECT_SND : - /* this is a ack for a file send. */ - { - struct sendfile_chunk chunk; - - memset( &chunk, 0, sizeof( struct sendfile_chunk ) ); - mxit_chunk_parse_sendfile( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); - - purple_debug_info( MXIT_PLUGIN_ID, "file-send send to '%s' [status=%i message='%s']\n", chunk.username, chunk.status, chunk.statusmsg ); - - if ( chunk.status != 0 ) /* not success */ - mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "File Send Failed" ), chunk.statusmsg ); - } - break; - - case CP_CHUNK_RECEIVED : - /* this is a ack for a file received. no action is required */ - break; - - default : - purple_debug_error( MXIT_PLUGIN_ID, "Unsupported chunked data packet type received (%i)\n", type ); - break; - } -} - - -/*------------------------------------------------------------------------ - * Handle a redirect sent from the MXit server. - * - * @param session The MXit session object - * @param url The redirect information - */ -static void mxit_perform_redirect( struct MXitSession* session, const char* url ) -{ - gchar** parts; - gchar** host; - int type; - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_perform_redirect: %s\n", url ); - - /* tokenize the URL string */ - parts = g_strsplit( url, ";", 0 ); - - /* Part 1: protocol://host:port */ - host = g_strsplit( parts[0], ":", 4 ); - if ( strcmp( host[0], "socket" ) == 0 ) { - /* redirect to a MXit socket proxy */ - g_strlcpy( session->server, &host[1][2], sizeof( session->server ) ); - session->port = atoi( host[2] ); - } - else { - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Cannot perform redirect using the specified protocol" ) ); - goto redirect_fail; - } - - /* Part 2: type of redirect */ - type = atoi( parts[1] ); - if ( type == CP_REDIRECT_PERMANENT ) { - /* permanent redirect, so save new MXit server and port */ - purple_account_set_string( session->acc, MXIT_CONFIG_SERVER_ADDR, session->server ); - purple_account_set_int( session->acc, MXIT_CONFIG_SERVER_PORT, session->port ); - } - - /* Part 3: message (optional) */ - if ( parts[2] != NULL ) - purple_connection_notice( session->con, parts[2] ); - - purple_debug_info( MXIT_PLUGIN_ID, "mxit_perform_redirect: %s redirect to %s:%i\n", - ( type == CP_REDIRECT_PERMANENT ) ? "Permanent" : "Temporary", session->server, session->port ); - - /* perform the re-connect to the new MXit server */ - mxit_reconnect( session ); - -redirect_fail: - g_strfreev( parts ); - g_strfreev( host ); -} - - -/*------------------------------------------------------------------------ - * Process a success response received from the MXit server. - * - * @param session The MXit session object - * @param packet The received packet - */ -static int process_success_response( struct MXitSession* session, struct rx_packet* packet ) -{ - /* ignore ping/poll packets */ - if ( ( packet->cmd != CP_CMD_PING ) && ( packet->cmd != CP_CMD_POLL ) ) - session->last_rx = mxit_now_milli(); - - /* - * when we pass the packet records to the next level for parsing - * we minus 3 records because 1) the first record is the packet - * type 2) packet reply status 3) the last record is bogus - */ - - /* packet command */ - switch ( packet->cmd ) { - - case CP_CMD_REGISTER : - /* fall through, when registeration successful, MXit will auto login */ - case CP_CMD_LOGIN : - /* login response */ - if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) { - mxit_parse_cmd_login( session, &packet->records[2], packet->rcount - 3 ); - } - break; - - case CP_CMD_LOGOUT : - /* logout response */ - session->flags &= ~MXIT_FLAG_LOGGEDIN; - purple_account_disconnect( session->acc ); - - /* note: - * we do not prompt the user here for a reconnect, because this could be the user - * logging in with his phone. so we just disconnect the account otherwise - * mxit will start to bounce between the phone and pidgin. also could be a valid - * disconnect selected by the user. - */ - return -1; - - case CP_CMD_CONTACT : - /* contact update */ - mxit_parse_cmd_contact( session, &packet->records[2], packet->rcount - 3 ); - break; - - case CP_CMD_PRESENCE : - /* presence update */ - mxit_parse_cmd_presence( session, &packet->records[2], packet->rcount - 3 ); - break; - - case CP_CMD_RX_MSG : - /* incoming message (no bogus record) */ - mxit_parse_cmd_message( session, &packet->records[2], packet->rcount - 2 ); - break; - - case CP_CMD_NEW_SUB : - /* new subscription request */ - mxit_parse_cmd_new_sub( session, &packet->records[2], packet->rcount - 3 ); - break; - - case CP_CMD_MEDIA : - /* multi-media message */ - mxit_parse_cmd_media( session, &packet->records[2], packet->rcount - 2 ); - break; - - case CP_CMD_EXTPROFILE_GET : - /* profile update */ - mxit_parse_cmd_extprofile( session, &packet->records[2], packet->rcount - 2 ); - break; - - case CP_CMD_SUGGESTCONTACTS : - /* suggest contacts */ - mxit_parse_cmd_suggestcontacts( session, &packet->records[2], packet->rcount - 2 ); - break; - - case CP_CMD_GOT_MSGEVENT : - /* received message event */ - mxit_parse_cmd_msgevent( session, &packet->records[2], packet->rcount - 2 ); - break; - - case CP_CMD_MOOD : - /* mood update */ - case CP_CMD_UPDATE : - /* update contact information */ - case CP_CMD_ALLOW : - /* allow subscription ack */ - case CP_CMD_DENY : - /* deny subscription ack */ - case CP_CMD_INVITE : - /* invite contact ack */ - case CP_CMD_REMOVE : - /* remove contact ack */ - case CP_CMD_TX_MSG : - /* outgoing message ack */ - case CP_CMD_STATUS : - /* presence update ack */ - case CP_CMD_GRPCHAT_CREATE : - /* create groupchat */ - case CP_CMD_GRPCHAT_INVITE : - /* groupchat invite */ - case CP_CMD_PING : - /* ping reply */ - case CP_CMD_POLL : - /* HTTP poll reply */ - case CP_CMD_EXTPROFILE_SET : - /* profile update */ - // TODO: Protocol 6.2 indicates status for each attribute, and current value. - case CP_CMD_SPLASHCLICK : - /* splash-screen clickthrough */ - case CP_CMD_MSGEVENT : - /* event message */ - break; - - default : - /* unknown packet */ - purple_debug_error( MXIT_PLUGIN_ID, "Received unknown client packet (cmd = %i)\n", packet->cmd ); - } - - return 0; -} - - -/*------------------------------------------------------------------------ - * Process an error response received from the MXit server. - * - * @param session The MXit session object - * @param packet The received packet - */ -static int process_error_response( struct MXitSession* session, struct rx_packet* packet ) -{ - char errmsg[256]; - const char* errdesc; - - /* set the error description to be shown to the user */ - if ( packet->errmsg ) - errdesc = packet->errmsg; - else - errdesc = _( "An internal MXit server error occurred." ); - - purple_debug_info( MXIT_PLUGIN_ID, "Error Reply %i:%s\n", packet->errcode, errdesc ); - - if ( packet->errcode == MXIT_ERRCODE_LOGGEDOUT ) { - /* we are not currently logged in, so we need to reconnect */ - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( errdesc ) ); - } - - /* packet command */ - switch ( packet->cmd ) { - - case CP_CMD_REGISTER : - case CP_CMD_LOGIN : - if ( packet->errcode == MXIT_ERRCODE_REDIRECT ) { - mxit_perform_redirect( session, packet->errmsg ); - return 0; - } - else { - g_snprintf( errmsg, sizeof( errmsg ), _( "Login error: %s (%i)" ), errdesc, packet->errcode ); - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errmsg ); - return -1; - } - case CP_CMD_LOGOUT : - g_snprintf( errmsg, sizeof( errmsg ), _( "Logout error: %s (%i)" ), errdesc, packet->errcode ); - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _( errmsg ) ); - return -1; - case CP_CMD_CONTACT : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Error" ), _( errdesc ) ); - break; - case CP_CMD_RX_MSG : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Error" ), _( errdesc ) ); - break; - case CP_CMD_TX_MSG : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Sending Error" ), _( errdesc ) ); - break; - case CP_CMD_STATUS : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Status Error" ), _( errdesc ) ); - break; - case CP_CMD_MOOD : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Mood Error" ), _( errdesc ) ); - break; - case CP_CMD_KICK : - /* - * the MXit server sends this packet if we were idle for too long. - * to stop the server from closing this connection we need to resend - * the login packet. - */ - mxit_send_login( session ); - break; - case CP_CMD_INVITE : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Invitation Error" ), _( errdesc ) ); - break; - case CP_CMD_REMOVE : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Removal Error" ), _( errdesc ) ); - break; - case CP_CMD_ALLOW : - case CP_CMD_DENY : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Subscription Error" ), _( errdesc ) ); - break; - case CP_CMD_UPDATE : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Update Error" ), _( errdesc ) ); - break; - case CP_CMD_MEDIA : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "File Transfer Error" ), _( errdesc ) ); - break; - case CP_CMD_GRPCHAT_CREATE : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Cannot create MultiMx room" ), _( errdesc ) ); - break; - case CP_CMD_GRPCHAT_INVITE : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "MultiMx Invitation Error" ), _( errdesc ) ); - break; - case CP_CMD_EXTPROFILE_GET : - case CP_CMD_EXTPROFILE_SET : - mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Profile Error" ), _( errdesc ) ); - break; - case CP_CMD_SPLASHCLICK : - case CP_CMD_MSGEVENT : - /* ignore error */ - break; - case CP_CMD_PING : - case CP_CMD_POLL : - break; - default : - mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Error" ), _( errdesc ) ); - break; - } - - return 0; -} - - -/*======================================================================================================================== - * Low-level Packet receive - */ - -#ifdef DEBUG_PROTOCOL -/*------------------------------------------------------------------------ - * Dump a received packet structure. - * - * @param p The received packet - */ -static void dump_packet( struct rx_packet* p ) -{ - struct record* r = NULL; - struct field* f = NULL; - int i; - int j; - - purple_debug_info( MXIT_PLUGIN_ID, "PACKET DUMP: (%i records)\n", p->rcount ); - - for ( i = 0; i < p->rcount; i++ ) { - r = p->records[i]; - purple_debug_info( MXIT_PLUGIN_ID, "RECORD: (%i fields)\n", r->fcount ); - - for ( j = 0; j < r->fcount; j++ ) { - f = r->fields[j]; - purple_debug_info( MXIT_PLUGIN_ID, "\tFIELD: (len=%i) '%s' \n", f->len, f->data ); - } - } -} -#endif - - -/*------------------------------------------------------------------------ - * Free up memory used by a packet structure. - * - * @param p The received packet - */ -static void free_rx_packet( struct rx_packet* p ) -{ - struct record* r = NULL; - struct field* f = NULL; - int i; - int j; - - for ( i = 0; i < p->rcount; i++ ) { - r = p->records[i]; - - for ( j = 0; j < r->fcount; j++ ) { - g_free( f ); - } - g_free( r->fields ); - g_free( r ); - } - g_free( p->records ); -} - - -/*------------------------------------------------------------------------ - * Add a new field to a record. - * - * @param r Parent record object - * @return The newly created field - */ -static struct field* add_field( struct record* r ) -{ - struct field* field; - - field = g_new0( struct field, 1 ); - - r->fields = g_realloc( r->fields, sizeof( struct field* ) * ( r->fcount + 1 ) ); - r->fields[r->fcount] = field; - r->fcount++; - - return field; -} - - -/*------------------------------------------------------------------------ - * Add a new record to a packet. - * - * @param p The packet object - * @return The newly created record - */ -static struct record* add_record( struct rx_packet* p ) -{ - struct record* rec; - - rec = g_new0( struct record, 1 ); - - p->records = g_realloc( p->records, sizeof( struct record* ) * ( p->rcount + 1 ) ); - p->records[p->rcount] = rec; - p->rcount++; - - return rec; -} - - -/*------------------------------------------------------------------------ - * Parse the received byte stream into a proper client protocol packet. - * - * @param session The MXit session object - * @return Success (0) or Failure (!0) - */ -int mxit_parse_packet( struct MXitSession* session ) -{ - struct rx_packet packet; - struct record* rec; - struct field* field; - gboolean pbreak; - unsigned int i; - int res = 0; - -#ifdef DEBUG_PROTOCOL - purple_debug_info( MXIT_PLUGIN_ID, "Received packet (%i bytes)\n", session->rx_i ); - dump_bytes( session, session->rx_dbuf, session->rx_i ); -#endif - - i = 0; - while ( i < session->rx_i ) { - - /* create first record and field */ - rec = NULL; - field = NULL; - memset( &packet, 0x00, sizeof( struct rx_packet ) ); - rec = add_record( &packet ); - pbreak = FALSE; - - /* break up the received packet into fields and records for easy parsing */ - while ( ( i < session->rx_i ) && ( !pbreak ) ) { - - switch ( session->rx_dbuf[i] ) { - case CP_SOCK_REC_TERM : - /* new record */ - if ( packet.rcount == 1 ) { - /* packet command */ - packet.cmd = atoi( packet.records[0]->fields[0]->data ); - } - else if ( packet.rcount == 2 ) { - /* special case: binary multimedia packets should not be parsed here */ - if ( packet.cmd == CP_CMD_MEDIA ) { - /* add the chunked to new record */ - rec = add_record( &packet ); - field = add_field( rec ); - field->data = &session->rx_dbuf[i + 1]; - field->len = session->rx_i - i; - /* now skip the binary data */ - res = get_chunk_len( field->data ); - /* determine if we have more packets */ - if ( res + 6 + i < session->rx_i ) { - /* we have more than one packet in this stream */ - i += res + 6; - pbreak = TRUE; - } - else { - i = session->rx_i; - } - } - } - else if ( !field ) { - field = add_field( rec ); - field->data = &session->rx_dbuf[i]; - } - session->rx_dbuf[i] = '\0'; - rec = add_record( &packet ); - field = NULL; - - break; - case CP_FLD_TERM : - /* new field */ - session->rx_dbuf[i] = '\0'; - if ( !field ) { - field = add_field( rec ); - field->data = &session->rx_dbuf[i]; - } - field = NULL; - break; - case CP_PKT_TERM : - /* packet is done! */ - session->rx_dbuf[i] = '\0'; - pbreak = TRUE; - break; - default : - /* skip non special characters */ - if ( !field ) { - field = add_field( rec ); - field->data = &session->rx_dbuf[i]; - } - field->len++; - break; - } - - i++; - } - - if ( packet.rcount < 2 ) { - /* bad packet */ - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Invalid packet received from MXit." ) ); - free_rx_packet( &packet ); - continue; - } - - session->rx_dbuf[session->rx_i] = '\0'; - packet.errcode = atoi( packet.records[1]->fields[0]->data ); - - purple_debug_info( MXIT_PLUGIN_ID, "Packet received CMD:%i (%i)\n", packet.cmd, packet.errcode ); -#ifdef DEBUG_PROTOCOL - /* debug */ - dump_packet( &packet ); -#endif - - /* reset the out ack */ - if ( session->outack == packet.cmd ) { - /* outstanding ack received from mxit server */ - session->outack = 0; - } - - /* check packet status */ - if ( packet.errcode != MXIT_ERRCODE_SUCCESS ) { - /* error reply! */ - if ( ( packet.records[1]->fcount > 1 ) && ( packet.records[1]->fields[1]->data ) ) - packet.errmsg = packet.records[1]->fields[1]->data; - else - packet.errmsg = NULL; - - res = process_error_response( session, &packet ); - } - else { - /* success reply! */ - res = process_success_response( session, &packet ); - } - - /* free up the packet resources */ - free_rx_packet( &packet ); - } - - if ( session->outack == 0 ) - mxit_manage_queue( session ); - - return res; -} - - -/*------------------------------------------------------------------------ - * Callback when data is received from the MXit server. - * - * @param user_data The MXit session object - * @param source The file-descriptor on which data was received - * @param cond Condition which caused the callback (PURPLE_INPUT_READ) - */ -void mxit_cb_rx( gpointer user_data, gint source, PurpleInputCondition cond ) -{ - struct MXitSession* session = (struct MXitSession*) user_data; - char ch; - int res; - int len; - - if ( session->rx_state == RX_STATE_RLEN ) { - /* we are reading in the packet length */ - len = read( session->fd, &ch, 1 ); - if ( len < 0 ) { - /* connection error */ - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x01)" ) ); - return; - } - else if ( len == 0 ) { - /* connection closed */ - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x02)" ) ); - return; - } - else { - /* byte read */ - if ( ch == CP_REC_TERM ) { - /* the end of the length record found */ - session->rx_lbuf[session->rx_i] = '\0'; - session->rx_res = atoi( &session->rx_lbuf[3] ); - if ( session->rx_res > CP_MAX_PACKET ) { - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x03)" ) ); - } - session->rx_state = RX_STATE_DATA; - session->rx_i = 0; - } - else { - /* still part of the packet length record */ - session->rx_lbuf[session->rx_i] = ch; - session->rx_i++; - if ( session->rx_i >= sizeof( session->rx_lbuf ) ) { - /* malformed packet length record (too long) */ - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x04)" ) ); - return; - } - } - } - } - else if ( session->rx_state == RX_STATE_DATA ) { - /* we are reading in the packet data */ - len = read( session->fd, &session->rx_dbuf[session->rx_i], session->rx_res ); - if ( len < 0 ) { - /* connection error */ - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x05)" ) ); - return; - } - else if ( len == 0 ) { - /* connection closed */ - purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x06)" ) ); - return; - } - else { - /* data read */ - session->rx_i += len; - session->rx_res -= len; - - if ( session->rx_res == 0 ) { - /* ok, so now we have read in the whole packet */ - session->rx_state = RX_STATE_PROC; - } - } - } - - if ( session->rx_state == RX_STATE_PROC ) { - /* we have a full packet, which we now need to process */ - res = mxit_parse_packet( session ); - - if ( res == 0 ) { - /* we are still logged in */ - session->rx_state = RX_STATE_RLEN; - session->rx_res = 0; - session->rx_i = 0; - } - } -} - - -/*------------------------------------------------------------------------ - * Log the user off MXit and close the connection - * - * @param session The MXit session object - */ -void mxit_close_connection( struct MXitSession* session ) -{ - purple_debug_info( MXIT_PLUGIN_ID, "mxit_close_connection\n" ); - - if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) { - /* we are already closed */ - return; - } - else if ( session->flags & MXIT_FLAG_LOGGEDIN ) { - /* we are currently logged in so we need to send a logout packet */ - if ( !session->http ) { - mxit_send_logout( session ); - } - session->flags &= ~MXIT_FLAG_LOGGEDIN; - } - session->flags &= ~MXIT_FLAG_CONNECTED; - - /* cancel all outstanding async calls */ - purple_http_connection_set_destroy(session->async_http_reqs); - session->async_http_reqs = NULL; - - /* remove the input cb function */ - if ( session->inpa ) { - purple_input_remove( session->inpa ); - session->inpa = 0; - } - - /* remove HTTP poll timer */ - if ( session->http_timer_id > 0 ) - purple_timeout_remove( session->http_timer_id ); - - /* remove slow queue manager timer */ - if ( session->q_slow_timer_id > 0 ) - purple_timeout_remove( session->q_slow_timer_id ); - - /* remove fast queue manager timer */ - if ( session->q_fast_timer_id > 0 ) - purple_timeout_remove( session->q_fast_timer_id ); - - /* remove all groupchat rooms */ - while ( session->rooms != NULL ) { - struct multimx* multimx = (struct multimx *) session->rooms->data; - - session->rooms = g_list_remove( session->rooms, multimx ); - - free( multimx ); - } - g_list_free( session->rooms ); - session->rooms = NULL; - - /* remove all rx chats names */ - while ( session->active_chats != NULL ) { - char* chat = (char*) session->active_chats->data; - - session->active_chats = g_list_remove( session->active_chats, chat ); - - g_free( chat ); - } - g_list_free( session->active_chats ); - session->active_chats = NULL; - - /* clear the internal invites */ - while ( session->invites != NULL ) { - struct contact* contact = (struct contact*) session->invites->data; - - session->invites = g_list_remove( session->invites, contact ); - - if ( contact->msg ) - g_free( contact->msg ); - if ( contact->statusMsg ) - g_free( contact->statusMsg ); - if ( contact->profile ) - g_free( contact->profile ); - g_free( contact ); - } - g_list_free( session->invites ); - session->invites = NULL; - - /* free profile information */ - if ( session->profile ) - free( session->profile ); - - /* free custom emoticons */ - mxit_free_emoticon_cache( session ); - - /* free allocated memory */ - if ( session->uid ) - g_free( session->uid ); - g_free( session->encpwd ); - session->encpwd = NULL; - - /* flush all the commands still in the queue */ - flush_queue( session ); -} -
--- a/libpurple/protocols/mxit/protocol.h Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,352 +0,0 @@ -/* - * MXit Protocol libPurple Plugin - * - * -- MXit client protocol implementation -- - * - * Pieter Loubser <libpurple@mxit.com> - * - * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd. - * <http://www.mxitlifestyle.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _MXIT_PROTO_H_ -#define _MXIT_PROTO_H_ - - -/* Client protocol constants */ -#define CP_SOCK_REC_TERM '\x00' /* socket record terminator */ -#define CP_HTTP_REC_TERM '\x26' /* http record terminator '&' */ -#define CP_FLD_TERM '\x01' /* field terminator */ -#define CP_PKT_TERM '\x02' /* packet terminator */ - - -#define CP_MAX_PACKET ( 1 * 1000 * 1000 ) /* maximum client protocol packet size (1 MB) */ -#define CP_MAX_FILESIZE ( CP_MAX_PACKET - 1000 ) /* maximum file size (reserve some space for packet headers) */ -#define MXIT_EMOTICON_SIZE 18 /* icon size for custom emoticons */ -#define CP_MAX_STATUS_MSG 250 /* maximum status message length (in characters) */ - -/* Avatars */ -#define MXIT_AVATAR_SIZE 96 /* default avatar image size 96x96 */ -#define MXIT_AVATAR_TYPE "PNG" /* request avatars in this file type (only a suggestion) */ -#define MXIT_AVATAR_BITDEPT 24 /* request avatars with this bit depth (only a suggestion) */ - -/* Protocol error codes */ -#define MXIT_ERRCODE_SUCCESS 0 -#define MXIT_ERRCODE_REDIRECT 16 -#define MXIT_ERRCODE_LOGGEDOUT 42 - -/* MXit client features */ -#define MXIT_CF_NONE 0x000000 -#define MXIT_CF_FORMS 0x000001 -#define MXIT_CF_FILE_TRANSFER 0x000002 -#define MXIT_CF_CAMERA 0x000004 -#define MXIT_CF_COMMANDS 0x000008 -#define MXIT_CF_SMS 0x000010 -#define MXIT_CF_FILE_ACCESS 0x000020 -#define MXIT_CF_MIDP2 0x000040 -#define MXIT_CF_SKINS 0x000080 -#define MXIT_CF_AUDIO 0x000100 -#define MXIT_CF_ENCRYPTION 0x000200 -#define MXIT_CF_VOICE_REC 0x000400 -#define MXIT_CF_VECTOR_GFX 0x000800 -#define MXIT_CF_IMAGES 0x001000 -#define MXIT_CF_MARKUP 0x002000 -#define MXIT_CF_VIBES 0x004000 -#define MXIT_CF_SELECT_CONTACT 0x008000 -#define MXIT_CF_CUSTOM_EMO 0x010000 -#define MXIT_CF_ALERT_PROFILES 0x020000 -#define MXIT_CF_EXT_MARKUP 0x040000 -#define MXIT_CF_PLAIN_PWD 0x080000 -#define MXIT_CF_NO_GATEWAYS 0x100000 -#define MXIT_CF_NO_AVATARS 0x200000 -#define MXIT_CF_GAMING 0x400000 -#define MXIT_CF_GAMING_UPDATE 0x800000 -#define MXIT_CF_VOICE 0x1000000 -#define MXIT_CF_VIDEO 0x2000000 -#define MXIT_CF_TOUCHSCREEN 0x4000000 -#define MXIT_CF_SVC_CONNECTION 0x8000000 -#define MXIT_CF_MXML 0x10000000 -#define MXIT_CF_TYPING_NOTIFY 0x20000000 - -/* Client features supported by this implementation */ -#define MXIT_CP_FEATURES ( MXIT_CF_FILE_TRANSFER | MXIT_CF_FILE_ACCESS | MXIT_CF_AUDIO | MXIT_CF_MARKUP | MXIT_CF_EXT_MARKUP | MXIT_CF_NO_GATEWAYS | MXIT_CF_IMAGES | MXIT_CF_COMMANDS | MXIT_CF_VIBES | MXIT_CF_MIDP2 | MXIT_CF_TYPING_NOTIFY ) - - -#define MXIT_PING_INTERVAL ( 5 * 60 ) /* ping the server after X seconds of being idle (5 minutes) */ -#define MXIT_ACK_TIMEOUT ( 30 ) /* timeout after waiting X seconds for an ack from the server (30 seconds) */ -#define MXIT_TX_DELAY ( 100 ) /* delay between sending consecutive packets (100 ms) */ - -/* MXit client version */ -#define MXIT_CP_DISTCODE 'P' /* client distribution code (magic, do not touch!) */ -#define MXIT_CP_ARCH "Y" /* client architecture series (Y not for Yoda but for PC-client) */ -#define MXIT_CLIENT_ID "LP" /* client ID as specified by MXit */ -#define MXIT_CP_PLATFORM "PURPLE" /* client platform */ -#define MXIT_CP_PROTO_VESION 63 /* client protocol version */ - -/* set operating system name */ -#if defined( __APPLE__ ) -#define MXIT_CP_OS "apple" -#elif defined( _WIN32 ) -#define MXIT_CP_OS "windows" -#elif defined( __linux__ ) -#define MXIT_CP_OS "linux" -#else -#define MXIT_CP_OS "unknown" -#endif - -/* Client capabilities */ -#define MXIT_CP_CAP "utf8=true;cid="MXIT_CLIENT_ID - -/* Client settings */ -#define MAX_QUEUE_SIZE ( 1 << 5 ) /* tx queue size (32 packets) */ -#define MXIT_POPUP_WIN_NAME "MXit Notification" /* popup window name */ -#define MXIT_DEFAULT_LOCALE "en" /* default locale setting */ -#define MXIT_DEFAULT_LOC "planetpurple" /* the default location for registration */ - -/* Client protocol commands */ -#define CP_CMD_LOGIN 0x0001 /* (1) login */ -#define CP_CMD_LOGOUT 0x0002 /* (2) logout */ -#define CP_CMD_CONTACT 0x0003 /* (3) get contacts */ -#define CP_CMD_UPDATE 0x0005 /* (5) update contact information */ -#define CP_CMD_INVITE 0x0006 /* (6) subscribe to new contact */ -#define CP_CMD_PRESENCE 0x0007 /* (7) get presence */ -#define CP_CMD_REMOVE 0x0008 /* (8) remove contact */ -#define CP_CMD_RX_MSG 0x0009 /* (9) get new messages */ -#define CP_CMD_TX_MSG 0x000A /* (10) send new message */ -#define CP_CMD_REGISTER 0x000B /* (11) register */ -//#define CP_CMD_PROFILE_SET 0x000C /* (12) set profile (DEPRECATED see CP_CMD_EXTPROFILE_SET) */ -#define CP_CMD_SUGGESTCONTACTS 0x000D /* (13) suggest contacts */ -#define CP_CMD_POLL 0x0011 /* (17) poll the HTTP server for an update */ -//#define CP_CMD_PROFILE_GET 0x001A /* (26) get profile (DEPRECATED see CP_CMD_EXTPROFILE_GET) */ -#define CP_CMD_MEDIA 0x001B /* (27) get multimedia message */ -#define CP_CMD_SPLASHCLICK 0x001F /* (31) splash-screen clickthrough */ -#define CP_CMD_STATUS 0x0020 /* (32) set shown presence & status */ -#define CP_CMD_MSGEVENT 0x0023 /* (35) Raise message event */ -#define CP_CMD_GOT_MSGEVENT 0x0024 /* (36) Get message event */ -#define CP_CMD_MOOD 0x0029 /* (41) set mood */ -#define CP_CMD_KICK 0x002B /* (43) login kick */ -#define CP_CMD_GRPCHAT_CREATE 0x002C /* (44) create new groupchat */ -#define CP_CMD_GRPCHAT_INVITE 0x002D /* (45) add new groupchat member */ -#define CP_CMD_NEW_SUB 0x0033 /* (51) get new subscription */ -#define CP_CMD_ALLOW 0x0034 /* (52) allow subscription */ -#define CP_CMD_DENY 0x0037 /* (55) deny subscription */ -#define CP_CMD_EXTPROFILE_GET 0x0039 /* (57) get extended profile */ -#define CP_CMD_EXTPROFILE_SET 0x003A /* (58) set extended profile */ -#define CP_CMD_PING 0x03E8 /* (1000) ping (keepalive) */ - -/* HTTP connection */ -#define MXIT_HTTP_POLL_MIN 7 /* minimum time between HTTP polls (seconds) */ -#define MXIT_HTTP_POLL_MAX ( 10 * 60 ) /* maximum time between HTTP polls (seconds) */ - -/* receiver states */ -#define RX_STATE_RLEN 0x01 /* reading packet length section */ -#define RX_STATE_DATA 0x02 /* reading packet data section */ -#define RX_STATE_PROC 0x03 /* process read data */ - -/* message flags */ -#define CP_MSG_NOTIFY_DELIVERY 0x0002 /* request delivery notification */ -#define CP_MSG_NOTIFY_READ 0x0004 /* request read notification */ -#define CP_MSG_PWD_ENCRYPTED 0x0010 /* message is password encrypted */ -#define CP_MSG_TL_ENCRYPTED 0x0020 /* message is transport encrypted */ -#define CP_MSG_RPLY_PWD_ENCRYPT 0x0040 /* reply should be password encrypted */ -#define CP_MSG_RPLY_TL_ENCRYPT 0x0080 /* reply should be transport encrypted */ -#define CP_MSG_MARKUP 0x0200 /* message may contain markup */ -#define CP_MSG_EMOTICON 0x0400 /* message may contain custom emoticons */ -#define CP_MSG_FAREWELL 0x0800 /* this is a farewell message */ - -/* redirect types */ -#define CP_REDIRECT_PERMANENT 1 /* permanent redirect */ -#define CP_REDIRECT_TEMPORARY 2 /* temporary redirect */ - -/* message tx types */ -#define CP_MSGTYPE_NORMAL 0x01 /* normal message */ -#define CP_MSGTYPE_CHAT 0x02 /* chat message */ -#define CP_MSGTYPE_HEADLINE 0x03 /* headline message */ -#define CP_MSGTYPE_ERROR 0x04 /* error message */ -#define CP_MSGTYPE_GROUPCHAT 0x05 /* groupchat message */ -#define CP_MSGTYPE_FORM 0x06 /* mxit custom form */ -#define CP_MSGTYPE_COMMAND 0x07 /* mxit command */ - -/* message event types */ -#define CP_MSGEVENT_DELIVERED 0x02 /* message was delivered */ -#define CP_MSGEVENT_DISPLAYED 0x04 /* message was viewed */ -#define CP_MSGEVENT_TYPING 0x10 /* user is typing */ -#define CP_MSGEVENT_STOPPED 0x20 /* user has stopped typing */ -#define CP_MSGEVENT_ANGRY 0x40 /* user is typing angrily */ -#define CP_MSGEVENT_ERASING 0x80 /* user is erasing text */ - -/* extended profile attribute fields */ -#define CP_PROFILE_BIRTHDATE "birthdate" /* Birthdate (String - ISO 8601 format) */ -#define CP_PROFILE_GENDER "gender" /* Gender (Boolean - 0=female, 1=male) */ -// #define CP_PROFILE_HIDENUMBER "hidenumber" /* Hide Number (Boolean - 0=false, 1=true) (DEPRECATED) */ -#define CP_PROFILE_FULLNAME "fullname" /* Fullname (UTF8 String) */ -#define CP_PROFILE_STATUS "statusmsg" /* Status Message (UTF8 String) */ -#define CP_PROFILE_PREVSTATUS "prevstatusmsgs" /* Previous Status Messages (UTF8 String) */ -#define CP_PROFILE_AVATAR "avatarid" /* Avatar ID (String) */ -#define CP_PROFILE_MODIFIED "lastmodified" /* Last-Modified timestamp */ -#define CP_PROFILE_TITLE "title" /* Title (UTF8 String) */ -#define CP_PROFILE_FIRSTNAME "firstname" /* First name (UTF8 String) */ -#define CP_PROFILE_LASTNAME "lastname" /* Last name (UTF8 String) */ -#define CP_PROFILE_EMAIL "email" /* Email address (UTF8 String) */ -#define CP_PROFILE_MOBILENR "mobilenumber" /* Mobile Number (UTF8 String) */ -#define CP_PROFILE_REGCOUNTRY "registeredcountry" /* Registered Country Code (UTF8 String) */ -#define CP_PROFILE_FLAGS "flags" /* Profile flags (Bitset) */ -#define CP_PROFILE_LASTSEEN "lastseen" /* Last-Online timestamp */ -#define CP_PROFILE_WHEREAMI "whereami" /* Where am I / Where I live */ -#define CP_PROFILE_ABOUTME "aboutme" /* About me */ -#define CP_PROFILE_RELATIONSHIP "relationship" /* Relationship Status */ - -/* extended profile field types */ -#define CP_PROFILE_TYPE_BOOL 0x02 /* boolean (0 or 1) */ -#define CP_PROFILE_TYPE_SHORT 0x04 /* short (16-bit) */ -#define CP_PROFILE_TYPE_INT 0x05 /* integer (32-bit) */ -#define CP_PROFILE_TYPE_LONG 0x06 /* long (64-bit) */ -#define CP_PROFILE_TYPE_UTF8 0x0A /* UTF8 string */ -#define CP_PROFILE_TYPE_DATE 0x0B /* date-time (ISO 8601 format) */ - -/* profile flags */ -#define CP_PROF_NOT_SEARCHABLE 0x02 /* user cannot be searched for */ -#define CP_PROF_NOT_SUGGESTABLE 0x08 /* user cannot be suggested as friend */ -#define CP_PROF_DOBLOCKED 0x40 /* date-of-birth cannot be changed */ - -/* suggestion types */ -#define CP_SUGGEST_ADDRESSBOOK 0 /* address book search */ -#define CP_SUGGEST_FRIENDS 1 /* suggested friends */ -#define CP_SUGGEST_SEARCH 2 /* free-text search */ -#define CP_SUGGEST_MXITID 3 /* MXitId search */ - -/* define this to enable protocol debugging (very verbose logging) */ -#define DEBUG_PROTOCOL - - -/* ======================================================================================= */ - -struct MXitSession; - -/*------------------------------------------*/ - -struct field { - char* data; - int len; -}; - -struct record { - struct field** fields; - int fcount; -}; - -struct rx_packet { - int cmd; - int errcode; - char* errmsg; - struct record** records; - int rcount; -}; - -struct tx_packet { - int cmd; - char header[256]; - int headerlen; - char* data; - int datalen; -}; - -/*------------------------------------------*/ - - -/* - * A received message data object - */ -struct RXMsgData { - struct MXitSession* session; /* MXit session object */ - char* from; /* the sender's name */ - time_t timestamp; /* time at which the message was sent */ - GString* msg; /* newly created message converted to libPurple formatting */ - gboolean got_img; /* flag to say if this message got any images/emoticons embedded */ - short img_count; /* the amount of images/emoticons still outstanding for the message */ - int chatid; /* multimx chatroom id */ - int flags; /* libPurple conversation flags */ - gboolean converted; /* true if the message has been completely parsed and converted to libPurple markup */ - gboolean processed; /* the message has been processed completely and should be freed up */ -}; - - - -/* - * The packet transmission queue. - */ -struct tx_queue { - struct tx_packet* packets[MAX_QUEUE_SIZE]; /* array of packet pointers */ - int count; /* number of packets queued */ - int rd_i; /* queue current read index (queue offset for reading a packet) */ - int wr_i; /* queue current write index (queue offset for adding new packet) */ -}; - - -/* ======================================================================================= */ - -void mxit_popup( int type, const char* heading, const char* message ); -void mxit_strip_domain( char* username ); -gboolean find_active_chat( const GList* chats, const char* who ); - -void mxit_cb_rx( gpointer data, gint source, PurpleInputCondition cond ); -gboolean mxit_manage_queue_slow( gpointer user_data ); -gboolean mxit_manage_queue_fast( gpointer user_data ); -gboolean mxit_manage_polling( gpointer user_data ); - -void mxit_send_register( struct MXitSession* session ); -void mxit_send_login( struct MXitSession* session ); -void mxit_send_logout( struct MXitSession* session ); -void mxit_send_ping( struct MXitSession* session ); -void mxit_send_poll( struct MXitSession* session ); - -void mxit_send_presence( struct MXitSession* session, int presence, const char* statusmsg ); -void mxit_send_mood( struct MXitSession* session, int mood ); -void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup, gboolean is_command ); - -void mxit_send_extprofile_update( struct MXitSession* session, const char* password, unsigned int nr_attrib, const char* attributes ); -void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] ); - -void mxit_send_suggest_friends( struct MXitSession* session, int max, unsigned int nr_attrib, const char* attribute[] ); -void mxit_send_suggest_search( struct MXitSession* session, int max, const char* text, unsigned int nr_attrib, const char* attribute[] ); - -void mxit_send_invite( struct MXitSession* session, const char* username, gboolean mxitid, const char* alias, const char* groupname, const char* message ); -void mxit_send_remove( struct MXitSession* session, const char* username ); -void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias ); -void mxit_send_deny_sub( struct MXitSession* session, const char* username, const char* reason ); -void mxit_send_update_contact( struct MXitSession* session, const char* username, const char* alias, const char* groupname ); -void mxit_send_splashclick( struct MXitSession* session, const char* splashid ); -void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event); - -void mxit_send_file( struct MXitSession* session, const char* username, const char* filename, const unsigned char* buf, int buflen ); -void mxit_send_file_reject( struct MXitSession* session, const char* fileid ); -void mxit_send_file_accept( struct MXitSession* session, const char* fileid, int filesize, int offset ); -void mxit_send_file_received( struct MXitSession* session, const char* fileid, short status ); -void mxit_set_avatar( struct MXitSession* session, const unsigned char* avatar, int avatarlen ); -void mxit_get_avatar( struct MXitSession* session, const char* mxitId, const char* avatarId ); - -void mxit_send_groupchat_create( struct MXitSession* session, const char* groupname, int nr_usernames, const char* usernames[] ); -void mxit_send_groupchat_invite( struct MXitSession* session, const char* roomid, int nr_usernames, const char* usernames[] ); - -int mxit_parse_packet( struct MXitSession* session ); -void dump_bytes( struct MXitSession* session, const char* buf, int len ); -void mxit_close_connection( struct MXitSession* session ); -gint64 mxit_now_milli( void ); - - -#endif /* _MXIT_PROTO_H_ */ -
--- a/libpurple/protocols/mxit/roster.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/roster.c Fri Jan 31 18:02:20 2014 +0530 @@ -26,7 +26,7 @@ #include "internal.h" #include "debug.h" -#include "protocol.h" +#include "client.h" #include "mxit.h" #include "roster.h" @@ -325,17 +325,17 @@ /* now re-instate his presence again */ if ( contact ) { - /* update the buddy's status (reference: "libpurple/prpl.h") */ + /* update the buddy's status (reference: "libpurple/protocol.h") */ if ( contact->statusMsg ) - purple_prpl_got_user_status( session->acc, newbuddy->name, mxit_statuses[contact->presence].id, "message", contact->statusMsg, NULL ); + purple_protocol_got_user_status( session->acc, newbuddy->name, mxit_statuses[contact->presence].id, "message", contact->statusMsg, NULL ); else - purple_prpl_got_user_status( session->acc, newbuddy->name, mxit_statuses[contact->presence].id, NULL ); + purple_protocol_got_user_status( session->acc, newbuddy->name, mxit_statuses[contact->presence].id, NULL ); /* update the buddy's mood */ if ( contact->mood == MXIT_MOOD_NONE ) - purple_prpl_got_user_status_deactive( session->acc, newbuddy->name, "mood" ); + purple_protocol_got_user_status_deactive( session->acc, newbuddy->name, "mood" ); else - purple_prpl_got_user_status( session->acc, newbuddy->name, "mood", PURPLE_MOOD_NAME, mxit_moods[contact->mood-1].mood, NULL ); + purple_protocol_got_user_status( session->acc, newbuddy->name, "mood", PURPLE_MOOD_NAME, mxit_moods[contact->mood-1].mood, NULL ); /* update avatar */ if ( contact->avatarId ) { @@ -422,14 +422,14 @@ else contact->avatarId = NULL; - /* update the buddy's status (reference: "libpurple/prpl.h") */ - purple_prpl_got_user_status( session->acc, contact->username, mxit_statuses[contact->presence].id, NULL ); + /* update the buddy's status (reference: "libpurple/protocol.h") */ + purple_protocol_got_user_status( session->acc, contact->username, mxit_statuses[contact->presence].id, NULL ); /* update the buddy's mood */ if ( contact->mood == MXIT_MOOD_NONE ) - purple_prpl_got_user_status_deactive( session->acc, contact->username, "mood" ); + purple_protocol_got_user_status_deactive( session->acc, contact->username, "mood" ); else - purple_prpl_got_user_status( session->acc, contact->username, "mood", PURPLE_MOOD_NAME, mxit_moods[contact->mood-1].mood, NULL ); + purple_protocol_got_user_status( session->acc, contact->username, "mood", PURPLE_MOOD_NAME, mxit_moods[contact->mood-1].mood, NULL ); } @@ -488,17 +488,17 @@ if ( ( statusMsg ) && ( statusMsg[0] != '\0' ) ) contact->statusMsg = g_markup_escape_text( statusMsg, -1 ); - /* update the buddy's status (reference: "libpurple/prpl.h") */ + /* update the buddy's status (reference: "libpurple/protocol.h") */ if ( contact->statusMsg ) - purple_prpl_got_user_status( session->acc, username, mxit_statuses[contact->presence].id, "message", contact->statusMsg, NULL ); + purple_protocol_got_user_status( session->acc, username, mxit_statuses[contact->presence].id, "message", contact->statusMsg, NULL ); else - purple_prpl_got_user_status( session->acc, username, mxit_statuses[contact->presence].id, NULL ); + purple_protocol_got_user_status( session->acc, username, mxit_statuses[contact->presence].id, NULL ); /* update the buddy's mood */ if ( contact->mood == MXIT_MOOD_NONE ) - purple_prpl_got_user_status_deactive( session->acc, username, "mood" ); + purple_protocol_got_user_status_deactive( session->acc, username, "mood" ); else - purple_prpl_got_user_status( session->acc, username, "mood", PURPLE_MOOD_NAME, mxit_moods[contact->mood-1].mood, NULL ); + purple_protocol_got_user_status( session->acc, username, "mood", PURPLE_MOOD_NAME, mxit_moods[contact->mood-1].mood, NULL ); }
--- a/libpurple/protocols/mxit/splashscreen.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/mxit/splashscreen.c Fri Jan 31 18:02:20 2014 +0530 @@ -28,7 +28,7 @@ #include "imgstore.h" #include "request.h" -#include "protocol.h" +#include "client.h" #include "mxit.h" #include "splashscreen.h"
--- a/libpurple/protocols/myspace/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/myspace/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -41,4 +41,5 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/myspace/myspace.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/myspace/myspace.c Fri Jan 31 18:02:20 2014 +0530 @@ -32,9 +32,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#define PURPLE_PLUGIN - #include "myspace.h" +#include "plugins.h" + +static PurpleProtocol *my_protocol = NULL; static void msim_set_status(PurpleAccount *account, PurpleStatus *status); static void msim_set_idle(PurpleConnection *gc, int time); @@ -1303,7 +1304,7 @@ PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, - MSIM_PRPL_VERSION_STRING); + MSIM_PROTOCOL_VERSION_STRING); ret = msim_send_bm(session, username, our_info, MSIM_BM_UNOFFICIAL_CLIENT); @@ -1442,14 +1443,14 @@ g_free(unrecognized_msg); } - purple_prpl_got_user_status(session->account, username, purple_primitive_get_id_from_type(purple_status_code), NULL); + purple_protocol_got_user_status(session->account, username, purple_primitive_get_id_from_type(purple_status_code), NULL); if (status_code == MSIM_STATUS_CODE_IDLE) { purple_debug_info("msim", "msim_status: got idle: %s\n", username); - purple_prpl_got_user_idle(session->account, username, TRUE, 0); + purple_protocol_got_user_idle(session->account, username, TRUE, 0); } else { /* All other statuses indicate going back to non-idle. */ - purple_prpl_got_user_idle(session->account, username, FALSE, 0); + purple_protocol_got_user_idle(session->account, username, FALSE, 0); } #ifdef MSIM_SEND_CLIENT_VERSION @@ -2247,7 +2248,7 @@ /* * Free our protocol-specific buddy data. It almost seems like libpurple - * should call our buddy_free prpl callback so that we don't need to do + * should call our buddy_free protocol callback so that we don't need to do * this... but it doesn't, so we do. */ buddies = purple_blist_find_buddies(purple_connection_get_account(gc), NULL); @@ -2932,8 +2933,8 @@ /** * Send raw data to the server, possibly with embedded NULs. * - * Used in prpl_info struct, so that plugins can have the most possible - * control of what is sent over the connection. Inside this prpl, + * Used in protocol struct, so that plugins can have the most possible + * control of what is sent over the connection. Inside this protocol, * msim_send_raw() is used, since it sends NUL-terminated strings (easier). * * @param gc PurpleConnection @@ -3015,97 +3016,6 @@ } /** - * Callbacks called by Purple, to access this plugin. - */ -static PurplePluginProtocolInfo prpl_info = { - sizeof(PurplePluginProtocolInfo), /* struct_size */ - /* options */ - OPT_PROTO_USE_POINTSIZE /* specify font size in sane point size */ - | OPT_PROTO_MAIL_CHECK, - - /* | OPT_PROTO_IM_IMAGE - TODO: direct images. */ - NULL, /* user_splits */ - NULL, /* protocol_options */ - NO_BUDDY_ICONS, /* icon_spec - TODO: eventually should add this */ - msim_list_icon, /* list_icon */ - NULL, /* list_emblems */ - msim_status_text, /* status_text */ - msim_tooltip_text, /* tooltip_text */ - msim_status_types, /* status_types */ - msim_blist_node_menu, /* blist_node_menu */ - NULL, /* chat_info */ - NULL, /* chat_info_defaults */ - msim_login, /* login */ - msim_close, /* close */ - msim_send_im, /* send_im */ - NULL, /* set_info */ - msim_send_typing, /* send_typing */ - msim_get_info, /* get_info */ - msim_set_status, /* set_status */ - msim_set_idle, /* set_idle */ - NULL, /* change_passwd */ - msim_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - msim_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - msim_add_deny, /* add_deny */ - NULL, /* rem_permit */ - msim_rem_deny, /* rem_deny */ - NULL, /* set_permit_deny */ - NULL, /* join_chat */ - NULL, /* reject chat invite */ - NULL, /* get_chat_name */ - NULL, /* chat_invite */ - NULL, /* chat_leave */ - NULL, /* chat_whisper */ - NULL, /* chat_send */ - NULL, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - msim_buddy_free, /* buddy_free */ - NULL, /* convo_closed */ - msim_normalize, /* normalize */ - NULL, /* set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ - msim_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - msim_send_really_raw, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - msim_send_attention, /* send_attention */ - msim_attention_types, /* attention_types */ - msim_get_account_text_table, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL /* get_max_message_size */ -}; - -/** - * Load the plugin. - */ -static gboolean -msim_load(PurplePlugin *plugin) -{ - return TRUE; -} - -/** * Called when friends have been imported to buddy list on server. */ static void @@ -3143,13 +3053,13 @@ /** * Import friends from myspace.com. */ -static void msim_import_friends(PurplePluginAction *action) +static void msim_import_friends(PurpleProtocolAction *action) { PurpleConnection *gc; MsimSession *session; gchar *group_name; - gc = (PurpleConnection *)action->context; + gc = action->connection; session = purple_connection_get_protocol_data(gc); group_name = "MySpace Friends"; @@ -3172,64 +3082,28 @@ * Actions menu for account. */ static GList * -msim_actions(PurplePlugin *plugin, gpointer context /* PurpleConnection* */) +msim_get_actions(PurpleConnection *gc) { GList *menu; - PurplePluginAction *act; + PurpleProtocolAction *act; menu = NULL; #if 0 /* TODO: find out how */ - act = purple_plugin_action_new(_("Find people..."), msim_); + act = purple_protocol_action_new(_("Find people..."), msim_); menu = g_list_append(menu, act); - act = purple_plugin_action_new(_("Change IM name..."), NULL); + act = purple_protocol_action_new(_("Change IM name..."), NULL); menu = g_list_append(menu, act); #endif - act = purple_plugin_action_new(_("Add friends from MySpace.com"), msim_import_friends); + act = purple_protocol_action_new(_("Add friends from MySpace.com"), msim_import_friends); menu = g_list_append(menu, act); return menu; } -/** - * Based on MSN's plugin info comments. - */ -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-myspace", /**< id */ - "MySpaceIM", /**< name */ - MSIM_PRPL_VERSION_STRING, /**< version */ - /** summary */ - "MySpaceIM Protocol Plugin", - /** description */ - "MySpaceIM Protocol Plugin", - "Jeff Connelly <jeff2@soc.pidgin.im>", /**< author */ - "https://developer.pidgin.im/wiki/MySpaceIM/", /**< homepage */ - - msim_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - msim_actions, /**< msim_actions */ - NULL, /**< reserved1 */ - NULL, /**< reserved2 */ - NULL, /**< reserved3 */ - NULL /**< reserved4 */ -}; - #ifdef MSIM_SELF_TEST /* * Test functions. @@ -3519,56 +3393,188 @@ } /** - * Initialize plugin. + * Protocol attributes and options. */ static void -init_plugin(PurplePlugin *plugin) +msim_protocol_init(PurpleProtocol *protocol) { -#ifdef MSIM_SELF_TEST - msim_test_all(); - exit(0); -#endif /* MSIM_SELF_TEST */ - PurpleAccountOption *option; - static gboolean initialized = FALSE; + + protocol->id = "prpl-myspace"; + protocol->name = "MySpaceIM"; + protocol->options = OPT_PROTO_USE_POINTSIZE /* specify font size in sane point size */ + | OPT_PROTO_MAIL_CHECK; + /* | OPT_PROTO_IM_IMAGE - TODO: direct images. */ + /* TODO: eventually should add icon_spec */ /* TODO: default to automatically try different ports. Make the user be * able to set the first port to try (like LastConnectedPort in Windows client). */ option = purple_account_option_string_new(_("Connect server"), "server", MSIM_SERVER); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_int_new(_("Connect port"), "port", MSIM_PORT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); #ifdef MSIM_USER_WANTS_TO_CONFIGURE_STATUS_TEXT option = purple_account_option_bool_new(_("Show display name in status text"), "show_display_name", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Show headline in status text"), "show_headline", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); #endif #ifdef MSIM_USER_WANTS_TO_DISABLE_EMOTICONS option = purple_account_option_bool_new(_("Send emoticons"), "emoticons", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); #endif #ifdef MSIM_USER_REALLY_CARES_ABOUT_PRECISE_FONT_SIZES option = purple_account_option_int_new(_("Screen resolution (dots per inch)"), "dpi", MSIM_DEFAULT_DPI); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_int_new(_("Base font size (points)"), "base_font_size", MSIM_BASE_FONT_POINT_SIZE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); #endif - - /* Code below only runs once. Based on oscar.c's oscar_init(). */ - if (initialized) - return; - - initialized = TRUE; - - purple_signal_connect(purple_get_core(), "uri-handler", &initialized, +} + +/** + * Protocol class and interfaces initialization. + */ + +static void +msim_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = msim_login; + klass->close = msim_close; + klass->status_types = msim_status_types; + klass->list_icon = msim_list_icon; +} + +static void +msim_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = msim_get_actions; + client_iface->status_text = msim_status_text; + client_iface->tooltip_text = msim_tooltip_text; + client_iface->blist_node_menu = msim_blist_node_menu; + client_iface->buddy_free = msim_buddy_free; + client_iface->normalize = msim_normalize; + client_iface->offline_message = msim_offline_message; + client_iface->get_account_text_table = msim_get_account_text_table; +} + +static void +msim_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->get_info = msim_get_info; + server_iface->set_status = msim_set_status; + server_iface->set_idle = msim_set_idle; + server_iface->add_buddy = msim_add_buddy; + server_iface->remove_buddy = msim_remove_buddy; + server_iface->send_raw = msim_send_really_raw; +} + +static void +msim_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = msim_send_im; + im_iface->send_typing = msim_send_typing; +} + +static void +msim_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) +{ + privacy_iface->add_deny = msim_add_deny; + privacy_iface->rem_deny = msim_rem_deny; +} + +static void +msim_protocol_attention_iface_init(PurpleProtocolAttentionIface *attention_iface) +{ + attention_iface->send = msim_send_attention; + attention_iface->get_types = msim_attention_types; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + MsimProtocol, msim_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + msim_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + msim_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + msim_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + msim_protocol_privacy_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE, + msim_protocol_attention_iface_init) +); + +/** + * Query the plugin + */ +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Jeff Connelly <jeff2@soc.pidgin.im>", + NULL + }; + + return purple_plugin_info_new( + "id", "prpl-myspace", + "name", "MySpaceIM Protocol", + "version", MSIM_PROTOCOL_VERSION_STRING, + "category", "Protocol", + "summary", "MySpaceIM Protocol Plugin", + "description", "MySpaceIM Protocol Plugin", + "authors", authors, + "website", "https://developer.pidgin.im/wiki/MySpaceIM/", + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +/** + * Load the plugin. + */ +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + msim_protocol_register_type(plugin); + +#ifdef MSIM_SELF_TEST + msim_test_all(); + g_set_error(error, MSIM_DOMAIN, 0, _("Finished MySpaceIM self test")); + return FALSE; +#endif /* MSIM_SELF_TEST */ + + my_protocol = purple_protocols_add(MSIM_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; + + purple_signal_connect(purple_get_core(), "uri-handler", my_protocol, PURPLE_CALLBACK(msim_uri_handler), NULL); + + return TRUE; } -PURPLE_INIT_PLUGIN(myspace, init_plugin, info); +/** + * Unload the plugin. + */ +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + + return TRUE; +} + +PURPLE_PLUGIN_INIT(myspace, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/protocols/myspace/myspace.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/myspace/myspace.h Fri Jan 31 18:02:20 2014 +0530 @@ -39,7 +39,7 @@ #endif #include "notify.h" -#include "plugin.h" +#include "plugins.h" #include "accountopt.h" #include "version.h" #include "util.h" /* for base64 */ @@ -61,6 +61,13 @@ #include "markup.h" #include "user.h" +#define MSIM_TYPE_PROTOCOL (msim_protocol_get_type()) +#define MSIM_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MSIM_TYPE_PROTOCOL, MsimProtocol)) +#define MSIM_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), MSIM_TYPE_PROTOCOL, MsimProtocolClass)) +#define MSIM_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), MSIM_TYPE_PROTOCOL)) +#define MSIM_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), MSIM_TYPE_PROTOCOL)) +#define MSIM_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), MSIM_TYPE_PROTOCOL, MsimProtocolClass)) + /* Conditional compilation options */ /* Send third-party client version? (Recognized by us and Miranda's plugin) */ /*#define MSIM_SEND_CLIENT_VERSION */ @@ -103,7 +110,7 @@ #define MSIM_LANGUAGE_NAME_ENGLISH "ENGLISH" /* msimprpl version string of this plugin */ -#define MSIM_PRPL_VERSION_STRING "0.18" +#define MSIM_PROTOCOL_VERSION_STRING "0.18" /* Default server */ #define MSIM_SERVER "im.myspace.akadns.net" @@ -189,7 +196,19 @@ #define MSIM_ERROR_INCORRECT_PASSWORD 260 #define MSIM_ERROR_LOGGED_IN_ELSEWHERE 6 +typedef struct _MsimProtocol +{ + PurpleProtocol parent; +} MsimProtocol; + +typedef struct _MsimProtocolClass +{ + PurpleProtocolClass parent_class; +} MsimProtocolClass; + /* Functions */ +G_MODULE_EXPORT GType msim_protocol_get_type(void); + gboolean msim_send_raw(MsimSession *session, const gchar *msg); gboolean msim_send_bm(MsimSession *session, const gchar *who, const gchar *text, int type);
--- a/libpurple/protocols/myspace/user.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/myspace/user.c Fri Jan 31 18:02:20 2014 +0530 @@ -113,7 +113,7 @@ guint cv; /* Useful to identify the account the tooltip refers to. - * Other prpls show this. */ + * Other protocols show this. */ if (user->username) { purple_notify_user_info_add_pair_plaintext(user_info, _("User"), user->username); } @@ -256,7 +256,7 @@ * If new_artist and new_title are NULL/empty, deactivate PURPLE_STATUS_TUNE. * * This function is useful because it lets you set the artist or title - * individually, which purple_prpl_got_user_status() doesn't do. + * individually, which purple_protocol_got_user_status() doesn't do. */ static void msim_set_artist_or_title(MsimUser *user, const char *new_artist, const char *new_title) { @@ -281,7 +281,7 @@ name = purple_buddy_get_name(user->buddy); if (!new_artist && !new_title) { - purple_prpl_got_user_status_deactive(account, name, "tune"); + purple_protocol_got_user_status_deactive(account, name, "tune"); return; } @@ -301,7 +301,7 @@ if (!new_title) new_title = prev_title; - purple_prpl_got_user_status(account, name, "tune", + purple_protocol_got_user_status(account, name, "tune", PURPLE_TUNE_TITLE, new_title, PURPLE_TUNE_ARTIST, new_artist, NULL);
--- a/libpurple/protocols/myspace/zap.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/myspace/zap.c Fri Jan 31 18:02:20 2014 +0530 @@ -179,7 +179,7 @@ zap = GPOINTER_TO_INT(zap_num_ptr); - purple_prpl_send_attention(session->gc, purple_buddy_get_name(buddy), zap); + purple_protocol_send_attention(session->gc, purple_buddy_get_name(buddy), zap); } /** Return menu, if any, for a buddy list node. */ @@ -237,7 +237,7 @@ zap = CLAMP(zap, 0, 9); - purple_prpl_got_attention(session->gc, username, zap); + purple_protocol_got_attention(session->gc, username, zap); g_free(msg_text); g_free(username);
--- a/libpurple/protocols/novell/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/novell/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -24,6 +24,7 @@ nmuser.c \ nmuserrecord.h \ nmuserrecord.c \ + novell.h \ novell.c AM_CFLAGS = $(st) @@ -50,4 +51,5 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ - $(GLIB_CFLAGS) + $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS)
--- a/libpurple/protocols/novell/novell.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/novell/novell.c Fri Jan 31 18:02:20 2014 +0530 @@ -19,12 +19,14 @@ */ #include "internal.h" + #include "accountopt.h" #include "debug.h" -#include "prpl.h" +#include "plugins.h" #include "server.h" #include "nmuser.h" #include "notify.h" +#include "novell.h" #include "util.h" #include "sslconn.h" #include "request.h" @@ -43,7 +45,7 @@ #define NOVELL_STATUS_TYPE_IDLE "idle" #define NOVELL_STATUS_TYPE_APPEAR_OFFLINE "appearoffline" -static PurplePlugin *my_protocol = NULL; +static PurpleProtocol *my_protocol = NULL; static gboolean _is_disconnect_error(NMERR_T err); @@ -1232,9 +1234,9 @@ } } - purple_prpl_got_user_status(account, name, status_id, + purple_protocol_got_user_status(account, name, status_id, "message", text, NULL); - purple_prpl_got_user_idle(account, name, + purple_protocol_got_user_idle(account, name, (novellstatus == NM_STATUS_AWAY_IDLE), idle); } @@ -2184,7 +2186,7 @@ } /******************************************************************************* - * Prpl Ops + * Protocol Ops ******************************************************************************/ static void @@ -3499,130 +3501,139 @@ return 1792; } -static PurplePluginProtocolInfo prpl_info = { - sizeof(PurplePluginProtocolInfo), /* struct_size */ - 0, - NULL, /* user_splits */ - NULL, /* protocol_options */ - NO_BUDDY_ICONS, /* icon_spec */ - novell_list_icon, /* list_icon */ - NULL, /* list_emblems */ - novell_status_text, /* status_text */ - novell_tooltip_text, /* tooltip_text */ - novell_status_types, /* status_types */ - novell_blist_node_menu, /* blist_node_menu */ - NULL, /* chat_info */ - NULL, /* chat_info_defaults */ - novell_login, /* login */ - novell_close, /* close */ - novell_send_im, /* send_im */ - NULL, /* set_info */ - novell_send_typing, /* send_typing */ - novell_get_info, /* get_info */ - novell_set_status, /* set_status */ - novell_set_idle, /* set_idle */ - NULL, /* change_passwd */ - novell_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - novell_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - novell_add_permit, /* add_permit */ - novell_add_deny, /* add_deny */ - novell_rem_permit, /* rem_permit */ - novell_rem_deny, /* rem_deny */ - novell_set_permit_deny, /* set_permit_deny */ - NULL, /* join_chat */ - NULL, /* reject_chat */ - NULL, /* get_chat_name */ - novell_chat_invite, /* chat_invite */ - novell_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - novell_chat_send, /* chat_send */ - novell_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - novell_alias_buddy, /* alias_buddy */ - novell_group_buddy, /* group_buddy */ - novell_rename_group, /* rename_group */ - NULL, /* buddy_free */ - novell_convo_closed, /* convo_closed */ - purple_normalize_nocase, /* normalize */ - NULL, /* set_buddy_icon */ - novell_remove_group, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ - NULL, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - novell_get_max_message_size /* get_max_message_size */ -}; - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - "prpl-novell", /**< id */ - "GroupWise", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Novell GroupWise Messenger Protocol Plugin"), - /** description */ - N_("Novell GroupWise Messenger Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; +static void +novell_protocol_init(PurpleProtocol *protocol) +{ + PurpleAccountOption *option; + + protocol->id = "prpl-novell"; + protocol->name = "GroupWise"; + + option = purple_account_option_string_new(_("Server address"), "server", NULL); + protocol->protocol_options = + g_list_append(protocol->protocol_options, option); + + option = purple_account_option_int_new(_("Server port"), "port", DEFAULT_PORT); + protocol->protocol_options = + g_list_append(protocol->protocol_options, option); +} + +static void +novell_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = novell_login; + klass->close = novell_close; + klass->status_types = novell_status_types; + klass->list_icon = novell_list_icon; +} + +static void +novell_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->status_text = novell_status_text; + client_iface->tooltip_text = novell_tooltip_text; + client_iface->blist_node_menu = novell_blist_node_menu; + client_iface->convo_closed = novell_convo_closed; + client_iface->normalize = purple_normalize_nocase; + client_iface->get_max_message_size = novell_get_max_message_size; +} + +static void +novell_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->get_info = novell_get_info; + server_iface->set_status = novell_set_status; + server_iface->set_idle = novell_set_idle; + server_iface->add_buddy = novell_add_buddy; + server_iface->remove_buddy = novell_remove_buddy; + server_iface->keepalive = novell_keepalive; + server_iface->alias_buddy = novell_alias_buddy; + server_iface->group_buddy = novell_group_buddy; + server_iface->rename_group = novell_rename_group; + server_iface->remove_group = novell_remove_group; +} + +static void +novell_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = novell_send_im; + im_iface->send_typing = novell_send_typing; +} + +static void +novell_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->invite = novell_chat_invite; + chat_iface->leave = novell_chat_leave; + chat_iface->send = novell_chat_send; +} static void -init_plugin(PurplePlugin * plugin) +novell_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) +{ + privacy_iface->add_permit = novell_add_permit; + privacy_iface->add_deny = novell_add_deny; + privacy_iface->rem_permit = novell_rem_permit; + privacy_iface->rem_deny = novell_rem_deny; + privacy_iface->set_permit_deny = novell_set_permit_deny; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + NovellProtocol, novell_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + novell_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + novell_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + novell_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + novell_protocol_chat_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + novell_protocol_privacy_iface_init) +); + +static PurplePluginInfo * +plugin_query(GError **error) { - PurpleAccountOption *option; - - option = purple_account_option_string_new(_("Server address"), "server", NULL); - prpl_info.protocol_options = - g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_int_new(_("Server port"), "port", DEFAULT_PORT); - prpl_info.protocol_options = - g_list_append(prpl_info.protocol_options, option); - - my_protocol = plugin; + return purple_plugin_info_new( + "id", "prpl-novell", + "name", "Novell GroupWise Protocol", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("Novell GroupWise Messenger Protocol Plugin"), + "description", N_("Novell GroupWise Messenger Protocol Plugin"), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); } -PURPLE_INIT_PLUGIN(novell, init_plugin, info); +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + novell_protocol_register_type(plugin); + + my_protocol = purple_protocols_add(NOVELL_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + + return TRUE; +} + +PURPLE_PLUGIN_INIT(novell, plugin_query, plugin_load, plugin_unload);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/novell/novell.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,49 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#ifndef _NOVELL_H_ +#define _NOVELL_H_ + +#include "protocol.h" + +#define NOVELL_TYPE_PROTOCOL (novell_protocol_get_type()) +#define NOVELL_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NOVELL_TYPE_PROTOCOL, NovellProtocol)) +#define NOVELL_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NOVELL_TYPE_PROTOCOL, NovellProtocolClass)) +#define NOVELL_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NOVELL_TYPE_PROTOCOL)) +#define NOVELL_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NOVELL_TYPE_PROTOCOL)) +#define NOVELL_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NOVELL_TYPE_PROTOCOL, NovellProtocolClass)) + +typedef struct _NovellProtocol +{ + PurpleProtocol parent; +} NovellProtocol; + +typedef struct _NovellProtocolClass +{ + PurpleProtocolClass parent_class; +} NovellProtocolClass; + +/** + * Returns the GType for the NovellProtocol object. + */ +G_MODULE_EXPORT GType novell_protocol_get_type(void); + +#endif /* _NOVELL_H_ */
--- a/libpurple/protocols/null/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/null/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -4,13 +4,15 @@ pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) -NULLSOURCES = nullprpl.c +NULLSOURCES = \ + nullprpl.h \ + nullprpl.c AM_CFLAGS = $(st) libnull_la_LDFLAGS = -module -avoid-version -# nullprpl isn't built by default. when it is built, it's dynamically linked. +# nullprpl isn't built by default; when it is built, it's dynamically linked st = pkg_LTLIBRARIES = libnull.la libnull_la_SOURCES = $(NULLSOURCES) @@ -20,4 +22,5 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/null/README Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/null/README Fri Jan 31 18:02:20 2014 +0530 @@ -15,8 +15,8 @@ Nullprpl is intended as an example of how to write a libpurple protocol plugin. It doesn't contain networking code or an event loop, but it does -demonstrate how to use the libpurple API to do pretty much everything a prpl -might need to do. +demonstrate how to use the libpurple API to do pretty much everything a +protocol might need to do. Nullprpl is also a useful tool for hacking on Pidgin, Finch, and other libpurple clients. It's a full-featured protocol plugin, but doesn't depend on @@ -37,8 +37,8 @@ USAGE ----- To add a nullprpl account, go to the account editor window and click Add. -Select Nullprpl from the protocol drop-down list, and enter any username you -want. +Select Null Protocol from the protocol drop-down list, and enter any username +you want. Now, use Pidgin like normal. You can add buddies, send IMs, set away messages, etc. If you send IMs to your own username, they will be echoed back to you.
--- a/libpurple/protocols/null/nullprpl.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/null/nullprpl.c Fri Jan 31 18:02:20 2014 +0530 @@ -17,8 +17,8 @@ * * Nullprpl is intended as an example of how to write a libpurple protocol * plugin. It doesn't contain networking code or an event loop, but it does - * demonstrate how to use the libpurple API to do pretty much everything a prpl - * might need to do. + * demonstrate how to use the libpurple API to do pretty much everything a + * protocol might need to do. * * Nullprpl is also a useful tool for hacking on Pidgin, Finch, and other * libpurple clients. It's a full-featured protocol plugin, but doesn't depend @@ -46,12 +46,14 @@ #include <glib.h> -/* If you're using this as the basis of a prpl that will be distributed +/* If you're using this as the basis of a protocol that will be distributed * separately from libpurple, remove the internal.h include below and replace * it with code to include your own config.h or similar. If you're going to * provide for translation, you'll also need to setup the gettext macros. */ #include "internal.h" +#include "nullprpl.h" + #include "account.h" #include "accountopt.h" #include "buddylist.h" @@ -60,15 +62,24 @@ #include "connection.h" #include "debug.h" #include "notify.h" -#include "prpl.h" +#include "plugins.h" #include "roomlist.h" #include "status.h" #include "util.h" #include "version.h" +/* + * reference to the protocol instance, used for registering signals, prefs, + * etc. it is set when the protocol is added in plugin_load and is required + * for removing the protocol in plugin_unload. + */ +static PurpleProtocol *my_protocol = NULL; -#define NULLPRPL_ID "prpl-null" -static PurplePlugin *_null_protocol = NULL; +/* + * list of commands registered by the protocol, used to unregister the commands + * when the protocol is removed. + */ +static GSList *cmds = NULL; #define NULL_STATUS_ONLINE "online" #define NULL_STATUS_AWAY "away" @@ -86,7 +97,7 @@ /* * stores offline messages that haven't been delivered yet. maps username - * (char *) to GList * of GOfflineMessages. initialized in nullprpl_init. + * (char *) to GList * of GOfflineMessages. initialized in plugin_load. */ GHashTable* goffline_messages = NULL; @@ -100,26 +111,26 @@ /* * helpers */ -static PurpleConnection *get_nullprpl_gc(const char *username) { - PurpleAccount *acct = purple_accounts_find(username, NULLPRPL_ID); +static PurpleConnection *get_null_gc(const char *username) { + PurpleAccount *acct = purple_accounts_find(username, "null"); if (acct && purple_account_is_connected(acct)) return purple_account_get_connection(acct); else return NULL; } -static void call_if_nullprpl(gpointer data, gpointer userdata) { +static void call_if_nullprotocol(gpointer data, gpointer userdata) { PurpleConnection *gc = (PurpleConnection *)(data); GcFuncData *gcfdata = (GcFuncData *)userdata; - if (!strcmp(purple_account_get_protocol_id(purple_connection_get_account(gc)), NULLPRPL_ID)) + if (!strcmp(purple_account_get_protocol_id(purple_connection_get_account(gc)), "null")) gcfdata->fn(gcfdata->from, gc, gcfdata->userdata); } -static void foreach_nullprpl_gc(GcFunc fn, PurpleConnection *from, +static void foreach_null_gc(GcFunc fn, PurpleConnection *from, gpointer userdata) { GcFuncData gcfdata = { fn, from, userdata }; - g_list_foreach(purple_connections_get_all(), call_if_nullprpl, + g_list_foreach(purple_connections_get_all(), call_if_nullprotocol, &gcfdata); } @@ -171,7 +182,7 @@ !strcmp(status_id, NULL_STATUS_OFFLINE)) { purple_debug_info("nullprpl", "%s sees that %s is %s: %s\n", from_username, to_username, status_id, message); - purple_prpl_got_user_status(purple_connection_get_account(from), to_username, status_id, + purple_protocol_got_user_status(purple_connection_get_account(from), to_username, status_id, (message) ? "message" : NULL, message, NULL); } else { purple_debug_error("nullprpl", @@ -192,9 +203,9 @@ /* * UI callbacks */ -static void nullprpl_input_user_info(PurplePluginAction *action) +static void null_input_user_info(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *)action->context; + PurpleConnection *gc = action->connection; PurpleAccount *acct = purple_connection_get_account(gc); purple_debug_info("nullprpl", "showing 'Set User Info' dialog for %s\n", purple_account_get_username(acct)); @@ -202,26 +213,22 @@ purple_account_request_change_user_info(acct); } -/* this is set to the actions member of the PurplePluginInfo struct at the - * bottom. +/* + * Protocol functions */ -static GList *nullprpl_actions(PurplePlugin *plugin, gpointer context) +static GList *null_get_actions(PurpleConnection *gc) { - PurplePluginAction *action = purple_plugin_action_new( - _("Set User Info..."), nullprpl_input_user_info); + PurpleProtocolAction *action = purple_protocol_action_new( + _("Set User Info..."), null_input_user_info); return g_list_append(NULL, action); } - -/* - * prpl functions - */ -static const char *nullprpl_list_icon(PurpleAccount *acct, PurpleBuddy *buddy) +static const char *null_list_icon(PurpleAccount *acct, PurpleBuddy *buddy) { return "null"; } -static char *nullprpl_status_text(PurpleBuddy *buddy) { +static char *null_status_text(PurpleBuddy *buddy) { purple_debug_info("nullprpl", "getting %s's status text for %s\n", purple_buddy_get_name(buddy), purple_account_get_username(purple_buddy_get_account(buddy))); @@ -248,18 +255,18 @@ } } -static void nullprpl_tooltip_text(PurpleBuddy *buddy, +static void null_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *info, gboolean full) { - PurpleConnection *gc = get_nullprpl_gc(purple_buddy_get_name(buddy)); + PurpleConnection *gc = get_null_gc(purple_buddy_get_name(buddy)); if (gc) { /* they're logged in */ PurplePresence *presence = purple_buddy_get_presence(buddy); PurpleStatus *status = purple_presence_get_active_status(presence); - char *msg = nullprpl_status_text(buddy); - /* TODO: Check whether it's correct to call add_pair_html, - or if we should be using add_pair_plaintext */ + char *msg = null_status_text(buddy); + /* TODO: Check whether it's correct to call add_pair_html, + or if we should be using add_pair_plaintext */ purple_notify_user_info_add_pair_html(info, purple_status_get_name(status), msg); g_free(msg); @@ -267,8 +274,8 @@ if (full) { const char *user_info = purple_account_get_user_info(purple_connection_get_account(gc)); if (user_info) - /* TODO: Check whether it's correct to call add_pair_html, - or if we should be using add_pair_plaintext */ + /* TODO: Check whether it's correct to call add_pair_html, + or if we should be using add_pair_plaintext */ purple_notify_user_info_add_pair_html(info, _("User info"), user_info); } @@ -281,7 +288,7 @@ (full) ? "full" : "short", purple_buddy_get_name(buddy)); } -static GList *nullprpl_status_types(PurpleAccount *acct) +static GList *null_status_types(PurpleAccount *acct) { GList *types = NULL; PurpleStatusType *type; @@ -318,16 +325,16 @@ purple_notify_info(NULL, /* plugin handle or PurpleConnection */ _("Primary title"), _("Secondary title"), - _("This is the callback for the nullprpl menu item."), + _("This is the callback for the NullProtocol menu item."), NULL); } -static GList *nullprpl_blist_node_menu(PurpleBlistNode *node) { +static GList *null_blist_node_menu(PurpleBlistNode *node) { purple_debug_info("nullprpl", "providing buddy list context menu item\n"); if (PURPLE_IS_BUDDY(node)) { PurpleMenuAction *action = purple_menu_action_new( - _("Nullprpl example menu item"), + _("NullProtocol example menu item"), PURPLE_CALLBACK(blist_example_menu_item), NULL, /* userdata passed to the callback */ NULL); /* child menu items */ @@ -337,12 +344,12 @@ } } -static GList *nullprpl_chat_info(PurpleConnection *gc) { - struct proto_chat_entry *pce; /* defined in prpl.h */ +static GList *null_chat_info(PurpleConnection *gc) { + PurpleProtocolChatEntry *pce; /* defined in protocols.h */ purple_debug_info("nullprpl", "returning chat setting 'room'\n"); - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("Chat _room"); pce->identifier = "room"; pce->required = TRUE; @@ -350,7 +357,7 @@ return g_list_append(NULL, pce); } -static GHashTable *nullprpl_chat_info_defaults(PurpleConnection *gc, +static GHashTable *null_chat_info_defaults(PurpleConnection *gc, const char *room) { GHashTable *defaults; @@ -362,7 +369,7 @@ return defaults; } -static void nullprpl_login(PurpleAccount *acct) +static void null_login(PurpleAccount *acct) { PurpleConnection *gc = purple_account_get_connection(acct); GList *offline_messages; @@ -379,10 +386,10 @@ purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED); /* tell purple about everyone on our buddy list who's connected */ - foreach_nullprpl_gc(discover_status, gc, NULL); + foreach_null_gc(discover_status, gc, NULL); - /* notify other nullprpl accounts */ - foreach_nullprpl_gc(report_status_change, gc, NULL); + /* notify other nullprotocol accounts */ + foreach_null_gc(report_status_change, gc, NULL); /* fetch stored offline messages */ purple_debug_info("nullprpl", "checking for offline messages for %s\n", @@ -405,19 +412,19 @@ g_hash_table_remove(goffline_messages, purple_account_get_username(acct)); } -static void nullprpl_close(PurpleConnection *gc) +static void null_close(PurpleConnection *gc) { - /* notify other nullprpl accounts */ - foreach_nullprpl_gc(report_status_change, gc, NULL); + /* notify other nullprotocol accounts */ + foreach_null_gc(report_status_change, gc, NULL); } -static int nullprpl_send_im(PurpleConnection *gc, const char *who, +static int null_send_im(PurpleConnection *gc, const char *who, const char *message, PurpleMessageFlags flags) { const char *from_username = purple_account_get_username(purple_connection_get_account(gc)); PurpleMessageFlags receive_flags = ((flags & ~PURPLE_MESSAGE_SEND) | PURPLE_MESSAGE_RECV); - PurpleAccount *to_acct = purple_accounts_find(who, NULLPRPL_ID); + PurpleAccount *to_acct = purple_accounts_find(who, "null"); PurpleConnection *to; purple_debug_info("nullprpl", "sending message from %s to %s: %s\n", @@ -437,7 +444,7 @@ } /* is the recipient online? */ - to = get_nullprpl_gc(who); + to = get_null_gc(who); if (to) { /* yes, send */ serv_got_im(to, from_username, message, receive_flags, time(NULL)); @@ -461,7 +468,7 @@ return 1; } -static void nullprpl_set_info(PurpleConnection *gc, const char *info) { +static void null_set_info(PurpleConnection *gc, const char *info) { purple_debug_info("nullprpl", "setting %s's user info to %s\n", purple_account_get_username(purple_connection_get_account(gc)), info); } @@ -489,15 +496,15 @@ (PurpleIMTypingState)typing); } -static unsigned int nullprpl_send_typing(PurpleConnection *gc, const char *name, +static unsigned int null_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState typing) { purple_debug_info("nullprpl", "%s %s\n", purple_account_get_username(purple_connection_get_account(gc)), typing_state_to_string(typing)); - foreach_nullprpl_gc(notify_typing, gc, (gpointer)typing); + foreach_null_gc(notify_typing, gc, (gpointer)typing); return 0; } -static void nullprpl_get_info(PurpleConnection *gc, const char *username) { +static void null_get_info(PurpleConnection *gc, const char *username) { const char *body; PurpleNotifyUserInfo *info = purple_notify_user_info_new(); PurpleAccount *acct; @@ -505,9 +512,9 @@ purple_debug_info("nullprpl", "Fetching %s's user info for %s\n", username, purple_account_get_username(purple_connection_get_account(gc))); - acct = purple_accounts_find(username, NULLPRPL_ID); + acct = purple_accounts_find(username, "null"); - if (!get_nullprpl_gc(username)) { + if (!get_null_gc(username)) { char *msg = g_strdup_printf(_("%s is not logged in."), username); purple_notify_error(gc, _("User Info"), _("User info not available. "), msg, purple_request_cpar_from_account(acct)); @@ -530,32 +537,32 @@ NULL); /* userdata for callback */ } -static void nullprpl_set_status(PurpleAccount *acct, PurpleStatus *status) { +static void null_set_status(PurpleAccount *acct, PurpleStatus *status) { const char *msg = purple_status_get_attr_string(status, "message"); purple_debug_info("nullprpl", "setting %s's status to %s: %s\n", purple_account_get_username(acct), purple_status_get_name(status), msg); - foreach_nullprpl_gc(report_status_change, get_nullprpl_gc(purple_account_get_username(acct)), + foreach_null_gc(report_status_change, get_null_gc(purple_account_get_username(acct)), NULL); } -static void nullprpl_set_idle(PurpleConnection *gc, int idletime) { +static void null_set_idle(PurpleConnection *gc, int idletime) { purple_debug_info("nullprpl", "purple reports that %s has been idle for %d seconds\n", purple_account_get_username(purple_connection_get_account(gc)), idletime); } -static void nullprpl_change_passwd(PurpleConnection *gc, const char *old_pass, +static void null_change_passwd(PurpleConnection *gc, const char *old_pass, const char *new_pass) { purple_debug_info("nullprpl", "%s wants to change their password\n", purple_account_get_username(purple_connection_get_account(gc))); } -static void nullprpl_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, +static void null_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message) { const char *username = purple_account_get_username(purple_connection_get_account(gc)); - PurpleConnection *buddy_gc = get_nullprpl_gc(purple_buddy_get_name(buddy)); + PurpleConnection *buddy_gc = get_null_gc(purple_buddy_get_name(buddy)); purple_debug_info("nullprpl", "adding %s to %s's buddy list\n", purple_buddy_get_name(buddy), username); @@ -580,7 +587,7 @@ } } -static void nullprpl_add_buddies(PurpleConnection *gc, GList *buddies, +static void null_add_buddies(PurpleConnection *gc, GList *buddies, GList *groups, const char *message) { GList *buddy = buddies; GList *group = groups; @@ -588,13 +595,13 @@ purple_debug_info("nullprpl", "adding multiple buddies\n"); while (buddy && group) { - nullprpl_add_buddy(gc, (PurpleBuddy *)buddy->data, (PurpleGroup *)group->data, message); + null_add_buddy(gc, (PurpleBuddy *)buddy->data, (PurpleGroup *)group->data, message); buddy = g_list_next(buddy); group = g_list_next(group); } } -static void nullprpl_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, +static void null_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { purple_debug_info("nullprpl", "removing %s from %s's buddy list\n", @@ -602,7 +609,7 @@ purple_account_get_username(purple_connection_get_account(gc))); } -static void nullprpl_remove_buddies(PurpleConnection *gc, GList *buddies, +static void null_remove_buddies(PurpleConnection *gc, GList *buddies, GList *groups) { GList *buddy = buddies; GList *group = groups; @@ -610,7 +617,7 @@ purple_debug_info("nullprpl", "removing multiple buddies\n"); while (buddy && group) { - nullprpl_remove_buddy(gc, (PurpleBuddy *)buddy->data, + null_remove_buddy(gc, (PurpleBuddy *)buddy->data, (PurpleGroup *)group->data); buddy = g_list_next(buddy); group = g_list_next(group); @@ -618,34 +625,34 @@ } /* - * nullprpl uses purple's local whitelist and blacklist, stored in blist.xml, as + * nullprotocol uses purple's local whitelist and blacklist, stored in blist.xml, as * its authoritative privacy settings, and uses purple's logic (specifically * purple_privacy_check(), from privacy.h), to determine whether messages are * allowed or blocked. */ -static void nullprpl_add_permit(PurpleConnection *gc, const char *name) { +static void null_add_permit(PurpleConnection *gc, const char *name) { purple_debug_info("nullprpl", "%s adds %s to their allowed list\n", purple_account_get_username(purple_connection_get_account(gc)), name); } -static void nullprpl_add_deny(PurpleConnection *gc, const char *name) { +static void null_add_deny(PurpleConnection *gc, const char *name) { purple_debug_info("nullprpl", "%s adds %s to their blocked list\n", purple_account_get_username(purple_connection_get_account(gc)), name); } -static void nullprpl_rem_permit(PurpleConnection *gc, const char *name) { +static void null_rem_permit(PurpleConnection *gc, const char *name) { purple_debug_info("nullprpl", "%s removes %s from their allowed list\n", purple_account_get_username(purple_connection_get_account(gc)), name); } -static void nullprpl_rem_deny(PurpleConnection *gc, const char *name) { +static void null_rem_deny(PurpleConnection *gc, const char *name) { purple_debug_info("nullprpl", "%s removes %s from their blocked list\n", purple_account_get_username(purple_connection_get_account(gc)), name); } -static void nullprpl_set_permit_deny(PurpleConnection *gc) { +static void null_set_permit_deny(PurpleConnection *gc) { /* this is for synchronizing the local black/whitelist with the server. - * for nullprpl, it's a noop. + * for nullprotocol, it's a noop. */ } @@ -672,7 +679,7 @@ } } -static void nullprpl_join_chat(PurpleConnection *gc, GHashTable *components) { +static void null_join_chat(PurpleConnection *gc, GHashTable *components) { const char *username = purple_account_get_username(purple_connection_get_account(gc)); const char *room = g_hash_table_lookup(components, "room"); int chat_id = g_str_hash(room); @@ -695,11 +702,11 @@ } } -static void nullprpl_reject_chat(PurpleConnection *gc, GHashTable *components) { +static void null_reject_chat(PurpleConnection *gc, GHashTable *components) { const char *invited_by = g_hash_table_lookup(components, "invited_by"); const char *room = g_hash_table_lookup(components, "room"); const char *username = purple_account_get_username(purple_connection_get_account(gc)); - PurpleConnection *invited_by_gc = get_nullprpl_gc(invited_by); + PurpleConnection *invited_by_gc = get_null_gc(invited_by); char *message = g_strdup_printf( "%s %s %s.", username, @@ -718,18 +725,18 @@ g_free(message); } -static char *nullprpl_get_chat_name(GHashTable *components) { +static char *null_get_chat_name(GHashTable *components) { const char *room = g_hash_table_lookup(components, "room"); purple_debug_info("nullprpl", "reporting chat room name '%s'\n", room); return g_strdup(room); } -static void nullprpl_chat_invite(PurpleConnection *gc, int id, +static void null_chat_invite(PurpleConnection *gc, int id, const char *message, const char *who) { const char *username = purple_account_get_username(purple_connection_get_account(gc)); PurpleChatConversation *chat = purple_conversations_find_chat(gc, id); const char *room = purple_conversation_get_name(PURPLE_CONVERSATION(chat)); - PurpleAccount *to_acct = purple_accounts_find(who, NULLPRPL_ID); + PurpleAccount *to_acct = purple_accounts_find(who, "null"); purple_debug_info("nullprpl", "%s is inviting %s to join chat room %s\n", username, who, room); @@ -767,7 +774,7 @@ } } -static void nullprpl_chat_leave(PurpleConnection *gc, int id) { +static void null_chat_leave(PurpleConnection *gc, int id) { PurpleChatConversation *chat = purple_conversations_find_chat(gc, id); purple_debug_info("nullprpl", "%s is leaving chat room %s\n", purple_account_get_username(purple_connection_get_account(gc)), @@ -803,7 +810,7 @@ purple_conversation_get_name(conv), message); chat_user = purple_chat_conversation_find_user(PURPLE_CHAT_CONVERSATION(conv), to_username); - to = get_nullprpl_gc(to_username); + to = get_null_gc(to_username); if (!chat_user) { /* this will be freed by the caller */ @@ -828,7 +835,7 @@ } } -static void nullprpl_chat_whisper(PurpleConnection *gc, int id, const char *who, +static void null_chat_whisper(PurpleConnection *gc, int id, const char *who, const char *message) { const char *username = purple_account_get_username(purple_connection_get_account(gc)); PurpleChatConversation *chat = purple_conversations_find_chat(gc, id); @@ -845,7 +852,7 @@ static void receive_chat_message(PurpleChatConversation *from, PurpleChatConversation *to, int id, const char *room, gpointer userdata) { const char *message = (const char *)userdata; - PurpleConnection *to_gc = get_nullprpl_gc(purple_chat_conversation_get_nick(to)); + PurpleConnection *to_gc = get_null_gc(purple_chat_conversation_get_nick(to)); purple_debug_info("nullprpl", "%s receives message from %s in chat room %s: %s\n", @@ -854,7 +861,7 @@ time(NULL)); } -static int nullprpl_chat_send(PurpleConnection *gc, int id, const char *message, +static int null_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) { const char *username = purple_account_get_username(purple_connection_get_account(gc)); PurpleChatConversation *chat = purple_conversations_find_chat(gc, id); @@ -876,62 +883,52 @@ } } -static void nullprpl_register_user(PurpleAccount *acct) { +static void null_register_user(PurpleAccount *acct) { purple_debug_info("nullprpl", "registering account for %s\n", purple_account_get_username(acct)); } -static void nullprpl_get_cb_info(PurpleConnection *gc, int id, const char *who) { - PurpleChatConversation *chat = purple_conversations_find_chat(gc, id); - purple_debug_info("nullprpl", - "retrieving %s's info for %s in chat room %s\n", who, - purple_account_get_username(purple_connection_get_account(gc)), - purple_conversation_get_name(PURPLE_CONVERSATION(chat))); - - nullprpl_get_info(gc, who); -} - -static void nullprpl_alias_buddy(PurpleConnection *gc, const char *who, +static void null_alias_buddy(PurpleConnection *gc, const char *who, const char *alias) { purple_debug_info("nullprpl", "%s sets %s's alias to %s\n", purple_account_get_username(purple_connection_get_account(gc)), who, alias); } -static void nullprpl_group_buddy(PurpleConnection *gc, const char *who, +static void null_group_buddy(PurpleConnection *gc, const char *who, const char *old_group, const char *new_group) { purple_debug_info("nullprpl", "%s has moved %s from group %s to group %s\n", purple_account_get_username(purple_connection_get_account(gc)), who, old_group, new_group); } -static void nullprpl_rename_group(PurpleConnection *gc, const char *old_name, +static void null_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies) { purple_debug_info("nullprpl", "%s has renamed group %s to %s\n", purple_account_get_username(purple_connection_get_account(gc)), old_name, purple_group_get_name(group)); } -static void nullprpl_convo_closed(PurpleConnection *gc, const char *who) { +static void null_convo_closed(PurpleConnection *gc, const char *who) { purple_debug_info("nullprpl", "%s's conversation with %s was closed\n", purple_account_get_username(purple_connection_get_account(gc)), who); } /* normalize a username (e.g. remove whitespace, add default domain, etc.) - * for nullprpl, this is a noop. + * for nullprotocol, this is a noop. */ -static const char *nullprpl_normalize(const PurpleAccount *acct, +static const char *null_normalize(const PurpleAccount *acct, const char *input) { return NULL; } -static void nullprpl_set_buddy_icon(PurpleConnection *gc, +static void null_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img) { purple_debug_info("nullprpl", "setting %s's buddy icon to %s\n", purple_account_get_username(purple_connection_get_account(gc)), img ? purple_imgstore_get_filename(img) : "(null)"); } -static void nullprpl_remove_group(PurpleConnection *gc, PurpleGroup *group) { +static void null_remove_group(PurpleConnection *gc, PurpleGroup *group) { purple_debug_info("nullprpl", "%s has removed group %s\n", purple_account_get_username(purple_connection_get_account(gc)), purple_group_get_name(group)); @@ -957,7 +954,7 @@ g_free(msg); } -static void nullprpl_set_chat_topic(PurpleConnection *gc, int id, +static void null_set_chat_topic(PurpleConnection *gc, int id, const char *topic) { PurpleChatConversation *chat = purple_conversations_find_chat(gc, id); const char *last_topic; @@ -977,14 +974,14 @@ foreach_gc_in_chat(set_chat_topic_fn, gc, id, (gpointer)topic); } -static gboolean nullprpl_finish_get_roomlist(gpointer roomlist) { +static gboolean null_finish_get_roomlist(gpointer roomlist) { purple_roomlist_set_in_progress(PURPLE_ROOMLIST(roomlist), FALSE); g_object_unref(roomlist); return FALSE; } -static PurpleRoomlist *nullprpl_roomlist_get_list(PurpleConnection *gc) { +static PurpleRoomlist *null_roomlist_get_list(PurpleConnection *gc) { const char *username = purple_account_get_username(purple_connection_get_account(gc)); PurpleRoomlist *roomlist = purple_roomlist_new(purple_connection_get_account(gc)); GList *fields = NULL; @@ -1029,17 +1026,17 @@ } g_list_free(seen_ids); - purple_timeout_add(1 /* ms */, nullprpl_finish_get_roomlist, g_object_ref(roomlist)); + purple_timeout_add(1 /* ms */, null_finish_get_roomlist, g_object_ref(roomlist)); return roomlist; } -static void nullprpl_roomlist_cancel(PurpleRoomlist *list) { +static void null_roomlist_cancel(PurpleRoomlist *list) { PurpleAccount *account = purple_roomlist_get_account(list); purple_debug_info("nullprpl", "%s asked to cancel room list request\n", purple_account_get_username(account)); } -static void nullprpl_roomlist_expand_category(PurpleRoomlist *list, +static void null_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category) { PurpleAccount *account = purple_roomlist_get_account(list); purple_debug_info("nullprpl", "%s asked to expand room list category %s\n", @@ -1047,134 +1044,199 @@ purple_roomlist_room_get_name(category)); } -/* nullprpl doesn't support file transfer...yet... */ -static gboolean nullprpl_can_receive_file(PurpleConnection *gc, - const char *who) { - return FALSE; -} - -static gboolean nullprpl_offline_message(const PurpleBuddy *buddy) { +static gboolean null_offline_message(const PurpleBuddy *buddy) { purple_debug_info("nullprpl", "reporting that offline messages are supported for %s\n", purple_buddy_get_name(buddy)); return TRUE; } - /* - * prpl stuff. see prpl.h for more information. + * Initialize the protocol instance. see protocol.h for more information. */ +static void +null_protocol_init(PurpleProtocol *protocol) +{ + PurpleAccountUserSplit *split; + PurpleAccountOption *option; -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_NO_PASSWORD | OPT_PROTO_CHAT_TOPIC, /* options */ - NULL, /* user_splits, initialized in nullprpl_init() */ - NULL, /* protocol_options, initialized in nullprpl_init() */ - { /* icon_spec, a PurpleBuddyIconSpec */ + protocol->id = "prpl-null"; + protocol->name = "Null - Testing Protocol"; + protocol->options = OPT_PROTO_NO_PASSWORD | OPT_PROTO_CHAT_TOPIC; + protocol->icon_spec = purple_buddy_icon_spec_new( "png,jpg,gif", /* format */ 0, /* min_width */ 0, /* min_height */ 128, /* max_width */ 128, /* max_height */ 10000, /* max_filesize */ - PURPLE_ICON_SCALE_DISPLAY, /* scale_rules */ - }, - nullprpl_list_icon, /* list_icon */ - NULL, /* list_emblem */ - nullprpl_status_text, /* status_text */ - nullprpl_tooltip_text, /* tooltip_text */ - nullprpl_status_types, /* status_types */ - nullprpl_blist_node_menu, /* blist_node_menu */ - nullprpl_chat_info, /* chat_info */ - nullprpl_chat_info_defaults, /* chat_info_defaults */ - nullprpl_login, /* login */ - nullprpl_close, /* close */ - nullprpl_send_im, /* send_im */ - nullprpl_set_info, /* set_info */ - nullprpl_send_typing, /* send_typing */ - nullprpl_get_info, /* get_info */ - nullprpl_set_status, /* set_status */ - nullprpl_set_idle, /* set_idle */ - nullprpl_change_passwd, /* change_passwd */ - nullprpl_add_buddy, /* add_buddy */ - nullprpl_add_buddies, /* add_buddies */ - nullprpl_remove_buddy, /* remove_buddy */ - nullprpl_remove_buddies, /* remove_buddies */ - nullprpl_add_permit, /* add_permit */ - nullprpl_add_deny, /* add_deny */ - nullprpl_rem_permit, /* rem_permit */ - nullprpl_rem_deny, /* rem_deny */ - nullprpl_set_permit_deny, /* set_permit_deny */ - nullprpl_join_chat, /* join_chat */ - nullprpl_reject_chat, /* reject_chat */ - nullprpl_get_chat_name, /* get_chat_name */ - nullprpl_chat_invite, /* chat_invite */ - nullprpl_chat_leave, /* chat_leave */ - nullprpl_chat_whisper, /* chat_whisper */ - nullprpl_chat_send, /* chat_send */ - NULL, /* keepalive */ - nullprpl_register_user, /* register_user */ - nullprpl_get_cb_info, /* get_cb_info */ - nullprpl_alias_buddy, /* alias_buddy */ - nullprpl_group_buddy, /* group_buddy */ - nullprpl_rename_group, /* rename_group */ - NULL, /* buddy_free */ - nullprpl_convo_closed, /* convo_closed */ - nullprpl_normalize, /* normalize */ - nullprpl_set_buddy_icon, /* set_buddy_icon */ - nullprpl_remove_group, /* remove_group */ - NULL, /* get_cb_real_name */ - nullprpl_set_chat_topic, /* set_chat_topic */ - NULL, /* find_blist_chat */ - nullprpl_roomlist_get_list, /* roomlist_get_list */ - nullprpl_roomlist_cancel, /* roomlist_cancel */ - nullprpl_roomlist_expand_category, /* roomlist_expand_category */ - nullprpl_can_receive_file, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ - nullprpl_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL /* get_max_message_size */ -}; + PURPLE_ICON_SCALE_DISPLAY /* scale_rules */ + ); -static void nullprpl_init(PurplePlugin *plugin) -{ /* see accountopt.h for information about user splits and protocol options */ - PurpleAccountUserSplit *split = purple_account_user_split_new( + split = purple_account_user_split_new( _("Example user split"), /* text shown to user */ "default", /* default value */ '@'); /* field separator */ - PurpleAccountOption *option = purple_account_option_string_new( + option = purple_account_option_string_new( _("Example option"), /* text shown to user */ "example", /* pref name */ "default"); /* default value */ - purple_debug_info("nullprpl", "starting up\n"); + protocol->user_splits = g_list_append(NULL, split); + protocol->protocol_options = g_list_append(NULL, option); +} + +/* + * Initialize the protocol class and interfaces. + * see protocol.h for more information. + */ + +static void +null_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = null_login; + klass->close = null_close; + klass->status_types = null_status_types; + klass->list_icon = null_list_icon; +} + +static void +null_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = null_get_actions; + client_iface->status_text = null_status_text; + client_iface->tooltip_text = null_tooltip_text; + client_iface->blist_node_menu = null_blist_node_menu; + client_iface->convo_closed = null_convo_closed; + client_iface->normalize = null_normalize; + client_iface->offline_message = null_offline_message; +} - prpl_info.user_splits = g_list_append(NULL, split); - prpl_info.protocol_options = g_list_append(NULL, option); +static void +null_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->register_user = null_register_user; + server_iface->set_info = null_set_info; + server_iface->get_info = null_get_info; + server_iface->set_status = null_set_status; + server_iface->set_idle = null_set_idle; + server_iface->change_passwd = null_change_passwd; + server_iface->add_buddy = null_add_buddy; + server_iface->add_buddies = null_add_buddies; + server_iface->remove_buddy = null_remove_buddy; + server_iface->remove_buddies = null_remove_buddies; + server_iface->alias_buddy = null_alias_buddy; + server_iface->group_buddy = null_group_buddy; + server_iface->rename_group = null_rename_group; + server_iface->set_buddy_icon = null_set_buddy_icon; + server_iface->remove_group = null_remove_group; +} + +static void +null_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = null_send_im; + im_iface->send_typing = null_send_typing; +} + +static void +null_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->info = null_chat_info; + chat_iface->info_defaults = null_chat_info_defaults; + chat_iface->join = null_join_chat; + chat_iface->reject = null_reject_chat; + chat_iface->get_name = null_get_chat_name; + chat_iface->invite = null_chat_invite; + chat_iface->leave = null_chat_leave; + chat_iface->whisper = null_chat_whisper; + chat_iface->send = null_chat_send; + chat_iface->set_topic = null_set_chat_topic; +} - /* register whisper chat command, /msg */ - purple_cmd_register("msg", - "ws", /* args: recipient and message */ - PURPLE_CMD_P_DEFAULT, /* priority */ - PURPLE_CMD_FLAG_CHAT, - "prpl-null", - send_whisper, - "msg <username> <message>: send a private message, aka a whisper", - NULL); /* userdata */ +static void +null_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) +{ + privacy_iface->add_permit = null_add_permit; + privacy_iface->add_deny = null_add_deny; + privacy_iface->rem_permit = null_rem_permit; + privacy_iface->rem_deny = null_rem_deny; + privacy_iface->set_permit_deny = null_set_permit_deny; +} + +static void +null_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface) +{ + roomlist_iface->get_list = null_roomlist_get_list; + roomlist_iface->cancel = null_roomlist_cancel; + roomlist_iface->expand_category = null_roomlist_expand_category; +} + +/* + * define the null protocol type. this macro defines + * null_protocol_register_type(PurplePlugin *) which is called in plugin_load() + * to register this type with the type system, and null_protocol_get_type() + * which returns the registered GType. + */ +PURPLE_DEFINE_TYPE_EXTENDED( + NullProtocol, null_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + null_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + null_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + null_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + null_protocol_chat_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + null_protocol_privacy_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE, + null_protocol_roomlist_iface_init) +); + +static PurplePluginInfo * +plugin_query(GError **error) +{ + return purple_plugin_info_new( + "id", "prpl-null", + "name", "Null Protocol", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("Null Protocol Plugin"), + "description", N_("Null Protocol Plugin"), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + + /* If you're using this protocol plugin as the basis of a plugin that will + * be distributed separately from libpurple, do not include these flags. */ + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + PurpleCmdId id; + + /* register the NULL_TYPE_PROTOCOL type in the type system. this function + * is defined by PURPLE_DEFINE_TYPE_EXTENDED. */ + null_protocol_register_type(plugin); + + /* add the protocol to the core */ + my_protocol = purple_protocols_add(NULL_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; + + purple_debug_info("nullprpl", "starting up\n"); /* get ready to store offline messages */ goffline_messages = g_hash_table_new_full(g_str_hash, /* hash fn */ @@ -1182,42 +1244,40 @@ g_free, /* key free fn */ NULL); /* value free fn */ - _null_protocol = plugin; -} + /* register whisper chat command, /msg */ + id = purple_cmd_register("msg", + "ws", /* args: recipient and message */ + PURPLE_CMD_P_DEFAULT, /* priority */ + PURPLE_CMD_FLAG_CHAT, + "null", + send_whisper, + "msg <username> <message>: send a private message, aka a whisper", + NULL); /* userdata */ -static void nullprpl_destroy(PurplePlugin *plugin) { - purple_debug_info("nullprpl", "shutting down\n"); + /* add /msg command to the commands list */ + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + return TRUE; } - -static PurplePluginInfo info = +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PURPLE_PLUGIN_MAGIC, /* magic */ - PURPLE_MAJOR_VERSION, /* major_version */ - PURPLE_MINOR_VERSION, /* minor_version */ - PURPLE_PLUGIN_PROTOCOL, /* type */ - NULL, /* ui_requirement */ - 0, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ - NULLPRPL_ID, /* id */ - "Null - Testing Plugin", /* name */ - DISPLAY_VERSION, /* version */ - N_("Null Protocol Plugin"), /* summary */ - N_("Null Protocol Plugin"), /* description */ - NULL, /* author */ - PURPLE_WEBSITE, /* homepage */ - NULL, /* load */ - NULL, /* unload */ - nullprpl_destroy, /* destroy */ - NULL, /* ui_info */ - &prpl_info, /* extra_info */ - NULL, /* prefs_info */ - nullprpl_actions, /* actions */ - NULL, /* padding... */ - NULL, - NULL, - NULL, -}; + /* unregister the commands */ + while (cmds) { + PurpleCmdId id = GPOINTER_TO_UINT(cmds->data); + purple_cmd_unregister(id); + cmds = g_slist_delete_link(cmds, cmds); + } + + purple_debug_info("nullprpl", "shutting down\n"); -PURPLE_INIT_PLUGIN(null, nullprpl_init, info); + /* remove the protocol from the core */ + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + + return TRUE; +} + +/* initialize the plugin */ +PURPLE_PLUGIN_INIT(null, plugin_query, plugin_load, plugin_unload);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/null/nullprpl.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,49 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#ifndef _NULL_H_ +#define _NULL_H_ + +#include "protocol.h" + +#define NULL_TYPE_PROTOCOL (null_protocol_get_type()) +#define NULL_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NULL_TYPE_PROTOCOL, NullProtocol)) +#define NULL_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NULL_TYPE_PROTOCOL, NullProtocolClass)) +#define NULL_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NULL_TYPE_PROTOCOL)) +#define NULL_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NULL_TYPE_PROTOCOL)) +#define NULL_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NULL_TYPE_PROTOCOL, NullProtocolClass)) + +typedef struct _NullProtocol +{ + PurpleProtocol parent; +} NullProtocol; + +typedef struct _NullProtocolClass +{ + PurpleProtocolClass parent_class; +} NullProtocolClass; + +/** + * Returns the GType for the NullProtocol object. + */ +G_MODULE_EXPORT GType null_protocol_get_type(void); + +#endif /* _NULL_H_ */
--- a/libpurple/protocols/oscar/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -7,6 +7,8 @@ OSCARSOURCES = \ authorization.c \ + aim.c \ + aim.h \ bstream.c \ clientlogin.c \ encoding.c \ @@ -28,7 +30,9 @@ family_stats.c \ family_userlookup.c \ flap_connection.c \ - misc.c \ + icq.c \ + icq.h \ + misc.c \ msgcookie.c \ odc.c \ oft.c \ @@ -50,32 +54,27 @@ AM_CFLAGS = $(st) -libaim_la_LDFLAGS = -module -avoid-version -libicq_la_LDFLAGS = -module -avoid-version +liboscar_la_LDFLAGS = -module -avoid-version + if STATIC_OSCAR st = -DPURPLE_STATIC_PRPL noinst_LTLIBRARIES = liboscar.la -liboscar_la_SOURCES = $(OSCARSOURCES) libaim.c libicq.c +liboscar_la_SOURCES = $(OSCARSOURCES) liboscar_la_CFLAGS = $(AM_CFLAGS) else st = -pkg_LTLIBRARIES = liboscar.la libaim.la libicq.la +pkg_LTLIBRARIES = liboscar.la liboscar_la_SOURCES = $(OSCARSOURCES) liboscar_la_LIBADD = $(GLIB_LIBS) -libaim_la_SOURCES = libaim.c -libaim_la_LIBADD = liboscar.la - -libicq_la_SOURCES = libicq.c -libicq_la_LIBADD = liboscar.la - endif AM_CPPFLAGS = \ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/oscar/Makefile.mingw Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/Makefile.mingw Fri Jan 31 18:02:20 2014 +0530 @@ -8,8 +8,6 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak TARGET = liboscar -AIM_TARGET = libaim -ICQ_TARGET = libicq TYPE = PLUGIN # Static or Plugin... @@ -41,6 +39,7 @@ ## SOURCES, OBJECTS ## C_SRC = \ + aim.c \ authorization.c \ bstream.c \ clientlogin.c \ @@ -62,6 +61,7 @@ family_stats.c \ family_userlookup.c \ flap_connection.c \ + icq.c \ misc.c \ msgcookie.c \ odc.c \ @@ -79,12 +79,6 @@ OBJECTS = $(C_SRC:%.c=%.o) -AIM_C_SRC = libaim.c -AIM_OBJECTS = $(AIM_C_SRC:%.c=%.o) - -ICQ_C_SRC = libicq.c -ICQ_OBJECTS = $(ICQ_C_SRC:%.c=%.o) - ## ## LIBRARIES ## @@ -102,29 +96,20 @@ ## .PHONY: all install clean -all: $(TARGET).dll $(AIM_TARGET).dll $(ICQ_TARGET).dll +all: $(TARGET).dll install: all $(DLL_INSTALL_DIR) - cp $(AIM_TARGET).dll $(ICQ_TARGET).dll $(DLL_INSTALL_DIR) - cp $(TARGET).dll $(PURPLE_INSTALL_DIR) + cp $(TARGET).dll $(DLL_INSTALL_DIR) $(OBJECTS): $(PURPLE_CONFIG_H) -$(TARGET).dll.a $(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--out-implib,$(TARGET).dll.a -o $(TARGET).dll - -$(AIM_TARGET).dll: $(TARGET).dll.a $(AIM_OBJECTS) - $(CC) -shared $(AIM_OBJECTS) $(LIB_PATHS) $(LIBS) -loscar $(DLL_LD_FLAGS) -o $(AIM_TARGET).dll - -$(ICQ_TARGET).dll: $(TARGET).dll.a $(ICQ_OBJECTS) - $(CC) -shared $(ICQ_OBJECTS) $(LIB_PATHS) $(LIBS) -loscar $(DLL_LD_FLAGS) -o $(ICQ_TARGET).dll +$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) + $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll ## ## CLEAN RULES ## clean: - rm -f $(OBJECTS) $(TARGET).dll $(TARGET).dll.a - rm -f $(AIM_OBJECTS) $(AIM_TARGET).dll - rm -f $(ICQ_OBJECTS) $(ICQ_TARGET).dll + rm -f $(OBJECTS) $(TARGET).dll include $(PIDGIN_COMMON_TARGETS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/oscar/aim.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,77 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "aim.h" + +#include "core.h" +#include "plugins.h" +#include "signals.h" + +#include "oscarcommon.h" + +static void +aim_protocol_init(PurpleProtocol *protocol) +{ + PurpleAccountOption *option; + + protocol->id = "prpl-aim"; + protocol->name = "AIM"; + + oscar_init_protocol_options(protocol); + + option = purple_account_option_bool_new(_("Allow multiple simultaneous logins"), "allow_multiple_logins", + OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_string_new(_("Server"), "server", oscar_get_login_server(FALSE, TRUE)); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); +} + +static void +aim_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->list_icon = oscar_list_icon_aim; +} + +static void +aim_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_max_message_size = oscar_get_max_message_size; +} + +static void +aim_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) +{ + privacy_iface->add_permit = oscar_add_permit; + privacy_iface->rem_permit = oscar_rem_permit; + privacy_iface->set_permit_deny = oscar_set_aim_permdeny; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + AIMProtocol, aim_protocol, OSCAR_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + aim_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + aim_protocol_privacy_iface_init) +);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/oscar/aim.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,54 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#ifndef _AIM_H_ +#define _AIM_H_ + +#include "oscar.h" + +#define AIM_TYPE_PROTOCOL (aim_protocol_get_type()) +#define AIM_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), AIM_TYPE_PROTOCOL, AIMProtocol)) +#define AIM_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), AIM_TYPE_PROTOCOL, AIMProtocolClass)) +#define AIM_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), AIM_TYPE_PROTOCOL)) +#define AIM_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), AIM_TYPE_PROTOCOL)) +#define AIM_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), AIM_TYPE_PROTOCOL, AIMProtocolClass)) + +typedef struct _AIMProtocol +{ + OscarProtocol parent; +} AIMProtocol; + +typedef struct _AIMProtocolClass +{ + OscarProtocolClass parent_class; +} AIMProtocolClass; + +/** + * Registers the AIMProtocol type in the type system. + */ +void aim_protocol_register_type(PurplePlugin *plugin); + +/** + * Returns the GType for the AIMProtocol object. + */ +G_MODULE_EXPORT GType aim_protocol_get_type(void); + +#endif /* _AIM_H_ */
--- a/libpurple/protocols/oscar/authorization.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/authorization.c Fri Jan 31 18:02:20 2014 +0530 @@ -55,10 +55,10 @@ /* Mobile users should always be online */ if (bname[0] == '+') { - purple_prpl_got_user_status(account, + purple_protocol_got_user_status(account, purple_buddy_get_name(buddy), OSCAR_STATUS_ID_AVAILABLE, NULL); - purple_prpl_got_user_status(account, + purple_protocol_got_user_status(account, purple_buddy_get_name(buddy), OSCAR_STATUS_ID_MOBILE, NULL); }
--- a/libpurple/protocols/oscar/family_icbm.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/family_icbm.c Fri Jan 31 18:02:20 2014 +0530 @@ -1728,7 +1728,7 @@ presence = purple_buddy_get_presence(buddy); status = purple_presence_get_status(presence, "mood"); if (status) { - purple_prpl_got_user_status(account, bn, + purple_protocol_got_user_status(account, bn, "mood", PURPLE_MOOD_NAME, purple_status_get_attr_string(status, PURPLE_MOOD_NAME), PURPLE_MOOD_COMMENT, unescaped_xstatus, NULL);
--- a/libpurple/protocols/oscar/family_icq.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/family_icq.c Fri Jan 31 18:02:20 2014 +0530 @@ -630,7 +630,7 @@ presence = purple_buddy_get_presence(buddy); status = purple_presence_get_active_status(presence); - purple_prpl_got_user_status(account, uin, + purple_protocol_got_user_status(account, uin, purple_status_get_id(status), "message", NULL, NULL);
--- a/libpurple/protocols/oscar/family_locate.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/family_locate.c Fri Jan 31 18:02:20 2014 +0530 @@ -929,11 +929,11 @@ mood = aim_receive_custom_icon(od, bs, length); if (mood) - purple_prpl_got_user_status(account, outinfo->bn, "mood", + purple_protocol_got_user_status(account, outinfo->bn, "mood", PURPLE_MOOD_NAME, mood, NULL); else - purple_prpl_got_user_status_deactive(account, outinfo->bn, "mood"); + purple_protocol_got_user_status_deactive(account, outinfo->bn, "mood"); } else if (type == 0x000e) { /* @@ -1093,11 +1093,11 @@ g_free(icqmood); if (mood) - purple_prpl_got_user_status(account, outinfo->bn, "mood", + purple_protocol_got_user_status(account, outinfo->bn, "mood", PURPLE_MOOD_NAME, mood, NULL); else - purple_prpl_got_user_status_deactive(account, outinfo->bn, "mood"); + purple_protocol_got_user_status_deactive(account, outinfo->bn, "mood"); } break; } @@ -1415,11 +1415,11 @@ mood = aim_receive_custom_icon(od, &cbs, tlv->length); if (mood) - purple_prpl_got_user_status(account, userinfo->bn, "mood", + purple_protocol_got_user_status(account, userinfo->bn, "mood", PURPLE_MOOD_NAME, mood, NULL); else - purple_prpl_got_user_status_deactive(account, userinfo->bn, "mood"); + purple_protocol_got_user_status_deactive(account, userinfo->bn, "mood"); } aim_tlvlist_free(tlvlist);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/oscar/icq.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,83 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "icq.h" + +#include "core.h" +#include "plugins.h" +#include "signals.h" + +#include "oscarcommon.h" + +static GHashTable * +icq_get_account_text_table(PurpleAccount *account) +{ + GHashTable *table; + table = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(table, "login_label", (gpointer)_("ICQ UIN...")); + return table; +} + +static gssize +icq_get_max_message_size(PurpleConversation *conv) +{ + /* XXX: got from pidgin-otr - verify and document it */ + return 2346; +} + +static void +icq_protocol_init(PurpleProtocol *protocol) +{ + PurpleAccountOption *option; + + protocol->id = "prpl-icq"; + protocol->name = "ICQ"; + + oscar_init_protocol_options(protocol); + + option = purple_account_option_string_new(_("Server"), "server", oscar_get_login_server(TRUE, TRUE)); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_string_new(_("Encoding"), "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); +} + +static void +icq_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->list_icon = oscar_list_icon_icq; +} + +static void +icq_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_account_text_table = icq_get_account_text_table; + client_iface->get_moods = oscar_get_purple_moods; + client_iface->get_max_message_size = icq_get_max_message_size; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + ICQProtocol, icq_protocol, OSCAR_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + icq_protocol_client_iface_init) +);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/oscar/icq.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,54 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#ifndef _ICQ_H_ +#define _ICQ_H_ + +#include "oscar.h" + +#define ICQ_TYPE_PROTOCOL (icq_protocol_get_type()) +#define ICQ_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ICQ_TYPE_PROTOCOL, ICQProtocol)) +#define ICQ_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ICQ_TYPE_PROTOCOL, ICQProtocolClass)) +#define ICQ_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ICQ_TYPE_PROTOCOL)) +#define ICQ_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ICQ_TYPE_PROTOCOL)) +#define ICQ_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), ICQ_TYPE_PROTOCOL, ICQProtocolClass)) + +typedef struct _ICQProtocol +{ + OscarProtocol parent; +} ICQProtocol; + +typedef struct _ICQProtocolClass +{ + OscarProtocolClass parent_class; +} ICQProtocolClass; + +/** + * Registers the ICQProtocol type in the type system. + */ +void icq_protocol_register_type(PurplePlugin *plugin); + +/** + * Returns the GType for the ICQProtocol object. + */ +G_MODULE_EXPORT GType icq_protocol_get_type(void); + +#endif /* _ICQ_H_ */
--- a/libpurple/protocols/oscar/libaim.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -/* libaim is the AIM protocol plugin. It is linked against liboscar, - * which contains all the shared implementation code with libicq - */ - -#include "oscarcommon.h" -#include "oscar.h" - -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE | OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"gif,jpeg,bmp,ico", 0, 0, 64, 64, 7168, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ - oscar_list_icon_aim, /* list_icon */ - oscar_list_emblem, /* list_emblems */ - oscar_status_text, /* status_text */ - oscar_tooltip_text, /* tooltip_text */ - oscar_status_types, /* status_types */ - oscar_blist_node_menu, /* blist_node_menu */ - oscar_chat_info, /* chat_info */ - oscar_chat_info_defaults, /* chat_info_defaults */ - oscar_login, /* login */ - oscar_close, /* close */ - oscar_send_im, /* send_im */ - oscar_set_info, /* set_info */ - oscar_send_typing, /* send_typing */ - oscar_get_info, /* get_info */ - oscar_set_status, /* set_status */ - oscar_set_idle, /* set_idle */ - oscar_change_passwd, /* change_passwd */ - oscar_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - oscar_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - oscar_add_permit, /* add_permit */ - oscar_add_deny, /* add_deny */ - oscar_rem_permit, /* rem_permit */ - oscar_rem_deny, /* rem_deny */ - oscar_set_aim_permdeny, /* set_permit_deny */ - oscar_join_chat, /* join_chat */ - NULL, /* reject_chat */ - oscar_get_chat_name, /* get_chat_name */ - oscar_chat_invite, /* chat_invite */ - oscar_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - oscar_send_chat, /* chat_send */ - oscar_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - oscar_alias_buddy, /* alias_buddy */ - oscar_move_buddy, /* group_buddy */ - oscar_rename_group, /* rename_group */ - NULL, /* buddy_free */ - oscar_convo_closed, /* convo_closed */ - oscar_normalize, /* normalize */ - oscar_set_icon, /* set_buddy_icon */ - oscar_remove_group, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - oscar_can_receive_file, /* can_receive_file */ - oscar_send_file, /* send_file */ - oscar_new_xfer, /* new_xfer */ - oscar_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - oscar_get_max_message_size /* get_max_message_size */ -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-aim", /**< id */ - "AIM", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("AIM Protocol Plugin"), - /** description */ - N_("AIM Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, - oscar_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - oscar_init(plugin, FALSE); -} - -PURPLE_INIT_PLUGIN(aim, init_plugin, info);
--- a/libpurple/protocols/oscar/libicq.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,170 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -/* libicq is the ICQ protocol plugin. It is linked against liboscar, - * which contains all the shared implementation code with libaim - */ - - -#include "oscarcommon.h" - -static GHashTable * -icq_get_account_text_table(PurpleAccount *account) -{ - GHashTable *table; - table = g_hash_table_new(g_str_hash, g_str_equal); - g_hash_table_insert(table, "login_label", (gpointer)_("ICQ UIN...")); - return table; -} - -static gssize -icq_get_max_message_size(PurpleConversation *conv) -{ - /* XXX: got from pidgin-otr - verify and document it */ - return 2346; -} - -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE | OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"gif,jpeg,bmp,ico", 0, 0, 64, 64, 7168, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ - oscar_list_icon_icq, /* list_icon */ - oscar_list_emblem, /* list_emblems */ - oscar_status_text, /* status_text */ - oscar_tooltip_text, /* tooltip_text */ - oscar_status_types, /* status_types */ - oscar_blist_node_menu, /* blist_node_menu */ - oscar_chat_info, /* chat_info */ - oscar_chat_info_defaults, /* chat_info_defaults */ - oscar_login, /* login */ - oscar_close, /* close */ - oscar_send_im, /* send_im */ - oscar_set_info, /* set_info */ - oscar_send_typing, /* send_typing */ - oscar_get_info, /* get_info */ - oscar_set_status, /* set_status */ - oscar_set_idle, /* set_idle */ - oscar_change_passwd, /* change_passwd */ - oscar_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - oscar_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - oscar_add_deny, /* add_deny */ - NULL, /* rem_permit */ - oscar_rem_deny, /* rem_deny */ - NULL, /* set_permit_deny */ - oscar_join_chat, /* join_chat */ - NULL, /* reject_chat */ - oscar_get_chat_name, /* get_chat_name */ - oscar_chat_invite, /* chat_invite */ - oscar_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - oscar_send_chat, /* chat_send */ - oscar_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - oscar_alias_buddy, /* alias_buddy */ - oscar_move_buddy, /* group_buddy */ - oscar_rename_group, /* rename_group */ - NULL, /* buddy_free */ - oscar_convo_closed, /* convo_closed */ - oscar_normalize, /* normalize */ - oscar_set_icon, /* set_buddy_icon */ - oscar_remove_group, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - oscar_can_receive_file, /* can_receive_file */ - oscar_send_file, /* send_file */ - oscar_new_xfer, /* new_xfer */ - oscar_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - icq_get_account_text_table, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* can_do_media */ - oscar_get_purple_moods, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - icq_get_max_message_size /* get_max_message_size */ -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-icq", /**< id */ - "ICQ", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("ICQ Protocol Plugin"), - /** description */ - N_("ICQ Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, - oscar_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - PurpleAccountOption *option; - - oscar_init(plugin, TRUE); - - option = purple_account_option_string_new(_("Encoding"), "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); -} - -PURPLE_INIT_PLUGIN(icq, init_plugin, info);
--- a/libpurple/protocols/oscar/oft.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/oft.c Fri Jan 31 18:02:20 2014 +0530 @@ -704,7 +704,7 @@ * those computers can use the same connection for transferring * multiple files. So we don't want the Purple core up and closing * the socket all willy-nilly. We want to do that in the oscar - * prpl, whenever one side or the other says they're finished + * protocol, whenever one side or the other says they're finished * using the connection. There might be a better way to intercept * the socket from the core... */
--- a/libpurple/protocols/oscar/oscar.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/oscar.c Fri Jan 31 18:02:20 2014 +0530 @@ -41,17 +41,22 @@ #include "imgstore.h" #include "network.h" #include "notify.h" -#include "prpl.h" +#include "protocol.h" #include "proxy.h" #include "request.h" #include "util.h" #include "version.h" #include "visibility.h" +#include "aim.h" +#include "icq.h" #include "oscarcommon.h" #include "oscar.h" #include "peer.h" +static PurpleProtocol *aim_protocol = NULL; +static PurpleProtocol *icq_protocol = NULL; + static guint64 purple_caps = OSCAR_CAPABILITY_CHAT | OSCAR_CAPABILITY_BUDDYICON @@ -608,8 +613,8 @@ ICQ_DEFAULT_SSL_LOGIN_SERVER, }; -static const gchar * -get_login_server(gboolean is_icq, gboolean use_ssl) +const gchar * +oscar_get_login_server(gboolean is_icq, gboolean use_ssl) { return login_servers[(is_icq ? 2 : 0) + (use_ssl ? 1 : 0)]; } @@ -726,7 +731,7 @@ } /* Set this flag based on the protocol_id rather than the username, - because that is what's tied to the get_moods prpl callback. */ + because that is what's tied to the get_moods protocol callback. */ if (g_str_equal(purple_account_get_protocol_id(account), "prpl-icq")) flags |= PURPLE_CONNECTION_FLAG_SUPPORT_MOODS; @@ -745,8 +750,8 @@ od->use_ssl = purple_ssl_is_supported() && strcmp(encryption_type, OSCAR_NO_ENCRYPTION) != 0; /* Connect to core Purple signals */ - purple_prefs_connect_callback(gc, "/purple/away/idle_reporting", idle_reporting_pref_cb, gc); - purple_prefs_connect_callback(gc, "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb, gc); + purple_prefs_connect_callback(purple_connection_get_protocol(gc), "/purple/away/idle_reporting", idle_reporting_pref_cb, gc); + purple_prefs_connect_callback(purple_connection_get_protocol(gc), "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb, gc); /* * On 2008-03-05 AOL released some documentation on the OSCAR protocol @@ -767,36 +772,36 @@ newconn = flap_connection_new(od, SNAC_FAMILY_AUTH); if (od->use_ssl) { - server = purple_account_get_string(account, "server", get_login_server(od->icq, TRUE)); + server = purple_account_get_string(account, "server", oscar_get_login_server(od->icq, TRUE)); /* - * If the account's server is what the oscar prpl has offered as + * If the account's server is what the oscar protocol has offered as * the default login server through the vast eons (all two of * said default options, AFAIK) and the user wants SSL, we'll * do what we know is best for them and change the setting out * from under them to the SSL login server. */ - if (!strcmp(server, get_login_server(od->icq, FALSE)) || !strcmp(server, AIM_ALT_LOGIN_SERVER)) { + if (!strcmp(server, oscar_get_login_server(od->icq, FALSE)) || !strcmp(server, AIM_ALT_LOGIN_SERVER)) { purple_debug_info("oscar", "Account uses SSL, so changing server to default SSL server\n"); - purple_account_set_string(account, "server", get_login_server(od->icq, TRUE)); - server = get_login_server(od->icq, TRUE); + purple_account_set_string(account, "server", oscar_get_login_server(od->icq, TRUE)); + server = oscar_get_login_server(od->icq, TRUE); } newconn->gsc = purple_ssl_connect(account, server, purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), ssl_connection_established_cb, ssl_connection_error_cb, newconn); } else { - server = purple_account_get_string(account, "server", get_login_server(od->icq, FALSE)); + server = purple_account_get_string(account, "server", oscar_get_login_server(od->icq, FALSE)); /* * See the comment above. We do the reverse here. If they don't want * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER, * set it back to the default. */ - if (!strcmp(server, get_login_server(od->icq, TRUE))) { + if (!strcmp(server, oscar_get_login_server(od->icq, TRUE))) { purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n"); - purple_account_set_string(account, "server", get_login_server(od->icq, FALSE)); - server = get_login_server(od->icq, FALSE); + purple_account_set_string(account, "server", oscar_get_login_server(od->icq, FALSE)); + server = oscar_get_login_server(od->icq, FALSE); } newconn->connect_data = purple_proxy_connect(NULL, account, server, @@ -1244,9 +1249,9 @@ } if (info->flags & AIM_FLAG_WIRELESS) { - purple_prpl_got_user_status(account, info->bn, OSCAR_STATUS_ID_MOBILE, NULL); + purple_protocol_got_user_status(account, info->bn, OSCAR_STATUS_ID_MOBILE, NULL); } else { - purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE); + purple_protocol_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE); } message = (info->status && info->status_len > 0) @@ -1262,10 +1267,10 @@ itmsurl = g_strdup(purple_status_get_attr_string(previous_status, "itmsurl")); } purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s', itmsurl = '%s'\n", status_id, info->bn, message ? message : "(null)", itmsurl ? itmsurl : "(null)"); - purple_prpl_got_user_status(account, info->bn, status_id, "message", message, "itmsurl", itmsurl, NULL); + purple_protocol_got_user_status(account, info->bn, status_id, "message", message, "itmsurl", itmsurl, NULL); } else { purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s'\n", status_id, info->bn, message ? message : "(null)"); - purple_prpl_got_user_status(account, info->bn, status_id, "message", message, NULL); + purple_protocol_got_user_status(account, info->bn, status_id, "message", message, NULL); } g_free(message); @@ -1276,7 +1281,7 @@ signon = info->onlinesince; else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) signon = time(NULL) - info->sessionlen; - purple_prpl_got_user_login_time(account, info->bn, signon); + purple_protocol_got_user_login_time(account, info->bn, signon); /* Idle time stuff */ /* info->idletime is the number of minutes that this user has been idle */ @@ -1284,9 +1289,9 @@ time_idle = time(NULL) - info->idletime * 60; if (time_idle > 0) - purple_prpl_got_user_idle(account, info->bn, TRUE, time_idle); + purple_protocol_got_user_idle(account, info->bn, TRUE, time_idle); else - purple_prpl_got_user_idle(account, info->bn, FALSE, 0); + purple_protocol_got_user_idle(account, info->bn, FALSE, 0); /* Server stored icon stuff */ bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, info->bn)); @@ -1337,8 +1342,8 @@ info = va_arg(ap, aim_userinfo_t *); va_end(ap); - purple_prpl_got_user_status(account, info->bn, OSCAR_STATUS_ID_OFFLINE, NULL); - purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE); + purple_protocol_got_user_status(account, info->bn, OSCAR_STATUS_ID_OFFLINE, NULL); + purple_protocol_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE); g_hash_table_remove(od->buddyinfo, purple_normalize(purple_connection_get_account(gc), info->bn)); return 1; @@ -3526,9 +3531,9 @@ /* Mobile users should always be online */ if (bname[0] == '+') { - purple_prpl_got_user_status(account, bname, + purple_protocol_got_user_status(account, bname, OSCAR_STATUS_ID_AVAILABLE, NULL); - purple_prpl_got_user_status(account, bname, + purple_protocol_got_user_status(account, bname, OSCAR_STATUS_ID_MOBILE, NULL); } } else if (aim_ssi_waitingforauth(&od->ssi.local, @@ -3846,10 +3851,10 @@ /* Mobile users should always be online */ if (curitem->name[0] == '+') { - purple_prpl_got_user_status(account, + purple_protocol_got_user_status(account, purple_buddy_get_name(b), OSCAR_STATUS_ID_AVAILABLE, NULL); - purple_prpl_got_user_status(account, + purple_protocol_got_user_status(account, purple_buddy_get_name(b), OSCAR_STATUS_ID_MOBILE, NULL); } @@ -4060,9 +4065,9 @@ /* Mobile users should always be online */ if (name[0] == '+') { - purple_prpl_got_user_status(account, + purple_protocol_got_user_status(account, name, OSCAR_STATUS_ID_AVAILABLE, NULL); - purple_prpl_got_user_status(account, + purple_protocol_got_user_status(account, name, OSCAR_STATUS_ID_MOBILE, NULL); } @@ -4212,15 +4217,15 @@ GList *oscar_chat_info(PurpleConnection *gc) { GList *m = NULL; - struct proto_chat_entry *pce; - - pce = g_new0(struct proto_chat_entry, 1); + PurpleProtocolChatEntry *pce; + + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Room:"); pce->identifier = "room"; pce->required = TRUE; m = g_list_append(m, pce); - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Exchange:"); pce->identifier = "exchange"; pce->required = TRUE; @@ -5002,9 +5007,9 @@ } static void -oscar_show_icq_privacy_opts(PurplePluginAction *action) +oscar_show_icq_privacy_opts(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; PurpleAccount *account = purple_connection_get_account(gc); PurpleRequestFields *fields; PurpleRequestFieldGroup *g; @@ -5034,13 +5039,13 @@ gc); } -static void oscar_confirm_account(PurplePluginAction *action) +static void oscar_confirm_account(PurpleProtocolAction *action) { PurpleConnection *gc; OscarData *od; FlapConnection *conn; - gc = (PurpleConnection *)action->context; + gc = action->connection; od = purple_connection_get_protocol_data(gc); conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN); @@ -5052,9 +5057,9 @@ } } -static void oscar_show_email(PurplePluginAction *action) +static void oscar_show_email(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; OscarData *od = purple_connection_get_protocol_data(gc); FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN); @@ -5080,9 +5085,9 @@ } } -static void oscar_show_change_email(PurplePluginAction *action) +static void oscar_show_change_email(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; purple_request_input(gc, NULL, _("Change Address To:"), NULL, NULL, FALSE, FALSE, NULL, _("_OK"), G_CALLBACK(oscar_change_email), @@ -5091,9 +5096,9 @@ gc); } -static void oscar_show_awaitingauth(PurplePluginAction *action) +static void oscar_show_awaitingauth(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; OscarData *od = purple_connection_get_protocol_data(gc); PurpleAccount *account = purple_connection_get_account(gc); GSList *buddies, *filtered_buddies, *cur; @@ -5134,9 +5139,9 @@ aim_search_address(od, email); } -static void oscar_show_find_email(PurplePluginAction *action) +static void oscar_show_find_email(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; purple_request_input(gc, _("Find Buddy by Email"), _("Search for a buddy by email address"), _("Type the email address of the buddy you are " @@ -5148,39 +5153,39 @@ gc); } -static void oscar_show_set_info(PurplePluginAction *action) +static void oscar_show_set_info(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; purple_account_request_change_user_info(purple_connection_get_account(gc)); } -static void oscar_show_set_info_icqurl(PurplePluginAction *action) +static void oscar_show_set_info_icqurl(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; purple_notify_uri(gc, "http://www.icq.com/whitepages/user_details.php"); } -static void oscar_change_pass(PurplePluginAction *action) +static void oscar_change_pass(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; purple_account_request_change_password(purple_connection_get_account(gc)); } /** * Only used when connecting with the old-style BUCP login. */ -static void oscar_show_chpassurl(PurplePluginAction *action) +static void oscar_show_chpassurl(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; OscarData *od = purple_connection_get_protocol_data(gc); gchar *substituted = purple_strreplace(od->authinfo->chpassurl, "%s", purple_account_get_username(purple_connection_get_account(gc))); purple_notify_uri(gc, substituted); g_free(substituted); } -static void oscar_show_imforwardingurl(PurplePluginAction *action) +static void oscar_show_imforwardingurl(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; purple_notify_uri(gc, "http://mymobile.aol.com/dbreg/register?action=imf&clientID=1"); } @@ -5286,39 +5291,38 @@ } GList * -oscar_actions(PurplePlugin *plugin, gpointer context) +oscar_get_actions(PurpleConnection *gc) { - PurpleConnection *gc = (PurpleConnection *) context; OscarData *od = purple_connection_get_protocol_data(gc); GList *menu = NULL; - PurplePluginAction *act; - - act = purple_plugin_action_new(_("Set User Info..."), + PurpleProtocolAction *act; + + act = purple_protocol_action_new(_("Set User Info..."), oscar_show_set_info); menu = g_list_prepend(menu, act); if (od->icq) { - act = purple_plugin_action_new(_("Set User Info (web)..."), + act = purple_protocol_action_new(_("Set User Info (web)..."), oscar_show_set_info_icqurl); menu = g_list_prepend(menu, act); } - act = purple_plugin_action_new(_("Change Password..."), + act = purple_protocol_action_new(_("Change Password..."), oscar_change_pass); menu = g_list_prepend(menu, act); if (od->authinfo != NULL && od->authinfo->chpassurl != NULL) { /* This only happens when connecting with the old-style BUCP login */ - act = purple_plugin_action_new(_("Change Password (web)"), + act = purple_protocol_action_new(_("Change Password (web)"), oscar_show_chpassurl); menu = g_list_prepend(menu, act); } if (!od->icq) { - act = purple_plugin_action_new(_("Configure IM Forwarding (web)"), + act = purple_protocol_action_new(_("Configure IM Forwarding (web)"), oscar_show_imforwardingurl); menu = g_list_prepend(menu, act); } @@ -5328,41 +5332,41 @@ if (od->icq) { /* ICQ actions */ - act = purple_plugin_action_new(_("Set Privacy Options..."), + act = purple_protocol_action_new(_("Set Privacy Options..."), oscar_show_icq_privacy_opts); menu = g_list_prepend(menu, act); - act = purple_plugin_action_new(_("Show Visible List"), oscar_show_visible_list); + act = purple_protocol_action_new(_("Show Visible List"), oscar_show_visible_list); menu = g_list_prepend(menu, act); - act = purple_plugin_action_new(_("Show Invisible List"), oscar_show_invisible_list); + act = purple_protocol_action_new(_("Show Invisible List"), oscar_show_invisible_list); menu = g_list_prepend(menu, act); } else { /* AIM actions */ - act = purple_plugin_action_new(_("Confirm Account"), + act = purple_protocol_action_new(_("Confirm Account"), oscar_confirm_account); menu = g_list_prepend(menu, act); - act = purple_plugin_action_new(_("Display Currently Registered Email Address"), + act = purple_protocol_action_new(_("Display Currently Registered Email Address"), oscar_show_email); menu = g_list_prepend(menu, act); - act = purple_plugin_action_new(_("Change Currently Registered Email Address..."), + act = purple_protocol_action_new(_("Change Currently Registered Email Address..."), oscar_show_change_email); menu = g_list_prepend(menu, act); } menu = g_list_prepend(menu, NULL); - act = purple_plugin_action_new(_("Show Buddies Awaiting Authorization"), + act = purple_protocol_action_new(_("Show Buddies Awaiting Authorization"), oscar_show_awaitingauth); menu = g_list_prepend(menu, act); menu = g_list_prepend(menu, NULL); - act = purple_plugin_action_new(_("Search for Buddy by Email Address..."), + act = purple_protocol_action_new(_("Search for Buddy by Email Address..."), oscar_show_find_email); menu = g_list_prepend(menu, act); @@ -5455,21 +5459,21 @@ } /* TODO: Find somewhere to put this instead of including it in a bunch of places. - * Maybe just change purple_accounts_find() to return anything for the prpl if there is no acct_id. + * Maybe just change purple_accounts_find() to return anything for the protocol if there is no acct_id. */ -static PurpleAccount *find_acct(const char *prpl, const char *acct_id) +static PurpleAccount *find_acct(const char *protocol, const char *acct_id) { PurpleAccount *acct = NULL; /* If we have a specific acct, use it */ if (acct_id) { - acct = purple_accounts_find(acct_id, prpl); + acct = purple_accounts_find(acct_id, protocol); if (acct && !purple_account_is_connected(acct)) acct = NULL; } else { /* Otherwise find an active account for the protocol */ GList *l = purple_accounts_get_all(); while (l) { - if (!strcmp(prpl, purple_account_get_protocol_id(l->data)) + if (!strcmp(protocol, purple_account_get_protocol_id(l->data)) && purple_account_is_connected(l->data)) { acct = l->data; break; @@ -5481,8 +5485,7 @@ return acct; } - -static gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params) +gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params) { char *acct_id = g_hash_table_lookup(params, "account"); char prpl[11]; @@ -5493,7 +5496,7 @@ g_snprintf(prpl, sizeof(prpl), "prpl-%s", proto); - acct = find_acct(prpl, acct_id); + acct = find_acct(proto, acct_id); if (!acct) return FALSE; @@ -5549,11 +5552,9 @@ return FALSE; } -void oscar_init(PurplePlugin *plugin, gboolean is_icq) +void oscar_init_protocol_options(PurpleProtocol *protocol) { - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); PurpleAccountOption *option; - static gboolean init = FALSE; static const gchar *encryption_keys[] = { N_("Use encryption if available"), N_("Require encryption"), @@ -5569,11 +5570,8 @@ GList *encryption_options = NULL; int i; - option = purple_account_option_string_new(_("Server"), "server", get_login_server(is_icq, TRUE)); - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); - option = purple_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT); - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); for (i = 0; encryption_keys[i]; i++) { PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); @@ -5582,26 +5580,163 @@ encryption_options = g_list_append(encryption_options, kvp); } option = purple_account_option_list_new(_("Connection security"), "encryption", encryption_options); - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Use clientLogin"), "use_clientlogin", OSCAR_DEFAULT_USE_CLIENTLOGIN); - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new( _("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy", OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY); - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); - - if (g_str_equal(purple_plugin_get_id(plugin), "prpl-aim")) { - option = purple_account_option_bool_new(_("Allow multiple simultaneous logins"), "allow_multiple_logins", - OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS); - prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); - } - - if (init) - return; - init = TRUE; + protocol->protocol_options = g_list_append(protocol->protocol_options, option); +} + +static void +oscar_protocol_init(PurpleProtocol *protocol) +{ + protocol->options = OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE | + OPT_PROTO_INVITE_MESSAGE | + OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE; + protocol->icon_spec = purple_buddy_icon_spec_new("gif,jpeg,bmp,ico", + 0, 0, 64, 64, 7168, + PURPLE_ICON_SCALE_SEND | + PURPLE_ICON_SCALE_DISPLAY); +} + +static void +oscar_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = oscar_login; + klass->close = oscar_close; + klass->status_types = oscar_status_types; +} + +static void +oscar_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = oscar_get_actions; + client_iface->list_emblem = oscar_list_emblem; + client_iface->status_text = oscar_status_text; + client_iface->tooltip_text = oscar_tooltip_text; + client_iface->blist_node_menu = oscar_blist_node_menu; + client_iface->convo_closed = oscar_convo_closed; + client_iface->normalize = oscar_normalize; + client_iface->offline_message = oscar_offline_message; +} + +static void +oscar_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->set_info = oscar_set_info; + server_iface->get_info = oscar_get_info; + server_iface->set_status = oscar_set_status; + server_iface->set_idle = oscar_set_idle; + server_iface->change_passwd = oscar_change_passwd; + server_iface->add_buddy = oscar_add_buddy; + server_iface->remove_buddy = oscar_remove_buddy; + server_iface->keepalive = oscar_keepalive; + server_iface->alias_buddy = oscar_alias_buddy; + server_iface->group_buddy = oscar_move_buddy; + server_iface->rename_group = oscar_rename_group; + server_iface->set_buddy_icon = oscar_set_icon; + server_iface->remove_group = oscar_remove_group; +} + +static void +oscar_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = oscar_send_im; + im_iface->send_typing = oscar_send_typing; +} + +static void +oscar_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->info = oscar_chat_info; + chat_iface->info_defaults = oscar_chat_info_defaults; + chat_iface->join = oscar_join_chat; + chat_iface->get_name = oscar_get_chat_name; + chat_iface->invite = oscar_chat_invite; + chat_iface->leave = oscar_chat_leave; + chat_iface->send = oscar_send_chat; +} + +static void +oscar_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) +{ + privacy_iface->add_deny = oscar_add_deny; + privacy_iface->rem_deny = oscar_rem_deny; +} + +static void +oscar_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->can_receive = oscar_can_receive_file; + xfer_iface->send = oscar_send_file; + xfer_iface->new_xfer = oscar_new_xfer; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + OscarProtocol, oscar_protocol, PURPLE_TYPE_PROTOCOL, G_TYPE_FLAG_ABSTRACT, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + oscar_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + oscar_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + oscar_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + oscar_protocol_chat_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + oscar_protocol_privacy_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + oscar_protocol_xfer_iface_init) +); + +static PurplePluginInfo * +plugin_query(GError **error) +{ + return purple_plugin_info_new( + "id", "prpl-oscar", + "name", "Oscar Protocols", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("Oscar (AIM/ICQ) Protocols Plugin"), + "description", N_("Oscar (AIM/ICQ) Protocols Plugin"), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + oscar_protocol_register_type(plugin); + + aim_protocol_register_type(plugin); + icq_protocol_register_type(plugin); + + aim_protocol = purple_protocols_add(AIM_TYPE_PROTOCOL, error); + if (!aim_protocol) + return FALSE; + + icq_protocol = purple_protocols_add(ICQ_TYPE_PROTOCOL, error); + if (!icq_protocol) + return FALSE; + + purple_signal_connect(purple_get_core(), "uri-handler", aim_protocol, + PURPLE_CALLBACK(oscar_uri_handler), NULL); + purple_signal_connect(purple_get_core(), "uri-handler", icq_protocol, + PURPLE_CALLBACK(oscar_uri_handler), NULL); /* Preferences */ purple_prefs_add_none("/plugins/prpl/oscar"); @@ -5610,9 +5745,19 @@ purple_prefs_remove("/plugins/prpl/oscar/show_idle"); purple_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy"); - /* protocol handler */ - /* TODO: figure out a good instance to use here */ - purple_signal_connect(purple_get_core(), "uri-handler", &init, - PURPLE_CALLBACK(oscar_uri_handler), NULL); + return TRUE; } +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + if (!purple_protocols_remove(icq_protocol, error)) + return FALSE; + + if (!purple_protocols_remove(aim_protocol, error)) + return FALSE; + + return TRUE; +} + +PURPLE_PLUGIN_INIT(oscar, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/protocols/oscar/oscar.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/oscar.h Fri Jan 31 18:02:20 2014 +0530 @@ -56,6 +56,13 @@ #include "libc_interface.h" #endif +#define OSCAR_TYPE_PROTOCOL (oscar_protocol_get_type()) +#define OSCAR_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), OSCAR_TYPE_PROTOCOL, OscarProtocol)) +#define OSCAR_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), OSCAR_TYPE_PROTOCOL, OscarProtocolClass)) +#define OSCAR_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), OSCAR_TYPE_PROTOCOL)) +#define OSCAR_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), OSCAR_TYPE_PROTOCOL)) +#define OSCAR_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), OSCAR_TYPE_PROTOCOL, OscarProtocolClass)) + typedef struct _ByteStream ByteStream; typedef struct _ClientInfo ClientInfo; typedef struct _FlapConnection FlapConnection; @@ -224,6 +231,21 @@ #define OSCAR_STATUS_ID_ATWORK "atwork" #define OSCAR_STATUS_ID_LUNCH "lunch" +typedef struct _OscarProtocol +{ + PurpleProtocol parent; +} OscarProtocol; + +typedef struct _OscarProtocolClass +{ + PurpleProtocolClass parent_class; +} OscarProtocolClass; + +/** + * Returns the GType for the OscarProtocol object. + */ +G_MODULE_EXPORT GType oscar_protocol_get_type(void); + /* * Byte Stream type. Sort of. * @@ -1346,6 +1368,8 @@ void oscar_free_name_data(struct name_data *data); +void oscar_init_protocol_options(PurpleProtocol *protocol); + #ifdef __cplusplus } #endif
--- a/libpurple/protocols/oscar/oscarcommon.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/oscarcommon.h Fri Jan 31 18:02:20 2014 +0530 @@ -20,14 +20,14 @@ * */ -/* oscarcommon.h contains prototypes for the prpl functions used by libaim.c +/* oscarcommon.h contains prototypes for the protocol functions used by libaim.c * and libicq.c */ #include "internal.h" #include "accountopt.h" -#include "prpl.h" +#include "protocol.h" #include "version.h" #include "notify.h" #include "status.h" @@ -103,5 +103,6 @@ PurpleXfer *oscar_new_xfer(PurpleConnection *gc, const char *who); gboolean oscar_offline_message(const PurpleBuddy *buddy); gssize oscar_get_max_message_size(PurpleConversation *conv); -GList *oscar_actions(PurplePlugin *plugin, gpointer context); -void oscar_init(PurplePlugin *plugin, gboolean is_icq); +GList *oscar_get_actions(PurpleConnection *gc); +const gchar *oscar_get_login_server(gboolean is_icq, gboolean use_ssl); +gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params);
--- a/libpurple/protocols/oscar/visibility.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/visibility.c Fri Jan 31 18:02:20 2014 +0530 @@ -86,9 +86,9 @@ } static void -show_private_list(PurplePluginAction *action, guint16 list_type, const gchar *title, const gchar *list_description, const gchar *menu_action_name) +show_private_list(PurpleProtocolAction *action, guint16 list_type, const gchar *title, const gchar *list_description, const gchar *menu_action_name) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; OscarData *od = purple_connection_get_protocol_data(gc); PurpleAccount *account = purple_connection_get_account(gc); GSList *buddies, *filtered_buddies, *cur; @@ -122,7 +122,7 @@ } void -oscar_show_visible_list(PurplePluginAction *action) +oscar_show_visible_list(PurpleProtocolAction *action) { show_private_list(action, AIM_SSI_TYPE_PERMIT, _("Visible List"), _("These buddies will see " @@ -132,7 +132,7 @@ } void -oscar_show_invisible_list(PurplePluginAction *action) +oscar_show_invisible_list(PurpleProtocolAction *action) { show_private_list(action, AIM_SSI_TYPE_DENY, _("Invisible List"), _("These buddies will always see you as offline"),
--- a/libpurple/protocols/oscar/visibility.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/oscar/visibility.h Fri Jan 31 18:02:20 2014 +0530 @@ -22,11 +22,11 @@ #define _VISIBILITY_H_ #include "oscar.h" -#include "plugin.h" +#include "plugins.h" #include "util.h" PurpleMenuAction * create_visibility_menu_item(OscarData *od, const char *bname); -void oscar_show_visible_list(PurplePluginAction *action); -void oscar_show_invisible_list(PurplePluginAction *action); +void oscar_show_visible_list(PurpleProtocolAction *action); +void oscar_show_invisible_list(PurpleProtocolAction *action); -#endif \ No newline at end of file +#endif
--- a/libpurple/protocols/sametime/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/sametime/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -32,6 +32,7 @@ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(MEANWHILE_CFLAGS) \ -DG_LOG_DOMAIN=\"sametime\"
--- a/libpurple/protocols/sametime/sametime.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/sametime/sametime.c Fri Jan 31 18:02:20 2014 +0530 @@ -40,8 +40,8 @@ #include "imgstore.h" #include "mime.h" #include "notify.h" -#include "plugin.h" -#include "prpl.h" +#include "plugins.h" +#include "protocol.h" #include "request.h" #include "util.h" #include "version.h" @@ -65,24 +65,32 @@ #include "sametime.h" -/* considering that there's no display of this information for prpls, +static PurpleProtocol *my_protocol = NULL; + +#define PROTOCOL_ID "prpl-meanwhile" +#define PROTOCOL_NAME "Sametime" + + +/* considering that there's no display of this information for protocols, I don't know why I even bother providing these. Oh valiant reader, I do it all for you. */ -/* scratch that, I just added it to the prpl options panel */ -#define PLUGIN_ID "prpl-meanwhile" -#define PLUGIN_NAME "Sametime" +/* scratch that, I just added it to the protocol options panel */ +#define PLUGIN_ID "prpl-sametime" +#define PLUGIN_NAME "Sametime Protocol" +#define PLUGIN_CATEGORY "Protocol" #define PLUGIN_SUMMARY "Sametime Protocol Plugin" #define PLUGIN_DESC "Open implementation of a Lotus Sametime client" -#define PLUGIN_AUTHOR "Christopher (siege) O'Brien <siege@preoccupied.net>" #define PLUGIN_HOMEPAGE "http://meanwhile.sourceforge.net/" +#define PLUGIN_AUTHORS \ + { "Christopher (siege) O'Brien <siege@preoccupied.net>", NULL } /* plugin preference names */ -#define MW_PRPL_OPT_BASE "/plugins/prpl/meanwhile" -#define MW_PRPL_OPT_BLIST_ACTION MW_PRPL_OPT_BASE "/blist_action" -#define MW_PRPL_OPT_PSYCHIC MW_PRPL_OPT_BASE "/psychic" -#define MW_PRPL_OPT_FORCE_LOGIN MW_PRPL_OPT_BASE "/force_login" -#define MW_PRPL_OPT_SAVE_DYNAMIC MW_PRPL_OPT_BASE "/save_dynamic" +#define MW_PROTOCOL_OPT_BASE "/plugins/prpl/meanwhile" +#define MW_PROTOCOL_OPT_BLIST_ACTION MW_PROTOCOL_OPT_BASE "/blist_action" +#define MW_PROTOCOL_OPT_PSYCHIC MW_PROTOCOL_OPT_BASE "/psychic" +#define MW_PROTOCOL_OPT_FORCE_LOGIN MW_PROTOCOL_OPT_BASE "/force_login" +#define MW_PROTOCOL_OPT_SAVE_DYNAMIC MW_PROTOCOL_OPT_BASE "/save_dynamic" /* stages of connecting-ness */ @@ -162,7 +170,7 @@ /* testing for the above */ -#define BLIST_PREF_IS(n) (purple_prefs_get_int(MW_PRPL_OPT_BLIST_ACTION)==(n)) +#define BLIST_PREF_IS(n) (purple_prefs_get_int(MW_PROTOCOL_OPT_BLIST_ACTION)==(n)) #define BLIST_PREF_IS_LOCAL() BLIST_PREF_IS(blist_choice_LOCAL) #define BLIST_PREF_IS_MERGE() BLIST_PREF_IS(blist_choice_MERGE) #define BLIST_PREF_IS_STORE() BLIST_PREF_IS(blist_choice_STORE) @@ -197,7 +205,7 @@ /** the purple plugin data. available as purple_connection_get_protocol_data(gc) and mwSession_getClientData */ -struct mwPurplePluginData { +struct mwPurpleProtocolData { struct mwSession *session; struct mwServiceAware *srvc_aware; @@ -236,9 +244,9 @@ static void blist_export(PurpleConnection *gc, struct mwSametimeList *stlist); -static void blist_store(struct mwPurplePluginData *pd); - -static void blist_schedule(struct mwPurplePluginData *pd); +static void blist_store(struct mwPurpleProtocolData *pd); + +static void blist_schedule(struct mwPurpleProtocolData *pd); static void blist_merge(PurpleConnection *gc, struct mwSametimeList *stlist); @@ -246,19 +254,19 @@ static gboolean buddy_is_external(PurpleBuddy *b); -static void buddy_add(struct mwPurplePluginData *pd, PurpleBuddy *buddy); +static void buddy_add(struct mwPurpleProtocolData *pd, PurpleBuddy *buddy); static PurpleBuddy * buddy_ensure(PurpleConnection *gc, PurpleGroup *group, struct mwSametimeUser *stuser); -static void group_add(struct mwPurplePluginData *pd, PurpleGroup *group); +static void group_add(struct mwPurpleProtocolData *pd, PurpleGroup *group); static PurpleGroup * group_ensure(PurpleConnection *gc, struct mwSametimeGroup *stgroup); static struct mwAwareList * -list_ensure(struct mwPurplePluginData *pd, PurpleGroup *group); +list_ensure(struct mwPurpleProtocolData *pd, PurpleGroup *group); /* session functions */ @@ -272,7 +280,7 @@ /* conference functions */ static struct mwConference * -conf_find_by_id(struct mwPurplePluginData *pd, int id); +conf_find_by_id(struct mwPurpleProtocolData *pd, int id); /* conversation functions */ @@ -316,7 +324,7 @@ /** resolves a mwSession from a PurpleConnection */ static struct mwSession *gc_to_session(PurpleConnection *gc) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; g_return_val_if_fail(gc != NULL, NULL); @@ -329,7 +337,7 @@ /** resolves a PurpleConnection from a mwSession */ static PurpleConnection *session_to_gc(struct mwSession *session) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; g_return_val_if_fail(session != NULL, NULL); @@ -341,7 +349,7 @@ static void write_cb(gpointer data, gint source, PurpleInputCondition cond) { - struct mwPurplePluginData *pd = data; + struct mwPurpleProtocolData *pd = data; PurpleCircularBuffer *circ = pd->sock_buf; gsize avail; int ret; @@ -373,7 +381,7 @@ static int mw_session_io_write(struct mwSession *session, const guchar *buf, gsize len) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; gssize ret = 0; int err = 0; @@ -431,7 +439,7 @@ static void mw_session_io_close(struct mwSession *session) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; pd = mwSession_getClientData(session); g_return_if_fail(pd != NULL); @@ -487,7 +495,7 @@ PurpleConnection *gc; PurpleAccount *acct; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; guint32 idle; guint stat; const char *id; @@ -592,11 +600,11 @@ } if(aware->online) { - purple_prpl_got_user_status(acct, id, status, NULL); - purple_prpl_got_user_idle(acct, id, !!idle, (time_t) idle); + purple_protocol_got_user_status(acct, id, status, NULL); + purple_protocol_got_user_idle(acct, id, !!idle, (time_t) idle); } else { - purple_prpl_got_user_status(acct, id, MW_STATE_OFFLINE, NULL); + purple_protocol_got_user_status(acct, id, MW_STATE_OFFLINE, NULL); } } @@ -624,7 +632,7 @@ /** Ensures that an Aware List is associated with the given group, and returns that list. */ static struct mwAwareList * -list_ensure(struct mwPurplePluginData *pd, PurpleGroup *group) { +list_ensure(struct mwPurpleProtocolData *pd, PurpleGroup *group) { struct mwAwareList *list; @@ -741,7 +749,7 @@ } -static void blist_store(struct mwPurplePluginData *pd) { +static void blist_store(struct mwPurpleProtocolData *pd) { struct mwSametimeList *stlist; struct mwServiceStorage *srvc; @@ -794,7 +802,7 @@ static gboolean blist_save_cb(gpointer data) { - struct mwPurplePluginData *pd = data; + struct mwPurpleProtocolData *pd = data; blist_store(pd); pd->save_event = 0; @@ -803,7 +811,7 @@ /** schedules the buddy list to be saved to the server */ -static void blist_schedule(struct mwPurplePluginData *pd) { +static void blist_schedule(struct mwPurpleProtocolData *pd) { if(pd->save_event) return; pd->save_event = purple_timeout_add_seconds(BLIST_SAVE_SECONDS, @@ -819,7 +827,7 @@ /** Actually add a buddy to the aware service, and schedule the buddy list to be saved to the server */ -static void buddy_add(struct mwPurplePluginData *pd, +static void buddy_add(struct mwPurpleProtocolData *pd, PurpleBuddy *buddy) { struct mwAwareIdBlock idb = { mwAware_USER, (char *) purple_buddy_get_name(buddy), NULL }; @@ -848,7 +856,7 @@ static PurpleBuddy *buddy_ensure(PurpleConnection *gc, PurpleGroup *group, struct mwSametimeUser *stuser) { - struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc); + struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc); PurpleBuddy *buddy; PurpleAccount *acct = purple_connection_get_account(gc); @@ -878,7 +886,7 @@ /** add aware watch for a dynamic group */ -static void group_add(struct mwPurplePluginData *pd, +static void group_add(struct mwPurpleProtocolData *pd, PurpleGroup *group) { struct mwAwareIdBlock idb = { mwAware_GROUP, NULL, NULL }; @@ -1227,7 +1235,7 @@ guint32 result, struct mwStorageUnit *item, gpointer data) { - struct mwPurplePluginData *pd = data; + struct mwPurpleProtocolData *pd = data; struct mwSametimeList *stlist; struct mwGetBuffer *b; @@ -1260,7 +1268,7 @@ /** signal triggered when a conversation is opened in Purple */ static void conversation_created_cb(PurpleConversation *g_conv, - struct mwPurplePluginData *pd) { + struct mwPurpleProtocolData *pd) { /* we need to tell the IM service to negotiate features for the conversation right away, otherwise it'll wait until the first @@ -1293,7 +1301,7 @@ static void blist_menu_nab(PurpleBlistNode *node, gpointer data) { - struct mwPurplePluginData *pd = data; + struct mwPurpleProtocolData *pd = data; PurpleConnection *gc; PurpleGroup *group = (PurpleGroup *) node; @@ -1327,11 +1335,11 @@ } -/** The normal blist menu prpl function doesn't get called for groups, +/** The normal blist menu protocol function doesn't get called for groups, so we use the blist-node-extended-menu signal to trigger this handler */ static void blist_node_menu_cb(PurpleBlistNode *node, - GList **menu, struct mwPurplePluginData *pd) { + GList **menu, struct mwPurpleProtocolData *pd) { const char *owner; PurpleAccount *acct; PurpleMenuAction *act; @@ -1405,7 +1413,7 @@ /** Last thing to happen from a started session */ -static void services_starting(struct mwPurplePluginData *pd) { +static void services_starting(struct mwPurpleProtocolData *pd) { PurpleConnection *gc; PurpleAccount *acct; @@ -1457,7 +1465,7 @@ static void session_loginRedirect(struct mwSession *session, const char *host) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; PurpleAccount *account; guint port; @@ -1483,20 +1491,20 @@ } -static void mw_prpl_set_status(PurpleAccount *acct, PurpleStatus *status); +static void mw_protocol_set_status(PurpleAccount *acct, PurpleStatus *status); /** called from mw_session_stateChange when the session's state is mwSession_STARTED. Any finalizing of start-up stuff should go here */ -static void session_started(struct mwPurplePluginData *pd) { +static void session_started(struct mwPurpleProtocolData *pd) { PurpleStatus *status; PurpleAccount *acct; /* set out initial status */ acct = purple_connection_get_account(pd->gc); status = purple_account_get_active_status(acct); - mw_prpl_set_status(acct, status); + mw_protocol_set_status(acct, status); /* start watching for new conversations */ purple_signal_connect(purple_conversations_get_handle(), @@ -1513,7 +1521,7 @@ } -static void session_stopping(struct mwPurplePluginData *pd) { +static void session_stopping(struct mwPurpleProtocolData *pd) { /* stop watching the signals from session_started */ purple_signals_disconnect_by_handle(pd); } @@ -1522,7 +1530,7 @@ static void mw_session_stateChange(struct mwSession *session, enum mwSessionState state, gpointer info) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; const char *msg = NULL; @@ -1629,7 +1637,7 @@ static void mw_session_setPrivacyInfo(struct mwSession *session) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; PurpleAccount *acct; struct mwPrivacyInfo *privacy; @@ -1675,7 +1683,7 @@ static void mw_session_setUserStatus(struct mwSession *session) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; struct mwAwareIdBlock idb = { mwAware_USER, NULL, NULL }; struct mwUserStatus *stat; @@ -1744,7 +1752,7 @@ /** callback triggered from purple_input_add, watches the socked for available data to be processed by the session */ static void read_cb(gpointer data, gint source, PurpleInputCondition cond) { - struct mwPurplePluginData *pd = data; + struct mwPurpleProtocolData *pd = data; int ret = 0, err = 0; g_return_if_fail(pd != NULL); @@ -1795,7 +1803,7 @@ in, and if the session logging in receives a redirect message */ static void connect_cb(gpointer data, gint source, const gchar *error_message) { - struct mwPurplePluginData *pd = data; + struct mwPurpleProtocolData *pd = data; if(source < 0) { /* connection failed */ @@ -1834,7 +1842,7 @@ struct mwLoginInfo *from, gboolean may_reply, const char *text) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleAccount *acct; PurpleIMConversation *im; PurpleBuddy *buddy; @@ -1904,7 +1912,7 @@ struct mwServiceConference *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; char *c_inviter, *c_name, *c_topic, *c_invitation; @@ -1953,7 +1961,7 @@ static struct mwConference * -conf_find_by_id(struct mwPurplePluginData *pd, int id) { +conf_find_by_id(struct mwPurpleProtocolData *pd, int id) { struct mwServiceConference *srvc = pd->srvc_conf; struct mwConference *conf = NULL; @@ -1978,7 +1986,7 @@ static void mw_conf_opened(struct mwConference *conf, GList *members) { struct mwServiceConference *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; PurpleChatConversation *g_conf; @@ -2009,7 +2017,7 @@ static void mw_conf_closed(struct mwConference *conf, guint32 reason) { struct mwServiceConference *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; const char *n = mwConference_getName(conf); @@ -2068,7 +2076,7 @@ struct mwServiceConference *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; char *esc; @@ -2162,7 +2170,7 @@ struct mwServiceFileTransfer *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; PurpleAccount *acct; const char *who; @@ -2401,7 +2409,7 @@ static PurpleIMConversation *convo_get_im(struct mwConversation *conv) { struct mwServiceIm *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; PurpleAccount *acct; @@ -2550,7 +2558,7 @@ static void mw_conversation_opened(struct mwConversation *conv) { struct mwServiceIm *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; PurpleAccount *acct; @@ -2625,7 +2633,7 @@ static void im_recv_text(struct mwConversation *conv, - struct mwPurplePluginData *pd, + struct mwPurpleProtocolData *pd, const char *msg) { struct mwIdBlock *idb; @@ -2646,7 +2654,7 @@ static void im_recv_typing(struct mwConversation *conv, - struct mwPurplePluginData *pd, + struct mwPurpleProtocolData *pd, gboolean typing) { struct mwIdBlock *idb; @@ -2658,7 +2666,7 @@ static void im_recv_html(struct mwConversation *conv, - struct mwPurplePluginData *pd, + struct mwPurpleProtocolData *pd, const char *msg) { struct mwIdBlock *idb; char *t1, *t2; @@ -2682,7 +2690,7 @@ static void im_recv_subj(struct mwConversation *conv, - struct mwPurplePluginData *pd, + struct mwPurpleProtocolData *pd, const char *subj) { /** @todo somehow indicate receipt of a conversation subject. It @@ -2711,7 +2719,7 @@ static void im_recv_mime(struct mwConversation *conv, - struct mwPurplePluginData *pd, + struct mwPurpleProtocolData *pd, const char *data) { GHashTable *img_by_cid; @@ -2765,7 +2773,7 @@ g_path_get_basename() and purple_escape_filename() on it before passing it in. This is easy, but it's not clear if there might be other implications because this filename is used elsewhere within - this PRPL. */ + this protocol. */ img = purple_imgstore_new_with_id(d_dat, d_len, cid); /* map the cid to the image store identifier */ @@ -2850,7 +2858,7 @@ gconstpointer msg) { struct mwServiceIm *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; srvc = mwConversation_getService(conv); session = mwService_getSession(MW_SERVICE(srvc)); @@ -2889,7 +2897,7 @@ const char *title, const char *name) { struct mwServiceIm *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwIdBlock *idb; GHashTable *ht; @@ -2950,7 +2958,7 @@ static struct mwPlace * -place_find_by_id(struct mwPurplePluginData *pd, int id) { +place_find_by_id(struct mwPurpleProtocolData *pd, int id) { struct mwServicePlace *srvc = pd->srvc_place; struct mwPlace *place = NULL; GList *l; @@ -2973,7 +2981,7 @@ static void mw_place_opened(struct mwPlace *place) { struct mwServicePlace *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; PurpleChatConversation *gconf; @@ -3009,7 +3017,7 @@ static void mw_place_closed(struct mwPlace *place, guint32 code) { struct mwServicePlace *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; const char *n = mwPlace_getName(place); @@ -3080,7 +3088,7 @@ const char *msg) { struct mwServicePlace *srvc; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; char *esc; @@ -3135,13 +3143,13 @@ } -/** allocate and associate a mwPurplePluginData with a PurpleConnection */ -static struct mwPurplePluginData *mwPurplePluginData_new(PurpleConnection *gc) { - struct mwPurplePluginData *pd; +/** allocate and associate a mwPurpleProtocolData with a PurpleConnection */ +static struct mwPurpleProtocolData *mwPurpleProtocolData_new(PurpleConnection *gc) { + struct mwPurpleProtocolData *pd; g_return_val_if_fail(gc != NULL, NULL); - pd = g_new0(struct mwPurplePluginData, 1); + pd = g_new0(struct mwPurpleProtocolData, 1); pd->gc = gc; pd->session = mwSession_new(&mw_session_handler); pd->srvc_aware = mw_srvc_aware_new(pd->session); @@ -3172,7 +3180,7 @@ } -static void mwPurplePluginData_free(struct mwPurplePluginData *pd) { +static void mwPurpleProtocolData_free(struct mwPurpleProtocolData *pd) { g_return_if_fail(pd != NULL); purple_connection_set_protocol_data(pd->gc, NULL); @@ -3205,7 +3213,7 @@ } -static const char *mw_prpl_list_icon(PurpleAccount *a, PurpleBuddy *b) { +static const char *mw_protocol_list_icon(PurpleAccount *a, PurpleBuddy *b) { /* my little green dude is a chopped up version of the aim running guy. First, cut off the head and store someplace safe. Then, take the left-half side of the body and throw it away. Make a @@ -3221,7 +3229,7 @@ } -static const char* mw_prpl_list_emblem(PurpleBuddy *b) +static const char* mw_protocol_list_emblem(PurpleBuddy *b) { if(buddy_is_external(b)) return "external"; @@ -3230,9 +3238,9 @@ } -static char *mw_prpl_status_text(PurpleBuddy *b) { +static char *mw_protocol_status_text(PurpleBuddy *b) { PurpleConnection *gc; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwAwareIdBlock t = { mwAware_USER, (char *)purple_buddy_get_name(b), NULL }; const char *ret = NULL; @@ -3290,9 +3298,9 @@ } -static void mw_prpl_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) { +static void mw_protocol_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) { PurpleConnection *gc; - struct mwPurplePluginData *pd = NULL; + struct mwPurpleProtocolData *pd = NULL; struct mwAwareIdBlock idb = { mwAware_USER, (char *)purple_buddy_get_name(b), NULL }; const char *message = NULL; @@ -3325,7 +3333,7 @@ } } -static GList *mw_prpl_status_types(PurpleAccount *acct) +static GList *mw_protocol_status_types(PurpleAccount *acct) { GList *types = NULL; PurpleStatusType *type; @@ -3366,7 +3374,7 @@ PurpleRequestFields *fields) { PurpleAccount *acct; PurpleConnection *gc; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwServiceConference *srvc; PurpleRequestField *f; @@ -3531,7 +3539,7 @@ PurpleBuddy *buddy = (PurpleBuddy *) node; PurpleAccount *acct; PurpleConnection *gc; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; GList *l; g_return_if_fail(node != NULL); @@ -3568,7 +3576,7 @@ PurpleBuddy *buddy = (PurpleBuddy *) node; PurpleAccount *acct; PurpleConnection *gc; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwSession *session; char *rcpt_name; GList *rcpt; @@ -3599,7 +3607,7 @@ #endif -static GList *mw_prpl_blist_node_menu(PurpleBlistNode *node) { +static GList *mw_protocol_blist_node_menu(PurpleBlistNode *node) { GList *l = NULL; PurpleMenuAction *act; @@ -3627,11 +3635,11 @@ } -static GList *mw_prpl_chat_info(PurpleConnection *gc) { +static GList *mw_protocol_chat_info(PurpleConnection *gc) { GList *l = NULL; - struct proto_chat_entry *pce; - - pce = g_new0(struct proto_chat_entry, 1); + PurpleProtocolChatEntry *pce; + + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("Topic:"); pce->identifier = CHAT_KEY_TOPIC; l = g_list_append(l, pce); @@ -3640,7 +3648,7 @@ } -static GHashTable *mw_prpl_chat_info_defaults(PurpleConnection *gc, +static GHashTable *mw_protocol_chat_info_defaults(PurpleConnection *gc, const char *name) { GHashTable *table; @@ -3656,18 +3664,18 @@ } -static void mw_prpl_login(PurpleAccount *acct); - - -static void mw_prpl_login(PurpleAccount *account) { +static void mw_protocol_login(PurpleAccount *acct); + + +static void mw_protocol_login(PurpleAccount *account) { PurpleConnection *gc; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; char *user, *pass, *host; guint port; gc = purple_account_get_connection(account); - pd = mwPurplePluginData_new(gc); + pd = mwPurpleProtocolData_new(gc); /* while we do support images, the default is to not offer it */ purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_NO_IMAGES); @@ -3741,8 +3749,8 @@ } -static void mw_prpl_close(PurpleConnection *gc) { - struct mwPurplePluginData *pd; +static void mw_protocol_close(PurpleConnection *gc) { + struct mwPurpleProtocolData *pd; g_return_if_fail(gc != NULL); @@ -3769,7 +3777,7 @@ } /* clean up the rest */ - mwPurplePluginData_free(pd); + mwPurpleProtocolData_free(pd); } @@ -3941,12 +3949,12 @@ } -static int mw_prpl_send_im(PurpleConnection *gc, +static int mw_protocol_send_im(PurpleConnection *gc, const char *name, const char *message, PurpleMessageFlags flags) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwIdBlock who = { (char *) name, NULL }; struct mwConversation *conv; @@ -4015,11 +4023,11 @@ } -static unsigned int mw_prpl_send_typing(PurpleConnection *gc, +static unsigned int mw_protocol_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwIdBlock who = { (char *) name, NULL }; struct mwConversation *conv; @@ -4108,11 +4116,11 @@ } -static void mw_prpl_get_info(PurpleConnection *gc, const char *who) { +static void mw_protocol_get_info(PurpleConnection *gc, const char *who) { struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL }; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleAccount *acct; PurpleBuddy *b; PurpleNotifyUserInfo *user_info; @@ -4181,7 +4189,7 @@ } -static void mw_prpl_set_status(PurpleAccount *acct, PurpleStatus *status) { +static void mw_protocol_set_status(PurpleAccount *acct, PurpleStatus *status) { PurpleConnection *gc; const char *state; char *message = NULL; @@ -4234,7 +4242,7 @@ } -static void mw_prpl_set_idle(PurpleConnection *gc, int t) { +static void mw_protocol_set_idle(PurpleConnection *gc, int t) { struct mwSession *session; struct mwUserStatus stat; @@ -4360,7 +4368,7 @@ BuddyAddData *data = b; PurpleBuddy *buddy = NULL; PurpleConnection *gc; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; g_return_if_fail(data != NULL); @@ -4444,12 +4452,12 @@ } -static void mw_prpl_add_buddy(PurpleConnection *gc, +static void mw_protocol_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message) { - struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc); + struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc); struct mwServiceResolve *srvc; GList *query; enum mwResolveFlag flags; @@ -4483,7 +4491,7 @@ static void foreach_add_buddies(PurpleGroup *group, GList *buddies, - struct mwPurplePluginData *pd) { + struct mwPurpleProtocolData *pd) { struct mwAwareList *list; list = list_ensure(pd, group); @@ -4492,12 +4500,12 @@ } -static void mw_prpl_add_buddies(PurpleConnection *gc, +static void mw_protocol_add_buddies(PurpleConnection *gc, GList *buddies, GList *groups, const char *message) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; GHashTable *group_sets; struct mwAwareIdBlock *idbs, *idb; @@ -4543,10 +4551,10 @@ } -static void mw_prpl_remove_buddy(PurpleConnection *gc, +static void mw_protocol_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwAwareIdBlock idb = { mwAware_USER, (char *)purple_buddy_get_name(buddy), NULL }; struct mwAwareList *list; @@ -4583,9 +4591,9 @@ } -static void mw_prpl_set_permit_deny(PurpleConnection *gc) { +static void mw_protocol_set_permit_deny(PurpleConnection *gc) { PurpleAccount *acct; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwSession *session; struct mwPrivacyInfo privacy = { @@ -4638,23 +4646,23 @@ } -static void mw_prpl_add_permit(PurpleConnection *gc, const char *name) { - mw_prpl_set_permit_deny(gc); -} - - -static void mw_prpl_add_deny(PurpleConnection *gc, const char *name) { - mw_prpl_set_permit_deny(gc); -} - - -static void mw_prpl_rem_permit(PurpleConnection *gc, const char *name) { - mw_prpl_set_permit_deny(gc); -} - - -static void mw_prpl_rem_deny(PurpleConnection *gc, const char *name) { - mw_prpl_set_permit_deny(gc); +static void mw_protocol_add_permit(PurpleConnection *gc, const char *name) { + mw_protocol_set_permit_deny(gc); +} + + +static void mw_protocol_add_deny(PurpleConnection *gc, const char *name) { + mw_protocol_set_permit_deny(gc); +} + + +static void mw_protocol_rem_permit(PurpleConnection *gc, const char *name) { + mw_protocol_set_permit_deny(gc); +} + + +static void mw_protocol_rem_deny(PurpleConnection *gc, const char *name) { + mw_protocol_set_permit_deny(gc); } @@ -4677,10 +4685,10 @@ } -static void mw_prpl_join_chat(PurpleConnection *gc, +static void mw_protocol_join_chat(PurpleConnection *gc, GHashTable *components) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; char *c, *t; pd = purple_connection_get_protocol_data(gc); @@ -4718,10 +4726,10 @@ } -static void mw_prpl_reject_chat(PurpleConnection *gc, +static void mw_protocol_reject_chat(PurpleConnection *gc, GHashTable *components) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwServiceConference *srvc; char *c; @@ -4742,17 +4750,17 @@ } -static char *mw_prpl_get_chat_name(GHashTable *components) { +static char *mw_protocol_get_chat_name(GHashTable *components) { return g_hash_table_lookup(components, CHAT_KEY_NAME); } -static void mw_prpl_chat_invite(PurpleConnection *gc, +static void mw_protocol_chat_invite(PurpleConnection *gc, int id, const char *invitation, const char *who) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwConference *conf; struct mwPlace *place; struct mwIdBlock idb = { (char *) who, NULL }; @@ -4775,10 +4783,10 @@ } -static void mw_prpl_chat_leave(PurpleConnection *gc, +static void mw_protocol_chat_leave(PurpleConnection *gc, int id) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwConference *conf; pd = purple_connection_get_protocol_data(gc); @@ -4798,21 +4806,21 @@ } -static void mw_prpl_chat_whisper(PurpleConnection *gc, +static void mw_protocol_chat_whisper(PurpleConnection *gc, int id, const char *who, const char *message) { - mw_prpl_send_im(gc, who, message, 0); -} - - -static int mw_prpl_chat_send(PurpleConnection *gc, + mw_protocol_send_im(gc, who, message, 0); +} + + +static int mw_protocol_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwConference *conf; char *msg; int ret; @@ -4839,7 +4847,7 @@ } -static void mw_prpl_keepalive(PurpleConnection *gc) { +static void mw_protocol_keepalive(PurpleConnection *gc) { struct mwSession *session; g_return_if_fail(gc != NULL); @@ -4851,11 +4859,11 @@ } -static void mw_prpl_alias_buddy(PurpleConnection *gc, +static void mw_protocol_alias_buddy(PurpleConnection *gc, const char *who, const char *alias) { - struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc); + struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc); g_return_if_fail(pd != NULL); /* it's a change to the buddy list, so we've gotta reflect that in @@ -4865,7 +4873,7 @@ } -static void mw_prpl_group_buddy(PurpleConnection *gc, +static void mw_protocol_group_buddy(PurpleConnection *gc, const char *who, const char *old_group, const char *new_group) { @@ -4873,7 +4881,7 @@ struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL }; GList *gl = g_list_prepend(NULL, &idb); - struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc); + struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc); PurpleGroup *group; struct mwAwareList *list; @@ -4894,12 +4902,12 @@ } -static void mw_prpl_rename_group(PurpleConnection *gc, +static void mw_protocol_rename_group(PurpleConnection *gc, const char *old, PurpleGroup *group, GList *buddies) { - struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc); + struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc); g_return_if_fail(pd != NULL); /* it's a change in the buddy list, so we've gotta reflect that in @@ -4912,14 +4920,14 @@ } -static void mw_prpl_buddy_free(PurpleBuddy *buddy) { +static void mw_protocol_buddy_free(PurpleBuddy *buddy) { /* I don't think we have any cleanup for buddies yet */ ; } -static void mw_prpl_convo_closed(PurpleConnection *gc, const char *who) { - struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc); +static void mw_protocol_convo_closed(PurpleConnection *gc, const char *who) { + struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc); struct mwServiceIm *srvc; struct mwConversation *conv; struct mwIdBlock idb = { (char *) who, NULL }; @@ -4937,7 +4945,7 @@ } -static const char *mw_prpl_normalize(const PurpleAccount *account, +static const char *mw_protocol_normalize(const PurpleAccount *account, const char *id) { /* code elsewhere assumes that the return value points to different @@ -4950,8 +4958,8 @@ } -static void mw_prpl_remove_group(PurpleConnection *gc, PurpleGroup *group) { - struct mwPurplePluginData *pd; +static void mw_protocol_remove_group(PurpleConnection *gc, PurpleGroup *group) { + struct mwPurpleProtocolData *pd; struct mwAwareList *list; pd = purple_connection_get_protocol_data(gc); @@ -4970,9 +4978,9 @@ } -static gboolean mw_prpl_can_receive_file(PurpleConnection *gc, +static gboolean mw_protocol_can_receive_file(PurpleConnection *gc, const char *who) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwServiceAware *srvc; PurpleAccount *acct; @@ -4996,7 +5004,7 @@ PurpleAccount *acct; PurpleConnection *gc; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwServiceFileTransfer *srvc; struct mwFileTransfer *ft; @@ -5059,7 +5067,7 @@ } -static PurpleXfer *mw_prpl_new_xfer(PurpleConnection *gc, const char *who) { +static PurpleXfer *mw_protocol_new_xfer(PurpleConnection *gc, const char *who) { PurpleAccount *acct; PurpleXfer *xfer; @@ -5075,10 +5083,10 @@ return xfer; } -static void mw_prpl_send_file(PurpleConnection *gc, +static void mw_protocol_send_file(PurpleConnection *gc, const char *who, const char *file) { - PurpleXfer *xfer = mw_prpl_new_xfer(gc, who); + PurpleXfer *xfer = mw_protocol_new_xfer(gc, who); if(file) { DEBUG_INFO("file != NULL\n"); @@ -5090,82 +5098,6 @@ } } - -static PurplePluginProtocolInfo mw_prpl_info = { - sizeof(PurplePluginProtocolInfo), - OPT_PROTO_IM_IMAGE, - NULL, /*< set in mw_plugin_init */ - NULL, /*< set in mw_plugin_init */ - NO_BUDDY_ICONS, - mw_prpl_list_icon, - mw_prpl_list_emblem, - mw_prpl_status_text, - mw_prpl_tooltip_text, - mw_prpl_status_types, - mw_prpl_blist_node_menu, - mw_prpl_chat_info, - mw_prpl_chat_info_defaults, - mw_prpl_login, - mw_prpl_close, - mw_prpl_send_im, - NULL, - mw_prpl_send_typing, - mw_prpl_get_info, - mw_prpl_set_status, - mw_prpl_set_idle, - NULL, - mw_prpl_add_buddy, - mw_prpl_add_buddies, - mw_prpl_remove_buddy, - NULL, - mw_prpl_add_permit, - mw_prpl_add_deny, - mw_prpl_rem_permit, - mw_prpl_rem_deny, - mw_prpl_set_permit_deny, - mw_prpl_join_chat, - mw_prpl_reject_chat, - mw_prpl_get_chat_name, - mw_prpl_chat_invite, - mw_prpl_chat_leave, - mw_prpl_chat_whisper, - mw_prpl_chat_send, - mw_prpl_keepalive, - NULL, - NULL, - mw_prpl_alias_buddy, - mw_prpl_group_buddy, - mw_prpl_rename_group, - mw_prpl_buddy_free, - mw_prpl_convo_closed, - mw_prpl_normalize, - NULL, - mw_prpl_remove_group, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - mw_prpl_can_receive_file, - mw_prpl_send_file, - mw_prpl_new_xfer, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - #if 0 static PurplePluginPrefFrame * mw_plugin_get_plugin_pref_frame(PurplePlugin *plugin) { @@ -5178,7 +5110,7 @@ purple_plugin_pref_frame_add(frame, pref); - pref = purple_plugin_pref_new_with_name(MW_PRPL_OPT_BLIST_ACTION); + pref = purple_plugin_pref_new_with_name(MW_PROTOCOL_OPT_BLIST_ACTION); purple_plugin_pref_set_label(pref, _("Buddy List Storage Mode")); purple_plugin_pref_set_type(pref, PURPLE_PLUGIN_PREF_CHOICE); @@ -5225,12 +5157,12 @@ /** prompts for a file to import blist from */ -static void st_import_action(PurplePluginAction *act) { +static void st_import_action(PurpleProtocolAction *act) { PurpleConnection *gc; PurpleAccount *account; char *title; - gc = act->context; + gc = act->connection; account = purple_connection_get_account(gc); title = g_strdup_printf(_("Import Sametime List for Account %s"), purple_account_get_username(account)); @@ -5265,12 +5197,12 @@ /** prompts for a file to export blist to */ -static void st_export_action(PurplePluginAction *act) { +static void st_export_action(PurpleProtocolAction *act) { PurpleConnection *gc; PurpleAccount *account; char *title; - gc = act->context; + gc = act->connection; account = purple_connection_get_account(gc); title = g_strdup_printf(_("Export Sametime List for Account %s"), purple_account_get_username(account)); @@ -5306,7 +5238,7 @@ } -static void remote_group_done(struct mwPurplePluginData *pd, +static void remote_group_done(struct mwPurpleProtocolData *pd, const char *id, const char *name) { PurpleConnection *gc; PurpleAccount *acct; @@ -5352,7 +5284,7 @@ } -static void remote_group_multi_cb(struct mwPurplePluginData *pd, +static void remote_group_multi_cb(struct mwPurpleProtocolData *pd, PurpleRequestFields *fields) { PurpleRequestField *f; GList *l; @@ -5373,7 +5305,7 @@ static void remote_group_multi(struct mwResolveResult *result, - struct mwPurplePluginData *pd) { + struct mwPurpleProtocolData *pd) { PurpleRequestFields *fields; PurpleRequestFieldGroup *g; @@ -5429,7 +5361,7 @@ struct mwResolveResult *res = NULL; struct mwSession *session; - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; PurpleConnection *gc; session = mwService_getSession(MW_SERVICE(srvc)); @@ -5470,7 +5402,7 @@ static void remote_group_action_cb(PurpleConnection *gc, const char *name) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwServiceResolve *srvc; GList *query; enum mwResolveFlag flags; @@ -5492,12 +5424,12 @@ } -static void remote_group_action(PurplePluginAction *act) { +static void remote_group_action(PurpleProtocolAction *act) { PurpleConnection *gc; const char *msgA; const char *msgB; - gc = act->context; + gc = act->connection; msgA = _("Notes Address Book Group"); msgB = _("Enter the name of a Notes Address Book group in the field below" @@ -5596,7 +5528,7 @@ static void search_action_cb(PurpleConnection *gc, const char *name) { - struct mwPurplePluginData *pd; + struct mwPurpleProtocolData *pd; struct mwServiceResolve *srvc; GList *query; enum mwResolveFlag flags; @@ -5618,12 +5550,12 @@ } -static void search_action(PurplePluginAction *act) { +static void search_action(PurpleProtocolAction *act) { PurpleConnection *gc; const char *msgA; const char *msgB; - gc = act->context; + gc = act->connection; msgA = _("Search for a user"); msgB = _("Enter a name or partial ID in the field below to search" @@ -5638,23 +5570,23 @@ } -static GList *mw_plugin_actions(PurplePlugin *plugin, gpointer context) { - PurplePluginAction *act; +static GList *mw_protocol_get_actions(PurpleConnection *gc) { + PurpleProtocolAction *act; GList *l = NULL; - act = purple_plugin_action_new(_("Import Sametime List..."), + act = purple_protocol_action_new(_("Import Sametime List..."), st_import_action); l = g_list_append(l, act); - act = purple_plugin_action_new(_("Export Sametime List..."), + act = purple_protocol_action_new(_("Export Sametime List..."), st_export_action); l = g_list_append(l, act); - act = purple_plugin_action_new(_("Add Notes Address Book Group..."), + act = purple_protocol_action_new(_("Add Notes Address Book Group..."), remote_group_action); l = g_list_append(l, act); - act = purple_plugin_action_new(_("User Search..."), + act = purple_protocol_action_new(_("User Search..."), search_action); l = g_list_append(l, act); @@ -5662,57 +5594,6 @@ } -static gboolean mw_plugin_load(PurplePlugin *plugin) { - return TRUE; -} - - -static gboolean mw_plugin_unload(PurplePlugin *plugin) { - return TRUE; -} - - -static void mw_plugin_destroy(PurplePlugin *plugin) { - g_log_remove_handler(G_LOG_DOMAIN, log_handler[0]); - g_log_remove_handler("meanwhile", log_handler[1]); -} - -static PurplePluginInfo mw_plugin_info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - PLUGIN_ID, /**< id */ - PLUGIN_NAME, /**< name */ - DISPLAY_VERSION, /**< version */ - PLUGIN_SUMMARY, /**< summary */ - PLUGIN_DESC, /**< description */ - PLUGIN_AUTHOR, /**< author */ - PLUGIN_HOMEPAGE, /**< homepage */ - - mw_plugin_load, /**< load */ - mw_plugin_unload, /**< unload */ - mw_plugin_destroy, /**< destroy */ - - NULL, /**< ui_info */ - &mw_prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - mw_plugin_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - - static void mw_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data) { @@ -5731,30 +5612,33 @@ } -static void mw_plugin_init(PurplePlugin *plugin) { +static void +mw_protocol_init(PurpleProtocol *protocol) +{ PurpleAccountUserSplit *split; PurpleAccountOption *opt; GList *l = NULL; - GLogLevelFlags logflags = - G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION; + protocol->id = PROTOCOL_ID; + protocol->name = PROTOCOL_NAME; + protocol->options = OPT_PROTO_IM_IMAGE; /* set up the preferences */ - purple_prefs_add_none(MW_PRPL_OPT_BASE); - purple_prefs_add_int(MW_PRPL_OPT_BLIST_ACTION, BLIST_CHOICE_DEFAULT); + purple_prefs_add_none(MW_PROTOCOL_OPT_BASE); + purple_prefs_add_int(MW_PROTOCOL_OPT_BLIST_ACTION, BLIST_CHOICE_DEFAULT); /* set up account ID as user:server */ split = purple_account_user_split_new(_("Server"), MW_PLUGIN_DEFAULT_HOST, ':'); - mw_prpl_info.user_splits = g_list_append(mw_prpl_info.user_splits, split); + protocol->user_splits = g_list_append(protocol->user_splits, split); /* remove dead preferences */ - purple_prefs_remove(MW_PRPL_OPT_PSYCHIC); - purple_prefs_remove(MW_PRPL_OPT_SAVE_DYNAMIC); + purple_prefs_remove(MW_PROTOCOL_OPT_PSYCHIC); + purple_prefs_remove(MW_PROTOCOL_OPT_SAVE_DYNAMIC); /* port to connect to */ opt = purple_account_option_int_new(_("Port"), MW_KEY_PORT, - MW_PLUGIN_DEFAULT_PORT); + MW_PLUGIN_DEFAULT_PORT); l = g_list_append(l, opt); { /* copy the old force login setting from prefs if it's @@ -5763,8 +5647,8 @@ gboolean b = FALSE; const char *label = _("Force login (ignore server redirects)"); - if(purple_prefs_exists(MW_PRPL_OPT_FORCE_LOGIN)) - b = purple_prefs_get_bool(MW_PRPL_OPT_FORCE_LOGIN); + if(purple_prefs_exists(MW_PROTOCOL_OPT_FORCE_LOGIN)) + b = purple_prefs_get_bool(MW_PROTOCOL_OPT_FORCE_LOGIN); opt = purple_account_option_bool_new(label, MW_KEY_FORCE, b); l = g_list_append(l, opt); @@ -5772,24 +5656,182 @@ /* pretend to be Sametime Connect */ opt = purple_account_option_bool_new(_("Hide client identity"), - MW_KEY_FAKE_IT, FALSE); + MW_KEY_FAKE_IT, FALSE); l = g_list_append(l, opt); - mw_prpl_info.protocol_options = l; + protocol->protocol_options = l; l = NULL; +} + + +static void +mw_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = mw_protocol_login; + klass->close = mw_protocol_close; + klass->status_types = mw_protocol_status_types; + klass->list_icon = mw_protocol_list_icon; +} + + +static void +mw_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = mw_protocol_get_actions; + client_iface->list_emblem = mw_protocol_list_emblem; + client_iface->status_text = mw_protocol_status_text; + client_iface->tooltip_text = mw_protocol_tooltip_text; + client_iface->blist_node_menu = mw_protocol_blist_node_menu; + client_iface->buddy_free = mw_protocol_buddy_free; + client_iface->convo_closed = mw_protocol_convo_closed; + client_iface->normalize = mw_protocol_normalize; +} + + +static void +mw_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->get_info = mw_protocol_get_info; + server_iface->set_status = mw_protocol_set_status; + server_iface->set_idle = mw_protocol_set_idle; + server_iface->add_buddy = mw_protocol_add_buddy; + server_iface->add_buddies = mw_protocol_add_buddies; + server_iface->remove_buddy = mw_protocol_remove_buddy; + server_iface->keepalive = mw_protocol_keepalive; + server_iface->alias_buddy = mw_protocol_alias_buddy; + server_iface->group_buddy = mw_protocol_group_buddy; + server_iface->rename_group = mw_protocol_rename_group; + server_iface->remove_group = mw_protocol_remove_group; +} + + +static void +mw_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = mw_protocol_send_im; + im_iface->send_typing = mw_protocol_send_typing; +} + + +static void +mw_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->info = mw_protocol_chat_info; + chat_iface->info_defaults = mw_protocol_chat_info_defaults; + chat_iface->join = mw_protocol_join_chat; + chat_iface->reject = mw_protocol_reject_chat; + chat_iface->get_name = mw_protocol_get_chat_name; + chat_iface->invite = mw_protocol_chat_invite; + chat_iface->leave = mw_protocol_chat_leave; + chat_iface->whisper = mw_protocol_chat_whisper; + chat_iface->send = mw_protocol_chat_send; +} + + +static void +mw_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) +{ + privacy_iface->add_permit = mw_protocol_add_permit; + privacy_iface->add_deny = mw_protocol_add_deny; + privacy_iface->rem_permit = mw_protocol_rem_permit; + privacy_iface->rem_deny = mw_protocol_rem_deny; + privacy_iface->set_permit_deny = mw_protocol_set_permit_deny; +} + + +static void +mw_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->can_receive = mw_protocol_can_receive_file; + xfer_iface->send = mw_protocol_send_file; + xfer_iface->new_xfer = mw_protocol_new_xfer; +} + + +PURPLE_DEFINE_TYPE_EXTENDED( + mwProtocol, mw_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + mw_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + mw_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + mw_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + mw_protocol_chat_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + mw_protocol_privacy_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + mw_protocol_xfer_iface_init) +); + + +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = PLUGIN_AUTHORS; + + return purple_plugin_info_new( + "id", PLUGIN_ID, + "name", PLUGIN_NAME, + "version", DISPLAY_VERSION, + "category", PLUGIN_CATEGORY, + "summary", PLUGIN_SUMMARY, + "description", PLUGIN_DESC, + "authors", authors, + "website", PLUGIN_HOMEPAGE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + GLogLevelFlags logflags = + G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION; + + mw_protocol_register_type(plugin); + + my_protocol = purple_protocols_add(MW_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; /* forward all our g_log messages to purple. Generally all the logging calls are using purple_log directly, but the g_return macros will get caught here */ log_handler[0] = g_log_set_handler(G_LOG_DOMAIN, logflags, - mw_log_handler, NULL); + mw_log_handler, NULL); /* redirect meanwhile's logging to purple's */ log_handler[1] = g_log_set_handler("meanwhile", logflags, - mw_log_handler, NULL); -} - - -PURPLE_INIT_PLUGIN(sametime, mw_plugin_init, mw_plugin_info); + mw_log_handler, NULL); + + return TRUE; +} + + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + g_log_remove_handler(G_LOG_DOMAIN, log_handler[0]); + g_log_remove_handler("meanwhile", log_handler[1]); + + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + + return TRUE; +} + + +PURPLE_PLUGIN_INIT(sametime, plugin_query, plugin_load, plugin_unload); /* The End. */
--- a/libpurple/protocols/sametime/sametime.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/sametime/sametime.h Fri Jan 31 18:02:20 2014 +0530 @@ -1,4 +1,12 @@ +#ifndef _SAMETIME_H_ +#define _SAMETIME_H_ +#define MW_TYPE_PROTOCOL (mw_protocol_get_type()) +#define MW_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MW_TYPE_PROTOCOL, mwProtocol)) +#define MW_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), MW_TYPE_PROTOCOL, mwProtocolClass)) +#define MW_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), MW_TYPE_PROTOCOL)) +#define MW_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), MW_TYPE_PROTOCOL)) +#define MW_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), MW_TYPE_PROTOCOL, mwProtocolClass)) /* CFLAGS trumps configure values */ @@ -24,3 +32,22 @@ #define MW_PLUGIN_DEFAULT_ENCODING "ISO-8859-1" #endif /* ISO-8859-1 */ + + +typedef struct _mwProtocol +{ + PurpleProtocol parent; +} mwProtocol; + +typedef struct _mwProtocolClass +{ + PurpleProtocolClass parent_class; +} mwProtocolClass; + + +/** + * Returns the GType for the mwProtocol object. + */ +G_MODULE_EXPORT GType mw_protocol_get_type(void); + +#endif /* _SAMETIME_H_ */
--- a/libpurple/protocols/silc/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/silc/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -43,4 +43,5 @@ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(SILC_CFLAGS)
--- a/libpurple/protocols/silc/buddy.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/silc/buddy.c Fri Jan 31 18:02:20 2014 +0530 @@ -741,7 +741,7 @@ "import his/her public key. You can use the Get Public Key " "command to get the public key."), purple_request_cpar_from_account(purple_buddy_get_account(r->b))); - purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); + purple_protocol_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); } static void @@ -784,7 +784,7 @@ "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub", silcpurple_silcdir(), fingerprint); purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename); - purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); + purple_protocol_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); silc_free(fingerprint); silc_free(r->offline_pk); if (r->public_key) @@ -1017,7 +1017,7 @@ purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename); /* Update online status */ - purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); + purple_protocol_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); /* Finally, start watching this user so we receive its status changes from the server */
--- a/libpurple/protocols/silc/chat.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/silc/chat.c Fri Jan 31 18:02:20 2014 +0530 @@ -28,15 +28,15 @@ GList *silcpurple_chat_info(PurpleConnection *gc) { GList *ci = NULL; - struct proto_chat_entry *pce; + PurpleProtocolChatEntry *pce; - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Channel:"); pce->identifier = "channel"; pce->required = TRUE; ci = g_list_append(ci, pce); - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Passphrase:"); pce->identifier = "passphrase"; pce->secret = TRUE;
--- a/libpurple/protocols/silc/ops.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/silc/ops.c Fri Jan 31 18:02:20 2014 +0530 @@ -905,7 +905,7 @@ client_entry->mode & SILC_UMODE_PAGE || client_entry->mode & SILC_UMODE_DETACHED)) { client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); + purple_protocol_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); } else if ((mode & SILC_UMODE_GONE) || (mode & SILC_UMODE_INDISPOSED) || @@ -913,16 +913,16 @@ (mode & SILC_UMODE_PAGE) || (mode & SILC_UMODE_DETACHED)) { client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); + purple_protocol_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); } } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF || notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF || notify == SILC_NOTIFY_TYPE_KILLED) { client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); + purple_protocol_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); } else if (notify == SILC_NOTIFY_TYPE_NONE) { client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); + purple_protocol_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); } } break;
--- a/libpurple/protocols/silc/silc.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/silc/silc.c Fri Jan 31 18:02:20 2014 +0530 @@ -18,6 +18,7 @@ */ #include "internal.h" +#include "plugins.h" #include "silc.h" #include "silcclient.h" #include "silcpurple.h" @@ -27,6 +28,9 @@ extern SilcClientOperations ops; +static PurpleProtocol *my_protocol = NULL; +static GSList *cmds = NULL; + /* Error log message callback */ static SilcBool silcpurple_log_error(SilcLogType type, char *message, @@ -903,9 +907,9 @@ } static void -silcpurple_attrs(PurplePluginAction *action) +silcpurple_attrs(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; SilcPurple sg = purple_connection_get_protocol_data(gc); SilcClient client = sg->client; SilcClientConnection conn = sg->conn; @@ -1070,9 +1074,9 @@ } static void -silcpurple_detach(PurplePluginAction *action) +silcpurple_detach(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; SilcPurple sg; if (!gc) @@ -1087,9 +1091,9 @@ } static void -silcpurple_view_motd(PurplePluginAction *action) +silcpurple_view_motd(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; SilcPurple sg; char *tmp; @@ -1213,9 +1217,9 @@ } static void -silcpurple_create_keypair(PurplePluginAction *action) +silcpurple_create_keypair(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; SilcPurple sg = purple_connection_get_protocol_data(gc); PurpleRequestFields *fields; PurpleRequestFieldGroup *g; @@ -1284,9 +1288,9 @@ } static void -silcpurple_change_pass(PurplePluginAction *action) +silcpurple_change_pass(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; purple_account_request_change_password(purple_connection_get_account(gc)); } @@ -1301,9 +1305,9 @@ } static void -silcpurple_show_set_info(PurplePluginAction *action) +silcpurple_show_set_info(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; purple_account_request_change_user_info(purple_connection_get_account(gc)); } @@ -1313,32 +1317,32 @@ } static GList * -silcpurple_actions(PurplePlugin *plugin, gpointer context) +silcpurple_get_actions(PurpleConnection *gc) { GList *list = NULL; - PurplePluginAction *act; + PurpleProtocolAction *act; - act = purple_plugin_action_new(_("Online Status"), + act = purple_protocol_action_new(_("Online Status"), silcpurple_attrs); list = g_list_append(list, act); - act = purple_plugin_action_new(_("Detach From Server"), + act = purple_protocol_action_new(_("Detach From Server"), silcpurple_detach); list = g_list_append(list, act); - act = purple_plugin_action_new(_("View Message of the Day"), + act = purple_protocol_action_new(_("View Message of the Day"), silcpurple_view_motd); list = g_list_append(list, act); - act = purple_plugin_action_new(_("Create SILC Key Pair..."), + act = purple_protocol_action_new(_("Create SILC Key Pair..."), silcpurple_create_keypair); list = g_list_append(list, act); - act = purple_plugin_action_new(_("Change Password..."), + act = purple_protocol_action_new(_("Change Password..."), silcpurple_change_pass); list = g_list_append(list, act); - act = purple_plugin_action_new(_("Set User Info..."), + act = purple_protocol_action_new(_("Set User Info..."), silcpurple_show_set_info); list = g_list_append(list, act); @@ -1919,126 +1923,193 @@ static void silcpurple_register_commands(void) { - purple_cmd_register("part", "w", PURPLE_CMD_P_PRPL, + PurpleCmdId id; + + id = purple_cmd_register("part", "w", PURPLE_CMD_P_PROTOCOL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, + PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_chat_part, _("part [channel]: Leave the chat"), NULL); - purple_cmd_register("leave", "w", PURPLE_CMD_P_PRPL, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("leave", "w", PURPLE_CMD_P_PROTOCOL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, + PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_chat_part, _("leave [channel]: Leave the chat"), NULL); - purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("topic", "s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_chat_topic, _("topic [<new topic>]: View or change the topic"), NULL); - purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("join", "ws", PURPLE_CMD_P_PROTOCOL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, + PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_chat_join, _("join <channel> [<password>]: Join a chat on this network"), NULL); - purple_cmd_register("list", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("list", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_chat_list, _("list: List channels on this network"), NULL); - purple_cmd_register("whois", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("whois", "w", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_whois, _("whois <nick>: View nick's information"), NULL); - purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("msg", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_msg, _("msg <nick> <message>: Send a private message to a user"), NULL); - purple_cmd_register("query", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("query", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_query, _("query <nick> [<message>]: Send a private message to a user"), NULL); - purple_cmd_register("motd", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("motd", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_motd, _("motd: View the server's Message Of The Day"), NULL); - purple_cmd_register("detach", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("detach", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_detach, _("detach: Detach this session"), NULL); - purple_cmd_register("quit", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("quit", "s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_quit, _("quit [message]: Disconnect from the server, with an optional message"), NULL); - purple_cmd_register("call", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("call", "s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_call, _("call <command>: Call any silc client command"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + /* These below just get passed through for the silc client library to deal * with */ - purple_cmd_register("kill", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + id = purple_cmd_register("kill", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("kill <nick> [-pubkey|<reason>]: Kill nick"), NULL); - purple_cmd_register("nick", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("nick", "w", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("nick <newnick>: Change your nickname"), NULL); - purple_cmd_register("whowas", "ww", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("whowas", "ww", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("whowas <nick>: View nick's information"), NULL); - purple_cmd_register("cmode", "wws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("cmode", "wws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_cmode, _("cmode <channel> [+|-<modes>] [arguments]: Change or display channel modes"), NULL); - purple_cmd_register("cumode", "wws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("cumode", "wws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("cumode <channel> +|-<modes> <nick>: Change nick's modes on channel"), NULL); - purple_cmd_register("umode", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("umode", "w", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("umode <usermodes>: Set your modes in the network"), NULL); - purple_cmd_register("oper", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("oper", "s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("oper <nick> [-pubkey]: Get server operator privileges"), NULL); - purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("invite", "ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("invite <channel> [-|+]<nick>: invite nick or add/remove from channel invite list"), NULL); - purple_cmd_register("kick", "wws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("kick", "wws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("kick <channel> <nick> [comment]: Kick client from channel"), NULL); - purple_cmd_register("info", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("info", "w", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("info [server]: View server administrative details"), NULL); - purple_cmd_register("ban", "ww", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("ban", "ww", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("ban [<channel> +|-<nick>]: Ban client from channel"), NULL); - purple_cmd_register("getkey", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("getkey", "w", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("getkey <nick|server>: Retrieve client's or server's public key"), NULL); - purple_cmd_register("stats", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("stats", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("stats: View server and network statistics"), NULL); - purple_cmd_register("ping", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("ping", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("ping: Send PING to the connected server"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + #if 0 /* Purple doesn't handle these yet */ - purple_cmd_register("users", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + id = purple_cmd_register("users", "w", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-silc", silcpurple_cmd_users, _("users <channel>: List users in channel")); - purple_cmd_register("names", "ww", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("names", "ww", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_names, _("names [-count|-ops|-halfops|-voices|-normal] <channel(s)>: List specific users in channel(s)")); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); #endif } -static PurpleWhiteboardPrplOps silcpurple_wb_ops = +static void +silcpurple_unregister_commands(void) +{ + while (cmds) { + PurpleCmdId id = GPOINTER_TO_UINT(cmds->data); + purple_cmd_unregister(id); + cmds = g_slist_delete_link(cmds, cmds); + } +} + +static PurpleWhiteboardOps silcpurple_wb_ops = { silcpurple_wb_start, silcpurple_wb_end, @@ -2056,121 +2127,6 @@ NULL }; -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | - OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE | - OPT_PROTO_SLASH_COMMANDS_NATIVE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"jpeg,gif,png,bmp", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ - silcpurple_list_icon, /* list_icon */ - NULL, /* list_emblems */ - silcpurple_status_text, /* status_text */ - silcpurple_tooltip_text, /* tooltip_text */ - silcpurple_away_states, /* away_states */ - silcpurple_blist_node_menu, /* blist_node_menu */ - silcpurple_chat_info, /* chat_info */ - silcpurple_chat_info_defaults, /* chat_info_defaults */ - silcpurple_login, /* login */ - silcpurple_close, /* close */ - silcpurple_send_im, /* send_im */ - silcpurple_set_info, /* set_info */ - NULL, /* send_typing */ - silcpurple_get_info, /* get_info */ - silcpurple_set_status, /* set_status */ - silcpurple_idle_set, /* set_idle */ - silcpurple_change_passwd, /* change_passwd */ - silcpurple_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - silcpurple_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* rem_permit */ - NULL, /* rem_deny */ - NULL, /* set_permit_deny */ - silcpurple_chat_join, /* join_chat */ - NULL, /* reject_chat */ - silcpurple_get_chat_name, /* get_chat_name */ - silcpurple_chat_invite, /* chat_invite */ - silcpurple_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - silcpurple_chat_send, /* chat_send */ - silcpurple_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - NULL, /* buddy_free */ - NULL, /* convo_closed */ - NULL, /* normalize */ - silcpurple_buddy_set_icon, /* set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* get_cb_real_name */ - silcpurple_chat_set_topic, /* set_chat_topic */ - NULL, /* find_blist_chat */ - silcpurple_roomlist_get_list, /* roomlist_get_list */ - silcpurple_roomlist_cancel, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - silcpurple_ftp_send_file, /* send_file */ - silcpurple_ftp_new_xfer, /* new_xfer */ - NULL, /* offline_message */ - &silcpurple_wb_ops, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL /* get_max_message_size */ -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-silc", /**< id */ - "SILC", /**< name */ - "1.1", /**< version */ - /** summary */ - N_("SILC Protocol Plugin"), - /** description */ - N_("Secure Internet Live Conferencing (SILC) Protocol"), - "Pekka Riikonen", /**< author */ - "http://silcnet.org/", /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - silcpurple_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - #if 0 static SilcBool silcpurple_debug_cb(char *file, char *function, int line, char *message, void *context) @@ -2181,7 +2137,7 @@ #endif static void -init_plugin(PurplePlugin *plugin) +silcpurple_protocol_init(PurpleProtocol *protocol) { PurpleAccountOption *option; PurpleAccountUserSplit *split; @@ -2190,24 +2146,35 @@ PurpleKeyValuePair *kvp; GList *list = NULL; + protocol->id = "prpl-silc"; + protocol->name = "SILC"; + protocol->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | + OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE | + OPT_PROTO_SLASH_COMMANDS_NATIVE; + protocol->icon_spec = purple_buddy_icon_spec_new("jpeg,gif,png,bmp", + 0, 0, 96, 96, 0, + PURPLE_ICON_SCALE_DISPLAY); + + protocol->whiteboard_ops = &silcpurple_wb_ops; + split = purple_account_user_split_new(_("Network"), "silcnet.org", '@'); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); + protocol->user_splits = g_list_append(protocol->user_splits, split); /* Account options */ option = purple_account_option_string_new(_("Connect server"), "server", "silc.silcnet.org"); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_int_new(_("Port"), "port", 706); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); option = purple_account_option_string_new(_("Public Key file"), "public-key", tmp); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir()); option = purple_account_option_string_new(_("Private Key file"), "private-key", tmp); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); for (i = 0; silc_default_ciphers[i].name; i++) { kvp = g_new0(PurpleKeyValuePair, 1); @@ -2216,7 +2183,7 @@ list = g_list_append(list, kvp); } option = purple_account_option_list_new(_("Cipher"), "cipher", list); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); list = NULL; for (i = 0; silc_default_hmacs[i].name; i++) { @@ -2226,27 +2193,148 @@ list = g_list_append(list, kvp); } option = purple_account_option_list_new(_("HMAC"), "hmac", list); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Use Perfect Forward Secrecy"), "pfs", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Public key authentication"), "pubkey-auth", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Block IMs without Key Exchange"), "block-ims", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Block messages to whiteboard"), "block-wb", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Automatically open whiteboard"), "open-wb", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Digitally sign and verify all messages"), "sign-verify", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); +} + +static void +silcpurple_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = silcpurple_login; + klass->close = silcpurple_close; + klass->status_types = silcpurple_away_states; + klass->list_icon = silcpurple_list_icon; +} + +static void +silcpurple_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = silcpurple_get_actions; + client_iface->status_text = silcpurple_status_text; + client_iface->tooltip_text = silcpurple_tooltip_text; + client_iface->blist_node_menu = silcpurple_blist_node_menu; +} + +static void +silcpurple_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->set_info = silcpurple_set_info; + server_iface->get_info = silcpurple_get_info; + server_iface->set_status = silcpurple_set_status; + server_iface->set_idle = silcpurple_idle_set; + server_iface->change_passwd = silcpurple_change_passwd; + server_iface->add_buddy = silcpurple_add_buddy; + server_iface->remove_buddy = silcpurple_remove_buddy; + server_iface->keepalive = silcpurple_keepalive; + server_iface->set_buddy_icon = silcpurple_buddy_set_icon; +} + +static void +silcpurple_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = silcpurple_send_im; +} + +static void +silcpurple_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->info = silcpurple_chat_info; + chat_iface->info_defaults = silcpurple_chat_info_defaults; + chat_iface->join = silcpurple_chat_join; + chat_iface->get_name = silcpurple_get_chat_name; + chat_iface->invite = silcpurple_chat_invite; + chat_iface->leave = silcpurple_chat_leave; + chat_iface->send = silcpurple_chat_send; + chat_iface->set_topic = silcpurple_chat_set_topic; +} + +static void +silcpurple_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface) +{ + roomlist_iface->get_list = silcpurple_roomlist_get_list; + roomlist_iface->cancel = silcpurple_roomlist_cancel; +} + +static void +silcpurple_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->send = silcpurple_ftp_send_file; + xfer_iface->new_xfer = silcpurple_ftp_new_xfer; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + SilcProtocol, silcpurple_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + silcpurple_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + silcpurple_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + silcpurple_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + silcpurple_protocol_chat_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE, + silcpurple_protocol_roomlist_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + silcpurple_protocol_xfer_iface_init) +); + +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Pekka Riikonen", + NULL + }; + + return purple_plugin_info_new( + "id", "prpl-silc", + "name", "SILC Protocol", + "version", "1.1", + "category", N_("Protocol"), + "summary", N_("SILC Protocol Plugin"), + "description", N_("Secure Internet Live Conferencing (SILC) Protocol"), + "authors", authors, + "website", "http://silcnet.org/", + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + silcpurple_protocol_register_type(plugin); + + my_protocol = purple_protocols_add(SILCPURPLE_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; purple_prefs_remove("/plugins/prpl/silc"); @@ -2260,6 +2348,18 @@ silc_log_set_debug_callbacks(silcpurple_debug_cb, NULL, NULL, NULL); #endif + return TRUE; } -PURPLE_INIT_PLUGIN(silc, init_plugin, info); +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + silcpurple_unregister_commands(); + + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + + return TRUE; +} + +PURPLE_PLUGIN_INIT(silc, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/protocols/silc/silcpurple.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/silc/silcpurple.h Fri Jan 31 18:02:20 2014 +0530 @@ -29,7 +29,7 @@ #include "debug.h" #include "xfer.h" #include "notify.h" -#include "prpl.h" +#include "protocol.h" #include "request.h" #include "roomlist.h" #include "server.h" @@ -38,6 +38,13 @@ #undef SILC_VERSION #define SILC_VERSION(a, b, c) (((a) << 24) + ((b) << 16) + ((c) << 8)) +#define SILCPURPLE_TYPE_PROTOCOL (silcpurple_protocol_get_type()) +#define SILCPURPLE_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SILCPURPLE_TYPE_PROTOCOL, SilcProtocol)) +#define SILCPURPLE_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SILCPURPLE_TYPE_PROTOCOL, SilcProtocolClass)) +#define SILCPURPLE_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SILCPURPLE_TYPE_PROTOCOL)) +#define SILCPURPLE_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SILCPURPLE_TYPE_PROTOCOL)) +#define SILCPURPLE_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SILCPURPLE_TYPE_PROTOCOL, SilcProtocolClass)) + /* Default public and private key file names */ #define SILCPURPLE_PUBLIC_KEY_NAME "public_key.pub" #define SILCPURPLE_PRIVATE_KEY_NAME "private_key.prv" @@ -57,6 +64,16 @@ #define SILCPURPLE_STATUS_ID_INDISPOSED "indisposed" #define SILCPURPLE_STATUS_ID_PAGE "page" +typedef struct _SilcProtocol +{ + PurpleProtocol parent; +} SilcProtocol; + +typedef struct _SilcProtocolClass +{ + PurpleProtocolClass parent_class; +} SilcProtocolClass; + typedef struct { unsigned long id; const char *channel; @@ -90,6 +107,8 @@ } *SilcPurple; +G_MODULE_EXPORT GType silcpurple_protocol_get_type(void); + void silc_say(SilcClient client, SilcClientConnection conn, SilcClientMessageType type, char *msg, ...); SilcBool silcpurple_command_reply(SilcClient client, SilcClientConnection conn,
--- a/libpurple/protocols/simple/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/simple/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -33,4 +33,5 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/simple/simple.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/simple/simple.c Fri Jan 31 18:02:20 2014 +0530 @@ -32,8 +32,8 @@ #include "dnsquery.h" #include "debug.h" #include "notify.h" -#include "prpl.h" -#include "plugin.h" +#include "protocol.h" +#include "plugins.h" #include "util.h" #include "version.h" #include "network.h" @@ -44,6 +44,8 @@ #include "dnssrv.h" #include "ntlm.h" +static PurpleProtocol *my_protocol = NULL; + static char *gentag(void) { return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF); } @@ -820,7 +822,7 @@ /* we can not subscribe -> user is offline (TODO unknown status?) */ - purple_prpl_got_user_status(sip->account, to, "offline", NULL); + purple_protocol_got_user_status(sip->account, to, "offline", NULL); g_free(to); return TRUE; } @@ -1239,7 +1241,7 @@ b->dialog = NULL; } - purple_prpl_got_user_status(sip->account, from, "offline", NULL); + purple_protocol_got_user_status(sip->account, from, "offline", NULL); break; } i++; @@ -1276,9 +1278,9 @@ if(isonline) - purple_prpl_got_user_status(sip->account, from, "available", NULL); + purple_protocol_got_user_status(sip->account, from, "available", NULL); else - purple_prpl_got_user_status(sip->account, from, "offline", NULL); + purple_protocol_got_user_status(sip->account, from, "offline", NULL); purple_xmlnode_free(pidf); g_free(from); @@ -2046,142 +2048,115 @@ purple_connection_set_protocol_data(gc, NULL); } -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - 0, - NULL, /* user_splits */ - NULL, /* protocol_options */ - NO_BUDDY_ICONS, /* icon_spec */ - simple_list_icon, /* list_icon */ - NULL, /* list_emblems */ - NULL, /* status_text */ - NULL, /* tooltip_text */ - simple_status_types, /* away_states */ - NULL, /* blist_node_menu */ - NULL, /* chat_info */ - NULL, /* chat_info_defaults */ - simple_login, /* login */ - simple_close, /* close */ - simple_im_send, /* send_im */ - NULL, /* set_info */ - simple_typing, /* send_typing */ - NULL, /* get_info */ - simple_set_status, /* set_status */ - NULL, /* set_idle */ - NULL, /* change_passwd */ - simple_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - simple_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* rem_permit */ - NULL, /* rem_deny */ - NULL, /* set_permit_deny */ - NULL, /* join_chat */ - NULL, /* reject_chat */ - NULL, /* get_chat_name */ - NULL, /* chat_invite */ - NULL, /* chat_leave */ - NULL, /* chat_whisper */ - NULL, /* chat_send */ - simple_keep_alive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - NULL, /* buddy_free */ - NULL, /* convo_closed */ - NULL, /* normalize */ - NULL, /* set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ - NULL, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - simple_send_raw, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL /* get_max_message_size */ -}; - - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-simple", /**< id */ - "SIMPLE", /**< name */ - DISPLAY_VERSION, /**< version */ - N_("SIP/SIMPLE Protocol Plugin"), /** summary */ - N_("The SIP/SIMPLE Protocol Plugin"), /** description */ - "Thomas Butter <butter@uni-mannheim.de>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void _init_plugin(PurplePlugin *plugin) +static void +simple_protocol_init(PurpleProtocol *protocol) { PurpleAccountUserSplit *split; PurpleAccountOption *option; + protocol->id = "prpl-simple"; + protocol->name = "SIMPLE"; + split = purple_account_user_split_new(_("Server"), "", '@'); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); + protocol->user_splits = g_list_append(protocol->user_splits, split); option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "dopublish", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_int_new(_("Connect port"), "port", 0); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Use UDP"), "udp", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Proxy"), "proxy", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Auth User"), "authuser", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Auth Domain"), "authdomain", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); +} + +static void +simple_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = simple_login; + klass->close = simple_close; + klass->status_types = simple_status_types; + klass->list_icon = simple_list_icon; +} + +static void +simple_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->set_status = simple_set_status; + server_iface->add_buddy = simple_add_buddy; + server_iface->remove_buddy = simple_remove_buddy; + server_iface->keepalive = simple_keep_alive; + server_iface->send_raw = simple_send_raw; +} + +static void +simple_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = simple_im_send; + im_iface->send_typing = simple_typing; } -PURPLE_INIT_PLUGIN(simple, _init_plugin, info); +PURPLE_DEFINE_TYPE_EXTENDED( + SIMPLEProtocol, simple_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + simple_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + simple_protocol_im_iface_init) +); + +static PurplePluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Thomas Butter <butter@uni-mannheim.de>", + NULL + }; + + return purple_plugin_info_new( + "id", "prpl-simple", + "name", "SIMPLE Protocol", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("SIP/SIMPLE Protocol Plugin"), + "description", N_("The SIP/SIMPLE Protocol Plugin"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + simple_protocol_register_type(plugin); + + my_protocol = purple_protocols_add(SIMPLE_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + + return TRUE; +} + +PURPLE_PLUGIN_INIT(simple, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/protocols/simple/simple.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/simple/simple.h Fri Jan 31 18:02:20 2014 +0530 @@ -32,7 +32,7 @@ #include "dnssrv.h" #include "network.h" #include "proxy.h" -#include "prpl.h" +#include "protocol.h" #include "sipmsg.h" @@ -46,6 +46,23 @@ #define PUBLISH_EXPIRATION 600 #define SUBSCRIBE_EXPIRATION 1200 +#define SIMPLE_TYPE_PROTOCOL (simple_protocol_get_type()) +#define SIMPLE_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SIMPLE_TYPE_PROTOCOL, SIMPLEProtocol)) +#define SIMPLE_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SIMPLE_TYPE_PROTOCOL, SIMPLEProtocolClass)) +#define SIMPLE_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SIMPLE_TYPE_PROTOCOL)) +#define SIMPLE_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SIMPLE_TYPE_PROTOCOL)) +#define SIMPLE_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SIMPLE_TYPE_PROTOCOL, SIMPLEProtocolClass)) + +typedef struct _SIMPLEProtocol +{ + PurpleProtocol parent; +} SIMPLEProtocol; + +typedef struct _SIMPLEProtocolClass +{ + PurpleProtocolClass parent_class; +} SIMPLEProtocolClass; + struct sip_dialog { gchar *ourtag; gchar *theirtag; @@ -137,4 +154,6 @@ TransCallback callback; }; +G_MODULE_EXPORT GType simple_protocol_get_type(void); + #endif /* _PURPLE_SIMPLE_H */
--- a/libpurple/protocols/simple/sipmsg.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/simple/sipmsg.c Fri Jan 31 18:02:20 2014 +0530 @@ -27,8 +27,8 @@ #include "conversation.h" #include "debug.h" #include "notify.h" -#include "prpl.h" -#include "plugin.h" +#include "protocol.h" +#include "plugins.h" #include "util.h" #include "version.h"
--- a/libpurple/protocols/yahoo/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -4,11 +4,13 @@ pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) YAHOOSOURCES = \ - libymsg.c \ - libymsg.h \ util.c \ yahoochat.h \ yahoochat.c \ + yahoo.c \ + yahoo.h \ + yahoojp.c \ + yahoojp.h \ yahoo_aliases.c \ yahoo_aliases.h \ yahoo_doodle.h \ @@ -23,33 +25,27 @@ yahoo_picture.h \ yahoo_profile.c \ ycht.c \ - ycht.h + ycht.h \ + ymsg.c \ + ymsg.h AM_CFLAGS = $(st) libyahoo_la_LDFLAGS = -module -avoid-version -libyahoojp_la_LDFLAGS = -module -avoid-version if STATIC_YAHOO st = -DPURPLE_STATIC_PRPL -noinst_LTLIBRARIES = libymsg.la -libymsg_la_SOURCES = $(YAHOOSOURCES) libyahoo.c libyahoojp.c -libymsg_la_CFLAGS = $(AM_CFLAGS) +noinst_LTLIBRARIES = libyahoo.la +libyahoo_la_SOURCES = $(YAHOOSOURCES) +libyahoo_la_CFLAGS = $(AM_CFLAGS) else st = -pkg_LTLIBRARIES = libymsg.la libyahoo.la libyahoojp.la - -libymsg_la_SOURCES = $(YAHOOSOURCES) -libymsg_la_LIBADD = $(GLIB_LIBS) - -libyahoo_la_SOURCES = libyahoo.c -libyahoo_la_LIBADD = libymsg.la - -libyahoojp_la_SOURCES = libyahoojp.c -libyahoojp_la_LIBADD = libymsg.la +pkg_LTLIBRARIES = libyahoo.la +libyahoo_la_SOURCES = $(YAHOOSOURCES) +libyahoo_la_LIBADD = $(GLIB_LIBS) endif @@ -57,4 +53,5 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/yahoo/Makefile.mingw Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/Makefile.mingw Fri Jan 31 18:02:20 2014 +0530 @@ -7,9 +7,7 @@ PIDGIN_TREE_TOP := ../../.. include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak -TARGET = libymsg -YAHOO_TARGET = libyahoo -YAHOOJP_TARGET = libyahoojp +TARGET = libyahoo TYPE = PLUGIN # Static or Plugin... @@ -41,7 +39,8 @@ ## SOURCES, OBJECTS ## C_SRC = util.c \ - libymsg.c \ + yahoo.c \ + yahoojp.c \ yahoochat.c \ yahoo_aliases.c \ yahoo_doodle.c \ @@ -50,16 +49,11 @@ yahoo_packet.c \ yahoo_picture.c \ yahoo_profile.c \ - ycht.c + ycht.c \ + ymsg.c OBJECTS = $(C_SRC:%.c=%.o) -YAHOO_C_SRC = libyahoo.c -YAHOO_OBJECTS = $(YAHOO_C_SRC:%.c=%.o) - -YAHOOJP_C_SRC = libyahoojp.c -YAHOOJP_OBJECTS = $(YAHOOJP_C_SRC:%.c=%.o) - ## ## LIBRARIES ## @@ -78,29 +72,20 @@ .PHONY: all install clean -all: $(TARGET).dll $(YAHOO_TARGET).dll $(YAHOOJP_TARGET).dll +all: $(TARGET).dll install: all $(DLL_INSTALL_DIR) - cp $(YAHOO_TARGET).dll $(YAHOOJP_TARGET).dll $(DLL_INSTALL_DIR) - cp $(TARGET).dll $(PURPLE_INSTALL_DIR) + cp $(TARGET).dll $(DLL_INSTALL_DIR) $(OBJECTS): $(PURPLE_CONFIG_H) -$(TARGET).dll.a $(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--output-def,$(TARGET).def,--out-implib,$(TARGET).dll.a -o $(TARGET).dll - -$(YAHOO_TARGET).dll: $(TARGET).dll.a $(YAHOO_OBJECTS) - $(CC) -shared $(YAHOO_OBJECTS) $(LIB_PATHS) $(LIBS) -lymsg $(DLL_LD_FLAGS) -o $(YAHOO_TARGET).dll - -$(YAHOOJP_TARGET).dll: $(TARGET).dll.a $(YAHOOJP_OBJECTS) - $(CC) -shared $(YAHOOJP_OBJECTS) $(LIB_PATHS) $(LIBS) -lymsg $(DLL_LD_FLAGS) -o $(YAHOOJP_TARGET).dll +$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) + $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll ## ## CLEAN RULES ## clean: - rm -f $(OBJECTS) $(TARGET).dll $(TARGET).dll.a - rm -f $(YAHOO_OBJECTS) $(YAHOO_TARGET).dll - rm -f $(YAHOOJP_OBJECTS) $(YAHOOJP_TARGET).dll + rm -f $(OBJECTS) $(TARGET).dll include $(PIDGIN_COMMON_TARGETS)
--- a/libpurple/protocols/yahoo/libyahoo.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,341 +0,0 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include "internal.h" - -#include <account.h> -#include <core.h> - -#include "libymsg.h" -#include "yahoochat.h" -#include "yahoo_aliases.h" -#include "yahoo_doodle.h" -#include "yahoo_filexfer.h" -#include "yahoo_picture.h" - -static PurplePlugin *my_protocol = NULL; - -static void yahoo_register_commands(void) -{ - purple_cmd_register("join", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-yahoo", yahoopurple_cmd_chat_join, - _("join <room>: Join a chat room on the Yahoo network"), NULL); - purple_cmd_register("list", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-yahoo", yahoopurple_cmd_chat_list, - _("list: List rooms on the Yahoo network"), NULL); - purple_cmd_register("buzz", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-yahoo", yahoopurple_cmd_buzz, - _("buzz: Buzz a user to get their attention"), NULL); - purple_cmd_register("doodle", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-yahoo", yahoo_doodle_purple_cmd_start, - _("doodle: Request user to start a Doodle session"), NULL); -} - -static PurpleAccount *find_acct(const char *prpl, const char *acct_id) -{ - PurpleAccount *acct = NULL; - - /* If we have a specific acct, use it */ - if (acct_id) { - acct = purple_accounts_find(acct_id, prpl); - if (acct && !purple_account_is_connected(acct)) - acct = NULL; - } else { /* Otherwise find an active account for the protocol */ - GList *l = purple_accounts_get_all(); - while (l) { - if (!strcmp(prpl, purple_account_get_protocol_id(l->data)) - && purple_account_is_connected(l->data)) { - acct = l->data; - break; - } - l = l->next; - } - } - - return acct; -} - -/* This may not be the best way to do this, but we find the first key w/o a value - * and assume it is the buddy name */ -static void yahoo_find_uri_novalue_param(gpointer key, gpointer value, gpointer user_data) -{ - char **retval = user_data; - - if (value == NULL && *retval == NULL) { - *retval = key; - } -} - -static gboolean yahoo_uri_handler(const char *proto, const char *cmd, GHashTable *params) -{ - char *acct_id = g_hash_table_lookup(params, "account"); - PurpleAccount *acct; - - if (g_ascii_strcasecmp(proto, "ymsgr")) - return FALSE; - - acct = find_acct(purple_plugin_get_id(my_protocol), acct_id); - - if (!acct) - return FALSE; - - /* ymsgr:SendIM?screename&m=The+Message */ - if (!g_ascii_strcasecmp(cmd, "SendIM")) { - char *sname = NULL; - g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &sname); - if (sname) { - char *message = g_hash_table_lookup(params, "m"); - - PurpleIMConversation *im = purple_conversations_find_im_with_account( - sname, acct); - if (im == NULL) - im = purple_im_conversation_new(acct, sname); - purple_conversation_present(PURPLE_CONVERSATION(im)); - - if (message) { - /* Spaces are encoded as '+' */ - g_strdelimit(message, "+", ' '); - purple_conversation_send_confirm(PURPLE_CONVERSATION(im), message); - } - } - /* else - **If pidgindialogs_im() was in the core, we could use it here. - * It is all purple_request_* based, but I'm not sure it really belongs in the core - pidgindialogs_im(); */ - - return TRUE; - } - /* ymsgr:Chat?roomname */ - else if (!g_ascii_strcasecmp(cmd, "Chat")) { - char *rname = NULL; - g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &rname); - if (rname) { - /* This is somewhat hacky, but the params aren't useful after this command */ - g_hash_table_insert(params, g_strdup("room"), g_strdup(rname)); - g_hash_table_insert(params, g_strdup("type"), g_strdup("Chat")); - serv_join_chat(purple_account_get_connection(acct), params); - } - /* else - ** Same as above (except that this would have to be re-written using purple_request_*) - pidgin_blist_joinchat_show(); */ - - return TRUE; - } - /* ymsgr:AddFriend?name */ - else if (!g_ascii_strcasecmp(cmd, "AddFriend")) { - char *name = NULL; - g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &name); - purple_blist_request_add_buddy(acct, name, NULL, NULL); - return TRUE; - } - - return FALSE; -} - -static GHashTable * -yahoo_get_account_text_table(PurpleAccount *account) -{ - GHashTable *table; - table = g_hash_table_new(g_str_hash, g_str_equal); - g_hash_table_insert(table, "login_label", (gpointer)_("Yahoo ID...")); - return table; -} - -static gboolean yahoo_unload_plugin(PurplePlugin *plugin) -{ - yahoo_dest_colorht(); - - return TRUE; -} - -static PurpleWhiteboardPrplOps yahoo_whiteboard_prpl_ops = -{ - yahoo_doodle_start, - yahoo_doodle_end, - yahoo_doodle_get_dimensions, - NULL, - yahoo_doodle_get_brush, - yahoo_doodle_set_brush, - yahoo_doodle_send_draw_list, - yahoo_doodle_clear, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC | OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png,gif,jpeg", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, - yahoo_list_icon, - yahoo_list_emblem, - yahoo_status_text, - yahoo_tooltip_text, - yahoo_status_types, - yahoo_blist_node_menu, - yahoo_c_info, - yahoo_c_info_defaults, - yahoo_login, - yahoo_close, - yahoo_send_im, - NULL, /* set info */ - yahoo_send_typing, - yahoo_get_info, - yahoo_set_status, - yahoo_set_idle, - NULL, /* change_passwd*/ - yahoo_add_buddy, - NULL, /* add_buddies */ - yahoo_remove_buddy, - NULL, /* remove_buddies */ - NULL, /* add_permit */ - yahoo_add_deny, - NULL, /* rem_permit */ - yahoo_rem_deny, - yahoo_set_permit_deny, - yahoo_c_join, - NULL, /* reject chat invite */ - yahoo_get_chat_name, - yahoo_c_invite, - yahoo_c_leave, - NULL, /* chat whisper */ - yahoo_c_send, - yahoo_keepalive, - NULL, /* register_user */ - NULL, /* get_cb_info */ - yahoo_update_alias, /* alias_buddy */ - yahoo_change_buddys_group, - yahoo_rename_group, - NULL, /* buddy_free */ - NULL, /* convo_closed */ - purple_normalize_nocase, /* normalize */ - yahoo_set_buddy_icon, - NULL, /* void (*remove_group)(PurpleConnection *gc, const char *group);*/ - NULL, /* char *(*get_cb_real_name)(PurpleConnection *gc, int id, const char *who); */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - yahoo_roomlist_get_list, - yahoo_roomlist_cancel, - yahoo_roomlist_expand_category, - yahoo_can_receive_file, /* can_receive_file */ - yahoo_send_file, - yahoo_new_xfer, - yahoo_offline_message, /* offline_message */ - &yahoo_whiteboard_prpl_ops, - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - yahoo_send_attention, - yahoo_attention_types, - yahoo_get_account_text_table, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - yahoo_get_max_message_size -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - "prpl-yahoo", /**< id */ - "Yahoo", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Yahoo! Protocol Plugin"), - /** description */ - N_("Yahoo! Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - NULL, /**< load */ - yahoo_unload_plugin, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, - yahoo_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - PurpleAccountOption *option; - - option = purple_account_option_int_new(_("Pager port"), "port", YAHOO_PAGER_PORT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOO_XFER_HOST); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOO_ROOMLIST_LOCALE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("Encoding"), "local_charset", "UTF-8"); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - -#if 0 - option = purple_account_option_bool_new(_("Use account proxy for HTTP and HTTPS connections"), "proxy_ssl", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); -#endif - - my_protocol = plugin; - yahoo_register_commands(); - yahoo_init_colorht(); - - purple_signal_connect(purple_get_core(), "uri-handler", plugin, - PURPLE_CALLBACK(yahoo_uri_handler), NULL); -} - -PURPLE_INIT_PLUGIN(yahoo, init_plugin, info);
--- a/libpurple/protocols/yahoo/libyahoojp.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,236 +0,0 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include "internal.h" - -#include <account.h> - -#include "libymsg.h" -#include "yahoochat.h" -#include "yahoo_aliases.h" -#include "yahoo_doodle.h" -#include "yahoo_filexfer.h" -#include "yahoo_picture.h" - -static void yahoojp_register_commands(void) -{ - purple_cmd_register("join", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-yahoojp", yahoopurple_cmd_chat_join, - _("join <room>: Join a chat room on the Yahoo network"), NULL); - purple_cmd_register("list", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-yahoojp", yahoopurple_cmd_chat_list, - _("list: List rooms on the Yahoo network"), NULL); - purple_cmd_register("buzz", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-yahoojp", yahoopurple_cmd_buzz, - _("buzz: Buzz a user to get their attention"), NULL); - purple_cmd_register("doodle", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-yahoojp", yahoo_doodle_purple_cmd_start, - _("doodle: Request user to start a Doodle session"), NULL); -} - -static GHashTable * -yahoojp_get_account_text_table(PurpleAccount *account) -{ - GHashTable *table; - table = g_hash_table_new(g_str_hash, g_str_equal); - g_hash_table_insert(table, "login_label", (gpointer)_("Yahoo JAPAN ID...")); - return table; -} - -static gboolean yahoojp_unload_plugin(PurplePlugin *plugin) -{ - yahoo_dest_colorht(); - - return TRUE; -} - -static PurpleWhiteboardPrplOps yahoo_whiteboard_prpl_ops = -{ - yahoo_doodle_start, - yahoo_doodle_end, - yahoo_doodle_get_dimensions, - NULL, - yahoo_doodle_get_brush, - yahoo_doodle_set_brush, - yahoo_doodle_send_draw_list, - yahoo_doodle_clear, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginProtocolInfo prpl_info = -{ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC | OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png,gif,jpeg", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, - yahoo_list_icon, - yahoo_list_emblem, - yahoo_status_text, - yahoo_tooltip_text, - yahoo_status_types, - yahoo_blist_node_menu, - yahoo_c_info, - yahoo_c_info_defaults, - yahoo_login, - yahoo_close, - yahoo_send_im, - NULL, /* set info */ - yahoo_send_typing, - yahoo_get_info, - yahoo_set_status, - yahoo_set_idle, - NULL, /* change_passwd*/ - yahoo_add_buddy, - NULL, /* add_buddies */ - yahoo_remove_buddy, - NULL, /* remove_buddies */ - NULL, /* add_permit */ - yahoo_add_deny, - NULL, /* rem_permit */ - yahoo_rem_deny, - yahoo_set_permit_deny, - yahoo_c_join, - NULL, /* reject chat invite */ - yahoo_get_chat_name, - yahoo_c_invite, - yahoo_c_leave, - NULL, /* chat whisper */ - yahoo_c_send, - yahoo_keepalive, - NULL, /* register_user */ - NULL, /* get_cb_info */ - yahoo_update_alias, /* alias_buddy */ - yahoo_change_buddys_group, - yahoo_rename_group, - NULL, /* buddy_free */ - NULL, /* convo_closed */ - purple_normalize_nocase, /* normalize */ - yahoo_set_buddy_icon, - NULL, /* void (*remove_group)(PurpleConnection *gc, const char *group);*/ - NULL, /* char *(*get_cb_real_name)(PurpleConnection *gc, int id, const char *who); */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - yahoo_roomlist_get_list, - yahoo_roomlist_cancel, - yahoo_roomlist_expand_category, - NULL, /* can_receive_file */ - yahoo_send_file, - yahoo_new_xfer, - yahoo_offline_message, /* offline_message */ - &yahoo_whiteboard_prpl_ops, - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - - yahoo_send_attention, - yahoo_attention_types, - - yahoojp_get_account_text_table, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - yahoo_get_max_message_size -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - "prpl-yahoojp", /**< id */ - "Yahoo JAPAN", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Yahoo! JAPAN Protocol Plugin"), - /** description */ - N_("Yahoo! JAPAN Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - NULL, /**< load */ - yahoojp_unload_plugin, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, - yahoo_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - PurpleAccountOption *option; - - option = purple_account_option_int_new(_("Pager port"), "port", YAHOO_PAGER_PORT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOOJP_XFER_HOST); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOOJP_ROOMLIST_LOCALE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("Encoding"), "local_charset", "UTF-8"); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - -#if 0 - option = purple_account_option_bool_new(_("Use account proxy for HTTP and HTTPS connections"), "proxy_ssl", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); -#endif - - yahoojp_register_commands(); - yahoo_init_colorht(); -} - -PURPLE_INIT_PLUGIN(yahoojp, init_plugin, info); -
--- a/libpurple/protocols/yahoo/libymsg.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5342 +0,0 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -/* - * Note: When handling the list of struct yahoo_pair's from an incoming - * packet the value might not be UTF-8. You should either validate that - * it is UTF-8 using g_utf8_validate() or use yahoo_string_decode(). - */ - -#include "internal.h" - -#include "account.h" -#include "accountopt.h" -#include "buddylist.h" -#include "ciphers/md5hash.h" -#include "cmds.h" -#include "core.h" -#include "debug.h" -#include "http.h" -#include "network.h" -#include "notify.h" -#include "prpl.h" -#include "proxy.h" -#include "request.h" -#include "server.h" -#include "util.h" -#include "version.h" -#include "xmlnode.h" - -#include "libymsg.h" -#include "yahoochat.h" -#include "yahoo_aliases.h" -#include "yahoo_doodle.h" -#include "yahoo_filexfer.h" -#include "yahoo_friend.h" -#include "yahoo_packet.h" -#include "yahoo_picture.h" -#include "ycht.h" - -/* #define YAHOO_DEBUG */ - -/* It doesn't look like it is working (the previously used host is down, another - * one doesn't send us back cookies). - */ -#define TRY_WEBMESSENGER_LOGIN 0 - -/* One hour */ -#define PING_TIMEOUT 3600 - -/* One minute */ -#define KEEPALIVE_TIMEOUT 60 - -#if TRY_WEBMESSENGER_LOGIN -static void -yahoo_login_page_cb(PurpleHttpConnection *http_conn, - PurpleHttpResponse *response, gpointer _unused); -#endif /* TRY_WEBMESSENGER_LOGIN */ - -static gboolean yahoo_is_japan(PurpleAccount *account) -{ - return purple_strequal(purple_account_get_protocol_id(account), "prpl-yahoojp"); -} - -static void yahoo_update_status(PurpleConnection *gc, const char *name, YahooFriend *f) -{ - char *status = NULL; - - if (!gc || !name || !f || !purple_blist_find_buddy(purple_connection_get_account(gc), name)) - return; - - switch (f->status) { - case YAHOO_STATUS_OFFLINE: - status = YAHOO_STATUS_TYPE_OFFLINE; - break; - case YAHOO_STATUS_AVAILABLE: - status = YAHOO_STATUS_TYPE_AVAILABLE; - break; - case YAHOO_STATUS_BRB: - status = YAHOO_STATUS_TYPE_BRB; - break; - case YAHOO_STATUS_BUSY: - status = YAHOO_STATUS_TYPE_BUSY; - break; - case YAHOO_STATUS_NOTATHOME: - status = YAHOO_STATUS_TYPE_NOTATHOME; - break; - case YAHOO_STATUS_NOTATDESK: - status = YAHOO_STATUS_TYPE_NOTATDESK; - break; - case YAHOO_STATUS_NOTINOFFICE: - status = YAHOO_STATUS_TYPE_NOTINOFFICE; - break; - case YAHOO_STATUS_ONPHONE: - status = YAHOO_STATUS_TYPE_ONPHONE; - break; - case YAHOO_STATUS_ONVACATION: - status = YAHOO_STATUS_TYPE_ONVACATION; - break; - case YAHOO_STATUS_OUTTOLUNCH: - status = YAHOO_STATUS_TYPE_OUTTOLUNCH; - break; - case YAHOO_STATUS_STEPPEDOUT: - status = YAHOO_STATUS_TYPE_STEPPEDOUT; - break; - case YAHOO_STATUS_INVISIBLE: /* this should never happen? */ - status = YAHOO_STATUS_TYPE_INVISIBLE; - break; - case YAHOO_STATUS_CUSTOM: - case YAHOO_STATUS_IDLE: - if (!f->away) - status = YAHOO_STATUS_TYPE_AVAILABLE; - else - status = YAHOO_STATUS_TYPE_AWAY; - break; - default: - purple_debug_warning("yahoo", "Warning, unknown status %d\n", f->status); - break; - } - - if (status) { - if (f->status == YAHOO_STATUS_CUSTOM) - purple_prpl_got_user_status(purple_connection_get_account(gc), name, status, "message", - yahoo_friend_get_status_message(f), NULL); - else - purple_prpl_got_user_status(purple_connection_get_account(gc), name, status, NULL); - } - - if (f->idle != 0) - purple_prpl_got_user_idle(purple_connection_get_account(gc), name, TRUE, f->idle); - else - purple_prpl_got_user_idle(purple_connection_get_account(gc), name, FALSE, 0); - - if (f->sms) - purple_prpl_got_user_status(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE, NULL); - else - purple_prpl_got_user_status_deactive(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE); -} - -static void yahoo_process_status(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - PurpleAccount *account = purple_connection_get_account(gc); - GSList *l = pkt->hash; - YahooFriend *f = NULL; - char *name = NULL; - gboolean unicode = FALSE; - char *message = NULL; - YahooFederation fed = YAHOO_FEDERATION_NONE; - char *fedname = NULL; - - if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) { - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL, NULL, NULL); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE, - _("You have signed on from another location")); - return; - } - - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 0: /* we won't actually do anything with this */ - case 1: /* we won't actually do anything with this */ - break; - case 8: /* how many online buddies we have */ - break; - case 7: /* the current buddy */ - /* update the previous buddy before changing the variables */ - if (f) { - if (message) - yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode)); - if (name) - yahoo_update_status(gc, name, f); - } - name = message = NULL; - f = NULL; - if (pair->value && g_utf8_validate(pair->value, -1, NULL)) { - GSList *tmplist; - - name = pair->value; - - /* Look ahead to see if we have the federation info about the buddy */ - for (tmplist = l->next; tmplist; tmplist = tmplist->next) { - struct yahoo_pair *p = tmplist->data; - if (p->key == 7) - break; - if (p->key == 241) { - fed = strtol(p->value, NULL, 10); - g_free(fedname); - switch (fed) { - case YAHOO_FEDERATION_MSN: - name = fedname = g_strconcat("msn/", name, NULL); - break; - case YAHOO_FEDERATION_OCS: - name = fedname = g_strconcat("ocs/", name, NULL); - break; - case YAHOO_FEDERATION_IBM: - name = fedname = g_strconcat("ibm/", name, NULL); - break; - case YAHOO_FEDERATION_NONE: - default: - fedname = NULL; - break; - } - break; - } - } - f = yahoo_friend_find_or_new(gc, name); - f->fed = fed; - } - break; - case 10: /* state */ - if (!f) - break; - - f->status = strtol(pair->value, NULL, 10); - if ((f->status >= YAHOO_STATUS_BRB) && (f->status <= YAHOO_STATUS_STEPPEDOUT)) - f->away = 1; - else - f->away = 0; - - if (f->status == YAHOO_STATUS_IDLE) { - /* Idle may have already been set in a more precise way in case 137 */ - if (f->idle == 0) - { - if(pkt->service == YAHOO_SERVICE_STATUS_15) - f->idle = -1; - else - f->idle = time(NULL); - } - } else - f->idle = 0; - - if (f->status != YAHOO_STATUS_CUSTOM) - yahoo_friend_set_status_message(f, NULL); - - f->sms = 0; - break; - case 19: /* custom message */ - if (f) - message = pair->value; - break; - case 11: /* this is the buddy's session id */ - if (f) - f->session_id = strtol(pair->value, NULL, 10); - break; - case 17: /* in chat? */ - break; - case 47: /* is custom status away or not? 2=idle*/ - if (!f) - break; - - /* I have no idea what it means when this is - * set when someone's available, but it doesn't - * mean idle. */ - if (f->status == YAHOO_STATUS_AVAILABLE) - break; - - f->away = strtol(pair->value, NULL, 10); - if (f->away == 2) { - /* Idle may have already been set in a more precise way in case 137 */ - if (f->idle == 0) - { - if(pkt->service == YAHOO_SERVICE_STATUS_15) - f->idle = -1; - else - f->idle = time(NULL); - } - } - - break; - case 138: /* when value is 1, either we're not idle, or we are but won't say how long */ - if (!f) - break; - - if( (strtol(pair->value, NULL, 10) == 1) && (f->idle) ) - f->idle = -1; - break; - case 137: /* usually idle time in seconds, sometimes login time */ - if (!f) - break; - - if (f->status != YAHOO_STATUS_AVAILABLE) - f->idle = time(NULL) - strtol(pair->value, NULL, 10); - break; - case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */ - if (strtol(pair->value, NULL, 10) == 0) { - if (f) - f->status = YAHOO_STATUS_OFFLINE; - if (name) { - purple_prpl_got_user_status(account, name, "offline", NULL); - purple_prpl_got_user_status_deactive(account, name, YAHOO_STATUS_TYPE_MOBILE); - } - break; - } - break; - case 60: /* SMS */ - if (f) { - f->sms = strtol(pair->value, NULL, 10); - yahoo_update_status(gc, name, f); - } - break; - case 197: /* Avatars */ - { - guchar *decoded; - char *tmp; - gsize len; - - if (pair->value) { - decoded = purple_base64_decode(pair->value, &len); - if (decoded && len > 0) { - tmp = purple_str_binary_to_ascii(decoded, len); - purple_debug_info("yahoo", "Got key 197, value = %s\n", tmp); - g_free(tmp); - } - g_free(decoded); - } - break; - } - case 192: /* Pictures, aka Buddy Icons, checksum */ - { - /* FIXME: Please, if you know this protocol, - * FIXME: fix up the strtol() stuff if possible. */ - int cksum = strtol(pair->value, NULL, 10); - const char *locksum = NULL; - PurpleBuddy *b; - - if (!name) - break; - - b = purple_blist_find_buddy(purple_connection_get_account(gc), name); - - if (!cksum || (cksum == -1)) { - if (f) - yahoo_friend_set_buddy_icon_need_request(f, TRUE); - purple_buddy_icons_set_for_user(purple_connection_get_account(gc), name, NULL, 0, NULL); - break; - } - - if (!f) - break; - - yahoo_friend_set_buddy_icon_need_request(f, FALSE); - if (b) { - locksum = purple_buddy_icons_get_checksum_for_user(b); - if (!locksum || (cksum != strtol(locksum, NULL, 10))) - yahoo_send_picture_request(gc, name); - } - - break; - } - case 16: /* Custom error message */ - { - char *tmp = yahoo_string_decode(gc, pair->value, TRUE); - purple_notify_error(gc, NULL, tmp, NULL, - purple_request_cpar_from_connection(gc)); - g_free(tmp); - } - break; - case 97: /* Unicode status message */ - unicode = !strcmp(pair->value, "1"); - break; - case 244: /* client version number. Yahoo Client Detection */ - if(f && strtol(pair->value, NULL, 10)) - f->version_id = strtol(pair->value, NULL, 10); - break; - case 241: /* Federated network buddy belongs to */ - break; /* We process this when get '7' */ - default: - purple_debug_warning("yahoo", - "Unknown status key %d\n", pair->key); - break; - } - - l = l->next; - } - - if (f) { - if (pkt->service == YAHOO_SERVICE_LOGOFF) - f->status = YAHOO_STATUS_OFFLINE; - if (message) - yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode)); - - if (name) /* update the last buddy */ - yahoo_update_status(gc, name, f); - } - - g_free(fedname); -} - -static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const char *name, const char *group) -{ - PurpleBuddy *b; - PurpleGroup *g; - GSList *list, *i; - gboolean onlist = FALSE; - char *oname = NULL; - - if (g_hash_table_lookup_extended(ht, name, (gpointer *)&oname, (gpointer *)&list)) - g_hash_table_steal(ht, oname); - else - list = purple_blist_find_buddies(account, name); - - for (i = list; i; i = i->next) { - b = i->data; - g = purple_buddy_get_group(b); - if (!purple_utf8_strcasecmp(group, purple_group_get_name(g))) { - purple_debug_misc("yahoo", - "Oh good, %s is in the right group (%s).\n", name, group); - list = g_slist_delete_link(list, i); - onlist = TRUE; - break; - } - } - - if (!onlist) { - purple_debug_misc("yahoo", - "Uhoh, %s isn't on the list (or not in this group), adding him to group %s.\n", name, group); - if (!(g = purple_blist_find_group(group))) { - g = purple_group_new(group); - purple_blist_add_group(g, NULL); - } - b = purple_buddy_new(account, name, NULL); - purple_blist_add_buddy(b, NULL, g, NULL); - } - - if (list) { - if (!oname) - oname = g_strdup(name); - g_hash_table_insert(ht, oname, list); - } else - g_free(oname); -} - -static void yahoo_do_group_cleanup(gpointer key, gpointer value, gpointer user_data) -{ - char *name = key; - GSList *list = value, *i; - PurpleBuddy *b; - PurpleGroup *g; - - for (i = list; i; i = i->next) { - b = i->data; - g = purple_buddy_get_group(b); - purple_debug_misc("yahoo", "Deleting Buddy %s from group %s.\n", name, - purple_group_get_name(g)); - purple_blist_remove_buddy(b); - } -} - -static char *_getcookie(char *rawcookie) -{ - char *cookie = NULL; - char *tmpcookie; - char *cookieend; - - if (strlen(rawcookie) < 2) - return NULL; - tmpcookie = g_strdup(rawcookie+2); - cookieend = strchr(tmpcookie, ';'); - - if (cookieend) - *cookieend = '\0'; - - cookie = g_strdup(tmpcookie); - g_free(tmpcookie); - - return cookie; -} - -static void yahoo_process_cookie(YahooData *yd, char *c) -{ - if (c[0] == 'Y') { - g_free(yd->cookie_y); - yd->cookie_y = _getcookie(c); - } else if (c[0] == 'T') { - g_free(yd->cookie_t); - yd->cookie_t = _getcookie(c); - } else - purple_debug_info("yahoo", "Unrecognized cookie '%c'\n", c[0]); - yd->cookies = g_slist_prepend(yd->cookies, g_strdup(c)); -} - -static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - GSList *l = pkt->hash; - - PurpleAccount *account = purple_connection_get_account(gc); - YahooData *yd = purple_connection_get_protocol_data(gc); - GHashTable *ht; - char *norm_bud = NULL; - char *temp = NULL; - YahooFriend *f = NULL; /* It's your friends. They're going to want you to share your StarBursts. */ - /* But what if you had no friends? */ - YahooFederation fed = YAHOO_FEDERATION_NONE; - int stealth = 0; - - ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free); - - while (l) { - struct yahoo_pair *pair = l->data; - l = l->next; - - switch (pair->key) { - case 302: - /* This is always 318 before a group, 319 before the first s/n in a group, 320 before any ignored s/n. - * It is not sent for s/n's in a group after the first. - * All ignored s/n's are listed last, so when we see a 320 we clear the group and begin marking the - * s/n's as ignored. It is always followed by an identical 300 key. - */ - if (pair->value && !strcmp(pair->value, "320")) { - /* No longer in any group; this indicates the start of the ignore list. */ - g_free(yd->current_list15_grp); - yd->current_list15_grp = NULL; - } - - break; - case 301: /* This is 319 before all s/n's in a group after the first. It is followed by an identical 300. */ - if(temp != NULL) { - switch (fed) { - case YAHOO_FEDERATION_MSN: - norm_bud = g_strconcat("msn/", temp, NULL); - break; - case YAHOO_FEDERATION_OCS: - norm_bud = g_strconcat("ocs/", temp, NULL); - break; - case YAHOO_FEDERATION_IBM: - norm_bud = g_strconcat("ibm/", temp, NULL); - break; - case YAHOO_FEDERATION_PBX: - norm_bud = g_strconcat("pbx/", temp, NULL); - break; - case YAHOO_FEDERATION_NONE: - norm_bud = g_strdup(temp); - break; - } - if (yd->current_list15_grp) { - /* This buddy is in a group */ - f = yahoo_friend_find_or_new(gc, norm_bud); - if (!purple_blist_find_buddy(account, norm_bud)) { - PurpleBuddy *b; - PurpleGroup *g; - if (!(g = purple_blist_find_group(yd->current_list15_grp))) { - g = purple_group_new(yd->current_list15_grp); - purple_blist_add_group(g, NULL); - } - b = purple_buddy_new(account, norm_bud, NULL); - purple_blist_add_buddy(b, NULL, g, NULL); - } - yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp); - if(fed) { - f->fed = fed; - purple_debug_info("yahoo", "Setting federation to %d\n", f->fed); - } - if(stealth == 2) - f->presence = YAHOO_PRESENCE_PERM_OFFLINE; - - /* set p2p status not connected and no p2p packet sent */ - if(fed == YAHOO_FEDERATION_NONE) { - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); - f->p2p_packet_sent = 0; - } else - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT); - } else { - /* This buddy is on the ignore list (and therefore in no group) */ - purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n", purple_account_get_username(account), norm_bud); - purple_account_privacy_deny_add(account, norm_bud, 1); - } - - g_free(norm_bud); - norm_bud=NULL; - fed = YAHOO_FEDERATION_NONE; - stealth = 0; - g_free(temp); - temp = NULL; - } - break; - case 300: /* This is 318 before a group, 319 before any s/n in a group, and 320 before any ignored s/n. */ - break; - case 65: /* This is the group */ - g_free(yd->current_list15_grp); - yd->current_list15_grp = yahoo_string_decode(gc, pair->value, FALSE); - break; - case 7: /* buddy's s/n */ - if (g_utf8_validate(pair->value, -1, NULL)) { - g_free(temp); - temp = g_strdup(purple_normalize(account, pair->value)); - } else { - purple_debug_warning("yahoo", "yahoo_process_list_15 " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 241: /* user on federated network */ - fed = strtol(pair->value, NULL, 10); - break; - case 59: /* somebody told cookies come here too, but im not sure */ - if (g_utf8_validate(pair->value, -1, NULL)) { - yahoo_process_cookie(yd, pair->value); - } else { - purple_debug_warning("yahoo", "yahoo_process_list_15 " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 317: /* Stealth Setting */ - stealth = strtol(pair->value, NULL, 10); - break; - /* case 242: */ /* this seems related to 241 */ - /* break; */ - } - } - - g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL); - - /* The reporter of ticket #9745 determined that we weren't retrieving the - * aliases during buddy list retrieval, so we never updated aliases that - * changed while we were signed off. */ - yahoo_fetch_aliases(gc); - - /* Now that we have processed the buddy list, we can say yahoo has connected */ - purple_connection_set_display_name(gc, purple_normalize(account, purple_account_get_username(account))); - yd->logged_in = TRUE; - purple_debug_info("yahoo","Authentication: Connection established\n"); - purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED); - if (yd->picture_upload_todo) { - yahoo_buddy_icon_upload(gc, yd->picture_upload_todo); - yd->picture_upload_todo = NULL; - } - yahoo_set_status(account, purple_account_get_active_status(account)); - - g_hash_table_destroy(ht); - g_free(temp); -} - -static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - GSList *l = pkt->hash; - gboolean got_serv_list = FALSE; - YahooFriend *f = NULL; - PurpleAccount *account = purple_connection_get_account(gc); - YahooData *yd = purple_connection_get_protocol_data(gc); - GHashTable *ht; - - char **lines; - char **split; - char **buddies; - char **tmp, **bud, *norm_bud; - char *grp = NULL; - - if (pkt->id) - yd->session_id = pkt->id; - - while (l) { - struct yahoo_pair *pair = l->data; - l = l->next; - - switch (pair->key) { - case 87: - if (!yd->tmp_serv_blist) - yd->tmp_serv_blist = g_string_new(pair->value); - else - g_string_append(yd->tmp_serv_blist, pair->value); - break; - case 88: - if (g_utf8_validate(pair->value, -1, NULL)) { - if (!yd->tmp_serv_ilist) - yd->tmp_serv_ilist = g_string_new(pair->value); - else - g_string_append(yd->tmp_serv_ilist, pair->value); - } else { - purple_debug_warning("yahoo", "yahoo_process_list " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 89: - if (g_utf8_validate(pair->value, -1, NULL)) { - yd->profiles = g_strsplit(pair->value, ",", -1); - } else { - purple_debug_warning("yahoo", "yahoo_process_list " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 59: /* cookies, yum */ - if (g_utf8_validate(pair->value, -1, NULL)) { - yahoo_process_cookie(yd, pair->value); - } else { - purple_debug_warning("yahoo", "yahoo_process_list " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case YAHOO_SERVICE_PRESENCE_PERM: - if (g_utf8_validate(pair->value, -1, NULL)) { - if (!yd->tmp_serv_plist) - yd->tmp_serv_plist = g_string_new(pair->value); - else - g_string_append(yd->tmp_serv_plist, pair->value); - } else { - purple_debug_warning("yahoo", "yahoo_process_list " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - } - } - - if (pkt->status != 0) - return; - - if (yd->tmp_serv_blist) { - ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free); - - lines = g_strsplit(yd->tmp_serv_blist->str, "\n", -1); - for (tmp = lines; *tmp; tmp++) { - split = g_strsplit(*tmp, ":", 2); - if (!split) - continue; - if (!split[0] || !split[1]) { - g_strfreev(split); - continue; - } - grp = yahoo_string_decode(gc, split[0], FALSE); - buddies = g_strsplit(split[1], ",", -1); - for (bud = buddies; bud && *bud; bud++) { - if (!g_utf8_validate(*bud, -1, NULL)) { - purple_debug_warning("yahoo", "yahoo_process_list " - "got non-UTF-8 string for bud\n"); - continue; - } - - norm_bud = g_strdup(purple_normalize(account, *bud)); - f = yahoo_friend_find_or_new(gc, norm_bud); - - if (!purple_blist_find_buddy(account, norm_bud)) { - PurpleBuddy *b; - PurpleGroup *g; - if (!(g = purple_blist_find_group(grp))) { - g = purple_group_new(grp); - purple_blist_add_group(g, NULL); - } - b = purple_buddy_new(account, norm_bud, NULL); - purple_blist_add_buddy(b, NULL, g, NULL); - } - - yahoo_do_group_check(account, ht, norm_bud, grp); - /* set p2p status not connected and no p2p packet sent */ - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); - f->p2p_packet_sent = 0; - - g_free(norm_bud); - } - g_strfreev(buddies); - g_strfreev(split); - g_free(grp); - } - g_strfreev(lines); - - g_string_free(yd->tmp_serv_blist, TRUE); - yd->tmp_serv_blist = NULL; - g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL); - g_hash_table_destroy(ht); - } - - if (yd->tmp_serv_ilist) { - buddies = g_strsplit(yd->tmp_serv_ilist->str, ",", -1); - for (bud = buddies; bud && *bud; bud++) { - /* The server is already ignoring the user */ - got_serv_list = TRUE; - purple_account_privacy_deny_add(account, *bud, 1); - } - g_strfreev(buddies); - - g_string_free(yd->tmp_serv_ilist, TRUE); - yd->tmp_serv_ilist = NULL; - } - - if (got_serv_list && - ((purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST) && - (purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_DENY_ALL) && - (purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS))) - { - purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS); - purple_debug_info("yahoo", "%s privacy defaulting to PURPLE_ACCOUNT_PRIVACY_DENY_USERS.\n", - purple_account_get_username(account)); - } - - if (yd->tmp_serv_plist) { - buddies = g_strsplit(yd->tmp_serv_plist->str, ",", -1); - for (bud = buddies; bud && *bud; bud++) { - f = yahoo_friend_find(gc, *bud); - if (f) { - purple_debug_info("yahoo", "%s setting presence for %s to PERM_OFFLINE\n", - purple_account_get_username(account), *bud); - f->presence = YAHOO_PRESENCE_PERM_OFFLINE; - } - } - g_strfreev(buddies); - g_string_free(yd->tmp_serv_plist, TRUE); - yd->tmp_serv_plist = NULL; - - } - /* Now that we've got the list, request aliases */ - yahoo_fetch_aliases(gc); -} - -/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */ -static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type) -{ - PurpleAccount *account; - char *msg = NULL; - char *from = NULL; - char *stat = NULL; - char *game = NULL; - YahooFriend *f = NULL; - GSList *l = pkt->hash; - gint val_11 = 0; - YahooData *yd = purple_connection_get_protocol_data(gc); - YahooFederation fed = YAHOO_FEDERATION_NONE; - - account = purple_connection_get_account(gc); - - while (l) { - struct yahoo_pair *pair = l->data; - if (pair->key == 4 || pair->key == 1) { - if (g_utf8_validate(pair->value, -1, NULL)) { - from = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_notify " - "got non-UTF-8 string for key %d\n", pair->key); - } - } else if (pair->key == 49) { - msg = pair->value; - } else if (pair->key == 13) { - stat = pair->value; - } else if (pair->key == 14) { - if (g_utf8_validate(pair->value, -1, NULL)) { - game = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_notify " - "got non-UTF-8 string for key %d\n", pair->key); - } - } else if (pair->key == 11) { - val_11 = strtol(pair->value, NULL, 10); - } else if (pair->key == 241) { - fed = strtol(pair->value, NULL, 10); - } - l = l->next; - } - - if (!from || !msg) - return; - - /* disconnect the peer if connected through p2p and sends wrong value for session id */ - if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) { - purple_debug_warning("yahoo","p2p: %s sent us notify with wrong session id. Disconnecting p2p connection to peer\n", from); - /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ - g_hash_table_remove(yd->peers, from); - return; - } - - if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING")) - && (purple_account_privacy_check(account, from))) - { - char *fed_from = from; - switch (fed) { - case YAHOO_FEDERATION_MSN: - fed_from = g_strconcat("msn/", from, NULL); - break; - case YAHOO_FEDERATION_OCS: - fed_from = g_strconcat("ocs/", from, NULL); - break; - case YAHOO_FEDERATION_IBM: - fed_from = g_strconcat("ibm/", from, NULL); - break; - case YAHOO_FEDERATION_PBX: - fed_from = g_strconcat("pbx/", from, NULL); - break; - case YAHOO_FEDERATION_NONE: - default: - break; - } - - if (stat && *stat == '1') - serv_got_typing(gc, fed_from, 0, PURPLE_IM_TYPING); - else - serv_got_typing_stopped(gc, fed_from); - - if (fed_from != from) - g_free(fed_from); - - } else if (!g_ascii_strncasecmp(msg, "GAME", strlen("GAME"))) { - PurpleBuddy *bud = purple_blist_find_buddy(account, from); - - if (!bud) { - purple_debug_warning("yahoo", - "%s is playing a game, and doesn't want you to know.\n", from); - } - - f = yahoo_friend_find(gc, from); - if (!f) - return; /* if they're not on the list, don't bother */ - - yahoo_friend_set_game(f, NULL); - - if (stat && *stat == '1') { - yahoo_friend_set_game(f, game); - if (bud) - yahoo_update_status(gc, from, f); - } - } else if (!g_ascii_strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) { - PurpleIMConversation *im = purple_conversations_find_im_with_account(from, account); - char *buf = g_strdup_printf(_("%s has sent you a webcam invite, which is not yet supported."), from); - purple_conversation_write(PURPLE_CONVERSATION(im), NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL)); - g_free(buf); - } -} - - -struct _yahoo_im { - char *from; - char *active_id; - int time; - int utf8; - int buddy_icon; - char *id; - char *msg; - YahooFederation fed; - char *fed_from; -}; - -static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - PurpleAccount *account; - GSList *l = pkt->hash; - struct _yahoo_im *sms = NULL; - YahooData *yd; - char *server_msg = NULL; - char *m; - - yd = purple_connection_get_protocol_data(gc); - account = purple_connection_get_account(gc); - - while (l != NULL) { - struct yahoo_pair *pair = l->data; - if (pair->key == 4) { - if (g_utf8_validate(pair->value, -1, NULL)) { - sms = g_new0(struct _yahoo_im, 1); - sms->from = g_strdup_printf("+%s", pair->value); - sms->time = time(NULL); - sms->utf8 = TRUE; - } else { - purple_debug_warning("yahoo", "yahoo_process_sms_message " - "got non-UTF-8 string for key %d\n", pair->key); - } - } else if (pair->key == 14) { - if (sms) - sms->msg = pair->value; - } else if (pair->key == 68) { - if(sms) - g_hash_table_insert(yd->sms_carrier, g_strdup(sms->from), g_strdup(pair->value)); - } else if (pair->key == 16) { - if (g_utf8_validate(pair->value, -1, NULL)) { - server_msg = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_sms_message " - "got non-UTF-8 string for key %d\n", pair->key); - } - } - l = l->next; - } - - if(!sms) { - purple_debug_info("yahoo", "Received a malformed SMS packet!\n"); - return; - } - - if( (pkt->status == -1) || (pkt->status == YAHOO_STATUS_DISCONNECTED) ) { - if (server_msg) { - PurpleIMConversation *im; - im = purple_conversations_find_im_with_account(sms->from, account); - if (im == NULL) - im = purple_im_conversation_new(account, sms->from); - purple_conversation_write(PURPLE_CONVERSATION(im), NULL, server_msg, PURPLE_MESSAGE_SYSTEM, time(NULL)); - } - else { - purple_notify_error(gc, NULL, - _("Your SMS was not delivered"), NULL, - purple_request_cpar_from_connection(gc)); - } - - g_free(sms->from); - g_free(sms); - return ; - } - - if (!sms->from || !sms->msg) { - g_free(sms); - return; - } - - m = yahoo_string_decode(gc, sms->msg, sms->utf8); - serv_got_im(gc, sms->from, m, 0, sms->time); - - g_free(m); - g_free(sms->from); - g_free(sms); -} - -/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */ -static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type) -{ - PurpleAccount *account; - YahooData *yd = purple_connection_get_protocol_data(gc); - GSList *l = pkt->hash; - GSList *list = NULL; - struct _yahoo_im *im = NULL; - - account = purple_connection_get_account(gc); - - if (pkt->status <= 1 || pkt->status == 5 || pkt->status == YAHOO_STATUS_OFFLINE) { - /* messages are received with status YAHOO_STATUS_OFFLINE in case of p2p */ - while (l != NULL) { - struct yahoo_pair *pair = l->data; - if (pair->key == 4 || pair->key == 1) { - if (g_utf8_validate(pair->value, -1, NULL)) { - im = g_new0(struct _yahoo_im, 1); - list = g_slist_append(list, im); - im->from = pair->value; - im->time = time(NULL); - im->utf8 = TRUE; - im->fed = YAHOO_FEDERATION_NONE; - im->fed_from = g_strdup(im->from); - } else { - purple_debug_warning("yahoo", "yahoo_process_message " - "got non-UTF-8 string for key %d\n", pair->key); - } - } else if (im && pair->key == 5) { - im->active_id = pair->value; - } else if (pair->key == 97) { - if (im) - im->utf8 = strtol(pair->value, NULL, 10); - } else if (pair->key == 15) { - if (im) - im->time = strtol(pair->value, NULL, 10); - } else if (pair->key == 206) { - if (im) - im->buddy_icon = strtol(pair->value, NULL, 10); - } else if (pair->key == 14) { - if (im) - im->msg = pair->value; - } else if (im && pair->key == 241) { - im->fed = strtol(pair->value, NULL, 10); - g_free(im->fed_from); - switch (im->fed) { - case YAHOO_FEDERATION_MSN: - im->fed_from = g_strconcat("msn/",im->from, NULL); - break; - case YAHOO_FEDERATION_OCS: - im->fed_from = g_strconcat("ocs/",im->from, NULL); - break; - case YAHOO_FEDERATION_IBM: - im->fed_from = g_strconcat("ibm/",im->from, NULL); - break; - case YAHOO_FEDERATION_PBX: - im->fed_from = g_strconcat("pbx/",im->from, NULL); - break; - case YAHOO_FEDERATION_NONE: - default: - im->fed_from = g_strdup(im->from); - break; - } - purple_debug_info("yahoo", "Message from federated (%d) buddy %s.\n", im->fed, im->fed_from); - - } else if (im && (pair->key == 11)) { - /* peer session id */ - /* disconnect the peer if connected through p2p and sends wrong value for session id */ - if( (im->fed == YAHOO_FEDERATION_NONE) && (pkt_type == YAHOO_PKT_TYPE_P2P) - && (yd->session_id != strtol(pair->value, NULL, 10)) ) - { - purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->fed_from); - /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ - g_hash_table_remove(yd->peers, im->fed_from); - g_free(im->fed_from); - g_free(im); - return; /* Not sure whether we should process remaining IMs in this packet */ - } - - } else if (im && pair->key == 63 && g_utf8_validate(pair->value, -1, NULL)) { - /* IMV key */ - /* Check for the Doodle IMV, no IMvironment for federated buddies */ - if (im->from != NULL && im->fed == YAHOO_FEDERATION_NONE) - { - g_hash_table_replace(yd->imvironments, g_strdup(im->from), g_strdup(pair->value)); - - if (strstr(pair->value, "doodle;") != NULL) - { - PurpleWhiteboard *wb; - - if (!purple_account_privacy_check(account, im->from)) { - purple_debug_info("yahoo", "Doodle request from %s dropped.\n", - im->from); - g_free(im->fed_from); - g_free(im); - return; - } - /* I'm not sure the following ever happens -DAA */ - wb = purple_whiteboard_get_session(account, im->from); - - /* If a Doodle session doesn't exist between this user */ - if(wb == NULL) - { - doodle_session *ds; - wb = purple_whiteboard_new(account, im->from, - DOODLE_STATE_REQUESTED); - ds = purple_whiteboard_get_protocol_data(wb); - ds->imv_key = g_strdup(pair->value); - - yahoo_doodle_command_send_request(gc, im->from, pair->value); - yahoo_doodle_command_send_ready(gc, im->from, pair->value); - } - } - } - } else if (pair->key == 429) { - if (im) - im->id = pair->value; - } - l = l->next; - } - } else if (pkt->status == 2) { - purple_notify_error(gc, NULL, - _("Your Yahoo! message did not get sent."), NULL, - purple_request_cpar_from_connection(gc)); - } - - for (l = list; l; l = l->next) { - YahooFriend *f; - char *m, *m2; - im = l->data; - - if (!im->fed_from || !im->msg) { - g_free(im->fed_from); - g_free(im); - continue; - } - - if (!purple_account_privacy_check(account, im->fed_from)) { - purple_debug_info("yahoo", "Message from %s dropped.\n", im->fed_from); - return; - } - - /* - * TODO: Is there anything else we should check when determining whether - * we should send an acknowledgement? - */ - if (im->id != NULL) { - /* Send acknowledgement. If we don't do this then the official - * Yahoo Messenger client for Windows will send us the same - * message 7 seconds later as an offline message. This is true - * for at least version 9.0.0.2162 on Windows XP. */ - struct yahoo_packet *pkt2; - pkt2 = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_ACK, - YAHOO_STATUS_AVAILABLE, pkt->id); - yahoo_packet_hash(pkt2, "ssisii", - 1, im->active_id, /* May not always be the connection's display name */ - 5, im->from, - 302, 430, - 430, im->id, - 303, 430, - 450, 0); - yahoo_packet_send_and_free(pkt2, yd); - } - - m = yahoo_string_decode(gc, im->msg, im->utf8); - /* This may actually not be necessary, but it appears - * that at least at one point some clients were sending - * "\r\n" as line delimiters, so we want to avoid double - * lines. */ - m2 = purple_strreplace(m, "\r\n", "\n"); - g_free(m); - m = m2; - purple_util_chrreplace(m, '\r', '\n'); - if (!strcmp(m, "<ding>")) { - char *username; - - username = g_markup_escape_text(im->fed_from, -1); - purple_prpl_got_attention(gc, username, YAHOO_BUZZ); - g_free(username); - g_free(m); - g_free(im->fed_from); - g_free(im); - continue; - } - - m2 = yahoo_codes_to_html(m); - g_free(m); - - serv_got_im(gc, im->fed_from, m2, 0, im->time); - g_free(m2); - - /* Official clients don't share buddy images with federated buddies */ - if (im->fed == YAHOO_FEDERATION_NONE) { - if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) { - if (yahoo_friend_get_buddy_icon_need_request(f)) { - yahoo_send_picture_request(gc, im->from); - yahoo_friend_set_buddy_icon_need_request(f, FALSE); - } - } - } - - g_free(im->fed_from); - g_free(im); - } - - g_slist_free(list); -} - -static void yahoo_process_sysmessage(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - GSList *l = pkt->hash; - char *prim, *me = NULL, *msg = NULL; - - while (l) { - struct yahoo_pair *pair = l->data; - - if (pair->key == 5) { - if (g_utf8_validate(pair->value, -1, NULL)) { - me = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_sysmessage " - "got non-UTF-8 string for key %d\n", pair->key); - } - } else if (pair->key == 14) { - if (g_utf8_validate(pair->value, -1, NULL)) { - msg = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_sysmessage " - "got non-UTF-8 string for key %d\n", pair->key); - } - } - - l = l->next; - } - - if (!msg || !g_utf8_validate(msg, -1, NULL)) - return; - - prim = g_strdup_printf(_("Yahoo! system message for %s:"), - me?me:purple_connection_get_display_name(gc)); - purple_notify_info(NULL, NULL, prim, msg, - purple_request_cpar_from_connection(gc)); - g_free(prim); -} - -struct yahoo_add_request { - PurpleConnection *gc; - char *id; - char *who; - YahooFederation fed; -}; - -static void -yahoo_buddy_add_authorize_cb(const char *message, gpointer data) -{ - struct yahoo_add_request *add_req = data; - struct yahoo_packet *pkt; - YahooData *yd = purple_connection_get_protocol_data(add_req->gc); - const char *who = add_req->who; - - pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, yd->session_id); - if (add_req->fed) { - who += 4; - yahoo_packet_hash(pkt, "ssiii", - 1, add_req->id, - 5, who, - 241, add_req->fed, - 13, 1, - 334, 0); - } - else { - yahoo_packet_hash(pkt, "ssii", - 1, add_req->id, - 5, who, - 13, 1, - 334, 0); - } - - yahoo_packet_send_and_free(pkt, yd); - - g_free(add_req->id); - g_free(add_req->who); - g_free(add_req); -} - -static void -yahoo_buddy_add_deny_cb(const char *msg, gpointer data) -{ - struct yahoo_add_request *add_req = data; - YahooData *yd = purple_connection_get_protocol_data(add_req->gc); - struct yahoo_packet *pkt; - char *encoded_msg = NULL; - const char *who = add_req->who; - - if (msg && *msg) - encoded_msg = yahoo_string_encode(add_req->gc, msg, FALSE); - - pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, - YAHOO_STATUS_AVAILABLE, yd->session_id); - - if (add_req->fed) { - who += 4; /* Skip fed identifier (msn|ocs|ibm)/' */ - yahoo_packet_hash(pkt, "ssiiiis", - 1, add_req->id, - 5, who, - 241, add_req->fed, - 13, 2, - 334, 0, - 97, 1, /* UTF-8 */ - 14, encoded_msg ? encoded_msg : ""); - } - else { - yahoo_packet_hash(pkt, "ssiiis", - 1, add_req->id, - 5, who, - 13, 2, - 334, 0, - 97, 1, /* UTF-8 */ - 14, encoded_msg ? encoded_msg : ""); - } - - - yahoo_packet_send_and_free(pkt, yd); - - g_free(encoded_msg); - - g_free(add_req->id); - g_free(add_req->who); - g_free(add_req); -} - -static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason) -{ - char *notify_msg; - YahooData *yd = purple_connection_get_protocol_data(gc); - - if (who == NULL) - return; - - if (reason != NULL) { - char *msg2 = yahoo_string_decode(gc, reason, FALSE); - notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2); - g_free(msg2); - } else - notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list."), who); - - purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg, - purple_request_cpar_from_connection(gc)); - g_free(notify_msg); - - g_hash_table_remove(yd->friends, who); - purple_prpl_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */ - /* TODO: Shouldn't we remove the buddy from our local list? */ -} - -static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *pkt) { - PurpleAccount *account; - GSList *l = pkt->hash; - const char *msg = NULL; - - account = purple_connection_get_account(gc); - - /* Buddy authorized/declined our addition */ - if (pkt->status == 1) { - char *temp = NULL; - char *who = NULL; - int response = 0; - YahooFederation fed = YAHOO_FEDERATION_NONE; - - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 4: - if (g_utf8_validate(pair->value, -1, NULL)) { - temp = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 13: - response = strtol(pair->value, NULL, 10); - break; - case 14: - msg = pair->value; - break; - case 241: - fed = strtol(pair->value, NULL, 10); - break; - } - l = l->next; - } - - switch (fed) { - case YAHOO_FEDERATION_MSN: - who = g_strconcat("msn/", temp, NULL); - break; - case YAHOO_FEDERATION_OCS: - who = g_strconcat("ocs/", temp, NULL); - break; - case YAHOO_FEDERATION_IBM: - who = g_strconcat("ibm/", temp, NULL); - break; - case YAHOO_FEDERATION_NONE: - default: - who = g_strdup(temp); - break; - } - - if (response == 1) /* Authorized */ - purple_debug_info("yahoo", "Received authorization from buddy '%s'.\n", who ? who : "(Unknown Buddy)"); - else if (response == 2) { /* Declined */ - purple_debug_info("yahoo", "Received authorization decline from buddy '%s'.\n", who ? who : "(Unknown Buddy)"); - yahoo_buddy_denied_our_add(gc, who, msg); - } else - purple_debug_error("yahoo", "Received unknown authorization response of %d from buddy '%s'.\n", response, who ? who : "(Unknown Buddy)"); - g_free(who); - } - /* Buddy requested authorization to add us. */ - else if (pkt->status == 3) { - struct yahoo_add_request *add_req; - const char *firstname = NULL, *lastname = NULL; - char *temp = NULL; - - add_req = g_new0(struct yahoo_add_request, 1); - add_req->gc = gc; - add_req->fed = YAHOO_FEDERATION_NONE; - - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 4: - if (g_utf8_validate(pair->value, -1, NULL)) { - temp = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 5: - if (g_utf8_validate(pair->value, -1, NULL)) { - add_req->id = g_strdup(pair->value); - } else { - purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 14: - msg = pair->value; - break; - case 216: - if (g_utf8_validate(pair->value, -1, NULL)) { - firstname = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 241: - add_req->fed = strtol(pair->value, NULL, 10); - break; - case 254: - if (g_utf8_validate(pair->value, -1, NULL)) { - lastname = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - - } - l = l->next; - } - switch (add_req->fed) { - case YAHOO_FEDERATION_MSN: - add_req->who = g_strconcat("msn/", temp, NULL); - break; - case YAHOO_FEDERATION_OCS: - add_req->who = g_strconcat("ocs/", temp, NULL); - break; - case YAHOO_FEDERATION_IBM: - add_req->who = g_strconcat("ibm/", temp, NULL); - break; - case YAHOO_FEDERATION_NONE: - default: - add_req->who = g_strdup(temp); - break; - } - - if (add_req->id && add_req->who) { - char *alias = NULL, *dec_msg = NULL; - - if (!purple_account_privacy_check(account, add_req->who)) - { - purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n", - add_req->who); - yahoo_buddy_add_deny_cb(NULL, add_req); - return; - } - - if (msg) - dec_msg = yahoo_string_decode(gc, msg, FALSE); - - if (firstname && lastname) - alias = g_strdup_printf("%s %s", firstname, lastname); - else if (firstname) - alias = g_strdup(firstname); - else if (lastname) - alias = g_strdup(lastname); - - /* DONE! this is almost exactly the same as what MSN does, - * this should probably be moved to the core. - */ - purple_account_request_authorization(account, add_req->who, add_req->id, - alias, dec_msg, - purple_blist_find_buddy(account, add_req->who) != NULL, - yahoo_buddy_add_authorize_cb, - yahoo_buddy_add_deny_cb, - add_req); - g_free(alias); - g_free(dec_msg); - } else { - g_free(add_req->id); - g_free(add_req->who); - g_free(add_req); - } - } else { - purple_debug_error("yahoo", "Received authorization of unknown status (%d).\n", pkt->status); - } -} - -/* I don't think this happens anymore in Version 15 */ -static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt) { - PurpleAccount *account; - struct yahoo_add_request *add_req; - char *msg = NULL; - GSList *l = pkt->hash; - - account = purple_connection_get_account(gc); - - add_req = g_new0(struct yahoo_add_request, 1); - add_req->gc = gc; - - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 1: - if (g_utf8_validate(pair->value, -1, NULL)) { - add_req->id = g_strdup(pair->value); - } else { - purple_debug_warning("yahoo", "yahoo_buddy_added_us " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 3: - if (g_utf8_validate(pair->value, -1, NULL)) { - add_req->who = g_strdup(pair->value); - } else { - purple_debug_warning("yahoo", "yahoo_buddy_added_us " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 15: /* time, for when they add us and we're offline */ - break; - case 14: - msg = pair->value; - break; - } - l = l->next; - } - - if (add_req->id && add_req->who) { - char *dec_msg = NULL; - - if (!purple_account_privacy_check(account, add_req->who)) { - purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n", - add_req->who); - yahoo_buddy_add_deny_cb(NULL, add_req); - return; - } - - if (msg) - dec_msg = yahoo_string_decode(gc, msg, FALSE); - - /* DONE! this is almost exactly the same as what MSN does, - * this should probably be moved to the core. - */ - purple_account_request_authorization(account, add_req->who, add_req->id, - NULL, dec_msg, - purple_blist_find_buddy(account,add_req->who) != NULL, - yahoo_buddy_add_authorize_cb, - yahoo_buddy_add_deny_cb, add_req); - g_free(dec_msg); - } else { - g_free(add_req->id); - g_free(add_req->who); - g_free(add_req); - } -} - -/* I have no idea if this every gets called in version 15 */ -static void yahoo_buddy_denied_our_add_old(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - char *who = NULL; - char *msg = NULL; - GSList *l = pkt->hash; - - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 3: - if (g_utf8_validate(pair->value, -1, NULL)) { - who = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_buddy_denied_our_add_old " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 14: - if (g_utf8_validate(pair->value, -1, NULL)) { - msg = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_buddy_denied_our_add_old " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - } - l = l->next; - } - - yahoo_buddy_denied_our_add(gc, who, msg); -} - -static void yahoo_process_contact(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - switch (pkt->status) { - case 1: - yahoo_process_status(gc, pkt); - return; - case 3: - yahoo_buddy_added_us(gc, pkt); - break; - case 7: - yahoo_buddy_denied_our_add_old(gc, pkt); - break; - default: - break; - } -} - -#define OUT_CHARSET "utf-8" - -static char *yahoo_decode(const char *text) -{ - char *converted = NULL; - char *n, *new; - const char *end, *p; - int i, k; - - n = new = g_malloc(strlen (text) + 1); - end = text + strlen(text); - - for (p = text; p < end; p++, n++) { - if (*p == '\\') { - if (p[1] >= '0' && p[1] <= '7') { - p += 1; - for (i = 0, k = 0; k < 3; k += 1) { - char c = p[k]; - if (c < '0' || c > '7') break; - i *= 8; - i += c - '0'; - } - *n = i; - p += k - 1; - } else { /* bug 959248 */ - /* If we see a \ not followed by an octal number, - * it means that it is actually a \\ with one \ - * already eaten by some unknown function. - * This is arguably broken. - * - * I think wing is wrong here, there is no function - * called that I see that could have done it. I guess - * it is just really sending single \'s. That's yahoo - * for you. - */ - *n = *p; - } - } - else - *n = *p; - } - - *n = '\0'; - - if (strstr(text, "\033$B")) - converted = g_convert(new, n - new, OUT_CHARSET, "iso-2022-jp", NULL, NULL, NULL); - if (!converted) - converted = g_convert(new, n - new, OUT_CHARSET, "iso-8859-1", NULL, NULL, NULL); - g_free(new); - - return converted; -} - -static void yahoo_process_mail(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - PurpleAccount *account = purple_connection_get_account(gc); - YahooData *yd = purple_connection_get_protocol_data(gc); - const char *who = NULL; - const char *email = NULL; - const char *subj = NULL; - const char *yahoo_mail_url = (yd->jp? YAHOOJP_MAIL_URL: YAHOO_MAIL_URL); - int count = 0; - GSList *l = pkt->hash; - - if (!purple_account_get_check_mail(account)) - return; - - while (l) { - struct yahoo_pair *pair = l->data; - if (pair->key == 9) { - count = strtol(pair->value, NULL, 10); - } else if (pair->key == 43) { - if (g_utf8_validate(pair->value, -1, NULL)) { - who = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_mail " - "got non-UTF-8 string for key %d\n", pair->key); - } - } else if (pair->key == 42) { - if (g_utf8_validate(pair->value, -1, NULL)) { - email = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_mail " - "got non-UTF-8 string for key %d\n", pair->key); - } - } else if (pair->key == 18) { - if (g_utf8_validate(pair->value, -1, NULL)) { - subj = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_mail " - "got non-UTF-8 string for key %d\n", pair->key); - } - } - l = l->next; - } - - if (who && subj && email && *email) { - char *dec_who = yahoo_decode(who); - char *dec_subj = yahoo_decode(subj); - char *from = g_strdup_printf("%s (%s)", dec_who, email); - - purple_notify_email(gc, dec_subj, from, purple_account_get_username(account), - yahoo_mail_url, NULL, NULL); - - g_free(dec_who); - g_free(dec_subj); - g_free(from); - } else if (count > 0) { - const char *tos[2] = { purple_account_get_username(account) }; - const char *urls[2] = { yahoo_mail_url }; - - purple_notify_emails(gc, count, FALSE, NULL, NULL, tos, urls, - NULL, NULL); - } -} - -/* We use this structure once while we authenticate */ -struct yahoo_auth_data -{ - PurpleConnection *gc; - char *seed; -}; - -/* This is the y64 alphabet... it's like base64, but has a . and a _ */ -static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._"; - -/* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function - * in util.c, but it is different from the one yahoo uses */ -static void to_y64(char *out, const unsigned char *in, gsize inlen) - /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ -{ - for (; inlen >= 3; inlen -= 3) - { - *out++ = base64digits[in[0] >> 2]; - *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; - *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; - *out++ = base64digits[in[2] & 0x3f]; - in += 3; - } - if (inlen > 0) - { - unsigned char fragment; - - *out++ = base64digits[in[0] >> 2]; - fragment = (in[0] << 4) & 0x30; - if (inlen > 1) - fragment |= in[1] >> 4; - *out++ = base64digits[fragment]; - *out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c]; - *out++ = '-'; - } - *out = '\0'; -} - -static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - PurpleAccount *account = purple_connection_get_account(gc); - const char *name = purple_normalize(account, purple_account_get_username(account)); - PurpleHash *md5_hash; - guchar md5_digest[16]; - gchar base64_string[25]; - struct yahoo_packet *pkt; - - purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage3\n"); - - g_return_if_fail(crypt != NULL); - - md5_hash = purple_md5_hash_new(); - purple_hash_append(md5_hash, (guchar *)crypt, strlen(crypt)); - purple_hash_digest(md5_hash, md5_digest, sizeof(md5_digest)); - - to_y64(base64_string, md5_digest, 16); - - purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status); - pkt = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->current_status, yd->session_id); - - if(yd->cookie_b) { /* send B cookie if we have it */ - yahoo_packet_hash(pkt, "ssssssssss", - 1, name, - 0, name, - 277, yd->cookie_y, - 278, yd->cookie_t, - 307, base64_string, - 244, yd->jp ? YAHOOJP_CLIENT_VERSION_ID : YAHOO_CLIENT_VERSION_ID, - 2, name, - 2, "1", - 59, yd->cookie_b, - 98, purple_account_get_string(account, "room_list_locale", yd->jp ? "jp" : "us"), - 135, yd->jp ? YAHOOJP_CLIENT_VERSION : YAHOO_CLIENT_VERSION); - } else { /* don't try to send an empty B cookie - the server will be mad */ - yahoo_packet_hash(pkt, "sssssssss", - 1, name, - 0, name, - 277, yd->cookie_y, - 278, yd->cookie_t, - 307, base64_string, - 244, yd->jp ? YAHOOJP_CLIENT_VERSION_ID : YAHOO_CLIENT_VERSION_ID, - 2, name, - 2, "1", - 98, purple_account_get_string(account, "room_list_locale", yd->jp ? "jp" : "us"), - 135, yd->jp ? YAHOOJP_CLIENT_VERSION : YAHOO_CLIENT_VERSION); - } - - if (yd->picture_checksum) - yahoo_packet_hash_int(pkt, 192, yd->picture_checksum); - yahoo_packet_send_and_free(pkt, yd); - - g_object_unref(md5_hash); -} - -static void yahoo_auth16_stage2(PurpleHttpConnection *http_conn, - PurpleHttpResponse *response, gpointer _auth_data) -{ - struct yahoo_auth_data *auth_data = _auth_data; - PurpleConnection *gc = auth_data->gc; - YahooData *yd = purple_connection_get_protocol_data(gc); - - int i; - gchar **splits; - int response_no = -1; - char *crumb = NULL; - char *crypt = NULL; - PurpleHttpCookieJar *cookiejar; - - purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n"); - - if (!purple_http_response_is_successful(response)) { - const gchar *error_message = purple_http_response_get_error(response); - purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); - g_free(auth_data->seed); - g_free(auth_data); - return; - } - - splits = g_strsplit(purple_http_response_get_data(response, NULL), - "\r\n", -1); - - cookiejar = purple_http_conn_get_cookie_jar(http_conn); - yd->cookie_b = g_strdup(purple_http_cookie_jar_get(cookiejar, "B")); - yd->cookie_t = g_strdup(purple_http_cookie_jar_get(cookiejar, "T")); - yd->cookie_y = g_strdup(purple_http_cookie_jar_get(cookiejar, "Y")); - - i = 0; - while (splits[i]) { - /* I'm not exactly a fan of the magic numbers, but it's obvious, - * so no sense in wasting a bajillion vars or calls to strlen */ - - if (i == 0 && g_ascii_isdigit(splits[i][0])) { - response_no = strtol(splits[i], NULL, 10); - purple_debug_info("yahoo", "Got auth16 stage 2 response code: %d\n", - response_no); - } else if (strncmp(splits[i], "crumb=", 6) == 0) { - crumb = g_strdup(&splits[i][6]); - - if (purple_debug_is_unsafe()) - purple_debug_info("yahoo", "Got crumb: %s\n", crumb); - } - i++; - } - - g_strfreev(splits); - - if (crumb == NULL) - response_no = -1; - - if(response_no != 0) { - /* Some error in the login process */ - PurpleConnectionError error; - char *error_reason = NULL; - - switch (response_no) { - case -1: - /* Some error in the received stream */ - error_reason = g_strdup(_("Received invalid data")); - error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - break; - case 100: - /* Unknown error */ - error_reason = g_strdup(_("Unknown error")); - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; - break; - default: - /* if we have everything we need, why not try to login irrespective of response */ - if ((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) { -#if 0 - try_login_on_error = TRUE; -#endif - break; - } - error_reason = g_strdup(_("Unknown error")); - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; - break; - } - if (error_reason) { - purple_debug_error("yahoo", "Authentication error: %s. " - "Code %d\n", error_reason, response_no); - purple_connection_error(gc, error, error_reason); - g_free(error_reason); - g_free(crumb); - g_free(auth_data->seed); - g_free(auth_data); - return; - } - } - - crypt = g_strconcat(crumb, auth_data->seed, NULL); - yahoo_auth16_stage3(gc, crypt); - g_free(crypt); - g_free(crumb); - g_free(auth_data->seed); - g_free(auth_data); -} - -static void yahoo_auth16_stage1_cb(PurpleHttpConnection *http_conn, - PurpleHttpResponse *response, gpointer _auth_data) -{ - struct yahoo_auth_data *auth_data = _auth_data; - PurpleConnection *gc = auth_data->gc; - YahooData *yd = purple_connection_get_protocol_data(gc); - - purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n"); - - if (!purple_http_response_is_successful(response)) { - const gchar *error_message = purple_http_response_get_error(response); - purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); - g_free(auth_data->seed); - g_free(auth_data); - return; - } else { - PurpleAccount *account = purple_connection_get_account(gc); - gchar **split_data = g_strsplit(purple_http_response_get_data( - response, NULL), "\r\n", -1); - int totalelements = 0; - int response_no = -1; - char *token = NULL; - - totalelements = g_strv_length(split_data); - - if(totalelements == 1) { /* Received an error code */ - response_no = strtol(split_data[0], NULL, 10); - } else if(totalelements == 2 || totalelements == 3 ) { /* received valid data */ - response_no = strtol(split_data[0], NULL, 10); - token = g_strdup(split_data[1] + strlen("ymsgr=")); - } else { /* It looks like a transparent proxy has returned a document we don't want */ - response_no = -1; - } - - g_strfreev(split_data); - - if(response_no != 0) { - /* Some error in the login process */ - PurpleConnectionError error; - char *error_reason; - - switch(response_no) { - case -1: - /* Some error in the received stream */ - error_reason = g_strdup(_("Received invalid data")); - error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - break; - case 1212: - /* Password incorrect */ - /* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */ - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL, NULL, NULL); - error_reason = g_strdup(_("Incorrect password")); - error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - case 1213: - /* security lock from too many failed login attempts */ - error_reason = g_strdup(_("Account locked: Too many failed login " - "attempts. Logging into the Yahoo! website may fix this.")); - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; - break; - case 1235: - /* the username does not exist */ - error_reason = g_strdup(_("Username does not exist")); - error = PURPLE_CONNECTION_ERROR_INVALID_USERNAME; - break; - case 1214: - /* indicates a lock of some description */ - error_reason = g_strdup(_("Account locked: Unknown reason. Logging " - "into the Yahoo! website may fix this.")); - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; - break; - case 1236: - /* indicates a lock due to logging in too frequently */ - error_reason = g_strdup(_("Account locked: You have been logging in too " - "frequently. Wait a few minutes before trying to connect " - "again. Logging into the Yahoo! website may help.")); - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; - break; - case 100: - /* username or password missing */ - error_reason = g_strdup(_("Username or password missing")); - error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - default: - /* Unknown error! */ - error_reason = g_strdup_printf(_("Unknown error (%d)"), response_no); - error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; - break; - } - purple_debug_error("yahoo", "Authentication error: %s. Code %d\n", - error_reason, response_no); - purple_connection_error(gc, error, error_reason); - g_free(error_reason); - g_free(auth_data->seed); - g_free(auth_data); - g_free(token); - } - else { - /* OK to login, correct information provided */ - gboolean yahoojp = yahoo_is_japan(account); - PurpleHttpRequest *req; - - req = purple_http_request_new(NULL); - purple_http_request_set_url_printf(req, - yahoojp ? YAHOOJP_LOGIN_URL : YAHOO_LOGIN_URL, - token); - purple_http_request_header_set(req, "User-Agent", - YAHOO_CLIENT_USERAGENT); - purple_http_connection_set_add(yd->http_reqs, - purple_http_request(gc, req, - yahoo_auth16_stage2, auth_data)); - purple_http_request_unref(req); - - g_free(token); - } - } -} - -static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - PurpleAccount *account = purple_connection_get_account(gc); - PurpleHttpRequest *req; - struct yahoo_auth_data *auth_data = NULL; - char *encoded_username; - char *encoded_password; - gboolean yahoojp = yahoo_is_japan(account); - - purple_debug_info("yahoo", "Authentication: In yahoo_auth16_stage1\n"); - - if(!purple_ssl_is_supported()) { - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("SSL support unavailable")); - return; - } - - auth_data = g_new0(struct yahoo_auth_data, 1); - auth_data->gc = gc; - auth_data->seed = g_strdup(seed); - - encoded_username = g_strdup(purple_url_encode(purple_account_get_username(purple_connection_get_account(gc)))); - encoded_password = g_strdup(purple_url_encode(purple_connection_get_password(gc))); - - req = purple_http_request_new(NULL); - purple_http_request_set_url_printf(req, - yahoojp ? YAHOOJP_TOKEN_URL : YAHOO_TOKEN_URL, - encoded_username, encoded_password, purple_url_encode(seed)); - purple_http_request_header_set(req, "User-Agent", YAHOO_CLIENT_USERAGENT); - purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc, - req, yahoo_auth16_stage1_cb, auth_data)); - purple_http_request_unref(req); - - purple_str_wipe(encoded_password); - g_free(encoded_username); -} - -static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - char *seed = NULL; - GSList *l = pkt->hash; - int m = 0; - gchar *buf; - - while (l) { - struct yahoo_pair *pair = l->data; - /* (pair->key == 1) -> sn */ - if (pair->key == 94) { - if (g_utf8_validate(pair->value, -1, NULL)) { - seed = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_auth " - "got non-UTF-8 string for key %d\n", pair->key); - } - } else if (pair->key == 13) { - m = atoi(pair->value); - } - l = l->next; - } - - if (seed) { - switch (m) { - case 0: - /* used to be for really old auth routine, dont support now */ - case 1: - case 2: /* Yahoo ver 16 authentication */ - yahoo_auth16_stage1(gc, seed); - break; - default: - { - GHashTable *ui_info = purple_core_get_ui_info(); - - buf = g_strdup_printf(_("The Yahoo server has requested the use of an unrecognized " - "authentication method. You will probably not be able " - "to successfully sign on to Yahoo. Check %s for updates."), - ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); - purple_notify_error(gc, "", - _("Failed Yahoo! Authentication"), buf, - purple_request_cpar_from_connection(gc)); - g_free(buf); - yahoo_auth16_stage1(gc, seed); /* Can't hurt to try it anyway. */ - break; - } - } - } -} - -static void ignore_buddy(PurpleBuddy *buddy) { - PurpleGroup *group; - PurpleAccount *account; - gchar *name; - - if (!buddy) - return; - - group = purple_buddy_get_group(buddy); - name = g_strdup(purple_buddy_get_name(buddy)); - account = purple_buddy_get_account(buddy); - - purple_debug_info("yahoo", "blist: Removing '%s' from buddy list.\n", name); - purple_account_remove_buddy(account, buddy, group); - purple_blist_remove_buddy(buddy); - - serv_add_deny(purple_account_get_connection(account), name); - - g_free(name); -} - -static void keep_buddy(PurpleBuddy *b) -{ - purple_account_privacy_deny_remove(purple_buddy_get_account(b), - purple_buddy_get_name(b), 1); -} - -static void yahoo_process_ignore(PurpleConnection *gc, struct yahoo_packet *pkt) { - PurpleBuddy *b; - GSList *l; - gchar *who = NULL; - gchar buf[BUF_LONG]; - gboolean ignore = TRUE; - gint status = 0; - - for (l = pkt->hash; l; l = l->next) { - struct yahoo_pair *pair = l->data; - switch (pair->key) { - case 0: - if (g_utf8_validate(pair->value, -1, NULL)) { - who = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_ignore " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - /* 1 -> me */ - case 13: - /* 1 == ignore, 2 == unignore */ - ignore = (strtol(pair->value, NULL, 10) == 1); - break; - case 66: - status = strtol(pair->value, NULL, 10); - break; - default: - break; - } - } - - /* - * status - * 0 - ok - * 2 - already in ignore list, could not add - * 3 - not in ignore list, could not delete - * 12 - is a buddy, could not add (and possibly also a not-in-ignore list condition?) - */ - switch (status) { - case 12: - purple_debug_info("yahoo", "Server reported \"is a buddy\" for %s while %s", - who, (ignore ? "ignoring" : "unignoring")); - - if (ignore) { - b = purple_blist_find_buddy(purple_connection_get_account(gc), who); - g_snprintf(buf, sizeof(buf), _("You have tried to ignore %s, but the " - "user is on your buddy list. Clicking \"Yes\" " - "will remove and ignore the buddy."), who); - purple_request_yes_no(gc, NULL, - _("Ignore buddy?"), buf, 0, - purple_request_cpar_from_connection(gc), - b, G_CALLBACK(ignore_buddy), - G_CALLBACK(keep_buddy)); - break; - } - case 2: - purple_debug_info("yahoo", "Server reported that %s is already in the ignore list.\n", - who); - break; - case 3: - purple_debug_info("yahoo", "Server reported that %s is not in the ignore list; could not delete\n", - who); - case 0: - default: - break; - } -} - -#if TRY_WEBMESSENGER_LOGIN - -static gboolean -yahoo_try_webmessenger_login(PurpleConnection *gc) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - PurpleHttpRequest *req; - - if (yd->wm) - return FALSE; - - yd->wm = TRUE; - if (yd->fd >= 0) - close(yd->fd); - if (yd->inpa) { - purple_input_remove(yd->inpa); - yd->inpa = 0; - } - - req = purple_http_request_new(WEBMESSENGER_URL); - purple_http_request_header_set(req, "User-Agent", "Purple/" VERSION); - purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc, - req, yahoo_login_page_cb, NULL)); - purple_http_request_unref(req); - - return TRUE; -} - -#endif /* TRY_WEBMESSENGER_LOGIN */ - -static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - GSList *l = pkt->hash; - int err = 0; - char *msg; - char *url = NULL; - char *fullmsg; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR; - - while (l) { - struct yahoo_pair *pair = l->data; - - if (pair->key == 66) { - err = strtol(pair->value, NULL, 10); - } else if (pair->key == 20) { - if (g_utf8_validate(pair->value, -1, NULL)) { - url = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_authresp " - "got non-UTF-8 string for key %d\n", pair->key); - } - } - - l = l->next; - } - - switch (err) { - case 0: - msg = g_strdup(_("Unknown error")); - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - break; - case 3: - msg = g_strdup(_("Username does not exist")); - reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME; - break; - case 13: -#if TRY_WEBMESSENGER_LOGIN - if (yahoo_try_webmessenger_login(gc)) - return; -#else - purple_debug_info("yahoo", "Web messenger login is disabled\n"); -#endif /* TRY_WEBMESSENGER_LOGIN */ - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL, NULL, NULL); - - msg = g_strdup(_("Invalid username or password")); - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - case 14: - msg = g_strdup(_("Your account has been locked due to too many failed login attempts." - " Please try logging into the Yahoo! website.")); - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - case 52: - /* See #9660. As much as we know, reconnecting shouldn't hurt */ - purple_debug_info("yahoo", "Got error 52, Set to autoreconnect\n"); - msg = g_strdup(_("Unknown error 52. Reconnecting should fix this.")); - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - break; - case 1013: - msg = g_strdup(_("Error 1013: The username you have entered is invalid." - " The most common cause of this error is entering your email" - " address instead of your Yahoo! ID.")); - reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME; - break; - default: - msg = g_strdup_printf(_("Unknown error number %d. Logging into the Yahoo! website may fix this."), err); - } - - if (url) - fullmsg = g_strdup_printf("%s\n%s", msg, url); - else - fullmsg = g_strdup(msg); - - purple_connection_error(gc, reason, fullmsg); - g_free(msg); - g_free(fullmsg); -} - -static void yahoo_process_addbuddy(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - int err = 0; - char *who = NULL; - char *temp = NULL; - char *group = NULL; - char *decoded_group; - char *buf; - YahooFriend *f; - GSList *l = pkt->hash; - YahooData *yd = purple_connection_get_protocol_data(gc); - YahooFederation fed = YAHOO_FEDERATION_NONE; - - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 66: - err = strtol(pair->value, NULL, 10); - break; - case 7: - if (g_utf8_validate(pair->value, -1, NULL)) { - temp = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_addbuddy " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 65: - group = pair->value; - break; - case 241: - fed = strtol(pair->value, NULL, 10); - break; - } - - l = l->next; - } - - if (!temp) - return; - if (!group) - group = ""; - - switch (fed) { - case YAHOO_FEDERATION_MSN: - who = g_strconcat("msn/", temp, NULL); - break; - case YAHOO_FEDERATION_OCS: - who = g_strconcat("ocs/", temp, NULL); - break; - case YAHOO_FEDERATION_IBM: - who = g_strconcat("ibm/", temp, NULL); - break; - case YAHOO_FEDERATION_NONE: - default: - who = g_strdup(temp); - break; - } - - if (!err || (err == 2)) { /* 0 = ok, 2 = already on serv list */ - f = yahoo_friend_find_or_new(gc, who); - yahoo_update_status(gc, who, f); - f->fed = fed; - - if( !g_hash_table_lookup(yd->peers, who) ) { - /* we are not connected as client, so set friend to not connected */ - if(fed) - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT); - else { - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); - f->p2p_packet_sent = 0; - } - } - else /* we are already connected. set friend to YAHOO_P2PSTATUS_WE_ARE_CLIENT */ - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT); - g_free(who); - return; - } - - decoded_group = yahoo_string_decode(gc, group, FALSE); - buf = g_strdup_printf(_("Unable to add buddy %s to group %s to the server list on account %s."), - who, decoded_group, purple_connection_get_display_name(gc)); - if (!purple_conversation_present_error(who, purple_connection_get_account(gc), buf)) - purple_notify_error(gc, NULL, _("Unable to add buddy to server list"), buf, - purple_request_cpar_from_connection(gc)); - g_free(buf); - g_free(decoded_group); - g_free(who); -} - -/* write pkt to the source */ -static void yahoo_p2p_write_pkt(gint source, struct yahoo_packet *pkt) -{ - size_t pkt_len; - gssize written; - guchar *raw_packet; - - /*build the raw packet and send it to the host*/ - pkt_len = yahoo_packet_build(pkt, 0, 0, 0, &raw_packet); - written = write(source, raw_packet, pkt_len); - if (written < 0 || (gsize)written != pkt_len) - purple_debug_warning("yahoo","p2p: couldn't write to the source\n"); - g_free(raw_packet); -} - -static void yahoo_p2p_keepalive_cb(gpointer key, gpointer value, gpointer user_data) -{ - struct yahoo_p2p_data *p2p_data = value; - PurpleConnection *gc = user_data; - struct yahoo_packet *pkt_to_send; - PurpleAccount *account; - YahooData *yd = purple_connection_get_protocol_data(gc); - - account = purple_connection_get_account(gc); - - pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash(pkt_to_send, "ssisi", - 4, purple_normalize(account, purple_account_get_username(account)), - 5, p2p_data->host_username, - 241, 0, /* Protocol identifier */ - 49, "PEERTOPEER", - 13, 7); - yahoo_p2p_write_pkt(p2p_data->source, pkt_to_send); - - yahoo_packet_free(pkt_to_send); -} - -static gboolean yahoo_p2p_keepalive(gpointer data) -{ - PurpleConnection *gc = data; - YahooData *yd = purple_connection_get_protocol_data(gc); - - g_hash_table_foreach(yd->peers, yahoo_p2p_keepalive_cb, gc); - - return TRUE; -} - -/* destroy p2p_data associated with a peer and close p2p connection. - * g_hash_table_remove() calls this function to destroy p2p_data associated with the peer, - * call g_hash_table_remove() instead of this fucntion if peer has an entry in the table */ -static void yahoo_p2p_disconnect_destroy_data(gpointer data) -{ - struct yahoo_p2p_data *p2p_data; - YahooFriend *f; - - if(!(p2p_data = data)) - return ; - - /* If friend, set him not connected */ - f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username); - if (f) - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); - - if(p2p_data->source >= 0) - close(p2p_data->source); - if (p2p_data->input_event > 0) - purple_input_remove(p2p_data->input_event); - g_free(p2p_data->host_ip); - g_free(p2p_data->host_username); - g_free(p2p_data); -} - -/* exchange of initial p2pfilexfer packets, service type YAHOO_SERVICE_P2PFILEXFER */ -static void yahoo_p2p_process_p2pfilexfer(gpointer data, gint source, struct yahoo_packet *pkt) -{ - struct yahoo_p2p_data *p2p_data; - char *who = NULL; - GSList *l = pkt->hash; - struct yahoo_packet *pkt_to_send; - PurpleAccount *account; - int val_13_to_send = 0; - YahooData *yd; - YahooFriend *f; - - if(!(p2p_data = data)) - return ; - - yd = purple_connection_get_protocol_data(p2p_data->gc); - - /* lets see whats in the packet */ - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 4: - if (g_utf8_validate(pair->value, -1, NULL)) { - who = pair->value; - if(strncmp(who, p2p_data->host_username, strlen(p2p_data->host_username)) != 0) { - /* from whom are we receiving the packets ?? */ - purple_debug_warning("yahoo","p2p: received data from wrong user\n"); - return; - } - } else { - purple_debug_warning("yahoo", "yahoo_p2p_process_p2pfilexfer " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 13: - p2p_data->val_13 = strtol(pair->value, NULL, 10); /* Value should be 5-7 */ - break; - /* case 5, 49 look laters, no use right now */ - } - l = l->next; - } - - account = purple_connection_get_account(p2p_data->gc); - - /* key_13: sort of a counter. - * WHEN WE ARE CLIENT: yahoo server sends val_13 = 0, we send to peer val_13 = 1, receive back val_13 = 5, - * we send val_13=6, receive val_13=7, we send val_13=7, HALT. Keep sending val_13 = 7 as keep alive. - * WHEN WE ARE SERVER: we send val_13 = 0 to yahoo server, peer sends us val_13 = 1, we send val_13 = 5, - * receive val_13 = 6, send val_13 = 7, receive val_13 = 7. HALT. Keep sending val_13 = 7 as keep alive. */ - - switch(p2p_data->val_13) { - case 1 : val_13_to_send = 5; break; - case 5 : val_13_to_send = 6; break; - case 6 : val_13_to_send = 7; break; - case 7 : if( g_hash_table_lookup(yd->peers, p2p_data->host_username) ) - return; - val_13_to_send = 7; break; - default: purple_debug_warning("yahoo","p2p:Unknown value for key 13\n"); - return; - } - - /* Build the yahoo packet */ - pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash(pkt_to_send, "ssisi", - 4, purple_normalize(account, purple_account_get_username(account)), - 5, p2p_data->host_username, - 241, 0, /* Protocol identifier */ - 49, "PEERTOPEER", - 13, val_13_to_send); - - /* build the raw packet and send it to the host */ - yahoo_p2p_write_pkt(source, pkt_to_send); - yahoo_packet_free(pkt_to_send); - - if( val_13_to_send == 7 ) - if( !g_hash_table_lookup(yd->peers, p2p_data->host_username) ) { - g_hash_table_insert(yd->peers, g_strdup(p2p_data->host_username), p2p_data); - /* If the peer is a friend, set him connected */ - f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username); - if (f) { - if(p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) { - p2p_data->session_id = f->session_id; - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_SERVER); - } - else - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT); - } - } -} - -/* callback function associated with receiving of data, not considering receipt of multiple YMSG packets in a single TCP packet */ -static void yahoo_p2p_read_pkt_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - guchar buf[1024]; /* is it safe to assume a fixed array length of 1024 ?? */ - int len; - int pos = 0; - int pktlen; - struct yahoo_packet *pkt; - guchar *start; - struct yahoo_p2p_data *p2p_data; - YahooData *yd; - - if(!(p2p_data = data)) - return ; - yd = purple_connection_get_protocol_data(p2p_data->gc); - - len = read(source, buf, sizeof(buf)); - if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) - return ; /* No Worries*/ - else if (len <= 0) - { - purple_debug_warning("yahoo","p2p: Error in connection, or host disconnected\n"); - /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ - if( g_hash_table_lookup(yd->peers, p2p_data->host_username) ) - g_hash_table_remove(yd->peers,p2p_data->host_username); - else - yahoo_p2p_disconnect_destroy_data(data); - return; - } - - /* TODO: It looks like there's a bug here (and above) where an incorrect - * assumtion is being made that the buffer will be added to when this - * is next called, but that's not really the case! */ - if(len < YAHOO_PACKET_HDRLEN) - return; - - if(strncmp((char *)buf, "YMSG", 4) != 0) { - /* Not a YMSG packet */ - purple_debug_warning("yahoo", "p2p: Got something other than YMSG packet\n"); - - start = (guchar *) g_strstr_len((char *) buf + 1, len - 1 ,"YMSG"); - if (start == NULL) { - /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ - if (g_hash_table_lookup(yd->peers, p2p_data->host_username)) - g_hash_table_remove(yd->peers, p2p_data->host_username); - else - yahoo_p2p_disconnect_destroy_data(data); - return; - } - purple_debug_warning("yahoo","p2p: Got something other than YMSG packet\n"); - - len -= (start - buf); - g_memmove(buf, start, len); - } - - pos += 4; /* YMSG */ - pos += 2; - pos += 2; - - pktlen = yahoo_get16(buf + pos); pos += 2; - if (len < (YAHOO_PACKET_HDRLEN + pktlen)) { - purple_debug_error("yahoo", "p2p: packet length(%d) > buffer length(%d)\n", - pktlen, (len - pos)); - /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ - if (g_hash_table_lookup(yd->peers, p2p_data->host_username)) - g_hash_table_remove(yd->peers, p2p_data->host_username); - else - yahoo_p2p_disconnect_destroy_data(data); - return; - } else - purple_debug_misc("yahoo", "p2p: %d bytes to read\n", pktlen); - - pkt = yahoo_packet_new(0, 0, 0); - pkt->service = yahoo_get16(buf + pos); pos += 2; - pkt->status = yahoo_get32(buf + pos); pos += 4; - pkt->id = yahoo_get32(buf + pos); pos += 4; - - purple_debug_misc("yahoo", "p2p: Yahoo Service: 0x%02x Status: %d\n",pkt->service, pkt->status); - yahoo_packet_read(pkt, buf + pos, pktlen); - - /* packet processing */ - switch(pkt->service) { - case YAHOO_SERVICE_P2PFILEXFER: - yahoo_p2p_process_p2pfilexfer(data, source, pkt); - break; - case YAHOO_SERVICE_MESSAGE: - yahoo_process_message(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P); - break; - case YAHOO_SERVICE_NOTIFY: - yahoo_process_notify(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P); - break; - default: - purple_debug_warning("yahoo","p2p: p2p service %d Unhandled\n",pkt->service); - } - - yahoo_packet_free(pkt); -} - -static void yahoo_p2p_server_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - int acceptfd; - struct yahoo_p2p_data *p2p_data; - YahooData *yd; - - if(!(p2p_data = data)) - return ; - yd = purple_connection_get_protocol_data(p2p_data->gc); - - acceptfd = accept(source, NULL, 0); - if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) - return; - else if(acceptfd == -1) { - purple_debug_warning("yahoo","yahoo_p2p_server_send_connected_cb: accept: %s\n", g_strerror(errno)); - yahoo_p2p_disconnect_destroy_data(data); - return; - } - - /* remove timeout */ - if (yd->yahoo_p2p_server_timeout_handle) { - purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle); - yd->yahoo_p2p_server_timeout_handle = 0; - } - - /* remove watcher and close p2p server */ - if (yd->yahoo_p2p_server_watcher) { - purple_input_remove(yd->yahoo_p2p_server_watcher); - yd->yahoo_p2p_server_watcher = 0; - } - if (yd->yahoo_local_p2p_server_fd >= 0) { - close(yd->yahoo_local_p2p_server_fd); - yd->yahoo_local_p2p_server_fd = -1; - } - - /* Add an Input Read event to the file descriptor */ - p2p_data->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data); - p2p_data->source = acceptfd; -} - -static gboolean yahoo_cancel_p2p_server_listen_cb(gpointer data) -{ - struct yahoo_p2p_data *p2p_data; - YahooData *yd; - - if(!(p2p_data = data)) - return FALSE; - - yd = purple_connection_get_protocol_data(p2p_data->gc); - - purple_debug_warning("yahoo","yahoo p2p server timeout, peer failed to connect\n"); - yahoo_p2p_disconnect_destroy_data(data); - purple_input_remove(yd->yahoo_p2p_server_watcher); - yd->yahoo_p2p_server_watcher = 0; - close(yd->yahoo_local_p2p_server_fd); - yd->yahoo_local_p2p_server_fd = -1; - yd->yahoo_p2p_server_timeout_handle = 0; - - return FALSE; -} - -static void yahoo_p2p_server_listen_cb(int listenfd, gpointer data) -{ - struct yahoo_p2p_data *p2p_data; - YahooData *yd; - - if(!(p2p_data = data)) - return ; - - yd = purple_connection_get_protocol_data(p2p_data->gc); - yd->listen_data = NULL; - - if(listenfd == -1) { - purple_debug_warning("yahoo","p2p: error starting p2p server\n"); - yahoo_p2p_disconnect_destroy_data(data); - return; - } - - /* Add an Input Read event to the file descriptor */ - yd->yahoo_local_p2p_server_fd = listenfd; - yd->yahoo_p2p_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_server_send_connected_cb,data); - - /* add timeout */ - yd->yahoo_p2p_server_timeout_handle = purple_timeout_add_seconds(YAHOO_P2P_SERVER_TIMEOUT, yahoo_cancel_p2p_server_listen_cb, data); -} - -/* send p2p pkt containing our encoded ip, asking peer to connect to us */ -void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13) -{ - const char *public_ip; - guint32 temp[4]; - guint32 ip; - char temp_str[100]; - gchar *base64_ip = NULL; - YahooFriend *f; - struct yahoo_packet *pkt; - PurpleAccount *account; - YahooData *yd = purple_connection_get_protocol_data(gc); - struct yahoo_p2p_data *p2p_data; - const char *norm_username; - - f = yahoo_friend_find(gc, who); - account = purple_connection_get_account(gc); - - /* Do not send invitation if already listening for other connection */ - if(yd->yahoo_local_p2p_server_fd >= 0) - return; - - /* One shouldn't try to connect to self */ - if( strcmp(purple_normalize(account, purple_account_get_username(account)), who) == 0) - return; - - /* send packet to only those friends who arent p2p connected and to whom we havent already sent. Do not send if this condition doesn't hold good */ - if( !( f && (yahoo_friend_get_p2p_status(f) == YAHOO_P2PSTATUS_NOT_CONNECTED) && (f->p2p_packet_sent == 0)) ) - return; - - /* Dont send p2p packet to buddies of other protocols */ - if(f->fed) - return; - - /* Finally, don't try to connect to buddies not online or on sms */ - if( (f->status == YAHOO_STATUS_OFFLINE) || f->sms ) - return; - - public_ip = purple_network_get_public_ip(); - if( (sscanf(public_ip, "%u.%u.%u.%u", &temp[0], &temp[1], &temp[2], &temp[3])) !=4 ) - return ; - - ip = (temp[3] << 24) | (temp[2] <<16) | (temp[1] << 8) | temp[0]; - sprintf(temp_str, "%d", ip); - base64_ip = purple_base64_encode( (guchar *)temp_str, strlen(temp_str) ); - - norm_username = purple_normalize(account, purple_account_get_username(account)); - pkt = yahoo_packet_new(YAHOO_SERVICE_PEERTOPEER, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pkt, "sssissis", - 1, norm_username, - 4, norm_username, - 12, base64_ip, /* base64 encode ip */ - 61, 0, /* To-do : figure out what is 61 for?? */ - 2, "", - 5, who, - 13, val_13, - 49, "PEERTOPEER"); - yahoo_packet_send_and_free(pkt, yd); - - f->p2p_packet_sent = 1; /* set p2p_packet_sent to sent */ - - p2p_data = g_new0(struct yahoo_p2p_data, 1); - - p2p_data->gc = gc; - p2p_data->host_ip = NULL; - p2p_data->host_username = g_strdup(who); - p2p_data->val_13 = val_13; - p2p_data->connection_type = YAHOO_P2P_WE_ARE_SERVER; - p2p_data->source = -1; - - /* FIXME: If the port is already used, purple_network_listener returns NULL and old listener won't be canceled - * in yahoo_close function. */ - if (yd->listen_data) - purple_debug_warning("yahoo","p2p: Failed to create p2p server - server already exists\n"); - else { - yd->listen_data = purple_network_listen(YAHOO_PAGER_PORT_P2P, AF_UNSPEC, SOCK_STREAM, TRUE, yahoo_p2p_server_listen_cb, p2p_data); - if (yd->listen_data == NULL) - purple_debug_warning("yahoo","p2p: Failed to created p2p server\n"); - } - - g_free(base64_ip); -} - -/* function called when connection to p2p host is setup */ -static void yahoo_p2p_init_cb(gpointer data, gint source, const gchar *error_message) -{ - struct yahoo_p2p_data *p2p_data; - struct yahoo_packet *pkt_to_send; - PurpleAccount *account; - YahooData *yd; - - p2p_data = data; - yd = purple_connection_get_protocol_data(p2p_data->gc); - - if(error_message != NULL) { - purple_debug_warning("yahoo","p2p: %s\n",error_message); - yahoo_send_p2p_pkt(p2p_data->gc, p2p_data->host_username, 2);/* send p2p init packet with val_13=2 */ - - yahoo_p2p_disconnect_destroy_data(p2p_data); - return; - } - - /* Add an Input Read event to the file descriptor */ - p2p_data->input_event = purple_input_add(source, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data); - p2p_data->source = source; - - account = purple_connection_get_account(p2p_data->gc); - - /* Build the yahoo packet */ - pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash(pkt_to_send, "ssisi", - 4, purple_normalize(account, purple_account_get_username(account)), - 5, p2p_data->host_username, - 241, 0, /* Protocol identifier */ - 49, "PEERTOPEER", - 13, 1); /* we receive key13= 0 or 2, we send key13=1 */ - - yahoo_p2p_write_pkt(source, pkt_to_send); /* build raw packet and send */ - yahoo_packet_free(pkt_to_send); -} - -static void yahoo_process_p2p(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - GSList *l = pkt->hash; - char *who = NULL; - char *base64 = NULL; - guchar *decoded; - gsize len; - gint val_13 = 0; - gint val_11 = 0; - PurpleAccount *account; - YahooFriend *f; - - /* if status is not YAHOO_STATUS_BRB or YAHOO_STATUS_P2P, the packet bounced back, - * so it contains our own ip */ - if(pkt->status != YAHOO_STATUS_BRB && pkt->status != YAHOO_STATUS_P2P) - return ; - - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 5: - /* our identity */ - break; - case 4: - if (g_utf8_validate(pair->value, -1, NULL)) { - who = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_p2p " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 1: - /* who again, the master identity this time? */ - break; - case 12: - if (g_utf8_validate(pair->value, -1, NULL)) { - base64 = pair->value; - /* so, this is an ip address. in base64. decoded it's in ascii. - after strtol, it's in reversed byte order. Who thought this up?*/ - } else { - purple_debug_warning("yahoo", "yahoo_process_p2p " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 13: - val_13 = strtol(pair->value, NULL, 10); - break; - case 11: - val_11 = strtol(pair->value, NULL, 10); /* session id of peer */ - if( (f = yahoo_friend_find(gc, who)) ) - f->session_id = val_11; - break; - /* - TODO: figure these out - yahoo: Key: 61 Value: 0 - yahoo: Key: 2 Value: - yahoo: Key: 13 Value: 0 packet count ?? - yahoo: Key: 49 Value: PEERTOPEER - yahoo: Key: 140 Value: 1 - */ - - } - - l = l->next; - } - - if (base64) { - guint32 ip; - YahooFriend *f; - char *host_ip, *tmp; - struct yahoo_p2p_data *p2p_data; - - decoded = purple_base64_decode(base64, &len); - if (decoded == NULL) { - purple_debug_info("yahoo","p2p: Unable to decode base64 IP (%s) \n", base64); - return; - } - tmp = purple_str_binary_to_ascii(decoded, len); - purple_debug_info("yahoo", "Got P2P service packet (from server): who = %s, ip = %s\n", who, tmp); - g_free(tmp); - - ip = strtol((gchar *)decoded, NULL, 10); - g_free(decoded); - host_ip = g_strdup_printf("%u.%u.%u.%u", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, - (ip >> 24) & 0xff); - f = yahoo_friend_find(gc, who); - if (f) - yahoo_friend_set_ip(f, host_ip); - purple_debug_info("yahoo", "IP : %s\n", host_ip); - - account = purple_connection_get_account(gc); - - if(val_11==0) { - if(!f) - return; - else - val_11 = f->session_id; - } - - p2p_data = g_new0(struct yahoo_p2p_data, 1); - p2p_data->host_username = g_strdup(who); - p2p_data->val_13 = val_13; - p2p_data->session_id = val_11; - p2p_data->host_ip = host_ip; - p2p_data->gc = gc; - p2p_data->connection_type = YAHOO_P2P_WE_ARE_CLIENT; - p2p_data->source = -1; - - /* connect to host */ - if((purple_proxy_connect(gc, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) { - purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip); - g_free(p2p_data->host_ip); - g_free(p2p_data->host_username); - g_free(p2p_data); - } - } -} - -static void yahoo_process_audible(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - PurpleAccount *account; - char *who = NULL, *msg = NULL, *id = NULL; - GSList *l = pkt->hash; - - account = purple_connection_get_account(gc); - - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 4: - if (g_utf8_validate(pair->value, -1, NULL)) { - who = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_audible " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 5: - /* us */ - break; - case 230: - /* the audible, in foo.locale.bar.baz format - eg: base.tw.smiley.smiley43 */ - if (g_utf8_validate(pair->value, -1, NULL)) { - id = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_audible " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 231: - /* the text of the audible */ - if (g_utf8_validate(pair->value, -1, NULL)) { - msg = pair->value; - } else { - purple_debug_warning("yahoo", "yahoo_process_audible " - "got non-UTF-8 string for key %d\n", pair->key); - } - break; - case 232: - /* SHA-1 hash of audible SWF file (eg: 4e8691499d9c0fb8374478ff9720f4a9ea4a4915) */ - break; - } - - l = l->next; - } - - if (!msg) - msg = id; - if (!who || !msg) - return; - if (!g_utf8_validate(msg, -1, NULL)) { - purple_debug_misc("yahoo", "Warning, nonutf8 audible, ignoring!\n"); - return; - } - if (!purple_account_privacy_check(account, who)) { - purple_debug_misc("yahoo", "Audible message from %s for %s dropped!\n", - purple_account_get_username(account), who); - return; - } - if (id) { - /* "http://l.yimg.com/pu/dl/aud/"+locale+"/"+id+".swf" */ - char **audible_locale = g_strsplit(id, ".", 0); - char *buf = g_strdup_printf(_("[ Audible %s/%s/%s.swf ] %s"), YAHOO_AUDIBLE_URL, audible_locale[1], id, msg); - g_strfreev(audible_locale); - - serv_got_im(gc, who, buf, 0, time(NULL)); - g_free(buf); - } else - serv_got_im(gc, who, msg, 0, time(NULL)); -} - -static void yahoo_packet_process(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - switch (pkt->service) { - case YAHOO_SERVICE_LOGON: - case YAHOO_SERVICE_LOGOFF: - case YAHOO_SERVICE_ISAWAY: - case YAHOO_SERVICE_ISBACK: - case YAHOO_SERVICE_GAMELOGON: - case YAHOO_SERVICE_GAMELOGOFF: - case YAHOO_SERVICE_CHATLOGON: - case YAHOO_SERVICE_CHATLOGOFF: - case YAHOO_SERVICE_Y6_STATUS_UPDATE: - case YAHOO_SERVICE_STATUS_15: - yahoo_process_status(gc, pkt); - break; - case YAHOO_SERVICE_NOTIFY: - yahoo_process_notify(gc, pkt, YAHOO_PKT_TYPE_SERVER); - break; - case YAHOO_SERVICE_MESSAGE: - case YAHOO_SERVICE_GAMEMSG: - case YAHOO_SERVICE_CHATMSG: - yahoo_process_message(gc, pkt, YAHOO_PKT_TYPE_SERVER); - break; - case YAHOO_SERVICE_SYSMESSAGE: - yahoo_process_sysmessage(gc, pkt); - break; - case YAHOO_SERVICE_NEWMAIL: - yahoo_process_mail(gc, pkt); - break; - case YAHOO_SERVICE_NEWCONTACT: - yahoo_process_contact(gc, pkt); - break; - case YAHOO_SERVICE_AUTHRESP: - yahoo_process_authresp(gc, pkt); - break; - case YAHOO_SERVICE_LIST: - yahoo_process_list(gc, pkt); - break; - case YAHOO_SERVICE_LIST_15: - yahoo_process_list_15(gc, pkt); - break; - case YAHOO_SERVICE_AUTH: - yahoo_process_auth(gc, pkt); - break; - case YAHOO_SERVICE_AUTH_REQ_15: - yahoo_buddy_auth_req_15(gc, pkt); - break; - case YAHOO_SERVICE_ADDBUDDY: - yahoo_process_addbuddy(gc, pkt); - break; - case YAHOO_SERVICE_IGNORECONTACT: - yahoo_process_ignore(gc, pkt); - break; - case YAHOO_SERVICE_CONFINVITE: - case YAHOO_SERVICE_CONFADDINVITE: - yahoo_process_conference_invite(gc, pkt); - break; - case YAHOO_SERVICE_CONFDECLINE: - yahoo_process_conference_decline(gc, pkt); - break; - case YAHOO_SERVICE_CONFLOGON: - yahoo_process_conference_logon(gc, pkt); - break; - case YAHOO_SERVICE_CONFLOGOFF: - yahoo_process_conference_logoff(gc, pkt); - break; - case YAHOO_SERVICE_CONFMSG: - yahoo_process_conference_message(gc, pkt); - break; - case YAHOO_SERVICE_CHATONLINE: - yahoo_process_chat_online(gc, pkt); - break; - case YAHOO_SERVICE_CHATLOGOUT: - yahoo_process_chat_logout(gc, pkt); - break; - case YAHOO_SERVICE_CHATGOTO: - yahoo_process_chat_goto(gc, pkt); - break; - case YAHOO_SERVICE_CHATJOIN: - yahoo_process_chat_join(gc, pkt); - break; - case YAHOO_SERVICE_CHATLEAVE: /* XXX is this right? */ - case YAHOO_SERVICE_CHATEXIT: - yahoo_process_chat_exit(gc, pkt); - break; - case YAHOO_SERVICE_CHATINVITE: /* XXX never seen this one, might not do it right */ - case YAHOO_SERVICE_CHATADDINVITE: - yahoo_process_chat_addinvite(gc, pkt); - break; - case YAHOO_SERVICE_COMMENT: - yahoo_process_chat_message(gc, pkt); - break; - case YAHOO_SERVICE_PRESENCE_PERM: - case YAHOO_SERVICE_PRESENCE_SESSION: - yahoo_process_presence(gc, pkt); - break; - case YAHOO_SERVICE_P2PFILEXFER: - /* This case had no break and continued; thus keeping it this way.*/ - yahoo_process_p2p(gc, pkt); /* P2PFILEXFER handled the same way as process_p2p */ - yahoo_process_p2pfilexfer(gc, pkt); /* redundant ??, need to have a break now */ - case YAHOO_SERVICE_FILETRANSFER: - purple_debug_error("yahoo", "Legacy file transfers are not " - "supported anymore.\n"); - break; - case YAHOO_SERVICE_PEERTOPEER: - yahoo_process_p2p(gc, pkt); - break; - case YAHOO_SERVICE_PICTURE: - yahoo_process_picture(gc, pkt); - break; - case YAHOO_SERVICE_PICTURE_CHECKSUM: - yahoo_process_picture_checksum(gc, pkt); - break; - case YAHOO_SERVICE_PICTURE_UPLOAD: - yahoo_process_picture_upload(gc, pkt); - break; - case YAHOO_SERVICE_PICTURE_UPDATE: - case YAHOO_SERVICE_AVATAR_UPDATE: - yahoo_process_avatar_update(gc, pkt); - break; - case YAHOO_SERVICE_AUDIBLE: - yahoo_process_audible(gc, pkt); - break; - case YAHOO_SERVICE_CONTACT_DETAILS: - yahoo_process_contact_details(gc, pkt); - break; - case YAHOO_SERVICE_FILETRANS_15: - yahoo_process_filetrans_15(gc, pkt); - break; - case YAHOO_SERVICE_FILETRANS_INFO_15: - yahoo_process_filetrans_info_15(gc, pkt); - break; - case YAHOO_SERVICE_FILETRANS_ACC_15: - yahoo_process_filetrans_acc_15(gc, pkt); - break; - case YAHOO_SERVICE_SMS_MSG: - yahoo_process_sms_message(gc, pkt); - break; - - default: - purple_debug_error("yahoo", "Unhandled service 0x%02x\n", pkt->service); - break; - } -} - -static void yahoo_pending(gpointer data, gint source, PurpleInputCondition cond) -{ - PurpleConnection *gc = data; - YahooData *yd = purple_connection_get_protocol_data(gc); - char buf[1024]; - int len; - - len = read(yd->fd, buf, sizeof(buf)); - - if (len < 0) { - gchar *tmp; - - if (errno == EAGAIN) - /* No worries */ - return; - - tmp = g_strdup_printf(_("Lost connection with server: %s"), - g_strerror(errno)); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return; - } else if (len == 0) { - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Server closed the connection")); - return; - } - purple_connection_update_last_received(gc); - yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen); - memcpy(yd->rxqueue + yd->rxlen, buf, len); - yd->rxlen += len; - - while (1) { - struct yahoo_packet *pkt; - int pos = 0; - int pktlen; - - if (yd->rxlen < YAHOO_PACKET_HDRLEN) - return; - - if (strncmp((char *)yd->rxqueue, "YMSG", MIN(4, yd->rxlen)) != 0) { - /* HEY! This isn't even a YMSG packet. What - * are you trying to pull? */ - guchar *start; - - purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!\n"); - - start = memchr(yd->rxqueue + 1, 'Y', yd->rxlen - 1); - if (start) { - g_memmove(yd->rxqueue, start, yd->rxlen - (start - yd->rxqueue)); - yd->rxlen -= start - yd->rxqueue; - continue; - } else { - g_free(yd->rxqueue); - yd->rxqueue = NULL; - yd->rxlen = 0; - return; - } - } - - pos += 4; /* YMSG */ - pos += 2; - pos += 2; - - pktlen = yahoo_get16(yd->rxqueue + pos); pos += 2; - purple_debug_misc("yahoo", "%d bytes to read, rxlen is %d\n", pktlen, yd->rxlen); - - if (yd->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) - return; - - yahoo_packet_dump(yd->rxqueue, YAHOO_PACKET_HDRLEN + pktlen); - - pkt = yahoo_packet_new(0, 0, 0); - - pkt->service = yahoo_get16(yd->rxqueue + pos); pos += 2; - pkt->status = yahoo_get32(yd->rxqueue + pos); pos += 4; - purple_debug_misc("yahoo", "Yahoo Service: 0x%02x Status: %d\n", - pkt->service, pkt->status); - pkt->id = yahoo_get32(yd->rxqueue + pos); pos += 4; - - yahoo_packet_read(pkt, yd->rxqueue + pos, pktlen); - - yd->rxlen -= YAHOO_PACKET_HDRLEN + pktlen; - if (yd->rxlen) { - guchar *tmp = g_memdup(yd->rxqueue + YAHOO_PACKET_HDRLEN + pktlen, yd->rxlen); - g_free(yd->rxqueue); - yd->rxqueue = tmp; - } else { - g_free(yd->rxqueue); - yd->rxqueue = NULL; - } - - yahoo_packet_process(gc, pkt); - - yahoo_packet_free(pkt); - } -} - -static void yahoo_got_connected(gpointer data, gint source, const gchar *error_message) -{ - PurpleConnection *gc = data; - YahooData *yd; - struct yahoo_packet *pkt; - - if (source < 0) { - gchar *tmp; - tmp = g_strdup_printf(_("Unable to connect: %s"), error_message); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return; - } - - yd = purple_connection_get_protocol_data(gc); - yd->fd = source; - - pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, yd->current_status, yd->session_id); - - yahoo_packet_hash_str(pkt, 1, purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc)))); - yahoo_packet_send_and_free(pkt, yd); - - yd->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc); -} - -#if TRY_WEBMESSENGER_LOGIN - -static void yahoo_got_web_connected(gpointer data, gint source, const gchar *error_message) -{ - PurpleConnection *gc = data; - YahooData *yd; - struct yahoo_packet *pkt; - - if (source < 0) { - gchar *tmp; - tmp = g_strdup_printf(_("Unable to connect: %s"), error_message); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return; - } - - yd = purple_connection_get_protocol_data(gc); - yd->fd = source; - - pkt = yahoo_packet_new(YAHOO_SERVICE_WEBLOGIN, YAHOO_STATUS_WEBLOGIN, yd->session_id); - - yahoo_packet_hash(pkt, "sss", 0, - purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))), - 1, purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))), - 6, yd->auth); - yahoo_packet_send_and_free(pkt, yd); - - g_free(yd->auth); - yd->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc); -} - -static void -yahoo_login_page_got(PurpleHttpConnection *hc, PurpleHttpResponse *resp, - gpointer _unused) -{ - PurpleConnection *gc = purple_http_conn_get_purple_connection(hc); - YahooData *yd = purple_connection_get_protocol_data(gc); - PurpleAccount *account = purple_connection_get_account(gc); - PurpleHttpCookieJar *cjar; - GString *auth_s; - const gchar *cookie; - - if (purple_http_response_get_code(resp) != 302) { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - return; - } - - auth_s = g_string_new(NULL); - cjar = purple_http_conn_get_cookie_jar(hc); - cookie = purple_http_cookie_jar_get(cjar, "B"); - if (cookie) - g_string_append_printf(auth_s, "B=%s; ", cookie); - cookie = purple_http_cookie_jar_get(cjar, "T"); - if (cookie) - g_string_append_printf(auth_s, "T=%s; ", cookie); - cookie = purple_http_cookie_jar_get(cjar, "Y"); - if (cookie) - g_string_append_printf(auth_s, "Y=%s; ", cookie); - - yd->auth = g_string_free(auth_s, FALSE); - /* Now we have our cookies to login with. I'll go get the milk. */ - - /* XXX: wcs2.msg.dcn.yahoo.com is down, so I used - * YAHOO_PAGER_HOST_FALLBACK, but I'm not sure, if it is the correct - * host. - */ - if (purple_proxy_connect(gc, account, YAHOO_PAGER_HOST_FALLBACK, - purple_account_get_int(account, "port", YAHOO_PAGER_PORT), - yahoo_got_web_connected, gc) == NULL) - { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - return; - } -} - -static void yahoo_login_page_hash_iter(const char *key, const char *val, GString *url) -{ - if (!strcmp(key, "passwd") || !strcmp(key, "login")) - return; - g_string_append_c(url, '&'); - g_string_append(url, key); - g_string_append_c(url, '='); - if (!strcmp(key, ".save") || !strcmp(key, ".js")) - g_string_append_c(url, '1'); - else if (!strcmp(key, ".challenge")) - g_string_append(url, val); - else - g_string_append(url, purple_url_encode(val)); -} - -static GHashTable *yahoo_login_page_hash(const char *buf, size_t len) -{ - GHashTable *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - const char *c = buf; - char *d; - char name[64], value[64]; - int count; - int input_len = strlen("<input "); - int name_len = strlen("name=\""); - int value_len = strlen("value=\""); - while ((len > ((c - buf) + input_len)) - && (c = strstr(c, "<input "))) { - if (!(c = g_strstr_len(c, len - (c - buf), "name=\""))) - continue; - c += name_len; - count = sizeof(name)-1; - for (d = name; (len > ((c - buf) + 1)) && *c!='"' - && count; c++, d++, count--) - *d = *c; - *d = '\0'; - count = sizeof(value)-1; - if (!(d = g_strstr_len(c, len - (c - buf), "value=\""))) - continue; - d += value_len; - if (strchr(c, '>') < d) - break; - for (c = d, d = value; (len > ((c - buf) + 1)) - && *c!='"' && count; c++, d++, count--) - *d = *c; - *d = '\0'; - g_hash_table_insert(hash, g_strdup(name), g_strdup(value)); - } - return hash; -} - -static void -yahoo_login_page_cb(PurpleHttpConnection *http_conn, - PurpleHttpResponse *response, gpointer _unused) -{ - PurpleConnection *gc = purple_http_conn_get_purple_connection(http_conn); - PurpleAccount *account = purple_connection_get_account(gc); - YahooData *yd = purple_connection_get_protocol_data(gc); - const char *pass = purple_connection_get_password(gc); - size_t len; - const gchar *got_data; - GHashTable *hash; - GString *url; - char md5[33], *hashp = md5, *chal; - int i; - PurpleCipher *cipher; - guchar digest[16]; - PurpleHttpRequest *req; - - if (!purple_http_response_is_successful(response)) - { - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - purple_http_response_get_error(response)); - return; - } - - got_data = purple_http_response_get_data(response, &len); - hash = yahoo_login_page_hash(got_data, len); - - cipher = purple_md5_cipher_new(); - - purple_cipher_append(cipher, (const guchar *)pass, strlen(pass)); - purple_cipher_digest(cipher, digest, sizeof(digest)); - for (i = 0; i < 16; ++i) { - g_snprintf(hashp, 3, "%02x", digest[i]); - hashp += 2; - } - - chal = g_strconcat(md5, g_hash_table_lookup(hash, ".challenge"), NULL); - purple_cipher_reset(cipher); - purple_cipher_append(cipher, (const guchar *)chal, strlen(chal)); - purple_cipher_digest(cipher, digest, sizeof(digest)); - hashp = md5; - for (i = 0; i < 16; ++i) { - g_snprintf(hashp, 3, "%02x", digest[i]); - hashp += 2; - } - /* - * I dunno why this is here and commented out.. but in case it's needed - * I updated it.. - - purple_cipher_reset(cipher); - purple_cipher_append(cipher, md5, strlen(md5)); - purple_cipher_digest(cipher, sizeof(digest), digest, NULL); - hashp = md5; - for (i = 0; i < 16; ++i) { - g_snprintf(hashp, 3, "%02x", digest[i]); - hashp += 2; - } - */ - g_free(chal); - - url = g_string_new(NULL); - g_string_printf(url, "http://login.yahoo.com/config/login?login=%s&passwd=%s", purple_account_get_username(account), md5); - g_hash_table_foreach(hash, (GHFunc)yahoo_login_page_hash_iter, url); - url = g_string_append(url, "&.hash=1&.md5=1"); - - g_hash_table_destroy(hash); - g_object_unref(cipher); - - req = purple_http_request_new(g_string_free(url, FALSE)); - purple_http_request_set_max_redirects(req, 0); - purple_http_connection_set_add(yd->http_reqs, - purple_http_request(gc, req, yahoo_login_page_got, NULL)); - purple_http_request_unref(req); -} - -#endif /* TRY_WEBMESSENGER_LOGIN */ - -static void yahoo_picture_check(PurpleAccount *account) -{ - PurpleConnection *gc = purple_account_get_connection(account); - PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account); - - yahoo_set_buddy_icon(gc, img); - purple_imgstore_unref(img); -} - -static int get_yahoo_status_from_purple_status(PurpleStatus *status) -{ - PurplePresence *presence; - const char *status_id; - const char *msg; - - presence = purple_status_get_presence(status); - status_id = purple_status_get_id(status); - msg = purple_status_get_attr_string(status, "message"); - - if ((msg != NULL) && (*msg != '\0')) { - return YAHOO_STATUS_CUSTOM; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AVAILABLE)) { - return YAHOO_STATUS_AVAILABLE; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BRB)) { - return YAHOO_STATUS_BRB; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BUSY)) { - return YAHOO_STATUS_BUSY; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATHOME)) { - return YAHOO_STATUS_NOTATHOME; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATDESK)) { - return YAHOO_STATUS_NOTATDESK; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTINOFFICE)) { - return YAHOO_STATUS_NOTINOFFICE; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONPHONE)) { - return YAHOO_STATUS_ONPHONE; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONVACATION)) { - return YAHOO_STATUS_ONVACATION; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_OUTTOLUNCH)) { - return YAHOO_STATUS_OUTTOLUNCH; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_STEPPEDOUT)) { - return YAHOO_STATUS_STEPPEDOUT; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_INVISIBLE)) { - return YAHOO_STATUS_INVISIBLE; - } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AWAY)) { - return YAHOO_STATUS_CUSTOM; - } else if (purple_presence_is_idle(presence)) { - return YAHOO_STATUS_IDLE; - } else { - purple_debug_error("yahoo", "Unexpected PurpleStatus!\n"); - return YAHOO_STATUS_AVAILABLE; - } -} - -static void yahoo_got_pager_server(PurpleHttpConnection *http_conn, - PurpleHttpResponse *response, gpointer _yd) -{ - YahooData *yd = _yd; - PurpleConnection *gc = yd->gc; - PurpleAccount *a = purple_connection_get_account(gc); - gchar **strings = NULL, *cs_server = NULL; - int port = purple_account_get_int(a, "port", YAHOO_PAGER_PORT); - int stringslen = 0; - const gchar *got_data; - - if (!purple_http_response_is_successful(response)) { - purple_debug_error("yahoo", "Unable to retrieve server info: %s\n", - purple_http_response_get_error(response)); - - if(yahoo_is_japan(a)) { /* We don't know fallback hosts for Yahoo Japan :( */ - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect: The server returned an empty response.")); - } else { - if(purple_proxy_connect(gc, a, YAHOO_PAGER_HOST_FALLBACK, port, - yahoo_got_connected, gc) == NULL) { - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - } - } - } else { - got_data = purple_http_response_get_data(response, NULL); - strings = g_strsplit(got_data, "\r\n", -1); - - if((stringslen = g_strv_length(strings)) > 1) { - int i; - - for(i = 0; i < stringslen; i++) { - if(g_ascii_strncasecmp(strings[i], "COLO_CAPACITY=", 14) == 0) { - purple_debug_info("yahoo", "Got COLO Capacity: %s\n", &(strings[i][14])); - } else if(g_ascii_strncasecmp(strings[i], "CS_IP_ADDRESS=", 14) == 0) { - cs_server = g_strdup(&strings[i][14]); - purple_debug_info("yahoo", "Got CS IP address: %s\n", cs_server); - } - } - } - - if(cs_server) { /* got an address; get on with connecting */ - if(purple_proxy_connect(gc, a, cs_server, port, yahoo_got_connected, gc) == NULL) - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - } else { - purple_debug_error("yahoo", "No CS address retrieved! Server " - "response:\n%s\n", got_data); - - if(yahoo_is_japan(a)) { /* We don't know fallback hosts for Yahoo Japan :( */ - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect: The server's response did not contain " - "the necessary information")); - } else - if(purple_proxy_connect(gc, a, YAHOO_PAGER_HOST_FALLBACK, port, - yahoo_got_connected, gc) == NULL) { - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - } - } - } - - g_strfreev(strings); - g_free(cs_server); -} - -void yahoo_login(PurpleAccount *account) { - PurpleConnection *gc = purple_account_get_connection(account); - PurpleHttpRequest *req; - YahooData *yd = g_new0(YahooData, 1); - PurpleStatus *status = purple_account_get_active_status(account); - - purple_connection_set_protocol_data(gc, yd); - purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML | PURPLE_CONNECTION_FLAG_NO_BGCOLOR | PURPLE_CONNECTION_FLAG_NO_URLDESC); - - purple_connection_update_progress(gc, _("Connecting"), 1, 2); - - purple_connection_set_display_name(gc, purple_account_get_username(account)); - - yd->gc = gc; - yd->jp = yahoo_is_japan(account); - yd->yahoo_local_p2p_server_fd = -1; - yd->fd = -1; - yd->txhandler = 0; - /* TODO: Is there a good grow size for the buffer? */ - yd->txbuf = purple_circular_buffer_new(0); - yd->http_reqs = purple_http_connection_set_new(); - yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free); - yd->imvironments = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - yd->xfer_peer_idstring_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - yd->peers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - yahoo_p2p_disconnect_destroy_data); - yd->sms_carrier = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - yd->yahoo_p2p_timer = purple_timeout_add_seconds(YAHOO_P2P_KEEPALIVE_SECS, - yahoo_p2p_keepalive, gc); - yd->confs = NULL; - yd->conf_id = 2; - yd->last_keepalive = yd->last_ping = time(NULL); - - yd->current_status = get_yahoo_status_from_purple_status(status); - - yahoo_picture_check(account); - - /* Get the pager server. Actually start connecting in the callback since we - * must have the contents of the HTTP response to proceed. */ - req = purple_http_request_new(yd->jp ? YAHOOJP_PAGER_HOST_REQ_URL : - YAHOO_PAGER_HOST_REQ_URL); - purple_http_request_header_set(req, "User-Agent", YAHOO_CLIENT_USERAGENT); - purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc, - req, yahoo_got_pager_server, yd)); - purple_http_request_unref(req); - - return; -} - -void yahoo_close(PurpleConnection *gc) { - YahooData *yd = purple_connection_get_protocol_data(gc); - GSList *l; - - if (yd->inpa) { - purple_input_remove(yd->inpa); - yd->inpa = 0; - } - - purple_http_connection_set_destroy(yd->http_reqs); - yd->http_reqs = NULL; - - for (l = yd->confs; l; l = l->next) { - PurpleChatConversation *conv = l->data; - - yahoo_conf_leave(yd, purple_conversation_get_name(PURPLE_CONVERSATION(conv)), - purple_connection_get_display_name(gc), - purple_chat_conversation_get_users(conv)); - } - g_slist_free(yd->confs); - - g_slist_free_full(yd->cookies, g_free); - - yd->chat_online = FALSE; - if (yd->in_chat) - yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */ - - purple_timeout_remove(yd->yahoo_p2p_timer); - if(yd->yahoo_p2p_server_timeout_handle != 0) { - purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle); - yd->yahoo_p2p_server_timeout_handle = 0; - } - - /* close p2p server if it is waiting for a peer to connect */ - if (yd->yahoo_p2p_server_watcher) { - purple_input_remove(yd->yahoo_p2p_server_watcher); - yd->yahoo_p2p_server_watcher = 0; - } - if (yd->yahoo_local_p2p_server_fd >= 0) { - close(yd->yahoo_local_p2p_server_fd); - yd->yahoo_local_p2p_server_fd = -1; - } - - g_hash_table_destroy(yd->sms_carrier); - g_hash_table_destroy(yd->peers); - g_hash_table_destroy(yd->friends); - g_hash_table_destroy(yd->imvironments); - g_hash_table_destroy(yd->xfer_peer_idstring_map); - g_free(yd->chat_name); - - g_free(yd->cookie_y); - g_free(yd->cookie_t); - g_free(yd->cookie_b); - - if (yd->txhandler) - purple_input_remove(yd->txhandler); - - g_object_unref(G_OBJECT(yd->txbuf)); - - if (yd->fd >= 0) - close(yd->fd); - - g_free(yd->rxqueue); - yd->rxlen = 0; - g_free(yd->picture_url); - - purple_http_conn_cancel(yd->picture_upload_hc); - if (yd->picture_upload_todo) - yahoo_buddy_icon_upload_data_free(yd->picture_upload_todo); - if (yd->ycht) - ycht_connection_close(yd->ycht); - if (yd->listen_data != NULL) - purple_network_listen_cancel(yd->listen_data); - - g_free(yd->pending_chat_room); - g_free(yd->pending_chat_id); - g_free(yd->pending_chat_topic); - g_free(yd->pending_chat_goto); - g_strfreev(yd->profiles); - - yahoo_personal_details_reset(&yd->ypd, TRUE); - - g_free(yd->current_list15_grp); - - g_free(yd); - purple_connection_set_protocol_data(gc, NULL); -} - -const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b) -{ - return "yahoo"; -} - -const char *yahoo_list_emblem(PurpleBuddy *b) -{ - PurpleAccount *account; - PurpleConnection *gc; - YahooFriend *f; - PurplePresence *presence; - - if (!b || !(account = purple_buddy_get_account(b)) || - !(gc = purple_account_get_connection(account)) || - !purple_connection_get_protocol_data(gc)) - return NULL; - - f = yahoo_friend_find(gc, purple_buddy_get_name(b)); - if (!f) { - return "not-authorized"; - } - - presence = purple_buddy_get_presence(b); - - if (purple_presence_is_online(presence)) { - if (yahoo_friend_get_game(f)) - return "game"; - - if (f->fed) - return "external"; - } - return NULL; -} - -static const char *yahoo_get_status_string(enum yahoo_status a) -{ - switch (a) { - case YAHOO_STATUS_BRB: - return _("Be Right Back"); - case YAHOO_STATUS_BUSY: - return _("Busy"); - case YAHOO_STATUS_NOTATHOME: - return _("Not at Home"); - case YAHOO_STATUS_NOTATDESK: - return _("Not at Desk"); - case YAHOO_STATUS_NOTINOFFICE: - return _("Not in Office"); - case YAHOO_STATUS_ONPHONE: - return _("On the Phone"); - case YAHOO_STATUS_ONVACATION: - return _("On Vacation"); - case YAHOO_STATUS_OUTTOLUNCH: - return _("Out to Lunch"); - case YAHOO_STATUS_STEPPEDOUT: - return _("Stepped Out"); - case YAHOO_STATUS_INVISIBLE: - return _("Invisible"); - case YAHOO_STATUS_IDLE: - return _("Idle"); - case YAHOO_STATUS_OFFLINE: - return _("Offline"); - default: - return _("Available"); - } -} - -static void yahoo_initiate_conference(PurpleBlistNode *node, gpointer data) { - - PurpleBuddy *buddy; - PurpleConnection *gc; - - GHashTable *components; - YahooData *yd; - int id; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - yd = purple_connection_get_protocol_data(gc); - id = yd->conf_id; - - components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - g_hash_table_replace(components, g_strdup("room"), - g_strdup_printf("%s-%d", purple_connection_get_display_name(gc), id)); - g_hash_table_replace(components, g_strdup("topic"), g_strdup("Join my conference...")); - g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference")); - yahoo_c_join(gc, components); - g_hash_table_destroy(components); - - yahoo_c_invite(gc, id, "Join my conference...", purple_buddy_get_name(buddy)); -} - -static void yahoo_presence_settings(PurpleBlistNode *node, gpointer data) { - PurpleBuddy *buddy; - PurpleConnection *gc; - int presence_val = GPOINTER_TO_INT(data); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - - yahoo_friend_update_presence(gc, purple_buddy_get_name(buddy), presence_val); -} - -static void yahoo_game(PurpleBlistNode *node, gpointer data) { - - PurpleBuddy *buddy; - PurpleConnection *gc; - - const char *game; - char *game2; - char *t; - char url[256]; - YahooFriend *f; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - - f = yahoo_friend_find(gc, purple_buddy_get_name(buddy)); - if (!f) - return; - - game = yahoo_friend_get_game(f); - if (!game) - return; - - t = game2 = g_strdup(strstr(game, "ante?room=")); - while (*t && *t != '\t') - t++; - *t = 0; - g_snprintf(url, sizeof url, "http://games.yahoo.com/games/%s", game2); - purple_notify_uri(gc, url); - g_free(game2); -} - -char *yahoo_status_text(PurpleBuddy *b) -{ - YahooFriend *f = NULL; - const char *msg; - char *msg2; - PurpleAccount *account; - PurpleConnection *gc; - - account = purple_buddy_get_account(b); - gc = purple_account_get_connection(account); - if (!gc || !purple_connection_get_protocol_data(gc)) - return NULL; - - f = yahoo_friend_find(gc, purple_buddy_get_name(b)); - if (!f) - return g_strdup(_("Not on server list")); - - switch (f->status) { - case YAHOO_STATUS_AVAILABLE: - return NULL; - case YAHOO_STATUS_IDLE: - if (f->idle == -1) - return g_strdup(yahoo_get_status_string(f->status)); - return NULL; - case YAHOO_STATUS_CUSTOM: - if (!(msg = yahoo_friend_get_status_message(f))) - return NULL; - msg2 = g_markup_escape_text(msg, strlen(msg)); - purple_util_chrreplace(msg2, '\n', ' '); - return msg2; - - default: - return g_strdup(yahoo_get_status_string(f->status)); - } -} - -void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) -{ - YahooFriend *f; - char *status = NULL; - const char *presence = NULL; - PurpleAccount *account; - - account = purple_buddy_get_account(b); - f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b)); - if (!f) - status = g_strdup_printf("\n%s", _("Not on server list")); - else { - switch (f->status) { - case YAHOO_STATUS_CUSTOM: - if (!yahoo_friend_get_status_message(f)) - return; - status = g_strdup(yahoo_friend_get_status_message(f)); - break; - case YAHOO_STATUS_OFFLINE: - break; - default: - status = g_strdup(yahoo_get_status_string(f->status)); - break; - } - - switch (f->presence) { - case YAHOO_PRESENCE_ONLINE: - presence = _("Appear Online"); - break; - case YAHOO_PRESENCE_PERM_OFFLINE: - presence = _("Appear Permanently Offline"); - break; - case YAHOO_PRESENCE_DEFAULT: - break; - default: - purple_debug_error("yahoo", "Unknown presence in yahoo_tooltip_text\n"); - break; - } - } - - if (status != NULL) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status); - g_free(status); - } - - if (presence != NULL) - purple_notify_user_info_add_pair_plaintext(user_info, _("Presence"), presence); - - if (f && full) { - YahooPersonalDetails *ypd = &f->ypd; - if (ypd->phone.home && *ypd->phone.home) - purple_notify_user_info_add_pair_plaintext(user_info, _("Home Phone Number"), ypd->phone.home); - if (ypd->phone.work && *ypd->phone.work) - purple_notify_user_info_add_pair_plaintext(user_info, _("Work Phone Number"), ypd->phone.work); - if (ypd->phone.mobile && *ypd->phone.mobile) - purple_notify_user_info_add_pair_plaintext(user_info, _("Mobile Phone Number"), ypd->phone.mobile); - } -} - -static void yahoo_addbuddyfrommenu_cb(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - - yahoo_add_buddy(gc, buddy, NULL, NULL); -} - - -static void yahoo_chat_goto_menu(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - - yahoo_chat_goto(gc, purple_buddy_get_name(buddy)); -} - -static GList *build_presence_submenu(YahooFriend *f, PurpleConnection *gc) { - GList *m = NULL; - PurpleMenuAction *act; - YahooData *yd = purple_connection_get_protocol_data(gc); - - if (yd->current_status == YAHOO_STATUS_INVISIBLE) { - if (f->presence != YAHOO_PRESENCE_ONLINE) { - act = purple_menu_action_new(_("Appear Online"), - PURPLE_CALLBACK(yahoo_presence_settings), - GINT_TO_POINTER(YAHOO_PRESENCE_ONLINE), - NULL); - m = g_list_append(m, act); - } else if (f->presence != YAHOO_PRESENCE_DEFAULT) { - act = purple_menu_action_new(_("Appear Offline"), - PURPLE_CALLBACK(yahoo_presence_settings), - GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT), - NULL); - m = g_list_append(m, act); - } - } - - if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) { - act = purple_menu_action_new(_("Don't Appear Permanently Offline"), - PURPLE_CALLBACK(yahoo_presence_settings), - GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT), - NULL); - m = g_list_append(m, act); - } else { - act = purple_menu_action_new(_("Appear Permanently Offline"), - PURPLE_CALLBACK(yahoo_presence_settings), - GINT_TO_POINTER(YAHOO_PRESENCE_PERM_OFFLINE), - NULL); - m = g_list_append(m, act); - } - - return m; -} - -static void yahoo_doodle_blist_node(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *b = (PurpleBuddy *)node; - PurpleAccount *account = purple_buddy_get_account(b); - PurpleConnection *gc = purple_account_get_connection(account); - - yahoo_doodle_initiate(gc, purple_buddy_get_name(b)); -} - -#if 0 -/* XXX: it doesn't seems to work */ -static void -yahoo_userinfo_blist_node(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *b = (PurpleBuddy *)node; - PurpleAccount *account = purple_buddy_get_account(b); - PurpleConnection *gc = purple_account_get_connection(account); - - yahoo_set_userinfo_for_buddy(gc, b); -} -#endif - -static GList *yahoo_buddy_menu(PurpleBuddy *buddy) -{ - GList *m = NULL; - PurpleMenuAction *act; - - PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - YahooData *yd = purple_connection_get_protocol_data(gc); - static char buf2[1024]; - YahooFriend *f; - - f = yahoo_friend_find(gc, purple_buddy_get_name(buddy)); - - if (!f && !yd->wm) { - act = purple_menu_action_new(_("Add Buddy"), - PURPLE_CALLBACK(yahoo_addbuddyfrommenu_cb), - NULL, NULL); - m = g_list_append(m, act); - - return m; - - } - - if (f && f->status != YAHOO_STATUS_OFFLINE && f->fed == YAHOO_FEDERATION_NONE) { - if (!yd->wm) { - act = purple_menu_action_new(_("Join in Chat"), - PURPLE_CALLBACK(yahoo_chat_goto_menu), - NULL, NULL); - m = g_list_append(m, act); - } - - act = purple_menu_action_new(_("Initiate Conference"), - PURPLE_CALLBACK(yahoo_initiate_conference), - NULL, NULL); - m = g_list_append(m, act); - - if (yahoo_friend_get_game(f)) { - const char *game = yahoo_friend_get_game(f); - char *room; - char *t; - - if ((room = strstr(game, "&follow="))) {/* skip ahead to the url */ - while (*room && *room != '\t') /* skip to the tab */ - room++; - t = room++; /* room as now at the name */ - while (*t != '\n') - t++; /* replace the \n with a space */ - *t = ' '; - g_snprintf(buf2, sizeof buf2, "%s", room); - - act = purple_menu_action_new(buf2, - PURPLE_CALLBACK(yahoo_game), - NULL, NULL); - m = g_list_append(m, act); - } - } - } - - if (f) { - act = purple_menu_action_new(_("Presence Settings"), NULL, NULL, - build_presence_submenu(f, gc)); - m = g_list_append(m, act); - - if (f->fed == YAHOO_FEDERATION_NONE) { - act = purple_menu_action_new(_("Start Doodling"), - PURPLE_CALLBACK(yahoo_doodle_blist_node), - NULL, NULL); - m = g_list_append(m, act); - } - -#if 0 - /* XXX: it doesn't seems to work */ - act = purple_menu_action_new(_("Set User Info..."), - PURPLE_CALLBACK(yahoo_userinfo_blist_node), - NULL, NULL); - m = g_list_append(m, act); -#endif - } - - return m; -} - -GList *yahoo_blist_node_menu(PurpleBlistNode *node) -{ - if(PURPLE_IS_BUDDY(node)) { - return yahoo_buddy_menu((PurpleBuddy *) node); - } else { - return NULL; - } -} - -static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - const char *name = yd->profiles[GPOINTER_TO_INT(purple_request_fields_get_choice(fields, "id"))]; - - struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash_str(pkt, 3, name); - yahoo_packet_send_and_free(pkt, yd); - - purple_connection_set_display_name(gc, name); -} - -static void -yahoo_get_inbox_token_cb(PurpleHttpConnection *http_conn, - PurpleHttpResponse *response, gpointer _unused) -{ - PurpleConnection *gc = - purple_http_conn_get_purple_connection(http_conn); - gchar *url; - YahooData *yd = purple_connection_get_protocol_data(gc); - - g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); - - if (!purple_http_response_is_successful(response)) { - purple_debug_error("yahoo", - "Requesting mail login token failed: %s\n", - purple_http_response_get_error(response)); - url = g_strdup(yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL); - } else { - /* Should we not be hardcoding the rd url? */ - gchar *token; - token = g_strdup(purple_http_response_get_data(response, NULL)); - g_strstrip(token); - url = g_strdup_printf( - "http://login.yahoo.com/config/reset_cookies_token?" - ".token=%s" - "&.done=http://us.rd.yahoo.com/messenger/client/%%3f" - "http://mail.yahoo.com/", token); - purple_str_wipe(token); - } - - /* Open the mailbox with the parsed url data */ - purple_notify_uri(gc, url); - - g_free(url); -} - - -static void yahoo_show_inbox(PurplePluginAction *action) -{ - /* Setup a cookie that can be used by the browser */ - /* XXX I have no idea how this will work with Yahoo! Japan. */ - - PurpleConnection *gc = action->context; - YahooData *yd = purple_connection_get_protocol_data(gc); - PurpleHttpRequest *req; - PurpleHttpCookieJar *cookiejar; - - req = purple_http_request_new( - "https://login.yahoo.com/config/cookie_token"); - purple_http_request_set_method(req, "POST"); - purple_http_request_header_set(req, "User-Agent", - YAHOO_CLIENT_USERAGENT); - cookiejar = purple_http_request_get_cookie_jar(req); - purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t); - purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y); - purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc, - req, yahoo_get_inbox_token_cb, NULL)); - purple_http_request_unref(req); -} - -#if 0 -/* XXX: it doesn't seems to work */ -static void -yahoo_set_userinfo_fn(PurplePluginAction *action) -{ - yahoo_set_userinfo(action->context); -} -#endif - -static void yahoo_show_act_id(PurplePluginAction *action) -{ - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - PurpleConnection *gc = (PurpleConnection *) action->context; - YahooData *yd = purple_connection_get_protocol_data(gc); - const char *name = purple_connection_get_display_name(gc); - int iter; - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - field = purple_request_field_choice_new("id", "Activate which ID?", 0); - purple_request_field_group_add_field(group, field); - - for (iter = 0; yd->profiles[iter]; iter++) { - purple_request_field_choice_add(field, yd->profiles[iter], GINT_TO_POINTER(iter)); - if (purple_strequal(yd->profiles[iter], name)) - purple_request_field_choice_set_default_value(field, GINT_TO_POINTER(iter)); - } - - purple_request_fields(gc, NULL, _("Select the ID you want to activate"), NULL, - fields, - _("OK"), G_CALLBACK(yahoo_act_id), - _("Cancel"), NULL, - purple_request_cpar_from_connection(gc), gc); -} - -static void yahoo_show_chat_goto(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - purple_request_input(gc, NULL, _("Join whom in chat?"), NULL, - "", FALSE, FALSE, NULL, - _("OK"), G_CALLBACK(yahoo_chat_goto), - _("Cancel"), NULL, - purple_request_cpar_from_connection(gc), - gc); -} - -GList *yahoo_actions(PurplePlugin *plugin, gpointer context) { - GList *m = NULL; - PurplePluginAction *act; - -#if 0 - /* XXX: it doesn't seems to work */ - act = purple_plugin_action_new(_("Set User Info..."), - yahoo_set_userinfo_fn); - m = g_list_append(m, act); -#endif - - act = purple_plugin_action_new(_("Activate ID..."), - yahoo_show_act_id); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Join User in Chat..."), - yahoo_show_chat_goto); - m = g_list_append(m, act); - - m = g_list_append(m, NULL); - act = purple_plugin_action_new(_("Open Inbox"), - yahoo_show_inbox); - m = g_list_append(m, act); - - return m; -} - -struct yahoo_sms_carrier_cb_data { - PurpleConnection *gc; - char *who; - char *what; -}; - -static void yahoo_get_sms_carrier_cb(PurpleHttpConnection *http_conn, - PurpleHttpResponse *response, gpointer _sms_cb_data) -{ - struct yahoo_sms_carrier_cb_data *sms_cb_data = _sms_cb_data; - PurpleConnection *gc = sms_cb_data->gc; - YahooData *yd = purple_connection_get_protocol_data(gc); - char *status = NULL; - char *carrier = NULL; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleIMConversation *im = purple_conversations_find_im_with_account(sms_cb_data->who, account); - - if (!purple_http_response_is_successful(response)) { - purple_conversation_write(PURPLE_CONVERSATION(im), NULL, - _("Can't send SMS. Unable to obtain mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL)); - - g_free(sms_cb_data->who); - g_free(sms_cb_data->what); - g_free(sms_cb_data); - return ; - } else { - const gchar *got_data = purple_http_response_get_data(response, NULL); - PurpleXmlNode *validate_data_root = purple_xmlnode_from_str(got_data, -1); - PurpleXmlNode *validate_data_child = purple_xmlnode_get_child(validate_data_root, "mobile_no"); - const char *mobile_no = purple_xmlnode_get_attrib(validate_data_child, "msisdn"); - - validate_data_root = purple_xmlnode_copy(validate_data_child); - validate_data_child = purple_xmlnode_get_child(validate_data_root, "status"); - status = purple_xmlnode_get_data(validate_data_child); - - validate_data_child = purple_xmlnode_get_child(validate_data_root, "carrier"); - carrier = purple_xmlnode_get_data(validate_data_child); - - purple_debug_info("yahoo", "SMS validate data: %s\n", got_data); - - if (status && g_str_equal(status, "Valid")) { - g_hash_table_insert(yd->sms_carrier, - g_strdup_printf("+%s", mobile_no), g_strdup(carrier)); - yahoo_send_im(sms_cb_data->gc, sms_cb_data->who, - sms_cb_data->what, PURPLE_MESSAGE_SEND); - } else { - g_hash_table_insert(yd->sms_carrier, - g_strdup_printf("+%s", mobile_no), g_strdup("Unknown")); - purple_conversation_write(PURPLE_CONVERSATION(im), NULL, - _("Can't send SMS. Unknown mobile carrier."), - PURPLE_MESSAGE_SYSTEM, time(NULL)); - } - - purple_xmlnode_free(validate_data_child); - purple_xmlnode_free(validate_data_root); - g_free(sms_cb_data->who); - g_free(sms_cb_data->what); - g_free(sms_cb_data); - g_free(status); - g_free(carrier); - } -} - -static void yahoo_get_sms_carrier(PurpleConnection *gc, gpointer data) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - PurpleHttpRequest *req; - PurpleHttpCookieJar *cookiejar; - struct yahoo_sms_carrier_cb_data *sms_cb_data; - char *validate_request_str = NULL; - PurpleXmlNode *validate_request_root = NULL; - PurpleXmlNode *validate_request_child = NULL; - - if(!(sms_cb_data = data)) - return; - - validate_request_root = purple_xmlnode_new("validate"); - purple_xmlnode_set_attrib(validate_request_root, "intl", "us"); - purple_xmlnode_set_attrib(validate_request_root, "version", YAHOO_CLIENT_VERSION); - purple_xmlnode_set_attrib(validate_request_root, "qos", "0"); - - validate_request_child = purple_xmlnode_new_child(validate_request_root, "mobile_no"); - purple_xmlnode_set_attrib(validate_request_child, "msisdn", sms_cb_data->who + 1); - - validate_request_str = purple_xmlnode_to_str(validate_request_root, NULL); - - purple_xmlnode_free(validate_request_child); - purple_xmlnode_free(validate_request_root); - - req = purple_http_request_new(NULL); - purple_http_request_set_url_printf(req, "http://validate.msg.yahoo.com" - "/mobileno?intl=us&version=%s", YAHOO_CLIENT_VERSION); - purple_http_request_set_method(req, "POST"); - purple_http_request_header_set(req, "User-Agent", - YAHOO_CLIENT_USERAGENT); - cookiejar = purple_http_request_get_cookie_jar(req); - purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t); - purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y); - purple_http_request_set_contents(req, validate_request_str, -1); - purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc, - req, yahoo_get_sms_carrier_cb, data)); - purple_http_request_unref(req); - - g_free(validate_request_str); -} - -int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - struct yahoo_packet *pkt = NULL; - char *msg = yahoo_html_to_codes(what); - char *msg2; - PurpleWhiteboard *wb; - int ret = 1; - const char *fed_who; - gsize lenb = 0; - glong lenc = 0; - struct yahoo_p2p_data *p2p_data; - YahooFederation fed = YAHOO_FEDERATION_NONE; - msg2 = yahoo_string_encode(gc, msg, TRUE); - - if(msg2) { - lenb = strlen(msg2); - lenc = g_utf8_strlen(msg2, -1); - - if(lenb > YAHOO_MAX_MESSAGE_LENGTH_BYTES || lenc > YAHOO_MAX_MESSAGE_LENGTH_CHARS) { - purple_debug_info("yahoo", "Message too big. Length is %" G_GSIZE_FORMAT - " bytes, %ld characters. Max is %d bytes, %d chars." - " Message is '%s'.\n", lenb, lenc, YAHOO_MAX_MESSAGE_LENGTH_BYTES, - YAHOO_MAX_MESSAGE_LENGTH_CHARS, msg2); - g_free(msg); - g_free(msg2); - return -E2BIG; - } - } - - fed = yahoo_get_federation_from_name(who); - - if (who[0] == '+') { - /* we have an sms to be sent */ - gchar *carrier = NULL; - const char *alias = NULL; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleIMConversation *im = purple_conversations_find_im_with_account(who, account); - - carrier = g_hash_table_lookup(yd->sms_carrier, who); - if (!carrier) { - struct yahoo_sms_carrier_cb_data *sms_cb_data; - sms_cb_data = g_malloc(sizeof(struct yahoo_sms_carrier_cb_data)); - sms_cb_data->gc = gc; - sms_cb_data->who = g_strdup(who); - sms_cb_data->what = g_strdup(what); - - purple_conversation_write(PURPLE_CONVERSATION(im), NULL, _("Getting mobile carrier to send the SMS."), PURPLE_MESSAGE_SYSTEM, time(NULL)); - - yahoo_get_sms_carrier(gc, sms_cb_data); - - g_free(msg); - g_free(msg2); - return ret; - } - else if( strcmp(carrier,"Unknown") == 0 ) { - purple_conversation_write(PURPLE_CONVERSATION(im), NULL, _("Can't send SMS. Unknown mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL)); - - g_free(msg); - g_free(msg2); - return -1; - } - - alias = purple_account_get_private_alias(account); - pkt = yahoo_packet_new(YAHOO_SERVICE_SMS_MSG, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash(pkt, "sssss", - 1, purple_connection_get_display_name(gc), - 69, alias, - 5, who + 1, - 68, carrier, - 14, msg2); - yahoo_packet_send_and_free(pkt, yd); - - g_free(msg); - g_free(msg2); - - return ret; - } - - pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id); - fed_who = who; - switch (fed) { - case YAHOO_FEDERATION_MSN: - case YAHOO_FEDERATION_OCS: - case YAHOO_FEDERATION_IBM: - case YAHOO_FEDERATION_PBX: - fed_who += 4; - break; - case YAHOO_FEDERATION_NONE: - default: - break; - } - yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, fed_who); - if (fed) - yahoo_packet_hash_int(pkt, 241, fed); - - yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */ - yahoo_packet_hash_str(pkt, 14, msg2); - - /* - * IMVironment. - * - * If this message is to a user who is also Doodling with the local user, - * format the chat packet with the correct IMV information (thanks Yahoo!) - * - * Otherwise attempt to use the same IMVironment as the remote user, - * just so that we don't inadvertantly reset their IMVironment back - * to nothing. - * - * If they have not set an IMVironment, then use the default. - */ - wb = purple_whiteboard_get_session(purple_connection_get_account(gc), who); - if (wb) - yahoo_packet_hash_str(pkt, 63, DOODLE_IMV_KEY); - else - { - const char *imv; - imv = g_hash_table_lookup(yd->imvironments, who); - if (imv != NULL) - yahoo_packet_hash_str(pkt, 63, imv); - else - yahoo_packet_hash_str(pkt, 63, ";0"); - } - - yahoo_packet_hash_str(pkt, 64, "0"); /* no idea */ - yahoo_packet_hash_str(pkt, 1002, "1"); /* no idea, Yahoo 6 or later only it seems */ - if (!yd->picture_url) - yahoo_packet_hash_str(pkt, 206, "0"); /* 0 = no picture, 2 = picture, maybe 1 = avatar? */ - else - yahoo_packet_hash_str(pkt, 206, "2"); - - /* We may need to not send any packets over 2000 bytes, but I'm not sure yet. */ - if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000) { - /* if p2p link exists, send through it. To-do: key 15, time value to be sent in case of p2p */ - if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !fed) { - yahoo_packet_hash_int(pkt, 11, p2p_data->session_id); - yahoo_p2p_write_pkt(p2p_data->source, pkt); - } - else { - yahoo_packet_send(pkt, yd); - if(!fed) - yahoo_send_p2p_pkt(gc, who, 0); /* send p2p packet, with val_13=0 */ - } - } - else - ret = -E2BIG; - - yahoo_packet_free(pkt); - - g_free(msg); - g_free(msg2); - - return ret; -} - -unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - struct yahoo_p2p_data *p2p_data; - YahooFederation fed = YAHOO_FEDERATION_NONE; - struct yahoo_packet *pkt = NULL; - - fed = yahoo_get_federation_from_name(who); - - /* Don't do anything if sms is being typed */ - if( strncmp(who, "+", 1) == 0 ) - return 0; - - pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, yd->session_id); - - /* check to see if p2p link exists, send through it */ - if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !fed) { - yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc), - 14, " ", 13, state == PURPLE_IM_TYPING ? "1" : "0", - 5, who, 11, p2p_data->session_id, 1002, "1"); /* To-do: key 15 to be sent in case of p2p */ - yahoo_p2p_write_pkt(p2p_data->source, pkt); - yahoo_packet_free(pkt); - } - else { /* send through yahoo server */ - - const char *fed_who = who; - switch (fed) { - case YAHOO_FEDERATION_MSN: - case YAHOO_FEDERATION_OCS: - case YAHOO_FEDERATION_IBM: - case YAHOO_FEDERATION_PBX: - fed_who += 4; - break; - case YAHOO_FEDERATION_NONE: - default: - break; - } - - yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc), - 14, " ", 13, state == PURPLE_IM_TYPING ? "1" : "0", - 5, fed_who, 1002, "1"); - if (fed) - yahoo_packet_hash_int(pkt, 241, fed); - yahoo_packet_send_and_free(pkt, yd); - } - - return 0; -} - -static void yahoo_session_presence_remove(gpointer key, gpointer value, gpointer data) -{ - YahooFriend *f = value; - if (f && f->presence == YAHOO_PRESENCE_ONLINE) - f->presence = YAHOO_PRESENCE_DEFAULT; -} - -void yahoo_set_status(PurpleAccount *account, PurpleStatus *status) -{ - PurpleConnection *gc; - PurplePresence *presence; - YahooData *yd; - struct yahoo_packet *pkt; - int old_status; - const char *msg = NULL; - char *tmp = NULL; - char *conv_msg = NULL; - - if (!purple_status_is_active(status)) - return; - - gc = purple_account_get_connection(account); - presence = purple_status_get_presence(status); - yd = purple_connection_get_protocol_data(gc); - old_status = yd->current_status; - - yd->current_status = get_yahoo_status_from_purple_status(status); - - if (yd->current_status == YAHOO_STATUS_CUSTOM) - { - msg = purple_status_get_attr_string(status, "message"); - - if (purple_status_is_available(status)) { - tmp = yahoo_string_encode(gc, msg, TRUE); - conv_msg = purple_markup_strip_html(tmp); - g_free(tmp); - } else { - if ((msg == NULL) || (*msg == '\0')) - msg = _("Away"); - tmp = yahoo_string_encode(gc, msg, TRUE); - conv_msg = purple_markup_strip_html(tmp); - g_free(tmp); - } - } - - if (yd->current_status == YAHOO_STATUS_INVISIBLE) { - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash_str(pkt, 13, "2"); - yahoo_packet_send_and_free(pkt, yd); - - return; - } - - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash_int(pkt, 10, yd->current_status); - - if (yd->current_status == YAHOO_STATUS_CUSTOM) { - yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */ - yahoo_packet_hash_str(pkt, 19, conv_msg); - } else { - yahoo_packet_hash_str(pkt, 19, ""); - } - - g_free(conv_msg); - - if (purple_presence_is_idle(presence)) - yahoo_packet_hash_str(pkt, 47, "2"); - else { - if (!purple_status_is_available(status)) - yahoo_packet_hash_str(pkt, 47, "1"); - else - yahoo_packet_hash_str(pkt, 47, "0"); - } - - yahoo_packet_send_and_free(pkt, yd); - - if (old_status == YAHOO_STATUS_INVISIBLE) { - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash_str(pkt, 13, "1"); - yahoo_packet_send_and_free(pkt, yd); - - /* Any per-session presence settings are removed */ - g_hash_table_foreach(yd->friends, yahoo_session_presence_remove, NULL); - - } -} - -void yahoo_set_idle(PurpleConnection *gc, int idle) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - struct yahoo_packet *pkt = NULL; - char *msg = NULL, *msg2 = NULL; - PurpleStatus *status = NULL; - gboolean invisible = FALSE; - - if (idle && yd->current_status != YAHOO_STATUS_CUSTOM) - yd->current_status = YAHOO_STATUS_IDLE; - else if (!idle && yd->current_status == YAHOO_STATUS_IDLE) { - status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc))); - yd->current_status = get_yahoo_status_from_purple_status(status); - } - - invisible = (yd->current_status == YAHOO_STATUS_INVISIBLE); - - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, yd->session_id); - - if (!idle && invisible) - yahoo_packet_hash_int(pkt, 10, YAHOO_STATUS_AVAILABLE); - else - yahoo_packet_hash_int(pkt, 10, yd->current_status); - - if (yd->current_status == YAHOO_STATUS_CUSTOM) { - const char *tmp; - if (status == NULL) - status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc))); - tmp = purple_status_get_attr_string(status, "message"); - if (tmp != NULL) { - msg = yahoo_string_encode(gc, tmp, TRUE); - msg2 = purple_markup_strip_html(msg); - yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */ - yahoo_packet_hash_str(pkt, 19, msg2); - } else { - /* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for - * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message */ - yahoo_packet_hash_str(pkt, 19, _("Away")); - } - } else { - yahoo_packet_hash_str(pkt, 19, ""); - } - - if (idle) - yahoo_packet_hash_str(pkt, 47, "2"); - else if (yd->current_status == YAHOO_STATUS_CUSTOM && - !purple_status_is_available(status)) - /* We are still unavailable in this case. - * Make sure Yahoo knows that */ - yahoo_packet_hash_str(pkt, 47, "1"); - - yahoo_packet_send_and_free(pkt, yd); - - g_free(msg); - g_free(msg2); -} - -GList *yahoo_status_types(PurpleAccount *account) -{ - PurpleStatusType *type; - GList *types = NULL; - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, YAHOO_STATUS_TYPE_AVAILABLE, - NULL, TRUE, TRUE, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - types = g_list_append(types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_AWAY, - NULL, TRUE, TRUE, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - types = g_list_append(types, type); - - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_BRB, _("Be Right Back"), TRUE); - types = g_list_append(types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_BUSY, - _("Busy"), TRUE, TRUE, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - types = g_list_append(types, type); - - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATHOME, _("Not at Home"), TRUE); - types = g_list_append(types, type); - - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATDESK, _("Not at Desk"), TRUE); - types = g_list_append(types, type); - - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTINOFFICE, _("Not in Office"), TRUE); - types = g_list_append(types, type); - - type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_ONPHONE, _("On the Phone"), TRUE); - types = g_list_append(types, type); - - type = purple_status_type_new(PURPLE_STATUS_EXTENDED_AWAY, YAHOO_STATUS_TYPE_ONVACATION, _("On Vacation"), TRUE); - types = g_list_append(types, type); - - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_OUTTOLUNCH, _("Out to Lunch"), TRUE); - types = g_list_append(types, type); - - type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_STEPPEDOUT, _("Stepped Out"), TRUE); - types = g_list_append(types, type); - - - type = purple_status_type_new(PURPLE_STATUS_INVISIBLE, YAHOO_STATUS_TYPE_INVISIBLE, NULL, TRUE); - types = g_list_append(types, type); - - type = purple_status_type_new(PURPLE_STATUS_OFFLINE, YAHOO_STATUS_TYPE_OFFLINE, NULL, TRUE); - types = g_list_append(types, type); - - type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, YAHOO_STATUS_TYPE_MOBILE, NULL, FALSE, FALSE, TRUE); - types = g_list_append(types, type); - - return types; -} - -void yahoo_keepalive(PurpleConnection *gc) -{ - struct yahoo_packet *pkt; - YahooData *yd = purple_connection_get_protocol_data(gc); - time_t now = time(NULL); - - /* We're only allowed to send a ping once an hour or the servers will boot us */ - if ((now - yd->last_ping) >= PING_TIMEOUT) { - yd->last_ping = now; - - /* The native client will only send PING or CHATPING */ - if (yd->chat_online) { - if (yd->wm) { - ycht_chat_send_keepalive(yd->ycht); - } else { - pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash_str(pkt, 109, purple_connection_get_display_name(gc)); - yahoo_packet_send_and_free(pkt, yd); - } - } else { - pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_send_and_free(pkt, yd); - } - } - - if ((now - yd->last_keepalive) >= KEEPALIVE_TIMEOUT) { - yd->last_keepalive = now; - pkt = yahoo_packet_new(YAHOO_SERVICE_KEEPALIVE, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc)); - yahoo_packet_send_and_free(pkt, yd); - } - -} - -void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - struct yahoo_packet *pkt; - const char *group = NULL; - char *group2; - const char *bname; - const char *fed_bname; - YahooFederation fed = YAHOO_FEDERATION_NONE; - - if (!yd->logged_in) - return; - - fed_bname = bname = purple_buddy_get_name(buddy); - if (!purple_account_privacy_check(purple_connection_get_account(gc), bname)) - return; - - fed = yahoo_get_federation_from_name(bname); - if (fed != YAHOO_FEDERATION_NONE) - fed_bname += 4; - - g = purple_buddy_get_group(buddy); - if (g) - group = purple_group_get_name(g); - else - group = "Buddies"; - - group2 = yahoo_string_encode(gc, group, FALSE); - pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); - if (fed) { - yahoo_packet_hash(pkt, "sssssssisss", - 14, "", - 65, group2, - 97, "1", /* UTF-8 */ - 1, purple_connection_get_display_name(gc), - 302, "319", - 300, "319", - 7, fed_bname, - 241, fed, - 334, "0", - 301, "319", - 303, "319" - ); - } - else { - yahoo_packet_hash(pkt, "ssssssssss", - 14, "", - 65, group2, - 97, "1", /* UTF-8 */ - 1, purple_connection_get_display_name(gc), - 302, "319", - 300, "319", - 7, fed_bname, - 334, "0", - 301, "319", - 303, "319" - ); - } - - yahoo_packet_send_and_free(pkt, yd); - g_free(group2); -} - -void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - struct yahoo_packet *pkt; - GSList *buddies, *l; - PurpleGroup *g; - gboolean remove = TRUE; - char *cg; - const char *bname, *gname; - YahooFriend *f = NULL; - YahooFederation fed = YAHOO_FEDERATION_NONE; - - bname = purple_buddy_get_name(buddy); - f = yahoo_friend_find(gc, bname); - if (!f) - return; - fed = f->fed; - - gname = purple_group_get_name(group); - buddies = purple_blist_find_buddies(purple_connection_get_account(gc), bname); - for (l = buddies; l; l = l->next) { - g = purple_buddy_get_group(l->data); - if (purple_utf8_strcasecmp(gname, purple_group_get_name(g))) { - remove = FALSE; - break; - } - } - - g_slist_free(buddies); - - if (remove) { - g_hash_table_remove(yd->friends, bname); - f = NULL; /* f no longer valid - Just making it clear */ - } - - cg = yahoo_string_encode(gc, gname, FALSE); - pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); - - switch (fed) { - case YAHOO_FEDERATION_MSN: - case YAHOO_FEDERATION_OCS: - case YAHOO_FEDERATION_IBM: - bname += 4; - break; - case YAHOO_FEDERATION_NONE: - default: - break; - } - - yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), - 7, bname, 65, cg); - if (fed) - yahoo_packet_hash_int(pkt, 241, fed); - yahoo_packet_send_and_free(pkt, yd); - g_free(cg); -} - -void yahoo_add_deny(PurpleConnection *gc, const char *who) { - YahooData *yd = purple_connection_get_protocol_data(gc); - struct yahoo_packet *pkt; - YahooFederation fed = YAHOO_FEDERATION_NONE; - - if (!yd->logged_in) - return; - - if (!who || who[0] == '\0') - return; - - fed = yahoo_get_federation_from_name(who); - - pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id); - - if(fed) - yahoo_packet_hash(pkt, "ssis", 1, purple_connection_get_display_name(gc), 7, who+4, 241, fed, 13, "1"); - else - yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "1"); - - yahoo_packet_send_and_free(pkt, yd); -} - -void yahoo_rem_deny(PurpleConnection *gc, const char *who) { - YahooData *yd = purple_connection_get_protocol_data(gc); - struct yahoo_packet *pkt; - YahooFederation fed = YAHOO_FEDERATION_NONE; - - if (!yd->logged_in) - return; - - if (!who || who[0] == '\0') - return; - fed = yahoo_get_federation_from_name(who); - - pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id); - - if(fed) - yahoo_packet_hash(pkt, "ssis", 1, purple_connection_get_display_name(gc), 7, who+4, 241, fed, 13, "2"); - else - yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "2"); - - yahoo_packet_send_and_free(pkt, yd); -} - -void yahoo_set_permit_deny(PurpleConnection *gc) -{ - PurpleAccount *account; - GSList *deny; - - account = purple_connection_get_account(gc); - - switch (purple_account_get_privacy_type(account)) - { - case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL: - for (deny = purple_account_privacy_get_denied(account); deny; deny = deny->next) - yahoo_rem_deny(gc, deny->data); - break; - - case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST: - case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS: - case PURPLE_ACCOUNT_PRIVACY_DENY_USERS: - case PURPLE_ACCOUNT_PRIVACY_DENY_ALL: - for (deny = purple_account_privacy_get_denied(account); deny; deny = deny->next) - yahoo_add_deny(gc, deny->data); - break; - } -} - -void yahoo_change_buddys_group(PurpleConnection *gc, const char *who, - const char *old_group, const char *new_group) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - struct yahoo_packet *pkt; - char *gpn, *gpo; - YahooFriend *f = yahoo_friend_find(gc, who); - const char *temp = NULL; - - /* Step 0: If they aren't on the server list anyway, - * don't bother letting the server know. - */ - if (!f) - return; - - if(f->fed) { - temp = who+4; - } else - temp = who; - - /* If old and new are the same, we would probably - * end up deleting the buddy, which would be bad. - * This might happen because of the charset conversation. - */ - gpn = yahoo_string_encode(gc, new_group, FALSE); - gpo = yahoo_string_encode(gc, old_group, FALSE); - if (!strcmp(gpn, gpo)) { - g_free(gpn); - g_free(gpo); - return; - } - - pkt = yahoo_packet_new(YAHOO_SERVICE_CHGRP_15, YAHOO_STATUS_AVAILABLE, yd->session_id); - if(f->fed) - yahoo_packet_hash(pkt, "ssssissss", 1, purple_connection_get_display_name(gc), - 302, "240", 300, "240", 7, temp, 241, f->fed, 224, gpo, 264, gpn, 301, - "240", 303, "240"); - else - yahoo_packet_hash(pkt, "ssssssss", 1, purple_connection_get_display_name(gc), - 302, "240", 300, "240", 7, temp, 224, gpo, 264, gpn, 301, - "240", 303, "240"); - yahoo_packet_send_and_free(pkt, yd); - - g_free(gpn); - g_free(gpo); -} - -void yahoo_rename_group(PurpleConnection *gc, const char *old_name, - PurpleGroup *group, GList *moved_buddies) -{ - YahooData *yd = purple_connection_get_protocol_data(gc); - struct yahoo_packet *pkt; - char *gpn, *gpo; - - gpn = yahoo_string_encode(gc, purple_group_get_name(group), FALSE); - gpo = yahoo_string_encode(gc, old_name, FALSE); - if (!strcmp(gpn, gpo)) { - g_free(gpn); - g_free(gpo); - return; - } - - pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), - 65, gpo, 67, gpn); - yahoo_packet_send_and_free(pkt, yd); - g_free(gpn); - g_free(gpo); -} - -/********************************* Commands **********************************/ - -PurpleCmdRet -yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data) { - PurpleAccount *account = purple_conversation_get_account(c); - - if (*args && args[0]) - return PURPLE_CMD_RET_FAILED; - - purple_prpl_send_attention(purple_account_get_connection(account), purple_conversation_get_name(c), YAHOO_BUZZ); - - return PURPLE_CMD_RET_OK; -} - -PurpleCmdRet -yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd, - char **args, char **error, void *data) -{ - GHashTable *comp; - PurpleConnection *gc; - - if (!args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - gc = purple_conversation_get_connection(conv); - purple_debug_info("yahoo", "Trying to join %s \n", args[0]); - - comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - g_hash_table_replace(comp, g_strdup("room"), g_ascii_strdown(args[0], -1)); - g_hash_table_replace(comp, g_strdup("type"), g_strdup("Chat")); - - yahoo_c_join(gc, comp); - - g_hash_table_destroy(comp); - return PURPLE_CMD_RET_OK; -} - -PurpleCmdRet -yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd, - char **args, char **error, void *data) -{ - PurpleAccount *account = purple_conversation_get_account(conv); - if (*args && args[0]) - return PURPLE_CMD_RET_FAILED; - purple_roomlist_show_with_account(account); - return PURPLE_CMD_RET_OK; -} - -gboolean yahoo_offline_message(const PurpleBuddy *buddy) -{ - return TRUE; -} - -gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type) -{ - PurpleIMConversation *im; - - im = purple_conversations_find_im_with_account(username, - purple_connection_get_account(gc)); - - g_return_val_if_fail(im != NULL, FALSE); - - purple_debug_info("yahoo", "Sending <ding> on account %s to buddy %s.\n", - username, purple_conversation_get_name(PURPLE_CONVERSATION(im))); - purple_conversation_send_with_flags(PURPLE_CONVERSATION(im), "<ding>", PURPLE_MESSAGE_INVISIBLE); - - return TRUE; -} - -GList *yahoo_attention_types(PurpleAccount *account) -{ - static GList *list = NULL; - - if (!list) { - /* Yahoo only supports one attention command: the 'buzz'. */ - /* This is index number YAHOO_BUZZ. */ - list = g_list_append(list, purple_attention_type_new("Buzz", _("Buzz"), - _("%s has buzzed you!"), _("Buzzing %s..."))); - } - - return list; -} - -gssize -yahoo_get_max_message_size(PurpleConversation *conv) -{ - return YAHOO_MAX_MESSAGE_LENGTH_CHARS; -}
--- a/libpurple/protocols/yahoo/libymsg.h Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,399 +0,0 @@ -/** - * @file libymsg.h The Yahoo! and Yahoo! JAPAN Protocol Plugins - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _LIBYMSG_H_ -#define _LIBYMSG_H_ - -#include "circularbuffer.h" -#include "cmds.h" -#include "http.h" -#include "prpl.h" -#include "network.h" - -#define YAHOO_PAGER_HOST_REQ_URL "http://vcs1.msg.yahoo.com/capacity" -#define YAHOO_PAGER_HOST_FALLBACK "scsa.msg.yahoo.com" -#define YAHOO_PAGER_PORT 5050 -#define YAHOO_PAGER_PORT_P2P 5101 -#define YAHOO_LOGIN_URL "https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=%s" -#define YAHOO_TOKEN_URL "https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s" -#define YAHOO_P2P_KEEPALIVE_SECS 300 -#define YAHOO_P2P_SERVER_TIMEOUT 10 -#define YAHOO_PROFILE_URL "http://profiles.yahoo.com/" -#define YAHOO_MAIL_URL "http://rd.yahoo.com/messenger/client/?http://mail.yahoo.com/" -#define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com" -#define YAHOO_XFER_RELAY_HOST "relay.msg.yahoo.com" -#define YAHOO_XFER_RELAY_PORT 80 -#define YAHOO_ROOMLIST_URL "http://insider.msg.yahoo.com/ycontent/" -#define YAHOO_ROOMLIST_LOCALE "us" - -/* Yahoo! JAPAN stuff */ -#define YAHOOJP_PAGER_HOST_REQ_URL "http://cs1.yahoo.co.jp/capacity" -#define YAHOOJP_TOKEN_URL "https://login.yahoo.co.jp/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s" -#define YAHOOJP_LOGIN_URL "https://login.yahoo.co.jp/config/pwtoken_login?src=ymsgr&ts=&token=%s" -#define YAHOOJP_PROFILE_URL "http://profiles.yahoo.co.jp/" -#define YAHOOJP_MAIL_URL "http://mail.yahoo.co.jp/" -#define YAHOOJP_XFER_HOST "filetransfer.msg.yahoo.co.jp" -#define YAHOOJP_WEBCAM_HOST "wc.yahoo.co.jp" -/* not sure, must test: */ -#define YAHOOJP_XFER_RELAY_HOST "relay.msg.yahoo.co.jp" -#define YAHOOJP_XFER_RELAY_PORT 80 -#define YAHOOJP_ROOMLIST_URL "http://insider.msg.yahoo.co.jp/ycontent/" -#define YAHOOJP_ROOMLIST_LOCALE "ja" - -#define YAHOO_AUDIBLE_URL "http://l.yimg.com/pu/dl/aud" - -#define WEBMESSENGER_URL "http://login.yahoo.com/config/login?.src=pg" - -#define YAHOO_SMS_CARRIER_URL "http://validate.msg.yahoo.com" - -#define YAHOO_USERINFO_URL "http://address.yahoo.com/yab/us?v=XM&sync=1&tags=short&useutf8=1&noclear=1&legenc=codepage-1252" -#define YAHOOJP_USERINFO_URL "http://address.yahoo.co.jp/yab/jp?v=XM&sync=1&tags=short&useutf8=1&noclear=1&legenc=codepage-1252" - -#define YAHOO_PICURL_SETTING "picture_url" -#define YAHOO_PICCKSUM_SETTING "picture_checksum" -#define YAHOO_PICEXPIRE_SETTING "picture_expire" - -#define YAHOO_STATUS_TYPE_OFFLINE "offline" -#define YAHOO_STATUS_TYPE_AVAILABLE "available" -#define YAHOO_STATUS_TYPE_BRB "brb" -#define YAHOO_STATUS_TYPE_BUSY "busy" -#define YAHOO_STATUS_TYPE_NOTATHOME "notathome" -#define YAHOO_STATUS_TYPE_NOTATDESK "notatdesk" -#define YAHOO_STATUS_TYPE_NOTINOFFICE "notinoffice" -#define YAHOO_STATUS_TYPE_ONPHONE "onphone" -#define YAHOO_STATUS_TYPE_ONVACATION "onvacation" -#define YAHOO_STATUS_TYPE_OUTTOLUNCH "outtolunch" -#define YAHOO_STATUS_TYPE_STEPPEDOUT "steppedout" -#define YAHOO_STATUS_TYPE_AWAY "away" -#define YAHOO_STATUS_TYPE_INVISIBLE "invisible" -#define YAHOO_STATUS_TYPE_MOBILE "mobile" - -#define YAHOO_CLIENT_VERSION_ID "4194239" -#define YAHOO_CLIENT_VERSION "9.0.0.2162" - -#define YAHOOJP_CLIENT_VERSION_ID "4186047" -#define YAHOOJP_CLIENT_VERSION "9.0.0.1727" - -#define YAHOO_CLIENT_USERAGENT "Mozilla/5.0" -#define YAHOO_CLIENT_USERAGENT_ALIAS "Mozilla/4.0 (compatible; MSIE 5.5)" - -/* Index into attention types list. */ -#define YAHOO_BUZZ 0 - -typedef enum { - YAHOO_PKT_TYPE_SERVER = 0, - YAHOO_PKT_TYPE_P2P -} yahoo_pkt_type; - -typedef enum { - YAHOO_P2P_WE_ARE_CLIENT =0, - YAHOO_P2P_WE_ARE_SERVER -} yahoo_p2p_connection_type; - -enum yahoo_status { - YAHOO_STATUS_AVAILABLE = 0, - YAHOO_STATUS_BRB, - YAHOO_STATUS_BUSY, - YAHOO_STATUS_NOTATHOME, - YAHOO_STATUS_NOTATDESK, - YAHOO_STATUS_NOTINOFFICE, - YAHOO_STATUS_ONPHONE, - YAHOO_STATUS_ONVACATION, - YAHOO_STATUS_OUTTOLUNCH, - YAHOO_STATUS_STEPPEDOUT, - YAHOO_STATUS_P2P = 11, - YAHOO_STATUS_INVISIBLE = 12, - YAHOO_STATUS_CUSTOM = 99, - YAHOO_STATUS_IDLE = 999, - YAHOO_STATUS_WEBLOGIN = 0x5a55aa55, - YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */ - YAHOO_STATUS_TYPING = 0x16, - YAHOO_STATUS_DISCONNECTED = -1 /* 0xffffffff; in ymsg 15. doesnt mean the normal sense of 'disconnected' */ -}; - -/* - * Yahoo federated networks. Key 241 in ymsg. - * If it doesn't exist, it is on Yahoo's netowrk. - * It if does exist, send to another IM network. - */ - -typedef enum { - YAHOO_FEDERATION_NONE = 0, /* No federation - Yahoo! network */ - YAHOO_FEDERATION_OCS = 1, /* LCS or OCS private networks */ - YAHOO_FEDERATION_MSN = 2, /* MSN or Windows Live network */ - YAHOO_FEDERATION_IBM = 9, /* IBM/Sametime network */ - YAHOO_FEDERATION_PBX = 100 /* Yahoo! Pingbox service */ -} YahooFederation; - - -struct yahoo_buddy_icon_upload_data { - PurpleConnection *gc; - char *filename; - GString *picture_data; -}; - -struct yahoo_p2p_data { - PurpleConnection *gc; - char *host_ip; - char *host_username; - int val_13; - guint input_event; - gint source; - int session_id; - yahoo_p2p_connection_type connection_type; -}; - -struct _YchtConn; - -typedef struct _YahooPersonalDetails { - char *id; - - struct { - char *first; - char *last; - char *middle; - char *nick; - } names; - - struct { - char *work; - char *home; - char *mobile; - } phone; -} YahooPersonalDetails; - -typedef struct { - PurpleConnection *gc; - int fd; - guint inpa; - guchar *rxqueue; - int rxlen; - PurpleCircularBuffer *txbuf; - guint txhandler; - GHashTable *friends; - - char **profiles; /* Multiple profiles can be associated with an account */ - YahooPersonalDetails ypd; - - /** - * This is used to keep track of the IMVironment chosen - * by people you talk to. We don't do very much with - * this right now... but at least now if the remote user - * selects an IMVironment we won't reset it back to the - * default of nothing. - */ - GHashTable *imvironments; - - int current_status; - gboolean logged_in; - GString *tmp_serv_blist, *tmp_serv_ilist, *tmp_serv_plist; - GSList *confs; - unsigned int conf_id; /* just a counter */ - gboolean chat_online; - gboolean in_chat; - char *chat_name; - char *pending_chat_room; - char *pending_chat_id; - char *pending_chat_topic; - char *pending_chat_goto; - char *auth; - char *cookie_y; - char *cookie_t; - char *cookie_b; - int session_id; - gboolean jp; - gboolean wm; /* connected w/ web messenger method */ - /* picture aka buddy icon stuff */ - char *picture_url; - int picture_checksum; - - /* ew. we have to check the icon before we connect, - * but can't upload it til we're connected. */ - struct yahoo_buddy_icon_upload_data *picture_upload_todo; - PurpleHttpConnection *picture_upload_hc; - - struct _YchtConn *ycht; - - /** - * This set contains HTTP connections - * for when we lookup people profile or photo information. - */ - PurpleHttpConnectionSet *http_reqs; - - GHashTable *xfer_peer_idstring_map;/* Hey, i dont know, but putting this HashTable next to friends gives a run time fault... */ - GSList *cookies;/* contains all cookies, including _y and _t */ - PurpleNetworkListenData *listen_data; - - /** - * We may receive a list15 in multiple packets with no prior warning as to how many we'll be getting; - * the server expects us to keep track of the group for which it is sending us contact names. - */ - char *current_list15_grp; - time_t last_ping; - time_t last_keepalive; - GHashTable *peers; /* information about p2p data */ - int yahoo_p2p_timer; - int yahoo_local_p2p_server_fd; - int yahoo_p2p_server_watcher; - GHashTable *sms_carrier; /* sms carrier data */ - guint yahoo_p2p_server_timeout_handle; -} YahooData; - -#define YAHOO_MAX_STATUS_MESSAGE_LENGTH (255) - -/* - * Current Maximum Length for Instant Messages - * - * This was found by experiment. - * - * The YMSG protocol allows a message of up to 948 bytes, but the official client - * limits to 800 characters. According to experiments I conducted, it seems that - * the discrepancy is to allow some leeway for messages with mixed single- and - * multi-byte characters, as I was able to send messages of 840 and 932 bytes - * by using some multibyte characters (some random Chinese or Japanese characters, - * to be precise). - rekkanoryo - */ -#define YAHOO_MAX_MESSAGE_LENGTH_BYTES 948 -#define YAHOO_MAX_MESSAGE_LENGTH_CHARS 800 - -/* sometimes i wish prpls could #include things from other prpls. then i could just - * use the routines from libfaim and not have to admit to knowing how they work. */ -#define yahoo_put16(buf, data) ( \ - (*(buf) = (unsigned char)((data)>>8)&0xff), \ - (*((buf)+1) = (unsigned char)(data)&0xff), \ - 2) -#define yahoo_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff)) -#define yahoo_put32(buf, data) ( \ - (*((buf)) = (unsigned char)((data)>>24)&0xff), \ - (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \ - (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \ - (*((buf)+3) = (unsigned char)(data)&0xff), \ - 4) -#define yahoo_get32(buf) ((((*(buf))<<24)&0xff000000) + \ - (((*((buf)+1))<<16)&0x00ff0000) + \ - (((*((buf)+2))<< 8)&0x0000ff00) + \ - (((*((buf)+3) )&0x000000ff))) - -/* util.c */ -void yahoo_init_colorht(void); -void yahoo_dest_colorht(void); -char *yahoo_codes_to_html(const char *x); - -/** - * This function takes a normal HTML message and converts it to the message - * format used by Yahoo, which uses a frankensteinish combination of ANSI - * escape codes and broken HTML. - * - * It results in slightly different output than would be sent by official - * Yahoo clients. The two main differences are: - * - * 1. We always close all tags, whereas official Yahoo clients leave tags - * dangling open at the end of each message (and the client treats them - * as closed). - * 2. We always close inner tags first before closing outter tags. - * - * For example, if you want to send this message: - * <b> bold <i> bolditalic </i></b><i> italic </i> - * Official Yahoo clients would send: - * ESC[1m bold ESC[2m bolditalic ESC[x1m italic - * But we will send: - * ESC[1m bold ESC[2m bolditalic ESC[x2mESC[x1mESC[2m italic ESC[x2m - */ -char *yahoo_html_to_codes(const char *src); - -gboolean -yahoo_account_use_http_proxy(PurpleConnection *conn); - -/** - * Encode some text to send to the yahoo server. - * - * @param gc The connection handle. - * @param str The null terminated utf8 string to encode. - * @param utf8 Whether to return a UTF-8 string. - * @return A g_malloc'ed string in the appropriate encoding. If jd->jp or - * utf8 is true then the string is copied verbatim. Otherwise the - * encoding from account settings is used. - */ -gchar *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean utf8); - -/** - * Decode some text received from the server. - * - * @param gc The gc handle. - * @param str The null terminated string to decode. - * @param utf8 Did the server tell us it was supposed to be utf8? - * @return The decoded, utf-8 string, which must be g_free()'d. - */ -char *yahoo_string_decode(PurpleConnection *gc, const char *str, gboolean utf8); - -char *yahoo_convert_to_numeric(const char *str); - -YahooFederation yahoo_get_federation_from_name(const char *who); - -/* yahoo_profile.c */ -void yahoo_get_info(PurpleConnection *gc, const char *name); - -/* libymsg.h - these functions were formerly static but need not to be for the - * new two-prpl model. */ -const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b); -const char *yahoo_list_emblem(PurpleBuddy *b); -char *yahoo_status_text(PurpleBuddy *b); -void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full); -GList *yahoo_status_types(PurpleAccount *account); -GList *yahoo_blist_node_menu(PurpleBlistNode *node); -void yahoo_login(PurpleAccount *account); -void yahoo_close(PurpleConnection *gc); -int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags); -unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state); -void yahoo_set_status(PurpleAccount *account, PurpleStatus *status); -void yahoo_set_idle(PurpleConnection *gc, int idle); -void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message); -void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); -void yahoo_add_deny(PurpleConnection *gc, const char *who); -void yahoo_rem_deny(PurpleConnection *gc, const char *who); -void yahoo_set_permit_deny(PurpleConnection *gc); -void yahoo_keepalive(PurpleConnection *gc); -void yahoo_change_buddys_group(PurpleConnection *gc, const char *who, const char *old_group, const char *new_group); -void yahoo_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies); -gboolean yahoo_offline_message(const PurpleBuddy *buddy); -gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type); -GList *yahoo_attention_types(PurpleAccount *account); - -GList *yahoo_actions(PurplePlugin *plugin, gpointer context); -void yahoopurple_register_commands(void); -gssize yahoo_get_max_message_size(PurpleConversation *conv); - -PurpleCmdRet yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data); -PurpleCmdRet yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data); -PurpleCmdRet yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data); -/* needed for xfer, thought theyd be useful for other enhancements later on - Returns list of cookies stored in yahoo_data formatted as a single null terminated string - returned value must be g_freed -*/ -gchar* yahoo_get_cookies(PurpleConnection *gc); - -/* send p2p pkt containing our encoded ip, asking peer to connect to us */ -void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13); - -#endif /* _LIBYMSG_H_ */
--- a/libpurple/protocols/yahoo/util.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/util.c Fri Jan 31 18:02:20 2014 +0530 @@ -26,9 +26,9 @@ #include "debug.h" #include "internal.h" -#include "prpl.h" +#include "protocol.h" -#include "libymsg.h" +#include "ymsg.h" #include <string.h>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,427 @@ +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "internal.h" + +#include <account.h> +#include <core.h> +#include <plugins.h> + +#include "ymsg.h" +#include "yahoo.h" +#include "yahoojp.h" +#include "yahoochat.h" +#include "yahoo_aliases.h" +#include "yahoo_doodle.h" +#include "yahoo_filexfer.h" +#include "yahoo_picture.h" + +static PurpleProtocol *yahoo_protocol = NULL; +static PurpleProtocol *yahoojp_protocol = NULL; + +static GSList *cmds = NULL; + +static void yahoo_register_commands(void) +{ + PurpleCmdId id; + + id = purple_cmd_register("join", "s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | + PURPLE_CMD_FLAG_PROTOCOL_ONLY, + "prpl-yahoo", yahoopurple_cmd_chat_join, + _("join <room>: Join a chat room on the Yahoo network"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("list", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | + PURPLE_CMD_FLAG_PROTOCOL_ONLY, + "prpl-yahoo", yahoopurple_cmd_chat_list, + _("list: List rooms on the Yahoo network"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("buzz", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY, + "prpl-yahoo", yahoopurple_cmd_buzz, + _("buzz: Buzz a user to get their attention"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("doodle", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY, + "prpl-yahoo", yahoo_doodle_purple_cmd_start, + _("doodle: Request user to start a Doodle session"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); +} + +static void yahoo_unregister_commands(void) +{ + while (cmds) { + PurpleCmdId id = GPOINTER_TO_UINT(cmds->data); + purple_cmd_unregister(id); + cmds = g_slist_delete_link(cmds, cmds); + } +} + +static PurpleAccount *find_acct(const char *protocol, const char *acct_id) +{ + PurpleAccount *acct = NULL; + + /* If we have a specific acct, use it */ + if (acct_id) { + acct = purple_accounts_find(acct_id, protocol); + if (acct && !purple_account_is_connected(acct)) + acct = NULL; + } else { /* Otherwise find an active account for the protocol */ + GList *l = purple_accounts_get_all(); + while (l) { + if (!strcmp(protocol, purple_account_get_protocol_id(l->data)) + && purple_account_is_connected(l->data)) { + acct = l->data; + break; + } + l = l->next; + } + } + + return acct; +} + +/* This may not be the best way to do this, but we find the first key w/o a value + * and assume it is the buddy name */ +static void yahoo_find_uri_novalue_param(gpointer key, gpointer value, gpointer user_data) +{ + char **retval = user_data; + + if (value == NULL && *retval == NULL) { + *retval = key; + } +} + +static gboolean yahoo_uri_handler(const char *proto, const char *cmd, GHashTable *params) +{ + char *acct_id = g_hash_table_lookup(params, "account"); + PurpleAccount *acct; + + if (g_ascii_strcasecmp(proto, "ymsgr")) + return FALSE; + + acct = find_acct(purple_protocol_get_id(yahoo_protocol), acct_id); + + if (!acct) + return FALSE; + + /* ymsgr:SendIM?screename&m=The+Message */ + if (!g_ascii_strcasecmp(cmd, "SendIM")) { + char *sname = NULL; + g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &sname); + if (sname) { + char *message = g_hash_table_lookup(params, "m"); + + PurpleIMConversation *im = purple_conversations_find_im_with_account( + sname, acct); + if (im == NULL) + im = purple_im_conversation_new(acct, sname); + purple_conversation_present(PURPLE_CONVERSATION(im)); + + if (message) { + /* Spaces are encoded as '+' */ + g_strdelimit(message, "+", ' '); + purple_conversation_send_confirm(PURPLE_CONVERSATION(im), message); + } + } + /* else + **If pidgindialogs_im() was in the core, we could use it here. + * It is all purple_request_* based, but I'm not sure it really belongs in the core + pidgindialogs_im(); */ + + return TRUE; + } + /* ymsgr:Chat?roomname */ + else if (!g_ascii_strcasecmp(cmd, "Chat")) { + char *rname = NULL; + g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &rname); + if (rname) { + /* This is somewhat hacky, but the params aren't useful after this command */ + g_hash_table_insert(params, g_strdup("room"), g_strdup(rname)); + g_hash_table_insert(params, g_strdup("type"), g_strdup("Chat")); + serv_join_chat(purple_account_get_connection(acct), params); + } + /* else + ** Same as above (except that this would have to be re-written using purple_request_*) + pidgin_blist_joinchat_show(); */ + + return TRUE; + } + /* ymsgr:AddFriend?name */ + else if (!g_ascii_strcasecmp(cmd, "AddFriend")) { + char *name = NULL; + g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &name); + purple_blist_request_add_buddy(acct, name, NULL, NULL); + return TRUE; + } + + return FALSE; +} + +static GHashTable * +yahoo_get_account_text_table(PurpleAccount *account) +{ + GHashTable *table; + table = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(table, "login_label", (gpointer)_("Yahoo ID...")); + return table; +} + +static PurpleWhiteboardOps yahoo_whiteboard_ops = +{ + yahoo_doodle_start, + yahoo_doodle_end, + yahoo_doodle_get_dimensions, + NULL, + yahoo_doodle_get_brush, + yahoo_doodle_set_brush, + yahoo_doodle_send_draw_list, + yahoo_doodle_clear, + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static void +yahoo_protocol_init(PurpleProtocol *protocol) +{ + PurpleAccountOption *option; + + protocol->id = "prpl-yahoo"; + protocol->name = "Yahoo"; + protocol->options = OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC | + OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE; + protocol->icon_spec = purple_buddy_icon_spec_new("png,gif,jpeg", + 96, 96, 96, 96, 0, + PURPLE_ICON_SCALE_SEND); + + protocol->whiteboard_ops = &yahoo_whiteboard_ops; + + option = purple_account_option_int_new(_("Pager port"), "port", YAHOO_PAGER_PORT); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOO_XFER_HOST); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOO_ROOMLIST_LOCALE); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_string_new(_("Encoding"), "local_charset", "UTF-8"); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + +#if 0 + option = purple_account_option_bool_new(_("Use account proxy for HTTP and HTTPS connections"), "proxy_ssl", FALSE); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); +#endif +} + +static void +yahoo_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = yahoo_login; + klass->close = yahoo_close; + klass->status_types = yahoo_status_types; + klass->list_icon = yahoo_list_icon; +} + +static void +yahoo_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = yahoo_get_actions; + client_iface->list_emblem = yahoo_list_emblem; + client_iface->status_text = yahoo_status_text; + client_iface->tooltip_text = yahoo_tooltip_text; + client_iface->blist_node_menu = yahoo_blist_node_menu; + client_iface->normalize = purple_normalize_nocase; + client_iface->offline_message = yahoo_offline_message; + client_iface->get_account_text_table = yahoo_get_account_text_table; + client_iface->get_max_message_size = yahoo_get_max_message_size; +} + +static void +yahoo_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->get_info = yahoo_get_info; + server_iface->set_status = yahoo_set_status; + server_iface->set_idle = yahoo_set_idle; + server_iface->add_buddy = yahoo_add_buddy; + server_iface->remove_buddy = yahoo_remove_buddy; + server_iface->keepalive = yahoo_keepalive; + server_iface->alias_buddy = yahoo_update_alias; + server_iface->group_buddy = yahoo_change_buddys_group; + server_iface->rename_group = yahoo_rename_group; + server_iface->set_buddy_icon = yahoo_set_buddy_icon; +} + +static void +yahoo_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = yahoo_send_im; + im_iface->send_typing = yahoo_send_typing; +} + +static void +yahoo_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->info = yahoo_c_info; + chat_iface->info_defaults = yahoo_c_info_defaults; + chat_iface->join = yahoo_c_join; + chat_iface->get_name = yahoo_get_chat_name; + chat_iface->invite = yahoo_c_invite; + chat_iface->leave = yahoo_c_leave; + chat_iface->send = yahoo_c_send; +} + +static void +yahoo_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) +{ + privacy_iface->add_deny = yahoo_add_deny; + privacy_iface->rem_deny = yahoo_rem_deny; + privacy_iface->set_permit_deny = yahoo_set_permit_deny; +} + +static void +yahoo_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface) +{ + roomlist_iface->get_list = yahoo_roomlist_get_list; + roomlist_iface->cancel = yahoo_roomlist_cancel; + roomlist_iface->expand_category = yahoo_roomlist_expand_category; +} + +static void +yahoo_protocol_attention_iface_init(PurpleProtocolAttentionIface *attention_iface) +{ + attention_iface->send = yahoo_send_attention; + attention_iface->get_types = yahoo_attention_types; +} + +static void +yahoo_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->can_receive = yahoo_can_receive_file; + xfer_iface->send = yahoo_send_file; + xfer_iface->new_xfer = yahoo_new_xfer; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + YahooProtocol, yahoo_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + yahoo_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + yahoo_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + yahoo_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + yahoo_protocol_chat_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, + yahoo_protocol_privacy_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE, + yahoo_protocol_roomlist_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE, + yahoo_protocol_attention_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + yahoo_protocol_xfer_iface_init) +); + +static PurplePluginInfo * +plugin_query(GError **error) +{ + return purple_plugin_info_new( + "id", "prpl-yahoo", + "name", "Yahoo Protocols", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("Yahoo! and Yahoo! JAPAN Protocols Plugin"), + "description", N_("Yahoo! and Yahoo! JAPAN Protocols Plugin"), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + yahoo_protocol_register_type(plugin); + yahoojp_protocol_register_type(plugin); + + yahoo_protocol = purple_protocols_add(YAHOO_TYPE_PROTOCOL, error); + if (!yahoo_protocol) + return FALSE; + + yahoojp_protocol = purple_protocols_add(YAHOOJP_TYPE_PROTOCOL, error); + if (!yahoojp_protocol) + return FALSE; + + yahoo_init_colorht(); + + yahoo_register_commands(); + yahoojp_register_commands(); + + purple_signal_connect(purple_get_core(), "uri-handler", yahoo_protocol, + PURPLE_CALLBACK(yahoo_uri_handler), NULL); + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + yahoojp_unregister_commands(); + yahoo_unregister_commands(); + + yahoo_dest_colorht(); + + if (!purple_protocols_remove(yahoojp_protocol, error)) + return FALSE; + + if (!purple_protocols_remove(yahoo_protocol, error)) + return FALSE; + + return TRUE; +} + +PURPLE_PLUGIN_INIT(yahoo, plugin_query, plugin_load, plugin_unload);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,49 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#ifndef _YAHOO_H_ +#define _YAHOO_H_ + +#include "protocol.h" + +#define YAHOO_TYPE_PROTOCOL (yahoo_protocol_get_type()) +#define YAHOO_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), YAHOO_TYPE_PROTOCOL, YahooProtocol)) +#define YAHOO_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), YAHOO_TYPE_PROTOCOL, YahooProtocolClass)) +#define YAHOO_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), YAHOO_TYPE_PROTOCOL)) +#define YAHOO_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), YAHOO_TYPE_PROTOCOL)) +#define YAHOO_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), YAHOO_TYPE_PROTOCOL, YahooProtocolClass)) + +typedef struct _YahooProtocol +{ + PurpleProtocol parent; +} YahooProtocol; + +typedef struct _YahooProtocolClass +{ + PurpleProtocolClass parent_class; +} YahooProtocolClass; + +/** + * Returns the GType for the YahooProtocol object. + */ +G_MODULE_EXPORT GType yahoo_protocol_get_type(void); + +#endif /* _YAHOO_H_ */
--- a/libpurple/protocols/yahoo/yahoo_aliases.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/yahoo_aliases.c Fri Jan 31 18:02:20 2014 +0530 @@ -32,7 +32,7 @@ #include "util.h" #include "request.h" #include "version.h" -#include "libymsg.h" +#include "ymsg.h" #include "yahoo_aliases.h" #include "yahoo_friend.h" #include "yahoo_packet.h"
--- a/libpurple/protocols/yahoo/yahoo_aliases.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/yahoo_aliases.h Fri Jan 31 18:02:20 2014 +0530 @@ -30,7 +30,7 @@ #include "debug.h" #include "util.h" #include "version.h" -#include "libymsg.h" +#include "ymsg.h" #include "yahoo_packet.h" void yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias);
--- a/libpurple/protocols/yahoo/yahoo_doodle.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/yahoo_doodle.c Fri Jan 31 18:02:20 2014 +0530 @@ -33,14 +33,14 @@ #include "cmds.h" #include "debug.h" #include "notify.h" -#include "prpl.h" +#include "protocol.h" #include "proxy.h" #include "request.h" #include "server.h" #include "util.h" #include "version.h" -#include "libymsg.h" +#include "ymsg.h" #include "yahoo_packet.h" #include "yahoo_friend.h" #include "yahoochat.h"
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Fri Jan 31 18:02:20 2014 +0530 @@ -25,7 +25,7 @@ #include "internal.h" #include "dnsquery.h" -#include "prpl.h" +#include "protocol.h" #include "util.h" #include "debug.h" #include "http.h" @@ -33,7 +33,7 @@ #include "notify.h" #include "proxy.h" #include "xfer.h" -#include "libymsg.h" +#include "ymsg.h" #include "yahoo_packet.h" #include "yahoo_filexfer.h" #include "yahoo_doodle.h"
--- a/libpurple/protocols/yahoo/yahoo_friend.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/yahoo_friend.c Fri Jan 31 18:02:20 2014 +0530 @@ -22,7 +22,7 @@ */ #include "internal.h" -#include "prpl.h" +#include "protocol.h" #include "util.h" #include "debug.h"
--- a/libpurple/protocols/yahoo/yahoo_friend.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/yahoo_friend.h Fri Jan 31 18:02:20 2014 +0530 @@ -25,7 +25,7 @@ #ifndef _YAHOO_FRIEND_H_ #define _YAHOO_FRIEND_H_ -#include "libymsg.h" +#include "ymsg.h" #include "yahoo_packet.h" typedef enum {
--- a/libpurple/protocols/yahoo/yahoo_packet.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/yahoo_packet.c Fri Jan 31 18:02:20 2014 +0530 @@ -24,7 +24,7 @@ #include "internal.h" #include "debug.h" -#include "libymsg.h" +#include "ymsg.h" #include "yahoo_packet.h" struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, enum yahoo_status status, int id) @@ -208,7 +208,7 @@ /* * Originally this function used g_slist_append(). I changed * it to use g_slist_prepend() for improved performance. - * Ideally the Yahoo! PRPL code would be indifferent to the + * Ideally the Yahoo! protocol code would be indifferent to the * order of the key/value pairs, but I don't know if this is * the case for all incoming messages. To be on the safe side * we reverse the list.
--- a/libpurple/protocols/yahoo/yahoo_picture.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/yahoo_picture.c Fri Jan 31 18:02:20 2014 +0530 @@ -28,11 +28,11 @@ #include "buddylist.h" #include "debug.h" #include "http.h" -#include "prpl.h" +#include "protocol.h" #include "proxy.h" #include "util.h" -#include "libymsg.h" +#include "ymsg.h" #include "yahoo_packet.h" #include "yahoo_friend.h" #include "yahoo_picture.h"
--- a/libpurple/protocols/yahoo/yahoo_profile.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/yahoo_profile.c Fri Jan 31 18:02:20 2014 +0530 @@ -32,7 +32,7 @@ #include "imgstore.h" #endif /* PHOTO_SUPPORT */ -#include "libymsg.h" +#include "ymsg.h" #include "yahoo_friend.h" typedef struct { @@ -974,7 +974,7 @@ #endif /* PHOTO_SUPPORT */ /* Jun 29 05 Bleeter: Y! changed their profile pages. Terminators now seem to be */ - /* </dd> and not \n. The prpl's need to be audited before it can be moved */ + /* </dd> and not \n. The protocol's need to be audited before it can be moved */ /* in to purple_markup_strip_html*/ char *fudged_buffer;
--- a/libpurple/protocols/yahoo/yahoochat.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/yahoochat.c Fri Jan 31 18:02:20 2014 +0530 @@ -34,13 +34,13 @@ #include "debug.h" #include "http.h" -#include "prpl.h" +#include "protocol.h" #include "conversation.h" #include "notify.h" #include "util.h" -#include "libymsg.h" +#include "ymsg.h" #include "yahoo_packet.h" #include "yahoochat.h" #include "ycht.h" @@ -1137,9 +1137,9 @@ GList *yahoo_c_info(PurpleConnection *gc) { GList *m = NULL; - struct proto_chat_entry *pce; + PurpleProtocolChatEntry *pce; - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Room:"); pce->identifier = "room"; pce->required = TRUE;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/yahoo/yahoojp.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,131 @@ +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "internal.h" + +#include <account.h> +#include <plugins.h> + +#include "yahoojp.h" +#include "ymsg.h" +#include "yahoochat.h" +#include "yahoo_aliases.h" +#include "yahoo_doodle.h" +#include "yahoo_filexfer.h" +#include "yahoo_picture.h" + +static GSList *cmds = NULL; + +void yahoojp_register_commands(void) +{ + PurpleCmdId id; + + id = purple_cmd_register("join", "s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | + PURPLE_CMD_FLAG_PROTOCOL_ONLY, + "prpl-yahoojp", yahoopurple_cmd_chat_join, + _("join <room>: Join a chat room on the Yahoo network"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("list", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | + PURPLE_CMD_FLAG_PROTOCOL_ONLY, + "prpl-yahoojp", yahoopurple_cmd_chat_list, + _("list: List rooms on the Yahoo network"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("buzz", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY, + "prpl-yahoojp", yahoopurple_cmd_buzz, + _("buzz: Buzz a user to get their attention"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("doodle", "", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY, + "prpl-yahoojp", yahoo_doodle_purple_cmd_start, + _("doodle: Request user to start a Doodle session"), NULL); + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); +} + +void yahoojp_unregister_commands(void) +{ + while (cmds) { + PurpleCmdId id = GPOINTER_TO_UINT(cmds->data); + purple_cmd_unregister(id); + cmds = g_slist_delete_link(cmds, cmds); + } +} + +static GHashTable * +yahoojp_get_account_text_table(PurpleAccount *account) +{ + GHashTable *table; + table = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(table, "login_label", (gpointer)_("Yahoo JAPAN ID...")); + return table; +} + +static void +yahoojp_protocol_init(PurpleProtocol *protocol) +{ + PurpleAccountOption *option; + + protocol->id = "prpl-yahoojp"; + protocol->name = "Yahoo JAPAN"; + + /* delete yahoo's protocol options */ + purple_protocol_override(protocol, PURPLE_PROTOCOL_OVERRIDE_PROTOCOL_OPTIONS); + + option = purple_account_option_int_new(_("Pager port"), "port", YAHOO_PAGER_PORT); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOOJP_XFER_HOST); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOOJP_ROOMLIST_LOCALE); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_string_new(_("Encoding"), "local_charset", "UTF-8"); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); + + option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); +} + +static void +yahoojp_protocol_class_init(PurpleProtocolClass *klass) +{ +} + +static void +yahoojp_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_account_text_table = yahoojp_get_account_text_table; +} + +PURPLE_DEFINE_TYPE_EXTENDED( + YahooJPProtocol, yahoojp_protocol, YAHOO_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + yahoojp_protocol_client_iface_init) +);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/yahoo/yahoojp.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,57 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#ifndef _YAHOOJP_H_ +#define _YAHOOJP_H_ + +#include "yahoo.h" + +#define YAHOOJP_TYPE_PROTOCOL (yahoojp_protocol_get_type()) +#define YAHOOJP_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), YAHOOJP_TYPE_PROTOCOL, YahooJPProtocol)) +#define YAHOOJP_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), YAHOOJP_TYPE_PROTOCOL, YahooJPProtocolClass)) +#define YAHOOJP_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), YAHOOJP_TYPE_PROTOCOL)) +#define YAHOOJP_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), YAHOOJP_TYPE_PROTOCOL)) +#define YAHOOJP_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), YAHOOJP_TYPE_PROTOCOL, YahooJPProtocolClass)) + +typedef struct _YahooJPProtocol +{ + YahooProtocol parent; +} YahooJPProtocol; + +typedef struct _YahooJPProtocolClass +{ + YahooProtocolClass parent_class; +} YahooJPProtocolClass; + +/** + * Registers the YahooJPProtocol type in the type system. + */ +void yahoojp_protocol_register_type(PurplePlugin *plugin); + +/** + * Returns the GType for the YahooJPProtocol object. + */ +G_MODULE_EXPORT GType yahoojp_protocol_get_type(void); + +void yahoojp_register_commands(void); +void yahoojp_unregister_commands(void); + +#endif /* _YAHOOJP_H_ */
--- a/libpurple/protocols/yahoo/ycht.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/yahoo/ycht.c Fri Jan 31 18:02:20 2014 +0530 @@ -26,7 +26,7 @@ */ #include "internal.h" -#include "prpl.h" +#include "protocol.h" #include "notify.h" #include "account.h" #include "proxy.h" @@ -34,7 +34,7 @@ #include "conversation.h" #include "util.h" -#include "libymsg.h" +#include "ymsg.h" #include "yahoo_packet.h" #include "ycht.h" #include "yahoochat.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/yahoo/ymsg.c Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,5342 @@ +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +/* + * Note: When handling the list of struct yahoo_pair's from an incoming + * packet the value might not be UTF-8. You should either validate that + * it is UTF-8 using g_utf8_validate() or use yahoo_string_decode(). + */ + +#include "internal.h" + +#include "account.h" +#include "accountopt.h" +#include "buddylist.h" +#include "ciphers/md5hash.h" +#include "cmds.h" +#include "core.h" +#include "debug.h" +#include "http.h" +#include "network.h" +#include "notify.h" +#include "protocol.h" +#include "proxy.h" +#include "request.h" +#include "server.h" +#include "util.h" +#include "version.h" +#include "xmlnode.h" + +#include "ymsg.h" +#include "yahoochat.h" +#include "yahoo_aliases.h" +#include "yahoo_doodle.h" +#include "yahoo_filexfer.h" +#include "yahoo_friend.h" +#include "yahoo_packet.h" +#include "yahoo_picture.h" +#include "ycht.h" + +/* #define YAHOO_DEBUG */ + +/* It doesn't look like it is working (the previously used host is down, another + * one doesn't send us back cookies). + */ +#define TRY_WEBMESSENGER_LOGIN 0 + +/* One hour */ +#define PING_TIMEOUT 3600 + +/* One minute */ +#define KEEPALIVE_TIMEOUT 60 + +#if TRY_WEBMESSENGER_LOGIN +static void +yahoo_login_page_cb(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, gpointer _unused); +#endif /* TRY_WEBMESSENGER_LOGIN */ + +static gboolean yahoo_is_japan(PurpleAccount *account) +{ + return purple_strequal(purple_account_get_protocol_id(account), "prpl-yahoojp"); +} + +static void yahoo_update_status(PurpleConnection *gc, const char *name, YahooFriend *f) +{ + char *status = NULL; + + if (!gc || !name || !f || !purple_blist_find_buddy(purple_connection_get_account(gc), name)) + return; + + switch (f->status) { + case YAHOO_STATUS_OFFLINE: + status = YAHOO_STATUS_TYPE_OFFLINE; + break; + case YAHOO_STATUS_AVAILABLE: + status = YAHOO_STATUS_TYPE_AVAILABLE; + break; + case YAHOO_STATUS_BRB: + status = YAHOO_STATUS_TYPE_BRB; + break; + case YAHOO_STATUS_BUSY: + status = YAHOO_STATUS_TYPE_BUSY; + break; + case YAHOO_STATUS_NOTATHOME: + status = YAHOO_STATUS_TYPE_NOTATHOME; + break; + case YAHOO_STATUS_NOTATDESK: + status = YAHOO_STATUS_TYPE_NOTATDESK; + break; + case YAHOO_STATUS_NOTINOFFICE: + status = YAHOO_STATUS_TYPE_NOTINOFFICE; + break; + case YAHOO_STATUS_ONPHONE: + status = YAHOO_STATUS_TYPE_ONPHONE; + break; + case YAHOO_STATUS_ONVACATION: + status = YAHOO_STATUS_TYPE_ONVACATION; + break; + case YAHOO_STATUS_OUTTOLUNCH: + status = YAHOO_STATUS_TYPE_OUTTOLUNCH; + break; + case YAHOO_STATUS_STEPPEDOUT: + status = YAHOO_STATUS_TYPE_STEPPEDOUT; + break; + case YAHOO_STATUS_INVISIBLE: /* this should never happen? */ + status = YAHOO_STATUS_TYPE_INVISIBLE; + break; + case YAHOO_STATUS_CUSTOM: + case YAHOO_STATUS_IDLE: + if (!f->away) + status = YAHOO_STATUS_TYPE_AVAILABLE; + else + status = YAHOO_STATUS_TYPE_AWAY; + break; + default: + purple_debug_warning("yahoo", "Warning, unknown status %d\n", f->status); + break; + } + + if (status) { + if (f->status == YAHOO_STATUS_CUSTOM) + purple_protocol_got_user_status(purple_connection_get_account(gc), name, status, "message", + yahoo_friend_get_status_message(f), NULL); + else + purple_protocol_got_user_status(purple_connection_get_account(gc), name, status, NULL); + } + + if (f->idle != 0) + purple_protocol_got_user_idle(purple_connection_get_account(gc), name, TRUE, f->idle); + else + purple_protocol_got_user_idle(purple_connection_get_account(gc), name, FALSE, 0); + + if (f->sms) + purple_protocol_got_user_status(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE, NULL); + else + purple_protocol_got_user_status_deactive(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE); +} + +static void yahoo_process_status(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + PurpleAccount *account = purple_connection_get_account(gc); + GSList *l = pkt->hash; + YahooFriend *f = NULL; + char *name = NULL; + gboolean unicode = FALSE; + char *message = NULL; + YahooFederation fed = YAHOO_FEDERATION_NONE; + char *fedname = NULL; + + if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) { + if (!purple_account_get_remember_password(account)) + purple_account_set_password(account, NULL, NULL, NULL); + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE, + _("You have signed on from another location")); + return; + } + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 0: /* we won't actually do anything with this */ + case 1: /* we won't actually do anything with this */ + break; + case 8: /* how many online buddies we have */ + break; + case 7: /* the current buddy */ + /* update the previous buddy before changing the variables */ + if (f) { + if (message) + yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode)); + if (name) + yahoo_update_status(gc, name, f); + } + name = message = NULL; + f = NULL; + if (pair->value && g_utf8_validate(pair->value, -1, NULL)) { + GSList *tmplist; + + name = pair->value; + + /* Look ahead to see if we have the federation info about the buddy */ + for (tmplist = l->next; tmplist; tmplist = tmplist->next) { + struct yahoo_pair *p = tmplist->data; + if (p->key == 7) + break; + if (p->key == 241) { + fed = strtol(p->value, NULL, 10); + g_free(fedname); + switch (fed) { + case YAHOO_FEDERATION_MSN: + name = fedname = g_strconcat("msn/", name, NULL); + break; + case YAHOO_FEDERATION_OCS: + name = fedname = g_strconcat("ocs/", name, NULL); + break; + case YAHOO_FEDERATION_IBM: + name = fedname = g_strconcat("ibm/", name, NULL); + break; + case YAHOO_FEDERATION_NONE: + default: + fedname = NULL; + break; + } + break; + } + } + f = yahoo_friend_find_or_new(gc, name); + f->fed = fed; + } + break; + case 10: /* state */ + if (!f) + break; + + f->status = strtol(pair->value, NULL, 10); + if ((f->status >= YAHOO_STATUS_BRB) && (f->status <= YAHOO_STATUS_STEPPEDOUT)) + f->away = 1; + else + f->away = 0; + + if (f->status == YAHOO_STATUS_IDLE) { + /* Idle may have already been set in a more precise way in case 137 */ + if (f->idle == 0) + { + if(pkt->service == YAHOO_SERVICE_STATUS_15) + f->idle = -1; + else + f->idle = time(NULL); + } + } else + f->idle = 0; + + if (f->status != YAHOO_STATUS_CUSTOM) + yahoo_friend_set_status_message(f, NULL); + + f->sms = 0; + break; + case 19: /* custom message */ + if (f) + message = pair->value; + break; + case 11: /* this is the buddy's session id */ + if (f) + f->session_id = strtol(pair->value, NULL, 10); + break; + case 17: /* in chat? */ + break; + case 47: /* is custom status away or not? 2=idle*/ + if (!f) + break; + + /* I have no idea what it means when this is + * set when someone's available, but it doesn't + * mean idle. */ + if (f->status == YAHOO_STATUS_AVAILABLE) + break; + + f->away = strtol(pair->value, NULL, 10); + if (f->away == 2) { + /* Idle may have already been set in a more precise way in case 137 */ + if (f->idle == 0) + { + if(pkt->service == YAHOO_SERVICE_STATUS_15) + f->idle = -1; + else + f->idle = time(NULL); + } + } + + break; + case 138: /* when value is 1, either we're not idle, or we are but won't say how long */ + if (!f) + break; + + if( (strtol(pair->value, NULL, 10) == 1) && (f->idle) ) + f->idle = -1; + break; + case 137: /* usually idle time in seconds, sometimes login time */ + if (!f) + break; + + if (f->status != YAHOO_STATUS_AVAILABLE) + f->idle = time(NULL) - strtol(pair->value, NULL, 10); + break; + case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */ + if (strtol(pair->value, NULL, 10) == 0) { + if (f) + f->status = YAHOO_STATUS_OFFLINE; + if (name) { + purple_protocol_got_user_status(account, name, "offline", NULL); + purple_protocol_got_user_status_deactive(account, name, YAHOO_STATUS_TYPE_MOBILE); + } + break; + } + break; + case 60: /* SMS */ + if (f) { + f->sms = strtol(pair->value, NULL, 10); + yahoo_update_status(gc, name, f); + } + break; + case 197: /* Avatars */ + { + guchar *decoded; + char *tmp; + gsize len; + + if (pair->value) { + decoded = purple_base64_decode(pair->value, &len); + if (decoded && len > 0) { + tmp = purple_str_binary_to_ascii(decoded, len); + purple_debug_info("yahoo", "Got key 197, value = %s\n", tmp); + g_free(tmp); + } + g_free(decoded); + } + break; + } + case 192: /* Pictures, aka Buddy Icons, checksum */ + { + /* FIXME: Please, if you know this protocol, + * FIXME: fix up the strtol() stuff if possible. */ + int cksum = strtol(pair->value, NULL, 10); + const char *locksum = NULL; + PurpleBuddy *b; + + if (!name) + break; + + b = purple_blist_find_buddy(purple_connection_get_account(gc), name); + + if (!cksum || (cksum == -1)) { + if (f) + yahoo_friend_set_buddy_icon_need_request(f, TRUE); + purple_buddy_icons_set_for_user(purple_connection_get_account(gc), name, NULL, 0, NULL); + break; + } + + if (!f) + break; + + yahoo_friend_set_buddy_icon_need_request(f, FALSE); + if (b) { + locksum = purple_buddy_icons_get_checksum_for_user(b); + if (!locksum || (cksum != strtol(locksum, NULL, 10))) + yahoo_send_picture_request(gc, name); + } + + break; + } + case 16: /* Custom error message */ + { + char *tmp = yahoo_string_decode(gc, pair->value, TRUE); + purple_notify_error(gc, NULL, tmp, NULL, + purple_request_cpar_from_connection(gc)); + g_free(tmp); + } + break; + case 97: /* Unicode status message */ + unicode = !strcmp(pair->value, "1"); + break; + case 244: /* client version number. Yahoo Client Detection */ + if(f && strtol(pair->value, NULL, 10)) + f->version_id = strtol(pair->value, NULL, 10); + break; + case 241: /* Federated network buddy belongs to */ + break; /* We process this when get '7' */ + default: + purple_debug_warning("yahoo", + "Unknown status key %d\n", pair->key); + break; + } + + l = l->next; + } + + if (f) { + if (pkt->service == YAHOO_SERVICE_LOGOFF) + f->status = YAHOO_STATUS_OFFLINE; + if (message) + yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode)); + + if (name) /* update the last buddy */ + yahoo_update_status(gc, name, f); + } + + g_free(fedname); +} + +static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const char *name, const char *group) +{ + PurpleBuddy *b; + PurpleGroup *g; + GSList *list, *i; + gboolean onlist = FALSE; + char *oname = NULL; + + if (g_hash_table_lookup_extended(ht, name, (gpointer *)&oname, (gpointer *)&list)) + g_hash_table_steal(ht, oname); + else + list = purple_blist_find_buddies(account, name); + + for (i = list; i; i = i->next) { + b = i->data; + g = purple_buddy_get_group(b); + if (!purple_utf8_strcasecmp(group, purple_group_get_name(g))) { + purple_debug_misc("yahoo", + "Oh good, %s is in the right group (%s).\n", name, group); + list = g_slist_delete_link(list, i); + onlist = TRUE; + break; + } + } + + if (!onlist) { + purple_debug_misc("yahoo", + "Uhoh, %s isn't on the list (or not in this group), adding him to group %s.\n", name, group); + if (!(g = purple_blist_find_group(group))) { + g = purple_group_new(group); + purple_blist_add_group(g, NULL); + } + b = purple_buddy_new(account, name, NULL); + purple_blist_add_buddy(b, NULL, g, NULL); + } + + if (list) { + if (!oname) + oname = g_strdup(name); + g_hash_table_insert(ht, oname, list); + } else + g_free(oname); +} + +static void yahoo_do_group_cleanup(gpointer key, gpointer value, gpointer user_data) +{ + char *name = key; + GSList *list = value, *i; + PurpleBuddy *b; + PurpleGroup *g; + + for (i = list; i; i = i->next) { + b = i->data; + g = purple_buddy_get_group(b); + purple_debug_misc("yahoo", "Deleting Buddy %s from group %s.\n", name, + purple_group_get_name(g)); + purple_blist_remove_buddy(b); + } +} + +static char *_getcookie(char *rawcookie) +{ + char *cookie = NULL; + char *tmpcookie; + char *cookieend; + + if (strlen(rawcookie) < 2) + return NULL; + tmpcookie = g_strdup(rawcookie+2); + cookieend = strchr(tmpcookie, ';'); + + if (cookieend) + *cookieend = '\0'; + + cookie = g_strdup(tmpcookie); + g_free(tmpcookie); + + return cookie; +} + +static void yahoo_process_cookie(YahooData *yd, char *c) +{ + if (c[0] == 'Y') { + g_free(yd->cookie_y); + yd->cookie_y = _getcookie(c); + } else if (c[0] == 'T') { + g_free(yd->cookie_t); + yd->cookie_t = _getcookie(c); + } else + purple_debug_info("yahoo", "Unrecognized cookie '%c'\n", c[0]); + yd->cookies = g_slist_prepend(yd->cookies, g_strdup(c)); +} + +static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + GSList *l = pkt->hash; + + PurpleAccount *account = purple_connection_get_account(gc); + YahooData *yd = purple_connection_get_protocol_data(gc); + GHashTable *ht; + char *norm_bud = NULL; + char *temp = NULL; + YahooFriend *f = NULL; /* It's your friends. They're going to want you to share your StarBursts. */ + /* But what if you had no friends? */ + YahooFederation fed = YAHOO_FEDERATION_NONE; + int stealth = 0; + + ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free); + + while (l) { + struct yahoo_pair *pair = l->data; + l = l->next; + + switch (pair->key) { + case 302: + /* This is always 318 before a group, 319 before the first s/n in a group, 320 before any ignored s/n. + * It is not sent for s/n's in a group after the first. + * All ignored s/n's are listed last, so when we see a 320 we clear the group and begin marking the + * s/n's as ignored. It is always followed by an identical 300 key. + */ + if (pair->value && !strcmp(pair->value, "320")) { + /* No longer in any group; this indicates the start of the ignore list. */ + g_free(yd->current_list15_grp); + yd->current_list15_grp = NULL; + } + + break; + case 301: /* This is 319 before all s/n's in a group after the first. It is followed by an identical 300. */ + if(temp != NULL) { + switch (fed) { + case YAHOO_FEDERATION_MSN: + norm_bud = g_strconcat("msn/", temp, NULL); + break; + case YAHOO_FEDERATION_OCS: + norm_bud = g_strconcat("ocs/", temp, NULL); + break; + case YAHOO_FEDERATION_IBM: + norm_bud = g_strconcat("ibm/", temp, NULL); + break; + case YAHOO_FEDERATION_PBX: + norm_bud = g_strconcat("pbx/", temp, NULL); + break; + case YAHOO_FEDERATION_NONE: + norm_bud = g_strdup(temp); + break; + } + if (yd->current_list15_grp) { + /* This buddy is in a group */ + f = yahoo_friend_find_or_new(gc, norm_bud); + if (!purple_blist_find_buddy(account, norm_bud)) { + PurpleBuddy *b; + PurpleGroup *g; + if (!(g = purple_blist_find_group(yd->current_list15_grp))) { + g = purple_group_new(yd->current_list15_grp); + purple_blist_add_group(g, NULL); + } + b = purple_buddy_new(account, norm_bud, NULL); + purple_blist_add_buddy(b, NULL, g, NULL); + } + yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp); + if(fed) { + f->fed = fed; + purple_debug_info("yahoo", "Setting federation to %d\n", f->fed); + } + if(stealth == 2) + f->presence = YAHOO_PRESENCE_PERM_OFFLINE; + + /* set p2p status not connected and no p2p packet sent */ + if(fed == YAHOO_FEDERATION_NONE) { + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); + f->p2p_packet_sent = 0; + } else + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT); + } else { + /* This buddy is on the ignore list (and therefore in no group) */ + purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n", purple_account_get_username(account), norm_bud); + purple_account_privacy_deny_add(account, norm_bud, 1); + } + + g_free(norm_bud); + norm_bud=NULL; + fed = YAHOO_FEDERATION_NONE; + stealth = 0; + g_free(temp); + temp = NULL; + } + break; + case 300: /* This is 318 before a group, 319 before any s/n in a group, and 320 before any ignored s/n. */ + break; + case 65: /* This is the group */ + g_free(yd->current_list15_grp); + yd->current_list15_grp = yahoo_string_decode(gc, pair->value, FALSE); + break; + case 7: /* buddy's s/n */ + if (g_utf8_validate(pair->value, -1, NULL)) { + g_free(temp); + temp = g_strdup(purple_normalize(account, pair->value)); + } else { + purple_debug_warning("yahoo", "yahoo_process_list_15 " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 241: /* user on federated network */ + fed = strtol(pair->value, NULL, 10); + break; + case 59: /* somebody told cookies come here too, but im not sure */ + if (g_utf8_validate(pair->value, -1, NULL)) { + yahoo_process_cookie(yd, pair->value); + } else { + purple_debug_warning("yahoo", "yahoo_process_list_15 " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 317: /* Stealth Setting */ + stealth = strtol(pair->value, NULL, 10); + break; + /* case 242: */ /* this seems related to 241 */ + /* break; */ + } + } + + g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL); + + /* The reporter of ticket #9745 determined that we weren't retrieving the + * aliases during buddy list retrieval, so we never updated aliases that + * changed while we were signed off. */ + yahoo_fetch_aliases(gc); + + /* Now that we have processed the buddy list, we can say yahoo has connected */ + purple_connection_set_display_name(gc, purple_normalize(account, purple_account_get_username(account))); + yd->logged_in = TRUE; + purple_debug_info("yahoo","Authentication: Connection established\n"); + purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED); + if (yd->picture_upload_todo) { + yahoo_buddy_icon_upload(gc, yd->picture_upload_todo); + yd->picture_upload_todo = NULL; + } + yahoo_set_status(account, purple_account_get_active_status(account)); + + g_hash_table_destroy(ht); + g_free(temp); +} + +static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + GSList *l = pkt->hash; + gboolean got_serv_list = FALSE; + YahooFriend *f = NULL; + PurpleAccount *account = purple_connection_get_account(gc); + YahooData *yd = purple_connection_get_protocol_data(gc); + GHashTable *ht; + + char **lines; + char **split; + char **buddies; + char **tmp, **bud, *norm_bud; + char *grp = NULL; + + if (pkt->id) + yd->session_id = pkt->id; + + while (l) { + struct yahoo_pair *pair = l->data; + l = l->next; + + switch (pair->key) { + case 87: + if (!yd->tmp_serv_blist) + yd->tmp_serv_blist = g_string_new(pair->value); + else + g_string_append(yd->tmp_serv_blist, pair->value); + break; + case 88: + if (g_utf8_validate(pair->value, -1, NULL)) { + if (!yd->tmp_serv_ilist) + yd->tmp_serv_ilist = g_string_new(pair->value); + else + g_string_append(yd->tmp_serv_ilist, pair->value); + } else { + purple_debug_warning("yahoo", "yahoo_process_list " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 89: + if (g_utf8_validate(pair->value, -1, NULL)) { + yd->profiles = g_strsplit(pair->value, ",", -1); + } else { + purple_debug_warning("yahoo", "yahoo_process_list " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 59: /* cookies, yum */ + if (g_utf8_validate(pair->value, -1, NULL)) { + yahoo_process_cookie(yd, pair->value); + } else { + purple_debug_warning("yahoo", "yahoo_process_list " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case YAHOO_SERVICE_PRESENCE_PERM: + if (g_utf8_validate(pair->value, -1, NULL)) { + if (!yd->tmp_serv_plist) + yd->tmp_serv_plist = g_string_new(pair->value); + else + g_string_append(yd->tmp_serv_plist, pair->value); + } else { + purple_debug_warning("yahoo", "yahoo_process_list " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + } + } + + if (pkt->status != 0) + return; + + if (yd->tmp_serv_blist) { + ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free); + + lines = g_strsplit(yd->tmp_serv_blist->str, "\n", -1); + for (tmp = lines; *tmp; tmp++) { + split = g_strsplit(*tmp, ":", 2); + if (!split) + continue; + if (!split[0] || !split[1]) { + g_strfreev(split); + continue; + } + grp = yahoo_string_decode(gc, split[0], FALSE); + buddies = g_strsplit(split[1], ",", -1); + for (bud = buddies; bud && *bud; bud++) { + if (!g_utf8_validate(*bud, -1, NULL)) { + purple_debug_warning("yahoo", "yahoo_process_list " + "got non-UTF-8 string for bud\n"); + continue; + } + + norm_bud = g_strdup(purple_normalize(account, *bud)); + f = yahoo_friend_find_or_new(gc, norm_bud); + + if (!purple_blist_find_buddy(account, norm_bud)) { + PurpleBuddy *b; + PurpleGroup *g; + if (!(g = purple_blist_find_group(grp))) { + g = purple_group_new(grp); + purple_blist_add_group(g, NULL); + } + b = purple_buddy_new(account, norm_bud, NULL); + purple_blist_add_buddy(b, NULL, g, NULL); + } + + yahoo_do_group_check(account, ht, norm_bud, grp); + /* set p2p status not connected and no p2p packet sent */ + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); + f->p2p_packet_sent = 0; + + g_free(norm_bud); + } + g_strfreev(buddies); + g_strfreev(split); + g_free(grp); + } + g_strfreev(lines); + + g_string_free(yd->tmp_serv_blist, TRUE); + yd->tmp_serv_blist = NULL; + g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL); + g_hash_table_destroy(ht); + } + + if (yd->tmp_serv_ilist) { + buddies = g_strsplit(yd->tmp_serv_ilist->str, ",", -1); + for (bud = buddies; bud && *bud; bud++) { + /* The server is already ignoring the user */ + got_serv_list = TRUE; + purple_account_privacy_deny_add(account, *bud, 1); + } + g_strfreev(buddies); + + g_string_free(yd->tmp_serv_ilist, TRUE); + yd->tmp_serv_ilist = NULL; + } + + if (got_serv_list && + ((purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST) && + (purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_DENY_ALL) && + (purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS))) + { + purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS); + purple_debug_info("yahoo", "%s privacy defaulting to PURPLE_ACCOUNT_PRIVACY_DENY_USERS.\n", + purple_account_get_username(account)); + } + + if (yd->tmp_serv_plist) { + buddies = g_strsplit(yd->tmp_serv_plist->str, ",", -1); + for (bud = buddies; bud && *bud; bud++) { + f = yahoo_friend_find(gc, *bud); + if (f) { + purple_debug_info("yahoo", "%s setting presence for %s to PERM_OFFLINE\n", + purple_account_get_username(account), *bud); + f->presence = YAHOO_PRESENCE_PERM_OFFLINE; + } + } + g_strfreev(buddies); + g_string_free(yd->tmp_serv_plist, TRUE); + yd->tmp_serv_plist = NULL; + + } + /* Now that we've got the list, request aliases */ + yahoo_fetch_aliases(gc); +} + +/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */ +static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type) +{ + PurpleAccount *account; + char *msg = NULL; + char *from = NULL; + char *stat = NULL; + char *game = NULL; + YahooFriend *f = NULL; + GSList *l = pkt->hash; + gint val_11 = 0; + YahooData *yd = purple_connection_get_protocol_data(gc); + YahooFederation fed = YAHOO_FEDERATION_NONE; + + account = purple_connection_get_account(gc); + + while (l) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4 || pair->key == 1) { + if (g_utf8_validate(pair->value, -1, NULL)) { + from = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_notify " + "got non-UTF-8 string for key %d\n", pair->key); + } + } else if (pair->key == 49) { + msg = pair->value; + } else if (pair->key == 13) { + stat = pair->value; + } else if (pair->key == 14) { + if (g_utf8_validate(pair->value, -1, NULL)) { + game = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_notify " + "got non-UTF-8 string for key %d\n", pair->key); + } + } else if (pair->key == 11) { + val_11 = strtol(pair->value, NULL, 10); + } else if (pair->key == 241) { + fed = strtol(pair->value, NULL, 10); + } + l = l->next; + } + + if (!from || !msg) + return; + + /* disconnect the peer if connected through p2p and sends wrong value for session id */ + if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) { + purple_debug_warning("yahoo","p2p: %s sent us notify with wrong session id. Disconnecting p2p connection to peer\n", from); + /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ + g_hash_table_remove(yd->peers, from); + return; + } + + if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING")) + && (purple_account_privacy_check(account, from))) + { + char *fed_from = from; + switch (fed) { + case YAHOO_FEDERATION_MSN: + fed_from = g_strconcat("msn/", from, NULL); + break; + case YAHOO_FEDERATION_OCS: + fed_from = g_strconcat("ocs/", from, NULL); + break; + case YAHOO_FEDERATION_IBM: + fed_from = g_strconcat("ibm/", from, NULL); + break; + case YAHOO_FEDERATION_PBX: + fed_from = g_strconcat("pbx/", from, NULL); + break; + case YAHOO_FEDERATION_NONE: + default: + break; + } + + if (stat && *stat == '1') + serv_got_typing(gc, fed_from, 0, PURPLE_IM_TYPING); + else + serv_got_typing_stopped(gc, fed_from); + + if (fed_from != from) + g_free(fed_from); + + } else if (!g_ascii_strncasecmp(msg, "GAME", strlen("GAME"))) { + PurpleBuddy *bud = purple_blist_find_buddy(account, from); + + if (!bud) { + purple_debug_warning("yahoo", + "%s is playing a game, and doesn't want you to know.\n", from); + } + + f = yahoo_friend_find(gc, from); + if (!f) + return; /* if they're not on the list, don't bother */ + + yahoo_friend_set_game(f, NULL); + + if (stat && *stat == '1') { + yahoo_friend_set_game(f, game); + if (bud) + yahoo_update_status(gc, from, f); + } + } else if (!g_ascii_strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) { + PurpleIMConversation *im = purple_conversations_find_im_with_account(from, account); + char *buf = g_strdup_printf(_("%s has sent you a webcam invite, which is not yet supported."), from); + purple_conversation_write(PURPLE_CONVERSATION(im), NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL)); + g_free(buf); + } +} + + +struct _yahoo_im { + char *from; + char *active_id; + int time; + int utf8; + int buddy_icon; + char *id; + char *msg; + YahooFederation fed; + char *fed_from; +}; + +static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + PurpleAccount *account; + GSList *l = pkt->hash; + struct _yahoo_im *sms = NULL; + YahooData *yd; + char *server_msg = NULL; + char *m; + + yd = purple_connection_get_protocol_data(gc); + account = purple_connection_get_account(gc); + + while (l != NULL) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4) { + if (g_utf8_validate(pair->value, -1, NULL)) { + sms = g_new0(struct _yahoo_im, 1); + sms->from = g_strdup_printf("+%s", pair->value); + sms->time = time(NULL); + sms->utf8 = TRUE; + } else { + purple_debug_warning("yahoo", "yahoo_process_sms_message " + "got non-UTF-8 string for key %d\n", pair->key); + } + } else if (pair->key == 14) { + if (sms) + sms->msg = pair->value; + } else if (pair->key == 68) { + if(sms) + g_hash_table_insert(yd->sms_carrier, g_strdup(sms->from), g_strdup(pair->value)); + } else if (pair->key == 16) { + if (g_utf8_validate(pair->value, -1, NULL)) { + server_msg = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_sms_message " + "got non-UTF-8 string for key %d\n", pair->key); + } + } + l = l->next; + } + + if(!sms) { + purple_debug_info("yahoo", "Received a malformed SMS packet!\n"); + return; + } + + if( (pkt->status == -1) || (pkt->status == YAHOO_STATUS_DISCONNECTED) ) { + if (server_msg) { + PurpleIMConversation *im; + im = purple_conversations_find_im_with_account(sms->from, account); + if (im == NULL) + im = purple_im_conversation_new(account, sms->from); + purple_conversation_write(PURPLE_CONVERSATION(im), NULL, server_msg, PURPLE_MESSAGE_SYSTEM, time(NULL)); + } + else { + purple_notify_error(gc, NULL, + _("Your SMS was not delivered"), NULL, + purple_request_cpar_from_connection(gc)); + } + + g_free(sms->from); + g_free(sms); + return ; + } + + if (!sms->from || !sms->msg) { + g_free(sms); + return; + } + + m = yahoo_string_decode(gc, sms->msg, sms->utf8); + serv_got_im(gc, sms->from, m, 0, sms->time); + + g_free(m); + g_free(sms->from); + g_free(sms); +} + +/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */ +static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type) +{ + PurpleAccount *account; + YahooData *yd = purple_connection_get_protocol_data(gc); + GSList *l = pkt->hash; + GSList *list = NULL; + struct _yahoo_im *im = NULL; + + account = purple_connection_get_account(gc); + + if (pkt->status <= 1 || pkt->status == 5 || pkt->status == YAHOO_STATUS_OFFLINE) { + /* messages are received with status YAHOO_STATUS_OFFLINE in case of p2p */ + while (l != NULL) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4 || pair->key == 1) { + if (g_utf8_validate(pair->value, -1, NULL)) { + im = g_new0(struct _yahoo_im, 1); + list = g_slist_append(list, im); + im->from = pair->value; + im->time = time(NULL); + im->utf8 = TRUE; + im->fed = YAHOO_FEDERATION_NONE; + im->fed_from = g_strdup(im->from); + } else { + purple_debug_warning("yahoo", "yahoo_process_message " + "got non-UTF-8 string for key %d\n", pair->key); + } + } else if (im && pair->key == 5) { + im->active_id = pair->value; + } else if (pair->key == 97) { + if (im) + im->utf8 = strtol(pair->value, NULL, 10); + } else if (pair->key == 15) { + if (im) + im->time = strtol(pair->value, NULL, 10); + } else if (pair->key == 206) { + if (im) + im->buddy_icon = strtol(pair->value, NULL, 10); + } else if (pair->key == 14) { + if (im) + im->msg = pair->value; + } else if (im && pair->key == 241) { + im->fed = strtol(pair->value, NULL, 10); + g_free(im->fed_from); + switch (im->fed) { + case YAHOO_FEDERATION_MSN: + im->fed_from = g_strconcat("msn/",im->from, NULL); + break; + case YAHOO_FEDERATION_OCS: + im->fed_from = g_strconcat("ocs/",im->from, NULL); + break; + case YAHOO_FEDERATION_IBM: + im->fed_from = g_strconcat("ibm/",im->from, NULL); + break; + case YAHOO_FEDERATION_PBX: + im->fed_from = g_strconcat("pbx/",im->from, NULL); + break; + case YAHOO_FEDERATION_NONE: + default: + im->fed_from = g_strdup(im->from); + break; + } + purple_debug_info("yahoo", "Message from federated (%d) buddy %s.\n", im->fed, im->fed_from); + + } else if (im && (pair->key == 11)) { + /* peer session id */ + /* disconnect the peer if connected through p2p and sends wrong value for session id */ + if( (im->fed == YAHOO_FEDERATION_NONE) && (pkt_type == YAHOO_PKT_TYPE_P2P) + && (yd->session_id != strtol(pair->value, NULL, 10)) ) + { + purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->fed_from); + /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ + g_hash_table_remove(yd->peers, im->fed_from); + g_free(im->fed_from); + g_free(im); + return; /* Not sure whether we should process remaining IMs in this packet */ + } + + } else if (im && pair->key == 63 && g_utf8_validate(pair->value, -1, NULL)) { + /* IMV key */ + /* Check for the Doodle IMV, no IMvironment for federated buddies */ + if (im->from != NULL && im->fed == YAHOO_FEDERATION_NONE) + { + g_hash_table_replace(yd->imvironments, g_strdup(im->from), g_strdup(pair->value)); + + if (strstr(pair->value, "doodle;") != NULL) + { + PurpleWhiteboard *wb; + + if (!purple_account_privacy_check(account, im->from)) { + purple_debug_info("yahoo", "Doodle request from %s dropped.\n", + im->from); + g_free(im->fed_from); + g_free(im); + return; + } + /* I'm not sure the following ever happens -DAA */ + wb = purple_whiteboard_get_session(account, im->from); + + /* If a Doodle session doesn't exist between this user */ + if(wb == NULL) + { + doodle_session *ds; + wb = purple_whiteboard_new(account, im->from, + DOODLE_STATE_REQUESTED); + ds = purple_whiteboard_get_protocol_data(wb); + ds->imv_key = g_strdup(pair->value); + + yahoo_doodle_command_send_request(gc, im->from, pair->value); + yahoo_doodle_command_send_ready(gc, im->from, pair->value); + } + } + } + } else if (pair->key == 429) { + if (im) + im->id = pair->value; + } + l = l->next; + } + } else if (pkt->status == 2) { + purple_notify_error(gc, NULL, + _("Your Yahoo! message did not get sent."), NULL, + purple_request_cpar_from_connection(gc)); + } + + for (l = list; l; l = l->next) { + YahooFriend *f; + char *m, *m2; + im = l->data; + + if (!im->fed_from || !im->msg) { + g_free(im->fed_from); + g_free(im); + continue; + } + + if (!purple_account_privacy_check(account, im->fed_from)) { + purple_debug_info("yahoo", "Message from %s dropped.\n", im->fed_from); + return; + } + + /* + * TODO: Is there anything else we should check when determining whether + * we should send an acknowledgement? + */ + if (im->id != NULL) { + /* Send acknowledgement. If we don't do this then the official + * Yahoo Messenger client for Windows will send us the same + * message 7 seconds later as an offline message. This is true + * for at least version 9.0.0.2162 on Windows XP. */ + struct yahoo_packet *pkt2; + pkt2 = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_ACK, + YAHOO_STATUS_AVAILABLE, pkt->id); + yahoo_packet_hash(pkt2, "ssisii", + 1, im->active_id, /* May not always be the connection's display name */ + 5, im->from, + 302, 430, + 430, im->id, + 303, 430, + 450, 0); + yahoo_packet_send_and_free(pkt2, yd); + } + + m = yahoo_string_decode(gc, im->msg, im->utf8); + /* This may actually not be necessary, but it appears + * that at least at one point some clients were sending + * "\r\n" as line delimiters, so we want to avoid double + * lines. */ + m2 = purple_strreplace(m, "\r\n", "\n"); + g_free(m); + m = m2; + purple_util_chrreplace(m, '\r', '\n'); + if (!strcmp(m, "<ding>")) { + char *username; + + username = g_markup_escape_text(im->fed_from, -1); + purple_protocol_got_attention(gc, username, YAHOO_BUZZ); + g_free(username); + g_free(m); + g_free(im->fed_from); + g_free(im); + continue; + } + + m2 = yahoo_codes_to_html(m); + g_free(m); + + serv_got_im(gc, im->fed_from, m2, 0, im->time); + g_free(m2); + + /* Official clients don't share buddy images with federated buddies */ + if (im->fed == YAHOO_FEDERATION_NONE) { + if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) { + if (yahoo_friend_get_buddy_icon_need_request(f)) { + yahoo_send_picture_request(gc, im->from); + yahoo_friend_set_buddy_icon_need_request(f, FALSE); + } + } + } + + g_free(im->fed_from); + g_free(im); + } + + g_slist_free(list); +} + +static void yahoo_process_sysmessage(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + GSList *l = pkt->hash; + char *prim, *me = NULL, *msg = NULL; + + while (l) { + struct yahoo_pair *pair = l->data; + + if (pair->key == 5) { + if (g_utf8_validate(pair->value, -1, NULL)) { + me = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_sysmessage " + "got non-UTF-8 string for key %d\n", pair->key); + } + } else if (pair->key == 14) { + if (g_utf8_validate(pair->value, -1, NULL)) { + msg = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_sysmessage " + "got non-UTF-8 string for key %d\n", pair->key); + } + } + + l = l->next; + } + + if (!msg || !g_utf8_validate(msg, -1, NULL)) + return; + + prim = g_strdup_printf(_("Yahoo! system message for %s:"), + me?me:purple_connection_get_display_name(gc)); + purple_notify_info(NULL, NULL, prim, msg, + purple_request_cpar_from_connection(gc)); + g_free(prim); +} + +struct yahoo_add_request { + PurpleConnection *gc; + char *id; + char *who; + YahooFederation fed; +}; + +static void +yahoo_buddy_add_authorize_cb(const char *message, gpointer data) +{ + struct yahoo_add_request *add_req = data; + struct yahoo_packet *pkt; + YahooData *yd = purple_connection_get_protocol_data(add_req->gc); + const char *who = add_req->who; + + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, yd->session_id); + if (add_req->fed) { + who += 4; + yahoo_packet_hash(pkt, "ssiii", + 1, add_req->id, + 5, who, + 241, add_req->fed, + 13, 1, + 334, 0); + } + else { + yahoo_packet_hash(pkt, "ssii", + 1, add_req->id, + 5, who, + 13, 1, + 334, 0); + } + + yahoo_packet_send_and_free(pkt, yd); + + g_free(add_req->id); + g_free(add_req->who); + g_free(add_req); +} + +static void +yahoo_buddy_add_deny_cb(const char *msg, gpointer data) +{ + struct yahoo_add_request *add_req = data; + YahooData *yd = purple_connection_get_protocol_data(add_req->gc); + struct yahoo_packet *pkt; + char *encoded_msg = NULL; + const char *who = add_req->who; + + if (msg && *msg) + encoded_msg = yahoo_string_encode(add_req->gc, msg, FALSE); + + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, + YAHOO_STATUS_AVAILABLE, yd->session_id); + + if (add_req->fed) { + who += 4; /* Skip fed identifier (msn|ocs|ibm)/' */ + yahoo_packet_hash(pkt, "ssiiiis", + 1, add_req->id, + 5, who, + 241, add_req->fed, + 13, 2, + 334, 0, + 97, 1, /* UTF-8 */ + 14, encoded_msg ? encoded_msg : ""); + } + else { + yahoo_packet_hash(pkt, "ssiiis", + 1, add_req->id, + 5, who, + 13, 2, + 334, 0, + 97, 1, /* UTF-8 */ + 14, encoded_msg ? encoded_msg : ""); + } + + + yahoo_packet_send_and_free(pkt, yd); + + g_free(encoded_msg); + + g_free(add_req->id); + g_free(add_req->who); + g_free(add_req); +} + +static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason) +{ + char *notify_msg; + YahooData *yd = purple_connection_get_protocol_data(gc); + + if (who == NULL) + return; + + if (reason != NULL) { + char *msg2 = yahoo_string_decode(gc, reason, FALSE); + notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2); + g_free(msg2); + } else + notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list."), who); + + purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg, + purple_request_cpar_from_connection(gc)); + g_free(notify_msg); + + g_hash_table_remove(yd->friends, who); + purple_protocol_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */ + /* TODO: Shouldn't we remove the buddy from our local list? */ +} + +static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *pkt) { + PurpleAccount *account; + GSList *l = pkt->hash; + const char *msg = NULL; + + account = purple_connection_get_account(gc); + + /* Buddy authorized/declined our addition */ + if (pkt->status == 1) { + char *temp = NULL; + char *who = NULL; + int response = 0; + YahooFederation fed = YAHOO_FEDERATION_NONE; + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 4: + if (g_utf8_validate(pair->value, -1, NULL)) { + temp = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 13: + response = strtol(pair->value, NULL, 10); + break; + case 14: + msg = pair->value; + break; + case 241: + fed = strtol(pair->value, NULL, 10); + break; + } + l = l->next; + } + + switch (fed) { + case YAHOO_FEDERATION_MSN: + who = g_strconcat("msn/", temp, NULL); + break; + case YAHOO_FEDERATION_OCS: + who = g_strconcat("ocs/", temp, NULL); + break; + case YAHOO_FEDERATION_IBM: + who = g_strconcat("ibm/", temp, NULL); + break; + case YAHOO_FEDERATION_NONE: + default: + who = g_strdup(temp); + break; + } + + if (response == 1) /* Authorized */ + purple_debug_info("yahoo", "Received authorization from buddy '%s'.\n", who ? who : "(Unknown Buddy)"); + else if (response == 2) { /* Declined */ + purple_debug_info("yahoo", "Received authorization decline from buddy '%s'.\n", who ? who : "(Unknown Buddy)"); + yahoo_buddy_denied_our_add(gc, who, msg); + } else + purple_debug_error("yahoo", "Received unknown authorization response of %d from buddy '%s'.\n", response, who ? who : "(Unknown Buddy)"); + g_free(who); + } + /* Buddy requested authorization to add us. */ + else if (pkt->status == 3) { + struct yahoo_add_request *add_req; + const char *firstname = NULL, *lastname = NULL; + char *temp = NULL; + + add_req = g_new0(struct yahoo_add_request, 1); + add_req->gc = gc; + add_req->fed = YAHOO_FEDERATION_NONE; + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 4: + if (g_utf8_validate(pair->value, -1, NULL)) { + temp = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 5: + if (g_utf8_validate(pair->value, -1, NULL)) { + add_req->id = g_strdup(pair->value); + } else { + purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 14: + msg = pair->value; + break; + case 216: + if (g_utf8_validate(pair->value, -1, NULL)) { + firstname = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 241: + add_req->fed = strtol(pair->value, NULL, 10); + break; + case 254: + if (g_utf8_validate(pair->value, -1, NULL)) { + lastname = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + + } + l = l->next; + } + switch (add_req->fed) { + case YAHOO_FEDERATION_MSN: + add_req->who = g_strconcat("msn/", temp, NULL); + break; + case YAHOO_FEDERATION_OCS: + add_req->who = g_strconcat("ocs/", temp, NULL); + break; + case YAHOO_FEDERATION_IBM: + add_req->who = g_strconcat("ibm/", temp, NULL); + break; + case YAHOO_FEDERATION_NONE: + default: + add_req->who = g_strdup(temp); + break; + } + + if (add_req->id && add_req->who) { + char *alias = NULL, *dec_msg = NULL; + + if (!purple_account_privacy_check(account, add_req->who)) + { + purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n", + add_req->who); + yahoo_buddy_add_deny_cb(NULL, add_req); + return; + } + + if (msg) + dec_msg = yahoo_string_decode(gc, msg, FALSE); + + if (firstname && lastname) + alias = g_strdup_printf("%s %s", firstname, lastname); + else if (firstname) + alias = g_strdup(firstname); + else if (lastname) + alias = g_strdup(lastname); + + /* DONE! this is almost exactly the same as what MSN does, + * this should probably be moved to the core. + */ + purple_account_request_authorization(account, add_req->who, add_req->id, + alias, dec_msg, + purple_blist_find_buddy(account, add_req->who) != NULL, + yahoo_buddy_add_authorize_cb, + yahoo_buddy_add_deny_cb, + add_req); + g_free(alias); + g_free(dec_msg); + } else { + g_free(add_req->id); + g_free(add_req->who); + g_free(add_req); + } + } else { + purple_debug_error("yahoo", "Received authorization of unknown status (%d).\n", pkt->status); + } +} + +/* I don't think this happens anymore in Version 15 */ +static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt) { + PurpleAccount *account; + struct yahoo_add_request *add_req; + char *msg = NULL; + GSList *l = pkt->hash; + + account = purple_connection_get_account(gc); + + add_req = g_new0(struct yahoo_add_request, 1); + add_req->gc = gc; + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 1: + if (g_utf8_validate(pair->value, -1, NULL)) { + add_req->id = g_strdup(pair->value); + } else { + purple_debug_warning("yahoo", "yahoo_buddy_added_us " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 3: + if (g_utf8_validate(pair->value, -1, NULL)) { + add_req->who = g_strdup(pair->value); + } else { + purple_debug_warning("yahoo", "yahoo_buddy_added_us " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 15: /* time, for when they add us and we're offline */ + break; + case 14: + msg = pair->value; + break; + } + l = l->next; + } + + if (add_req->id && add_req->who) { + char *dec_msg = NULL; + + if (!purple_account_privacy_check(account, add_req->who)) { + purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n", + add_req->who); + yahoo_buddy_add_deny_cb(NULL, add_req); + return; + } + + if (msg) + dec_msg = yahoo_string_decode(gc, msg, FALSE); + + /* DONE! this is almost exactly the same as what MSN does, + * this should probably be moved to the core. + */ + purple_account_request_authorization(account, add_req->who, add_req->id, + NULL, dec_msg, + purple_blist_find_buddy(account,add_req->who) != NULL, + yahoo_buddy_add_authorize_cb, + yahoo_buddy_add_deny_cb, add_req); + g_free(dec_msg); + } else { + g_free(add_req->id); + g_free(add_req->who); + g_free(add_req); + } +} + +/* I have no idea if this every gets called in version 15 */ +static void yahoo_buddy_denied_our_add_old(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + char *who = NULL; + char *msg = NULL; + GSList *l = pkt->hash; + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 3: + if (g_utf8_validate(pair->value, -1, NULL)) { + who = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_buddy_denied_our_add_old " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 14: + if (g_utf8_validate(pair->value, -1, NULL)) { + msg = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_buddy_denied_our_add_old " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + } + l = l->next; + } + + yahoo_buddy_denied_our_add(gc, who, msg); +} + +static void yahoo_process_contact(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + switch (pkt->status) { + case 1: + yahoo_process_status(gc, pkt); + return; + case 3: + yahoo_buddy_added_us(gc, pkt); + break; + case 7: + yahoo_buddy_denied_our_add_old(gc, pkt); + break; + default: + break; + } +} + +#define OUT_CHARSET "utf-8" + +static char *yahoo_decode(const char *text) +{ + char *converted = NULL; + char *n, *new; + const char *end, *p; + int i, k; + + n = new = g_malloc(strlen (text) + 1); + end = text + strlen(text); + + for (p = text; p < end; p++, n++) { + if (*p == '\\') { + if (p[1] >= '0' && p[1] <= '7') { + p += 1; + for (i = 0, k = 0; k < 3; k += 1) { + char c = p[k]; + if (c < '0' || c > '7') break; + i *= 8; + i += c - '0'; + } + *n = i; + p += k - 1; + } else { /* bug 959248 */ + /* If we see a \ not followed by an octal number, + * it means that it is actually a \\ with one \ + * already eaten by some unknown function. + * This is arguably broken. + * + * I think wing is wrong here, there is no function + * called that I see that could have done it. I guess + * it is just really sending single \'s. That's yahoo + * for you. + */ + *n = *p; + } + } + else + *n = *p; + } + + *n = '\0'; + + if (strstr(text, "\033$B")) + converted = g_convert(new, n - new, OUT_CHARSET, "iso-2022-jp", NULL, NULL, NULL); + if (!converted) + converted = g_convert(new, n - new, OUT_CHARSET, "iso-8859-1", NULL, NULL, NULL); + g_free(new); + + return converted; +} + +static void yahoo_process_mail(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + PurpleAccount *account = purple_connection_get_account(gc); + YahooData *yd = purple_connection_get_protocol_data(gc); + const char *who = NULL; + const char *email = NULL; + const char *subj = NULL; + const char *yahoo_mail_url = (yd->jp? YAHOOJP_MAIL_URL: YAHOO_MAIL_URL); + int count = 0; + GSList *l = pkt->hash; + + if (!purple_account_get_check_mail(account)) + return; + + while (l) { + struct yahoo_pair *pair = l->data; + if (pair->key == 9) { + count = strtol(pair->value, NULL, 10); + } else if (pair->key == 43) { + if (g_utf8_validate(pair->value, -1, NULL)) { + who = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_mail " + "got non-UTF-8 string for key %d\n", pair->key); + } + } else if (pair->key == 42) { + if (g_utf8_validate(pair->value, -1, NULL)) { + email = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_mail " + "got non-UTF-8 string for key %d\n", pair->key); + } + } else if (pair->key == 18) { + if (g_utf8_validate(pair->value, -1, NULL)) { + subj = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_mail " + "got non-UTF-8 string for key %d\n", pair->key); + } + } + l = l->next; + } + + if (who && subj && email && *email) { + char *dec_who = yahoo_decode(who); + char *dec_subj = yahoo_decode(subj); + char *from = g_strdup_printf("%s (%s)", dec_who, email); + + purple_notify_email(gc, dec_subj, from, purple_account_get_username(account), + yahoo_mail_url, NULL, NULL); + + g_free(dec_who); + g_free(dec_subj); + g_free(from); + } else if (count > 0) { + const char *tos[2] = { purple_account_get_username(account) }; + const char *urls[2] = { yahoo_mail_url }; + + purple_notify_emails(gc, count, FALSE, NULL, NULL, tos, urls, + NULL, NULL); + } +} + +/* We use this structure once while we authenticate */ +struct yahoo_auth_data +{ + PurpleConnection *gc; + char *seed; +}; + +/* This is the y64 alphabet... it's like base64, but has a . and a _ */ +static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._"; + +/* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function + * in util.c, but it is different from the one yahoo uses */ +static void to_y64(char *out, const unsigned char *in, gsize inlen) + /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ +{ + for (; inlen >= 3; inlen -= 3) + { + *out++ = base64digits[in[0] >> 2]; + *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; + *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; + *out++ = base64digits[in[2] & 0x3f]; + in += 3; + } + if (inlen > 0) + { + unsigned char fragment; + + *out++ = base64digits[in[0] >> 2]; + fragment = (in[0] << 4) & 0x30; + if (inlen > 1) + fragment |= in[1] >> 4; + *out++ = base64digits[fragment]; + *out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c]; + *out++ = '-'; + } + *out = '\0'; +} + +static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + PurpleAccount *account = purple_connection_get_account(gc); + const char *name = purple_normalize(account, purple_account_get_username(account)); + PurpleHash *md5_hash; + guchar md5_digest[16]; + gchar base64_string[25]; + struct yahoo_packet *pkt; + + purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage3\n"); + + g_return_if_fail(crypt != NULL); + + md5_hash = purple_md5_hash_new(); + purple_hash_append(md5_hash, (guchar *)crypt, strlen(crypt)); + purple_hash_digest(md5_hash, md5_digest, sizeof(md5_digest)); + + to_y64(base64_string, md5_digest, 16); + + purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status); + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->current_status, yd->session_id); + + if(yd->cookie_b) { /* send B cookie if we have it */ + yahoo_packet_hash(pkt, "ssssssssss", + 1, name, + 0, name, + 277, yd->cookie_y, + 278, yd->cookie_t, + 307, base64_string, + 244, yd->jp ? YAHOOJP_CLIENT_VERSION_ID : YAHOO_CLIENT_VERSION_ID, + 2, name, + 2, "1", + 59, yd->cookie_b, + 98, purple_account_get_string(account, "room_list_locale", yd->jp ? "jp" : "us"), + 135, yd->jp ? YAHOOJP_CLIENT_VERSION : YAHOO_CLIENT_VERSION); + } else { /* don't try to send an empty B cookie - the server will be mad */ + yahoo_packet_hash(pkt, "sssssssss", + 1, name, + 0, name, + 277, yd->cookie_y, + 278, yd->cookie_t, + 307, base64_string, + 244, yd->jp ? YAHOOJP_CLIENT_VERSION_ID : YAHOO_CLIENT_VERSION_ID, + 2, name, + 2, "1", + 98, purple_account_get_string(account, "room_list_locale", yd->jp ? "jp" : "us"), + 135, yd->jp ? YAHOOJP_CLIENT_VERSION : YAHOO_CLIENT_VERSION); + } + + if (yd->picture_checksum) + yahoo_packet_hash_int(pkt, 192, yd->picture_checksum); + yahoo_packet_send_and_free(pkt, yd); + + g_object_unref(md5_hash); +} + +static void yahoo_auth16_stage2(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, gpointer _auth_data) +{ + struct yahoo_auth_data *auth_data = _auth_data; + PurpleConnection *gc = auth_data->gc; + YahooData *yd = purple_connection_get_protocol_data(gc); + + int i; + gchar **splits; + int response_no = -1; + char *crumb = NULL; + char *crypt = NULL; + PurpleHttpCookieJar *cookiejar; + + purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n"); + + if (!purple_http_response_is_successful(response)) { + const gchar *error_message = purple_http_response_get_error(response); + purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message); + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); + g_free(auth_data->seed); + g_free(auth_data); + return; + } + + splits = g_strsplit(purple_http_response_get_data(response, NULL), + "\r\n", -1); + + cookiejar = purple_http_conn_get_cookie_jar(http_conn); + yd->cookie_b = g_strdup(purple_http_cookie_jar_get(cookiejar, "B")); + yd->cookie_t = g_strdup(purple_http_cookie_jar_get(cookiejar, "T")); + yd->cookie_y = g_strdup(purple_http_cookie_jar_get(cookiejar, "Y")); + + i = 0; + while (splits[i]) { + /* I'm not exactly a fan of the magic numbers, but it's obvious, + * so no sense in wasting a bajillion vars or calls to strlen */ + + if (i == 0 && g_ascii_isdigit(splits[i][0])) { + response_no = strtol(splits[i], NULL, 10); + purple_debug_info("yahoo", "Got auth16 stage 2 response code: %d\n", + response_no); + } else if (strncmp(splits[i], "crumb=", 6) == 0) { + crumb = g_strdup(&splits[i][6]); + + if (purple_debug_is_unsafe()) + purple_debug_info("yahoo", "Got crumb: %s\n", crumb); + } + i++; + } + + g_strfreev(splits); + + if (crumb == NULL) + response_no = -1; + + if(response_no != 0) { + /* Some error in the login process */ + PurpleConnectionError error; + char *error_reason = NULL; + + switch (response_no) { + case -1: + /* Some error in the received stream */ + error_reason = g_strdup(_("Received invalid data")); + error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; + break; + case 100: + /* Unknown error */ + error_reason = g_strdup(_("Unknown error")); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; + default: + /* if we have everything we need, why not try to login irrespective of response */ + if ((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) { +#if 0 + try_login_on_error = TRUE; +#endif + break; + } + error_reason = g_strdup(_("Unknown error")); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; + } + if (error_reason) { + purple_debug_error("yahoo", "Authentication error: %s. " + "Code %d\n", error_reason, response_no); + purple_connection_error(gc, error, error_reason); + g_free(error_reason); + g_free(crumb); + g_free(auth_data->seed); + g_free(auth_data); + return; + } + } + + crypt = g_strconcat(crumb, auth_data->seed, NULL); + yahoo_auth16_stage3(gc, crypt); + g_free(crypt); + g_free(crumb); + g_free(auth_data->seed); + g_free(auth_data); +} + +static void yahoo_auth16_stage1_cb(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, gpointer _auth_data) +{ + struct yahoo_auth_data *auth_data = _auth_data; + PurpleConnection *gc = auth_data->gc; + YahooData *yd = purple_connection_get_protocol_data(gc); + + purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n"); + + if (!purple_http_response_is_successful(response)) { + const gchar *error_message = purple_http_response_get_error(response); + purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message); + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); + g_free(auth_data->seed); + g_free(auth_data); + return; + } else { + PurpleAccount *account = purple_connection_get_account(gc); + gchar **split_data = g_strsplit(purple_http_response_get_data( + response, NULL), "\r\n", -1); + int totalelements = 0; + int response_no = -1; + char *token = NULL; + + totalelements = g_strv_length(split_data); + + if(totalelements == 1) { /* Received an error code */ + response_no = strtol(split_data[0], NULL, 10); + } else if(totalelements == 2 || totalelements == 3 ) { /* received valid data */ + response_no = strtol(split_data[0], NULL, 10); + token = g_strdup(split_data[1] + strlen("ymsgr=")); + } else { /* It looks like a transparent proxy has returned a document we don't want */ + response_no = -1; + } + + g_strfreev(split_data); + + if(response_no != 0) { + /* Some error in the login process */ + PurpleConnectionError error; + char *error_reason; + + switch(response_no) { + case -1: + /* Some error in the received stream */ + error_reason = g_strdup(_("Received invalid data")); + error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; + break; + case 1212: + /* Password incorrect */ + /* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */ + if (!purple_account_get_remember_password(account)) + purple_account_set_password(account, NULL, NULL, NULL); + error_reason = g_strdup(_("Incorrect password")); + error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; + break; + case 1213: + /* security lock from too many failed login attempts */ + error_reason = g_strdup(_("Account locked: Too many failed login " + "attempts. Logging into the Yahoo! website may fix this.")); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; + case 1235: + /* the username does not exist */ + error_reason = g_strdup(_("Username does not exist")); + error = PURPLE_CONNECTION_ERROR_INVALID_USERNAME; + break; + case 1214: + /* indicates a lock of some description */ + error_reason = g_strdup(_("Account locked: Unknown reason. Logging " + "into the Yahoo! website may fix this.")); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; + case 1236: + /* indicates a lock due to logging in too frequently */ + error_reason = g_strdup(_("Account locked: You have been logging in too " + "frequently. Wait a few minutes before trying to connect " + "again. Logging into the Yahoo! website may help.")); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; + case 100: + /* username or password missing */ + error_reason = g_strdup(_("Username or password missing")); + error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; + break; + default: + /* Unknown error! */ + error_reason = g_strdup_printf(_("Unknown error (%d)"), response_no); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; + } + purple_debug_error("yahoo", "Authentication error: %s. Code %d\n", + error_reason, response_no); + purple_connection_error(gc, error, error_reason); + g_free(error_reason); + g_free(auth_data->seed); + g_free(auth_data); + g_free(token); + } + else { + /* OK to login, correct information provided */ + gboolean yahoojp = yahoo_is_japan(account); + PurpleHttpRequest *req; + + req = purple_http_request_new(NULL); + purple_http_request_set_url_printf(req, + yahoojp ? YAHOOJP_LOGIN_URL : YAHOO_LOGIN_URL, + token); + purple_http_request_header_set(req, "User-Agent", + YAHOO_CLIENT_USERAGENT); + purple_http_connection_set_add(yd->http_reqs, + purple_http_request(gc, req, + yahoo_auth16_stage2, auth_data)); + purple_http_request_unref(req); + + g_free(token); + } + } +} + +static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + PurpleAccount *account = purple_connection_get_account(gc); + PurpleHttpRequest *req; + struct yahoo_auth_data *auth_data = NULL; + char *encoded_username; + char *encoded_password; + gboolean yahoojp = yahoo_is_japan(account); + + purple_debug_info("yahoo", "Authentication: In yahoo_auth16_stage1\n"); + + if(!purple_ssl_is_supported()) { + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("SSL support unavailable")); + return; + } + + auth_data = g_new0(struct yahoo_auth_data, 1); + auth_data->gc = gc; + auth_data->seed = g_strdup(seed); + + encoded_username = g_strdup(purple_url_encode(purple_account_get_username(purple_connection_get_account(gc)))); + encoded_password = g_strdup(purple_url_encode(purple_connection_get_password(gc))); + + req = purple_http_request_new(NULL); + purple_http_request_set_url_printf(req, + yahoojp ? YAHOOJP_TOKEN_URL : YAHOO_TOKEN_URL, + encoded_username, encoded_password, purple_url_encode(seed)); + purple_http_request_header_set(req, "User-Agent", YAHOO_CLIENT_USERAGENT); + purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc, + req, yahoo_auth16_stage1_cb, auth_data)); + purple_http_request_unref(req); + + purple_str_wipe(encoded_password); + g_free(encoded_username); +} + +static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + char *seed = NULL; + GSList *l = pkt->hash; + int m = 0; + gchar *buf; + + while (l) { + struct yahoo_pair *pair = l->data; + /* (pair->key == 1) -> sn */ + if (pair->key == 94) { + if (g_utf8_validate(pair->value, -1, NULL)) { + seed = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_auth " + "got non-UTF-8 string for key %d\n", pair->key); + } + } else if (pair->key == 13) { + m = atoi(pair->value); + } + l = l->next; + } + + if (seed) { + switch (m) { + case 0: + /* used to be for really old auth routine, dont support now */ + case 1: + case 2: /* Yahoo ver 16 authentication */ + yahoo_auth16_stage1(gc, seed); + break; + default: + { + GHashTable *ui_info = purple_core_get_ui_info(); + + buf = g_strdup_printf(_("The Yahoo server has requested the use of an unrecognized " + "authentication method. You will probably not be able " + "to successfully sign on to Yahoo. Check %s for updates."), + ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); + purple_notify_error(gc, "", + _("Failed Yahoo! Authentication"), buf, + purple_request_cpar_from_connection(gc)); + g_free(buf); + yahoo_auth16_stage1(gc, seed); /* Can't hurt to try it anyway. */ + break; + } + } + } +} + +static void ignore_buddy(PurpleBuddy *buddy) { + PurpleGroup *group; + PurpleAccount *account; + gchar *name; + + if (!buddy) + return; + + group = purple_buddy_get_group(buddy); + name = g_strdup(purple_buddy_get_name(buddy)); + account = purple_buddy_get_account(buddy); + + purple_debug_info("yahoo", "blist: Removing '%s' from buddy list.\n", name); + purple_account_remove_buddy(account, buddy, group); + purple_blist_remove_buddy(buddy); + + serv_add_deny(purple_account_get_connection(account), name); + + g_free(name); +} + +static void keep_buddy(PurpleBuddy *b) +{ + purple_account_privacy_deny_remove(purple_buddy_get_account(b), + purple_buddy_get_name(b), 1); +} + +static void yahoo_process_ignore(PurpleConnection *gc, struct yahoo_packet *pkt) { + PurpleBuddy *b; + GSList *l; + gchar *who = NULL; + gchar buf[BUF_LONG]; + gboolean ignore = TRUE; + gint status = 0; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + switch (pair->key) { + case 0: + if (g_utf8_validate(pair->value, -1, NULL)) { + who = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_ignore " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + /* 1 -> me */ + case 13: + /* 1 == ignore, 2 == unignore */ + ignore = (strtol(pair->value, NULL, 10) == 1); + break; + case 66: + status = strtol(pair->value, NULL, 10); + break; + default: + break; + } + } + + /* + * status + * 0 - ok + * 2 - already in ignore list, could not add + * 3 - not in ignore list, could not delete + * 12 - is a buddy, could not add (and possibly also a not-in-ignore list condition?) + */ + switch (status) { + case 12: + purple_debug_info("yahoo", "Server reported \"is a buddy\" for %s while %s", + who, (ignore ? "ignoring" : "unignoring")); + + if (ignore) { + b = purple_blist_find_buddy(purple_connection_get_account(gc), who); + g_snprintf(buf, sizeof(buf), _("You have tried to ignore %s, but the " + "user is on your buddy list. Clicking \"Yes\" " + "will remove and ignore the buddy."), who); + purple_request_yes_no(gc, NULL, + _("Ignore buddy?"), buf, 0, + purple_request_cpar_from_connection(gc), + b, G_CALLBACK(ignore_buddy), + G_CALLBACK(keep_buddy)); + break; + } + case 2: + purple_debug_info("yahoo", "Server reported that %s is already in the ignore list.\n", + who); + break; + case 3: + purple_debug_info("yahoo", "Server reported that %s is not in the ignore list; could not delete\n", + who); + case 0: + default: + break; + } +} + +#if TRY_WEBMESSENGER_LOGIN + +static gboolean +yahoo_try_webmessenger_login(PurpleConnection *gc) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + PurpleHttpRequest *req; + + if (yd->wm) + return FALSE; + + yd->wm = TRUE; + if (yd->fd >= 0) + close(yd->fd); + if (yd->inpa) { + purple_input_remove(yd->inpa); + yd->inpa = 0; + } + + req = purple_http_request_new(WEBMESSENGER_URL); + purple_http_request_header_set(req, "User-Agent", "Purple/" VERSION); + purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc, + req, yahoo_login_page_cb, NULL)); + purple_http_request_unref(req); + + return TRUE; +} + +#endif /* TRY_WEBMESSENGER_LOGIN */ + +static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + GSList *l = pkt->hash; + int err = 0; + char *msg; + char *url = NULL; + char *fullmsg; + PurpleAccount *account = purple_connection_get_account(gc); + PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + + while (l) { + struct yahoo_pair *pair = l->data; + + if (pair->key == 66) { + err = strtol(pair->value, NULL, 10); + } else if (pair->key == 20) { + if (g_utf8_validate(pair->value, -1, NULL)) { + url = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_authresp " + "got non-UTF-8 string for key %d\n", pair->key); + } + } + + l = l->next; + } + + switch (err) { + case 0: + msg = g_strdup(_("Unknown error")); + reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; + break; + case 3: + msg = g_strdup(_("Username does not exist")); + reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME; + break; + case 13: +#if TRY_WEBMESSENGER_LOGIN + if (yahoo_try_webmessenger_login(gc)) + return; +#else + purple_debug_info("yahoo", "Web messenger login is disabled\n"); +#endif /* TRY_WEBMESSENGER_LOGIN */ + if (!purple_account_get_remember_password(account)) + purple_account_set_password(account, NULL, NULL, NULL); + + msg = g_strdup(_("Invalid username or password")); + reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; + break; + case 14: + msg = g_strdup(_("Your account has been locked due to too many failed login attempts." + " Please try logging into the Yahoo! website.")); + reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; + break; + case 52: + /* See #9660. As much as we know, reconnecting shouldn't hurt */ + purple_debug_info("yahoo", "Got error 52, Set to autoreconnect\n"); + msg = g_strdup(_("Unknown error 52. Reconnecting should fix this.")); + reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; + break; + case 1013: + msg = g_strdup(_("Error 1013: The username you have entered is invalid." + " The most common cause of this error is entering your email" + " address instead of your Yahoo! ID.")); + reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME; + break; + default: + msg = g_strdup_printf(_("Unknown error number %d. Logging into the Yahoo! website may fix this."), err); + } + + if (url) + fullmsg = g_strdup_printf("%s\n%s", msg, url); + else + fullmsg = g_strdup(msg); + + purple_connection_error(gc, reason, fullmsg); + g_free(msg); + g_free(fullmsg); +} + +static void yahoo_process_addbuddy(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + int err = 0; + char *who = NULL; + char *temp = NULL; + char *group = NULL; + char *decoded_group; + char *buf; + YahooFriend *f; + GSList *l = pkt->hash; + YahooData *yd = purple_connection_get_protocol_data(gc); + YahooFederation fed = YAHOO_FEDERATION_NONE; + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 66: + err = strtol(pair->value, NULL, 10); + break; + case 7: + if (g_utf8_validate(pair->value, -1, NULL)) { + temp = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_addbuddy " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 65: + group = pair->value; + break; + case 241: + fed = strtol(pair->value, NULL, 10); + break; + } + + l = l->next; + } + + if (!temp) + return; + if (!group) + group = ""; + + switch (fed) { + case YAHOO_FEDERATION_MSN: + who = g_strconcat("msn/", temp, NULL); + break; + case YAHOO_FEDERATION_OCS: + who = g_strconcat("ocs/", temp, NULL); + break; + case YAHOO_FEDERATION_IBM: + who = g_strconcat("ibm/", temp, NULL); + break; + case YAHOO_FEDERATION_NONE: + default: + who = g_strdup(temp); + break; + } + + if (!err || (err == 2)) { /* 0 = ok, 2 = already on serv list */ + f = yahoo_friend_find_or_new(gc, who); + yahoo_update_status(gc, who, f); + f->fed = fed; + + if( !g_hash_table_lookup(yd->peers, who) ) { + /* we are not connected as client, so set friend to not connected */ + if(fed) + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT); + else { + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); + f->p2p_packet_sent = 0; + } + } + else /* we are already connected. set friend to YAHOO_P2PSTATUS_WE_ARE_CLIENT */ + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT); + g_free(who); + return; + } + + decoded_group = yahoo_string_decode(gc, group, FALSE); + buf = g_strdup_printf(_("Unable to add buddy %s to group %s to the server list on account %s."), + who, decoded_group, purple_connection_get_display_name(gc)); + if (!purple_conversation_present_error(who, purple_connection_get_account(gc), buf)) + purple_notify_error(gc, NULL, _("Unable to add buddy to server list"), buf, + purple_request_cpar_from_connection(gc)); + g_free(buf); + g_free(decoded_group); + g_free(who); +} + +/* write pkt to the source */ +static void yahoo_p2p_write_pkt(gint source, struct yahoo_packet *pkt) +{ + size_t pkt_len; + gssize written; + guchar *raw_packet; + + /*build the raw packet and send it to the host*/ + pkt_len = yahoo_packet_build(pkt, 0, 0, 0, &raw_packet); + written = write(source, raw_packet, pkt_len); + if (written < 0 || (gsize)written != pkt_len) + purple_debug_warning("yahoo","p2p: couldn't write to the source\n"); + g_free(raw_packet); +} + +static void yahoo_p2p_keepalive_cb(gpointer key, gpointer value, gpointer user_data) +{ + struct yahoo_p2p_data *p2p_data = value; + PurpleConnection *gc = user_data; + struct yahoo_packet *pkt_to_send; + PurpleAccount *account; + YahooData *yd = purple_connection_get_protocol_data(gc); + + account = purple_connection_get_account(gc); + + pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt_to_send, "ssisi", + 4, purple_normalize(account, purple_account_get_username(account)), + 5, p2p_data->host_username, + 241, 0, /* Protocol identifier */ + 49, "PEERTOPEER", + 13, 7); + yahoo_p2p_write_pkt(p2p_data->source, pkt_to_send); + + yahoo_packet_free(pkt_to_send); +} + +static gboolean yahoo_p2p_keepalive(gpointer data) +{ + PurpleConnection *gc = data; + YahooData *yd = purple_connection_get_protocol_data(gc); + + g_hash_table_foreach(yd->peers, yahoo_p2p_keepalive_cb, gc); + + return TRUE; +} + +/* destroy p2p_data associated with a peer and close p2p connection. + * g_hash_table_remove() calls this function to destroy p2p_data associated with the peer, + * call g_hash_table_remove() instead of this fucntion if peer has an entry in the table */ +static void yahoo_p2p_disconnect_destroy_data(gpointer data) +{ + struct yahoo_p2p_data *p2p_data; + YahooFriend *f; + + if(!(p2p_data = data)) + return ; + + /* If friend, set him not connected */ + f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username); + if (f) + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); + + if(p2p_data->source >= 0) + close(p2p_data->source); + if (p2p_data->input_event > 0) + purple_input_remove(p2p_data->input_event); + g_free(p2p_data->host_ip); + g_free(p2p_data->host_username); + g_free(p2p_data); +} + +/* exchange of initial p2pfilexfer packets, service type YAHOO_SERVICE_P2PFILEXFER */ +static void yahoo_p2p_process_p2pfilexfer(gpointer data, gint source, struct yahoo_packet *pkt) +{ + struct yahoo_p2p_data *p2p_data; + char *who = NULL; + GSList *l = pkt->hash; + struct yahoo_packet *pkt_to_send; + PurpleAccount *account; + int val_13_to_send = 0; + YahooData *yd; + YahooFriend *f; + + if(!(p2p_data = data)) + return ; + + yd = purple_connection_get_protocol_data(p2p_data->gc); + + /* lets see whats in the packet */ + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 4: + if (g_utf8_validate(pair->value, -1, NULL)) { + who = pair->value; + if(strncmp(who, p2p_data->host_username, strlen(p2p_data->host_username)) != 0) { + /* from whom are we receiving the packets ?? */ + purple_debug_warning("yahoo","p2p: received data from wrong user\n"); + return; + } + } else { + purple_debug_warning("yahoo", "yahoo_p2p_process_p2pfilexfer " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 13: + p2p_data->val_13 = strtol(pair->value, NULL, 10); /* Value should be 5-7 */ + break; + /* case 5, 49 look laters, no use right now */ + } + l = l->next; + } + + account = purple_connection_get_account(p2p_data->gc); + + /* key_13: sort of a counter. + * WHEN WE ARE CLIENT: yahoo server sends val_13 = 0, we send to peer val_13 = 1, receive back val_13 = 5, + * we send val_13=6, receive val_13=7, we send val_13=7, HALT. Keep sending val_13 = 7 as keep alive. + * WHEN WE ARE SERVER: we send val_13 = 0 to yahoo server, peer sends us val_13 = 1, we send val_13 = 5, + * receive val_13 = 6, send val_13 = 7, receive val_13 = 7. HALT. Keep sending val_13 = 7 as keep alive. */ + + switch(p2p_data->val_13) { + case 1 : val_13_to_send = 5; break; + case 5 : val_13_to_send = 6; break; + case 6 : val_13_to_send = 7; break; + case 7 : if( g_hash_table_lookup(yd->peers, p2p_data->host_username) ) + return; + val_13_to_send = 7; break; + default: purple_debug_warning("yahoo","p2p:Unknown value for key 13\n"); + return; + } + + /* Build the yahoo packet */ + pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt_to_send, "ssisi", + 4, purple_normalize(account, purple_account_get_username(account)), + 5, p2p_data->host_username, + 241, 0, /* Protocol identifier */ + 49, "PEERTOPEER", + 13, val_13_to_send); + + /* build the raw packet and send it to the host */ + yahoo_p2p_write_pkt(source, pkt_to_send); + yahoo_packet_free(pkt_to_send); + + if( val_13_to_send == 7 ) + if( !g_hash_table_lookup(yd->peers, p2p_data->host_username) ) { + g_hash_table_insert(yd->peers, g_strdup(p2p_data->host_username), p2p_data); + /* If the peer is a friend, set him connected */ + f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username); + if (f) { + if(p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) { + p2p_data->session_id = f->session_id; + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_SERVER); + } + else + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT); + } + } +} + +/* callback function associated with receiving of data, not considering receipt of multiple YMSG packets in a single TCP packet */ +static void yahoo_p2p_read_pkt_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + guchar buf[1024]; /* is it safe to assume a fixed array length of 1024 ?? */ + int len; + int pos = 0; + int pktlen; + struct yahoo_packet *pkt; + guchar *start; + struct yahoo_p2p_data *p2p_data; + YahooData *yd; + + if(!(p2p_data = data)) + return ; + yd = purple_connection_get_protocol_data(p2p_data->gc); + + len = read(source, buf, sizeof(buf)); + if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) + return ; /* No Worries*/ + else if (len <= 0) + { + purple_debug_warning("yahoo","p2p: Error in connection, or host disconnected\n"); + /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ + if( g_hash_table_lookup(yd->peers, p2p_data->host_username) ) + g_hash_table_remove(yd->peers,p2p_data->host_username); + else + yahoo_p2p_disconnect_destroy_data(data); + return; + } + + /* TODO: It looks like there's a bug here (and above) where an incorrect + * assumtion is being made that the buffer will be added to when this + * is next called, but that's not really the case! */ + if(len < YAHOO_PACKET_HDRLEN) + return; + + if(strncmp((char *)buf, "YMSG", 4) != 0) { + /* Not a YMSG packet */ + purple_debug_warning("yahoo", "p2p: Got something other than YMSG packet\n"); + + start = (guchar *) g_strstr_len((char *) buf + 1, len - 1 ,"YMSG"); + if (start == NULL) { + /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ + if (g_hash_table_lookup(yd->peers, p2p_data->host_username)) + g_hash_table_remove(yd->peers, p2p_data->host_username); + else + yahoo_p2p_disconnect_destroy_data(data); + return; + } + purple_debug_warning("yahoo","p2p: Got something other than YMSG packet\n"); + + len -= (start - buf); + g_memmove(buf, start, len); + } + + pos += 4; /* YMSG */ + pos += 2; + pos += 2; + + pktlen = yahoo_get16(buf + pos); pos += 2; + if (len < (YAHOO_PACKET_HDRLEN + pktlen)) { + purple_debug_error("yahoo", "p2p: packet length(%d) > buffer length(%d)\n", + pktlen, (len - pos)); + /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ + if (g_hash_table_lookup(yd->peers, p2p_data->host_username)) + g_hash_table_remove(yd->peers, p2p_data->host_username); + else + yahoo_p2p_disconnect_destroy_data(data); + return; + } else + purple_debug_misc("yahoo", "p2p: %d bytes to read\n", pktlen); + + pkt = yahoo_packet_new(0, 0, 0); + pkt->service = yahoo_get16(buf + pos); pos += 2; + pkt->status = yahoo_get32(buf + pos); pos += 4; + pkt->id = yahoo_get32(buf + pos); pos += 4; + + purple_debug_misc("yahoo", "p2p: Yahoo Service: 0x%02x Status: %d\n",pkt->service, pkt->status); + yahoo_packet_read(pkt, buf + pos, pktlen); + + /* packet processing */ + switch(pkt->service) { + case YAHOO_SERVICE_P2PFILEXFER: + yahoo_p2p_process_p2pfilexfer(data, source, pkt); + break; + case YAHOO_SERVICE_MESSAGE: + yahoo_process_message(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P); + break; + case YAHOO_SERVICE_NOTIFY: + yahoo_process_notify(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P); + break; + default: + purple_debug_warning("yahoo","p2p: p2p service %d Unhandled\n",pkt->service); + } + + yahoo_packet_free(pkt); +} + +static void yahoo_p2p_server_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + int acceptfd; + struct yahoo_p2p_data *p2p_data; + YahooData *yd; + + if(!(p2p_data = data)) + return ; + yd = purple_connection_get_protocol_data(p2p_data->gc); + + acceptfd = accept(source, NULL, 0); + if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return; + else if(acceptfd == -1) { + purple_debug_warning("yahoo","yahoo_p2p_server_send_connected_cb: accept: %s\n", g_strerror(errno)); + yahoo_p2p_disconnect_destroy_data(data); + return; + } + + /* remove timeout */ + if (yd->yahoo_p2p_server_timeout_handle) { + purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle); + yd->yahoo_p2p_server_timeout_handle = 0; + } + + /* remove watcher and close p2p server */ + if (yd->yahoo_p2p_server_watcher) { + purple_input_remove(yd->yahoo_p2p_server_watcher); + yd->yahoo_p2p_server_watcher = 0; + } + if (yd->yahoo_local_p2p_server_fd >= 0) { + close(yd->yahoo_local_p2p_server_fd); + yd->yahoo_local_p2p_server_fd = -1; + } + + /* Add an Input Read event to the file descriptor */ + p2p_data->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data); + p2p_data->source = acceptfd; +} + +static gboolean yahoo_cancel_p2p_server_listen_cb(gpointer data) +{ + struct yahoo_p2p_data *p2p_data; + YahooData *yd; + + if(!(p2p_data = data)) + return FALSE; + + yd = purple_connection_get_protocol_data(p2p_data->gc); + + purple_debug_warning("yahoo","yahoo p2p server timeout, peer failed to connect\n"); + yahoo_p2p_disconnect_destroy_data(data); + purple_input_remove(yd->yahoo_p2p_server_watcher); + yd->yahoo_p2p_server_watcher = 0; + close(yd->yahoo_local_p2p_server_fd); + yd->yahoo_local_p2p_server_fd = -1; + yd->yahoo_p2p_server_timeout_handle = 0; + + return FALSE; +} + +static void yahoo_p2p_server_listen_cb(int listenfd, gpointer data) +{ + struct yahoo_p2p_data *p2p_data; + YahooData *yd; + + if(!(p2p_data = data)) + return ; + + yd = purple_connection_get_protocol_data(p2p_data->gc); + yd->listen_data = NULL; + + if(listenfd == -1) { + purple_debug_warning("yahoo","p2p: error starting p2p server\n"); + yahoo_p2p_disconnect_destroy_data(data); + return; + } + + /* Add an Input Read event to the file descriptor */ + yd->yahoo_local_p2p_server_fd = listenfd; + yd->yahoo_p2p_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_server_send_connected_cb,data); + + /* add timeout */ + yd->yahoo_p2p_server_timeout_handle = purple_timeout_add_seconds(YAHOO_P2P_SERVER_TIMEOUT, yahoo_cancel_p2p_server_listen_cb, data); +} + +/* send p2p pkt containing our encoded ip, asking peer to connect to us */ +void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13) +{ + const char *public_ip; + guint32 temp[4]; + guint32 ip; + char temp_str[100]; + gchar *base64_ip = NULL; + YahooFriend *f; + struct yahoo_packet *pkt; + PurpleAccount *account; + YahooData *yd = purple_connection_get_protocol_data(gc); + struct yahoo_p2p_data *p2p_data; + const char *norm_username; + + f = yahoo_friend_find(gc, who); + account = purple_connection_get_account(gc); + + /* Do not send invitation if already listening for other connection */ + if(yd->yahoo_local_p2p_server_fd >= 0) + return; + + /* One shouldn't try to connect to self */ + if( strcmp(purple_normalize(account, purple_account_get_username(account)), who) == 0) + return; + + /* send packet to only those friends who arent p2p connected and to whom we havent already sent. Do not send if this condition doesn't hold good */ + if( !( f && (yahoo_friend_get_p2p_status(f) == YAHOO_P2PSTATUS_NOT_CONNECTED) && (f->p2p_packet_sent == 0)) ) + return; + + /* Dont send p2p packet to buddies of other protocols */ + if(f->fed) + return; + + /* Finally, don't try to connect to buddies not online or on sms */ + if( (f->status == YAHOO_STATUS_OFFLINE) || f->sms ) + return; + + public_ip = purple_network_get_public_ip(); + if( (sscanf(public_ip, "%u.%u.%u.%u", &temp[0], &temp[1], &temp[2], &temp[3])) !=4 ) + return ; + + ip = (temp[3] << 24) | (temp[2] <<16) | (temp[1] << 8) | temp[0]; + sprintf(temp_str, "%d", ip); + base64_ip = purple_base64_encode( (guchar *)temp_str, strlen(temp_str) ); + + norm_username = purple_normalize(account, purple_account_get_username(account)); + pkt = yahoo_packet_new(YAHOO_SERVICE_PEERTOPEER, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, "sssissis", + 1, norm_username, + 4, norm_username, + 12, base64_ip, /* base64 encode ip */ + 61, 0, /* To-do : figure out what is 61 for?? */ + 2, "", + 5, who, + 13, val_13, + 49, "PEERTOPEER"); + yahoo_packet_send_and_free(pkt, yd); + + f->p2p_packet_sent = 1; /* set p2p_packet_sent to sent */ + + p2p_data = g_new0(struct yahoo_p2p_data, 1); + + p2p_data->gc = gc; + p2p_data->host_ip = NULL; + p2p_data->host_username = g_strdup(who); + p2p_data->val_13 = val_13; + p2p_data->connection_type = YAHOO_P2P_WE_ARE_SERVER; + p2p_data->source = -1; + + /* FIXME: If the port is already used, purple_network_listener returns NULL and old listener won't be canceled + * in yahoo_close function. */ + if (yd->listen_data) + purple_debug_warning("yahoo","p2p: Failed to create p2p server - server already exists\n"); + else { + yd->listen_data = purple_network_listen(YAHOO_PAGER_PORT_P2P, AF_UNSPEC, SOCK_STREAM, TRUE, yahoo_p2p_server_listen_cb, p2p_data); + if (yd->listen_data == NULL) + purple_debug_warning("yahoo","p2p: Failed to created p2p server\n"); + } + + g_free(base64_ip); +} + +/* function called when connection to p2p host is setup */ +static void yahoo_p2p_init_cb(gpointer data, gint source, const gchar *error_message) +{ + struct yahoo_p2p_data *p2p_data; + struct yahoo_packet *pkt_to_send; + PurpleAccount *account; + YahooData *yd; + + p2p_data = data; + yd = purple_connection_get_protocol_data(p2p_data->gc); + + if(error_message != NULL) { + purple_debug_warning("yahoo","p2p: %s\n",error_message); + yahoo_send_p2p_pkt(p2p_data->gc, p2p_data->host_username, 2);/* send p2p init packet with val_13=2 */ + + yahoo_p2p_disconnect_destroy_data(p2p_data); + return; + } + + /* Add an Input Read event to the file descriptor */ + p2p_data->input_event = purple_input_add(source, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data); + p2p_data->source = source; + + account = purple_connection_get_account(p2p_data->gc); + + /* Build the yahoo packet */ + pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt_to_send, "ssisi", + 4, purple_normalize(account, purple_account_get_username(account)), + 5, p2p_data->host_username, + 241, 0, /* Protocol identifier */ + 49, "PEERTOPEER", + 13, 1); /* we receive key13= 0 or 2, we send key13=1 */ + + yahoo_p2p_write_pkt(source, pkt_to_send); /* build raw packet and send */ + yahoo_packet_free(pkt_to_send); +} + +static void yahoo_process_p2p(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + GSList *l = pkt->hash; + char *who = NULL; + char *base64 = NULL; + guchar *decoded; + gsize len; + gint val_13 = 0; + gint val_11 = 0; + PurpleAccount *account; + YahooFriend *f; + + /* if status is not YAHOO_STATUS_BRB or YAHOO_STATUS_P2P, the packet bounced back, + * so it contains our own ip */ + if(pkt->status != YAHOO_STATUS_BRB && pkt->status != YAHOO_STATUS_P2P) + return ; + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 5: + /* our identity */ + break; + case 4: + if (g_utf8_validate(pair->value, -1, NULL)) { + who = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_p2p " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 1: + /* who again, the master identity this time? */ + break; + case 12: + if (g_utf8_validate(pair->value, -1, NULL)) { + base64 = pair->value; + /* so, this is an ip address. in base64. decoded it's in ascii. + after strtol, it's in reversed byte order. Who thought this up?*/ + } else { + purple_debug_warning("yahoo", "yahoo_process_p2p " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 13: + val_13 = strtol(pair->value, NULL, 10); + break; + case 11: + val_11 = strtol(pair->value, NULL, 10); /* session id of peer */ + if( (f = yahoo_friend_find(gc, who)) ) + f->session_id = val_11; + break; + /* + TODO: figure these out + yahoo: Key: 61 Value: 0 + yahoo: Key: 2 Value: + yahoo: Key: 13 Value: 0 packet count ?? + yahoo: Key: 49 Value: PEERTOPEER + yahoo: Key: 140 Value: 1 + */ + + } + + l = l->next; + } + + if (base64) { + guint32 ip; + YahooFriend *f; + char *host_ip, *tmp; + struct yahoo_p2p_data *p2p_data; + + decoded = purple_base64_decode(base64, &len); + if (decoded == NULL) { + purple_debug_info("yahoo","p2p: Unable to decode base64 IP (%s) \n", base64); + return; + } + tmp = purple_str_binary_to_ascii(decoded, len); + purple_debug_info("yahoo", "Got P2P service packet (from server): who = %s, ip = %s\n", who, tmp); + g_free(tmp); + + ip = strtol((gchar *)decoded, NULL, 10); + g_free(decoded); + host_ip = g_strdup_printf("%u.%u.%u.%u", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, + (ip >> 24) & 0xff); + f = yahoo_friend_find(gc, who); + if (f) + yahoo_friend_set_ip(f, host_ip); + purple_debug_info("yahoo", "IP : %s\n", host_ip); + + account = purple_connection_get_account(gc); + + if(val_11==0) { + if(!f) + return; + else + val_11 = f->session_id; + } + + p2p_data = g_new0(struct yahoo_p2p_data, 1); + p2p_data->host_username = g_strdup(who); + p2p_data->val_13 = val_13; + p2p_data->session_id = val_11; + p2p_data->host_ip = host_ip; + p2p_data->gc = gc; + p2p_data->connection_type = YAHOO_P2P_WE_ARE_CLIENT; + p2p_data->source = -1; + + /* connect to host */ + if((purple_proxy_connect(gc, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) { + purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip); + g_free(p2p_data->host_ip); + g_free(p2p_data->host_username); + g_free(p2p_data); + } + } +} + +static void yahoo_process_audible(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + PurpleAccount *account; + char *who = NULL, *msg = NULL, *id = NULL; + GSList *l = pkt->hash; + + account = purple_connection_get_account(gc); + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 4: + if (g_utf8_validate(pair->value, -1, NULL)) { + who = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_audible " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 5: + /* us */ + break; + case 230: + /* the audible, in foo.locale.bar.baz format + eg: base.tw.smiley.smiley43 */ + if (g_utf8_validate(pair->value, -1, NULL)) { + id = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_audible " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 231: + /* the text of the audible */ + if (g_utf8_validate(pair->value, -1, NULL)) { + msg = pair->value; + } else { + purple_debug_warning("yahoo", "yahoo_process_audible " + "got non-UTF-8 string for key %d\n", pair->key); + } + break; + case 232: + /* SHA-1 hash of audible SWF file (eg: 4e8691499d9c0fb8374478ff9720f4a9ea4a4915) */ + break; + } + + l = l->next; + } + + if (!msg) + msg = id; + if (!who || !msg) + return; + if (!g_utf8_validate(msg, -1, NULL)) { + purple_debug_misc("yahoo", "Warning, nonutf8 audible, ignoring!\n"); + return; + } + if (!purple_account_privacy_check(account, who)) { + purple_debug_misc("yahoo", "Audible message from %s for %s dropped!\n", + purple_account_get_username(account), who); + return; + } + if (id) { + /* "http://l.yimg.com/pu/dl/aud/"+locale+"/"+id+".swf" */ + char **audible_locale = g_strsplit(id, ".", 0); + char *buf = g_strdup_printf(_("[ Audible %s/%s/%s.swf ] %s"), YAHOO_AUDIBLE_URL, audible_locale[1], id, msg); + g_strfreev(audible_locale); + + serv_got_im(gc, who, buf, 0, time(NULL)); + g_free(buf); + } else + serv_got_im(gc, who, msg, 0, time(NULL)); +} + +static void yahoo_packet_process(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + switch (pkt->service) { + case YAHOO_SERVICE_LOGON: + case YAHOO_SERVICE_LOGOFF: + case YAHOO_SERVICE_ISAWAY: + case YAHOO_SERVICE_ISBACK: + case YAHOO_SERVICE_GAMELOGON: + case YAHOO_SERVICE_GAMELOGOFF: + case YAHOO_SERVICE_CHATLOGON: + case YAHOO_SERVICE_CHATLOGOFF: + case YAHOO_SERVICE_Y6_STATUS_UPDATE: + case YAHOO_SERVICE_STATUS_15: + yahoo_process_status(gc, pkt); + break; + case YAHOO_SERVICE_NOTIFY: + yahoo_process_notify(gc, pkt, YAHOO_PKT_TYPE_SERVER); + break; + case YAHOO_SERVICE_MESSAGE: + case YAHOO_SERVICE_GAMEMSG: + case YAHOO_SERVICE_CHATMSG: + yahoo_process_message(gc, pkt, YAHOO_PKT_TYPE_SERVER); + break; + case YAHOO_SERVICE_SYSMESSAGE: + yahoo_process_sysmessage(gc, pkt); + break; + case YAHOO_SERVICE_NEWMAIL: + yahoo_process_mail(gc, pkt); + break; + case YAHOO_SERVICE_NEWCONTACT: + yahoo_process_contact(gc, pkt); + break; + case YAHOO_SERVICE_AUTHRESP: + yahoo_process_authresp(gc, pkt); + break; + case YAHOO_SERVICE_LIST: + yahoo_process_list(gc, pkt); + break; + case YAHOO_SERVICE_LIST_15: + yahoo_process_list_15(gc, pkt); + break; + case YAHOO_SERVICE_AUTH: + yahoo_process_auth(gc, pkt); + break; + case YAHOO_SERVICE_AUTH_REQ_15: + yahoo_buddy_auth_req_15(gc, pkt); + break; + case YAHOO_SERVICE_ADDBUDDY: + yahoo_process_addbuddy(gc, pkt); + break; + case YAHOO_SERVICE_IGNORECONTACT: + yahoo_process_ignore(gc, pkt); + break; + case YAHOO_SERVICE_CONFINVITE: + case YAHOO_SERVICE_CONFADDINVITE: + yahoo_process_conference_invite(gc, pkt); + break; + case YAHOO_SERVICE_CONFDECLINE: + yahoo_process_conference_decline(gc, pkt); + break; + case YAHOO_SERVICE_CONFLOGON: + yahoo_process_conference_logon(gc, pkt); + break; + case YAHOO_SERVICE_CONFLOGOFF: + yahoo_process_conference_logoff(gc, pkt); + break; + case YAHOO_SERVICE_CONFMSG: + yahoo_process_conference_message(gc, pkt); + break; + case YAHOO_SERVICE_CHATONLINE: + yahoo_process_chat_online(gc, pkt); + break; + case YAHOO_SERVICE_CHATLOGOUT: + yahoo_process_chat_logout(gc, pkt); + break; + case YAHOO_SERVICE_CHATGOTO: + yahoo_process_chat_goto(gc, pkt); + break; + case YAHOO_SERVICE_CHATJOIN: + yahoo_process_chat_join(gc, pkt); + break; + case YAHOO_SERVICE_CHATLEAVE: /* XXX is this right? */ + case YAHOO_SERVICE_CHATEXIT: + yahoo_process_chat_exit(gc, pkt); + break; + case YAHOO_SERVICE_CHATINVITE: /* XXX never seen this one, might not do it right */ + case YAHOO_SERVICE_CHATADDINVITE: + yahoo_process_chat_addinvite(gc, pkt); + break; + case YAHOO_SERVICE_COMMENT: + yahoo_process_chat_message(gc, pkt); + break; + case YAHOO_SERVICE_PRESENCE_PERM: + case YAHOO_SERVICE_PRESENCE_SESSION: + yahoo_process_presence(gc, pkt); + break; + case YAHOO_SERVICE_P2PFILEXFER: + /* This case had no break and continued; thus keeping it this way.*/ + yahoo_process_p2p(gc, pkt); /* P2PFILEXFER handled the same way as process_p2p */ + yahoo_process_p2pfilexfer(gc, pkt); /* redundant ??, need to have a break now */ + case YAHOO_SERVICE_FILETRANSFER: + purple_debug_error("yahoo", "Legacy file transfers are not " + "supported anymore.\n"); + break; + case YAHOO_SERVICE_PEERTOPEER: + yahoo_process_p2p(gc, pkt); + break; + case YAHOO_SERVICE_PICTURE: + yahoo_process_picture(gc, pkt); + break; + case YAHOO_SERVICE_PICTURE_CHECKSUM: + yahoo_process_picture_checksum(gc, pkt); + break; + case YAHOO_SERVICE_PICTURE_UPLOAD: + yahoo_process_picture_upload(gc, pkt); + break; + case YAHOO_SERVICE_PICTURE_UPDATE: + case YAHOO_SERVICE_AVATAR_UPDATE: + yahoo_process_avatar_update(gc, pkt); + break; + case YAHOO_SERVICE_AUDIBLE: + yahoo_process_audible(gc, pkt); + break; + case YAHOO_SERVICE_CONTACT_DETAILS: + yahoo_process_contact_details(gc, pkt); + break; + case YAHOO_SERVICE_FILETRANS_15: + yahoo_process_filetrans_15(gc, pkt); + break; + case YAHOO_SERVICE_FILETRANS_INFO_15: + yahoo_process_filetrans_info_15(gc, pkt); + break; + case YAHOO_SERVICE_FILETRANS_ACC_15: + yahoo_process_filetrans_acc_15(gc, pkt); + break; + case YAHOO_SERVICE_SMS_MSG: + yahoo_process_sms_message(gc, pkt); + break; + + default: + purple_debug_error("yahoo", "Unhandled service 0x%02x\n", pkt->service); + break; + } +} + +static void yahoo_pending(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleConnection *gc = data; + YahooData *yd = purple_connection_get_protocol_data(gc); + char buf[1024]; + int len; + + len = read(yd->fd, buf, sizeof(buf)); + + if (len < 0) { + gchar *tmp; + + if (errno == EAGAIN) + /* No worries */ + return; + + tmp = g_strdup_printf(_("Lost connection with server: %s"), + g_strerror(errno)); + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); + g_free(tmp); + return; + } else if (len == 0) { + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Server closed the connection")); + return; + } + purple_connection_update_last_received(gc); + yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen); + memcpy(yd->rxqueue + yd->rxlen, buf, len); + yd->rxlen += len; + + while (1) { + struct yahoo_packet *pkt; + int pos = 0; + int pktlen; + + if (yd->rxlen < YAHOO_PACKET_HDRLEN) + return; + + if (strncmp((char *)yd->rxqueue, "YMSG", MIN(4, yd->rxlen)) != 0) { + /* HEY! This isn't even a YMSG packet. What + * are you trying to pull? */ + guchar *start; + + purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!\n"); + + start = memchr(yd->rxqueue + 1, 'Y', yd->rxlen - 1); + if (start) { + g_memmove(yd->rxqueue, start, yd->rxlen - (start - yd->rxqueue)); + yd->rxlen -= start - yd->rxqueue; + continue; + } else { + g_free(yd->rxqueue); + yd->rxqueue = NULL; + yd->rxlen = 0; + return; + } + } + + pos += 4; /* YMSG */ + pos += 2; + pos += 2; + + pktlen = yahoo_get16(yd->rxqueue + pos); pos += 2; + purple_debug_misc("yahoo", "%d bytes to read, rxlen is %d\n", pktlen, yd->rxlen); + + if (yd->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) + return; + + yahoo_packet_dump(yd->rxqueue, YAHOO_PACKET_HDRLEN + pktlen); + + pkt = yahoo_packet_new(0, 0, 0); + + pkt->service = yahoo_get16(yd->rxqueue + pos); pos += 2; + pkt->status = yahoo_get32(yd->rxqueue + pos); pos += 4; + purple_debug_misc("yahoo", "Yahoo Service: 0x%02x Status: %d\n", + pkt->service, pkt->status); + pkt->id = yahoo_get32(yd->rxqueue + pos); pos += 4; + + yahoo_packet_read(pkt, yd->rxqueue + pos, pktlen); + + yd->rxlen -= YAHOO_PACKET_HDRLEN + pktlen; + if (yd->rxlen) { + guchar *tmp = g_memdup(yd->rxqueue + YAHOO_PACKET_HDRLEN + pktlen, yd->rxlen); + g_free(yd->rxqueue); + yd->rxqueue = tmp; + } else { + g_free(yd->rxqueue); + yd->rxqueue = NULL; + } + + yahoo_packet_process(gc, pkt); + + yahoo_packet_free(pkt); + } +} + +static void yahoo_got_connected(gpointer data, gint source, const gchar *error_message) +{ + PurpleConnection *gc = data; + YahooData *yd; + struct yahoo_packet *pkt; + + if (source < 0) { + gchar *tmp; + tmp = g_strdup_printf(_("Unable to connect: %s"), error_message); + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); + g_free(tmp); + return; + } + + yd = purple_connection_get_protocol_data(gc); + yd->fd = source; + + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, yd->current_status, yd->session_id); + + yahoo_packet_hash_str(pkt, 1, purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc)))); + yahoo_packet_send_and_free(pkt, yd); + + yd->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc); +} + +#if TRY_WEBMESSENGER_LOGIN + +static void yahoo_got_web_connected(gpointer data, gint source, const gchar *error_message) +{ + PurpleConnection *gc = data; + YahooData *yd; + struct yahoo_packet *pkt; + + if (source < 0) { + gchar *tmp; + tmp = g_strdup_printf(_("Unable to connect: %s"), error_message); + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); + g_free(tmp); + return; + } + + yd = purple_connection_get_protocol_data(gc); + yd->fd = source; + + pkt = yahoo_packet_new(YAHOO_SERVICE_WEBLOGIN, YAHOO_STATUS_WEBLOGIN, yd->session_id); + + yahoo_packet_hash(pkt, "sss", 0, + purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))), + 1, purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))), + 6, yd->auth); + yahoo_packet_send_and_free(pkt, yd); + + g_free(yd->auth); + yd->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc); +} + +static void +yahoo_login_page_got(PurpleHttpConnection *hc, PurpleHttpResponse *resp, + gpointer _unused) +{ + PurpleConnection *gc = purple_http_conn_get_purple_connection(hc); + YahooData *yd = purple_connection_get_protocol_data(gc); + PurpleAccount *account = purple_connection_get_account(gc); + PurpleHttpCookieJar *cjar; + GString *auth_s; + const gchar *cookie; + + if (purple_http_response_get_code(resp) != 302) { + purple_connection_error(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect")); + return; + } + + auth_s = g_string_new(NULL); + cjar = purple_http_conn_get_cookie_jar(hc); + cookie = purple_http_cookie_jar_get(cjar, "B"); + if (cookie) + g_string_append_printf(auth_s, "B=%s; ", cookie); + cookie = purple_http_cookie_jar_get(cjar, "T"); + if (cookie) + g_string_append_printf(auth_s, "T=%s; ", cookie); + cookie = purple_http_cookie_jar_get(cjar, "Y"); + if (cookie) + g_string_append_printf(auth_s, "Y=%s; ", cookie); + + yd->auth = g_string_free(auth_s, FALSE); + /* Now we have our cookies to login with. I'll go get the milk. */ + + /* XXX: wcs2.msg.dcn.yahoo.com is down, so I used + * YAHOO_PAGER_HOST_FALLBACK, but I'm not sure, if it is the correct + * host. + */ + if (purple_proxy_connect(gc, account, YAHOO_PAGER_HOST_FALLBACK, + purple_account_get_int(account, "port", YAHOO_PAGER_PORT), + yahoo_got_web_connected, gc) == NULL) + { + purple_connection_error(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect")); + return; + } +} + +static void yahoo_login_page_hash_iter(const char *key, const char *val, GString *url) +{ + if (!strcmp(key, "passwd") || !strcmp(key, "login")) + return; + g_string_append_c(url, '&'); + g_string_append(url, key); + g_string_append_c(url, '='); + if (!strcmp(key, ".save") || !strcmp(key, ".js")) + g_string_append_c(url, '1'); + else if (!strcmp(key, ".challenge")) + g_string_append(url, val); + else + g_string_append(url, purple_url_encode(val)); +} + +static GHashTable *yahoo_login_page_hash(const char *buf, size_t len) +{ + GHashTable *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + const char *c = buf; + char *d; + char name[64], value[64]; + int count; + int input_len = strlen("<input "); + int name_len = strlen("name=\""); + int value_len = strlen("value=\""); + while ((len > ((c - buf) + input_len)) + && (c = strstr(c, "<input "))) { + if (!(c = g_strstr_len(c, len - (c - buf), "name=\""))) + continue; + c += name_len; + count = sizeof(name)-1; + for (d = name; (len > ((c - buf) + 1)) && *c!='"' + && count; c++, d++, count--) + *d = *c; + *d = '\0'; + count = sizeof(value)-1; + if (!(d = g_strstr_len(c, len - (c - buf), "value=\""))) + continue; + d += value_len; + if (strchr(c, '>') < d) + break; + for (c = d, d = value; (len > ((c - buf) + 1)) + && *c!='"' && count; c++, d++, count--) + *d = *c; + *d = '\0'; + g_hash_table_insert(hash, g_strdup(name), g_strdup(value)); + } + return hash; +} + +static void +yahoo_login_page_cb(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, gpointer _unused) +{ + PurpleConnection *gc = purple_http_conn_get_purple_connection(http_conn); + PurpleAccount *account = purple_connection_get_account(gc); + YahooData *yd = purple_connection_get_protocol_data(gc); + const char *pass = purple_connection_get_password(gc); + size_t len; + const gchar *got_data; + GHashTable *hash; + GString *url; + char md5[33], *hashp = md5, *chal; + int i; + PurpleCipher *cipher; + guchar digest[16]; + PurpleHttpRequest *req; + + if (!purple_http_response_is_successful(response)) + { + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + purple_http_response_get_error(response)); + return; + } + + got_data = purple_http_response_get_data(response, &len); + hash = yahoo_login_page_hash(got_data, len); + + cipher = purple_md5_cipher_new(); + + purple_cipher_append(cipher, (const guchar *)pass, strlen(pass)); + purple_cipher_digest(cipher, digest, sizeof(digest)); + for (i = 0; i < 16; ++i) { + g_snprintf(hashp, 3, "%02x", digest[i]); + hashp += 2; + } + + chal = g_strconcat(md5, g_hash_table_lookup(hash, ".challenge"), NULL); + purple_cipher_reset(cipher); + purple_cipher_append(cipher, (const guchar *)chal, strlen(chal)); + purple_cipher_digest(cipher, digest, sizeof(digest)); + hashp = md5; + for (i = 0; i < 16; ++i) { + g_snprintf(hashp, 3, "%02x", digest[i]); + hashp += 2; + } + /* + * I dunno why this is here and commented out.. but in case it's needed + * I updated it.. + + purple_cipher_reset(cipher); + purple_cipher_append(cipher, md5, strlen(md5)); + purple_cipher_digest(cipher, sizeof(digest), digest, NULL); + hashp = md5; + for (i = 0; i < 16; ++i) { + g_snprintf(hashp, 3, "%02x", digest[i]); + hashp += 2; + } + */ + g_free(chal); + + url = g_string_new(NULL); + g_string_printf(url, "http://login.yahoo.com/config/login?login=%s&passwd=%s", purple_account_get_username(account), md5); + g_hash_table_foreach(hash, (GHFunc)yahoo_login_page_hash_iter, url); + url = g_string_append(url, "&.hash=1&.md5=1"); + + g_hash_table_destroy(hash); + g_object_unref(cipher); + + req = purple_http_request_new(g_string_free(url, FALSE)); + purple_http_request_set_max_redirects(req, 0); + purple_http_connection_set_add(yd->http_reqs, + purple_http_request(gc, req, yahoo_login_page_got, NULL)); + purple_http_request_unref(req); +} + +#endif /* TRY_WEBMESSENGER_LOGIN */ + +static void yahoo_picture_check(PurpleAccount *account) +{ + PurpleConnection *gc = purple_account_get_connection(account); + PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account); + + yahoo_set_buddy_icon(gc, img); + purple_imgstore_unref(img); +} + +static int get_yahoo_status_from_purple_status(PurpleStatus *status) +{ + PurplePresence *presence; + const char *status_id; + const char *msg; + + presence = purple_status_get_presence(status); + status_id = purple_status_get_id(status); + msg = purple_status_get_attr_string(status, "message"); + + if ((msg != NULL) && (*msg != '\0')) { + return YAHOO_STATUS_CUSTOM; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AVAILABLE)) { + return YAHOO_STATUS_AVAILABLE; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BRB)) { + return YAHOO_STATUS_BRB; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BUSY)) { + return YAHOO_STATUS_BUSY; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATHOME)) { + return YAHOO_STATUS_NOTATHOME; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATDESK)) { + return YAHOO_STATUS_NOTATDESK; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTINOFFICE)) { + return YAHOO_STATUS_NOTINOFFICE; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONPHONE)) { + return YAHOO_STATUS_ONPHONE; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONVACATION)) { + return YAHOO_STATUS_ONVACATION; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_OUTTOLUNCH)) { + return YAHOO_STATUS_OUTTOLUNCH; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_STEPPEDOUT)) { + return YAHOO_STATUS_STEPPEDOUT; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_INVISIBLE)) { + return YAHOO_STATUS_INVISIBLE; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AWAY)) { + return YAHOO_STATUS_CUSTOM; + } else if (purple_presence_is_idle(presence)) { + return YAHOO_STATUS_IDLE; + } else { + purple_debug_error("yahoo", "Unexpected PurpleStatus!\n"); + return YAHOO_STATUS_AVAILABLE; + } +} + +static void yahoo_got_pager_server(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, gpointer _yd) +{ + YahooData *yd = _yd; + PurpleConnection *gc = yd->gc; + PurpleAccount *a = purple_connection_get_account(gc); + gchar **strings = NULL, *cs_server = NULL; + int port = purple_account_get_int(a, "port", YAHOO_PAGER_PORT); + int stringslen = 0; + const gchar *got_data; + + if (!purple_http_response_is_successful(response)) { + purple_debug_error("yahoo", "Unable to retrieve server info: %s\n", + purple_http_response_get_error(response)); + + if(yahoo_is_japan(a)) { /* We don't know fallback hosts for Yahoo Japan :( */ + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect: The server returned an empty response.")); + } else { + if(purple_proxy_connect(gc, a, YAHOO_PAGER_HOST_FALLBACK, port, + yahoo_got_connected, gc) == NULL) { + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect")); + } + } + } else { + got_data = purple_http_response_get_data(response, NULL); + strings = g_strsplit(got_data, "\r\n", -1); + + if((stringslen = g_strv_length(strings)) > 1) { + int i; + + for(i = 0; i < stringslen; i++) { + if(g_ascii_strncasecmp(strings[i], "COLO_CAPACITY=", 14) == 0) { + purple_debug_info("yahoo", "Got COLO Capacity: %s\n", &(strings[i][14])); + } else if(g_ascii_strncasecmp(strings[i], "CS_IP_ADDRESS=", 14) == 0) { + cs_server = g_strdup(&strings[i][14]); + purple_debug_info("yahoo", "Got CS IP address: %s\n", cs_server); + } + } + } + + if(cs_server) { /* got an address; get on with connecting */ + if(purple_proxy_connect(gc, a, cs_server, port, yahoo_got_connected, gc) == NULL) + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect")); + } else { + purple_debug_error("yahoo", "No CS address retrieved! Server " + "response:\n%s\n", got_data); + + if(yahoo_is_japan(a)) { /* We don't know fallback hosts for Yahoo Japan :( */ + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect: The server's response did not contain " + "the necessary information")); + } else + if(purple_proxy_connect(gc, a, YAHOO_PAGER_HOST_FALLBACK, port, + yahoo_got_connected, gc) == NULL) { + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect")); + } + } + } + + g_strfreev(strings); + g_free(cs_server); +} + +void yahoo_login(PurpleAccount *account) { + PurpleConnection *gc = purple_account_get_connection(account); + PurpleHttpRequest *req; + YahooData *yd = g_new0(YahooData, 1); + PurpleStatus *status = purple_account_get_active_status(account); + + purple_connection_set_protocol_data(gc, yd); + purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML | PURPLE_CONNECTION_FLAG_NO_BGCOLOR | PURPLE_CONNECTION_FLAG_NO_URLDESC); + + purple_connection_update_progress(gc, _("Connecting"), 1, 2); + + purple_connection_set_display_name(gc, purple_account_get_username(account)); + + yd->gc = gc; + yd->jp = yahoo_is_japan(account); + yd->yahoo_local_p2p_server_fd = -1; + yd->fd = -1; + yd->txhandler = 0; + /* TODO: Is there a good grow size for the buffer? */ + yd->txbuf = purple_circular_buffer_new(0); + yd->http_reqs = purple_http_connection_set_new(); + yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free); + yd->imvironments = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + yd->xfer_peer_idstring_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); + yd->peers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + yahoo_p2p_disconnect_destroy_data); + yd->sms_carrier = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + yd->yahoo_p2p_timer = purple_timeout_add_seconds(YAHOO_P2P_KEEPALIVE_SECS, + yahoo_p2p_keepalive, gc); + yd->confs = NULL; + yd->conf_id = 2; + yd->last_keepalive = yd->last_ping = time(NULL); + + yd->current_status = get_yahoo_status_from_purple_status(status); + + yahoo_picture_check(account); + + /* Get the pager server. Actually start connecting in the callback since we + * must have the contents of the HTTP response to proceed. */ + req = purple_http_request_new(yd->jp ? YAHOOJP_PAGER_HOST_REQ_URL : + YAHOO_PAGER_HOST_REQ_URL); + purple_http_request_header_set(req, "User-Agent", YAHOO_CLIENT_USERAGENT); + purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc, + req, yahoo_got_pager_server, yd)); + purple_http_request_unref(req); + + return; +} + +void yahoo_close(PurpleConnection *gc) { + YahooData *yd = purple_connection_get_protocol_data(gc); + GSList *l; + + if (yd->inpa) { + purple_input_remove(yd->inpa); + yd->inpa = 0; + } + + purple_http_connection_set_destroy(yd->http_reqs); + yd->http_reqs = NULL; + + for (l = yd->confs; l; l = l->next) { + PurpleChatConversation *conv = l->data; + + yahoo_conf_leave(yd, purple_conversation_get_name(PURPLE_CONVERSATION(conv)), + purple_connection_get_display_name(gc), + purple_chat_conversation_get_users(conv)); + } + g_slist_free(yd->confs); + + g_slist_free_full(yd->cookies, g_free); + + yd->chat_online = FALSE; + if (yd->in_chat) + yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */ + + purple_timeout_remove(yd->yahoo_p2p_timer); + if(yd->yahoo_p2p_server_timeout_handle != 0) { + purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle); + yd->yahoo_p2p_server_timeout_handle = 0; + } + + /* close p2p server if it is waiting for a peer to connect */ + if (yd->yahoo_p2p_server_watcher) { + purple_input_remove(yd->yahoo_p2p_server_watcher); + yd->yahoo_p2p_server_watcher = 0; + } + if (yd->yahoo_local_p2p_server_fd >= 0) { + close(yd->yahoo_local_p2p_server_fd); + yd->yahoo_local_p2p_server_fd = -1; + } + + g_hash_table_destroy(yd->sms_carrier); + g_hash_table_destroy(yd->peers); + g_hash_table_destroy(yd->friends); + g_hash_table_destroy(yd->imvironments); + g_hash_table_destroy(yd->xfer_peer_idstring_map); + g_free(yd->chat_name); + + g_free(yd->cookie_y); + g_free(yd->cookie_t); + g_free(yd->cookie_b); + + if (yd->txhandler) + purple_input_remove(yd->txhandler); + + g_object_unref(G_OBJECT(yd->txbuf)); + + if (yd->fd >= 0) + close(yd->fd); + + g_free(yd->rxqueue); + yd->rxlen = 0; + g_free(yd->picture_url); + + purple_http_conn_cancel(yd->picture_upload_hc); + if (yd->picture_upload_todo) + yahoo_buddy_icon_upload_data_free(yd->picture_upload_todo); + if (yd->ycht) + ycht_connection_close(yd->ycht); + if (yd->listen_data != NULL) + purple_network_listen_cancel(yd->listen_data); + + g_free(yd->pending_chat_room); + g_free(yd->pending_chat_id); + g_free(yd->pending_chat_topic); + g_free(yd->pending_chat_goto); + g_strfreev(yd->profiles); + + yahoo_personal_details_reset(&yd->ypd, TRUE); + + g_free(yd->current_list15_grp); + + g_free(yd); + purple_connection_set_protocol_data(gc, NULL); +} + +const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b) +{ + return "yahoo"; +} + +const char *yahoo_list_emblem(PurpleBuddy *b) +{ + PurpleAccount *account; + PurpleConnection *gc; + YahooFriend *f; + PurplePresence *presence; + + if (!b || !(account = purple_buddy_get_account(b)) || + !(gc = purple_account_get_connection(account)) || + !purple_connection_get_protocol_data(gc)) + return NULL; + + f = yahoo_friend_find(gc, purple_buddy_get_name(b)); + if (!f) { + return "not-authorized"; + } + + presence = purple_buddy_get_presence(b); + + if (purple_presence_is_online(presence)) { + if (yahoo_friend_get_game(f)) + return "game"; + + if (f->fed) + return "external"; + } + return NULL; +} + +static const char *yahoo_get_status_string(enum yahoo_status a) +{ + switch (a) { + case YAHOO_STATUS_BRB: + return _("Be Right Back"); + case YAHOO_STATUS_BUSY: + return _("Busy"); + case YAHOO_STATUS_NOTATHOME: + return _("Not at Home"); + case YAHOO_STATUS_NOTATDESK: + return _("Not at Desk"); + case YAHOO_STATUS_NOTINOFFICE: + return _("Not in Office"); + case YAHOO_STATUS_ONPHONE: + return _("On the Phone"); + case YAHOO_STATUS_ONVACATION: + return _("On Vacation"); + case YAHOO_STATUS_OUTTOLUNCH: + return _("Out to Lunch"); + case YAHOO_STATUS_STEPPEDOUT: + return _("Stepped Out"); + case YAHOO_STATUS_INVISIBLE: + return _("Invisible"); + case YAHOO_STATUS_IDLE: + return _("Idle"); + case YAHOO_STATUS_OFFLINE: + return _("Offline"); + default: + return _("Available"); + } +} + +static void yahoo_initiate_conference(PurpleBlistNode *node, gpointer data) { + + PurpleBuddy *buddy; + PurpleConnection *gc; + + GHashTable *components; + YahooData *yd; + int id; + + g_return_if_fail(PURPLE_IS_BUDDY(node)); + + buddy = (PurpleBuddy *) node; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + yd = purple_connection_get_protocol_data(gc); + id = yd->conf_id; + + components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_replace(components, g_strdup("room"), + g_strdup_printf("%s-%d", purple_connection_get_display_name(gc), id)); + g_hash_table_replace(components, g_strdup("topic"), g_strdup("Join my conference...")); + g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference")); + yahoo_c_join(gc, components); + g_hash_table_destroy(components); + + yahoo_c_invite(gc, id, "Join my conference...", purple_buddy_get_name(buddy)); +} + +static void yahoo_presence_settings(PurpleBlistNode *node, gpointer data) { + PurpleBuddy *buddy; + PurpleConnection *gc; + int presence_val = GPOINTER_TO_INT(data); + + buddy = (PurpleBuddy *) node; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + + yahoo_friend_update_presence(gc, purple_buddy_get_name(buddy), presence_val); +} + +static void yahoo_game(PurpleBlistNode *node, gpointer data) { + + PurpleBuddy *buddy; + PurpleConnection *gc; + + const char *game; + char *game2; + char *t; + char url[256]; + YahooFriend *f; + + g_return_if_fail(PURPLE_IS_BUDDY(node)); + + buddy = (PurpleBuddy *) node; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + + f = yahoo_friend_find(gc, purple_buddy_get_name(buddy)); + if (!f) + return; + + game = yahoo_friend_get_game(f); + if (!game) + return; + + t = game2 = g_strdup(strstr(game, "ante?room=")); + while (*t && *t != '\t') + t++; + *t = 0; + g_snprintf(url, sizeof url, "http://games.yahoo.com/games/%s", game2); + purple_notify_uri(gc, url); + g_free(game2); +} + +char *yahoo_status_text(PurpleBuddy *b) +{ + YahooFriend *f = NULL; + const char *msg; + char *msg2; + PurpleAccount *account; + PurpleConnection *gc; + + account = purple_buddy_get_account(b); + gc = purple_account_get_connection(account); + if (!gc || !purple_connection_get_protocol_data(gc)) + return NULL; + + f = yahoo_friend_find(gc, purple_buddy_get_name(b)); + if (!f) + return g_strdup(_("Not on server list")); + + switch (f->status) { + case YAHOO_STATUS_AVAILABLE: + return NULL; + case YAHOO_STATUS_IDLE: + if (f->idle == -1) + return g_strdup(yahoo_get_status_string(f->status)); + return NULL; + case YAHOO_STATUS_CUSTOM: + if (!(msg = yahoo_friend_get_status_message(f))) + return NULL; + msg2 = g_markup_escape_text(msg, strlen(msg)); + purple_util_chrreplace(msg2, '\n', ' '); + return msg2; + + default: + return g_strdup(yahoo_get_status_string(f->status)); + } +} + +void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) +{ + YahooFriend *f; + char *status = NULL; + const char *presence = NULL; + PurpleAccount *account; + + account = purple_buddy_get_account(b); + f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b)); + if (!f) + status = g_strdup_printf("\n%s", _("Not on server list")); + else { + switch (f->status) { + case YAHOO_STATUS_CUSTOM: + if (!yahoo_friend_get_status_message(f)) + return; + status = g_strdup(yahoo_friend_get_status_message(f)); + break; + case YAHOO_STATUS_OFFLINE: + break; + default: + status = g_strdup(yahoo_get_status_string(f->status)); + break; + } + + switch (f->presence) { + case YAHOO_PRESENCE_ONLINE: + presence = _("Appear Online"); + break; + case YAHOO_PRESENCE_PERM_OFFLINE: + presence = _("Appear Permanently Offline"); + break; + case YAHOO_PRESENCE_DEFAULT: + break; + default: + purple_debug_error("yahoo", "Unknown presence in yahoo_tooltip_text\n"); + break; + } + } + + if (status != NULL) { + purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status); + g_free(status); + } + + if (presence != NULL) + purple_notify_user_info_add_pair_plaintext(user_info, _("Presence"), presence); + + if (f && full) { + YahooPersonalDetails *ypd = &f->ypd; + if (ypd->phone.home && *ypd->phone.home) + purple_notify_user_info_add_pair_plaintext(user_info, _("Home Phone Number"), ypd->phone.home); + if (ypd->phone.work && *ypd->phone.work) + purple_notify_user_info_add_pair_plaintext(user_info, _("Work Phone Number"), ypd->phone.work); + if (ypd->phone.mobile && *ypd->phone.mobile) + purple_notify_user_info_add_pair_plaintext(user_info, _("Mobile Phone Number"), ypd->phone.mobile); + } +} + +static void yahoo_addbuddyfrommenu_cb(PurpleBlistNode *node, gpointer data) +{ + PurpleBuddy *buddy; + PurpleConnection *gc; + + g_return_if_fail(PURPLE_IS_BUDDY(node)); + + buddy = (PurpleBuddy *) node; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + + yahoo_add_buddy(gc, buddy, NULL, NULL); +} + + +static void yahoo_chat_goto_menu(PurpleBlistNode *node, gpointer data) +{ + PurpleBuddy *buddy; + PurpleConnection *gc; + + g_return_if_fail(PURPLE_IS_BUDDY(node)); + + buddy = (PurpleBuddy *) node; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + + yahoo_chat_goto(gc, purple_buddy_get_name(buddy)); +} + +static GList *build_presence_submenu(YahooFriend *f, PurpleConnection *gc) { + GList *m = NULL; + PurpleMenuAction *act; + YahooData *yd = purple_connection_get_protocol_data(gc); + + if (yd->current_status == YAHOO_STATUS_INVISIBLE) { + if (f->presence != YAHOO_PRESENCE_ONLINE) { + act = purple_menu_action_new(_("Appear Online"), + PURPLE_CALLBACK(yahoo_presence_settings), + GINT_TO_POINTER(YAHOO_PRESENCE_ONLINE), + NULL); + m = g_list_append(m, act); + } else if (f->presence != YAHOO_PRESENCE_DEFAULT) { + act = purple_menu_action_new(_("Appear Offline"), + PURPLE_CALLBACK(yahoo_presence_settings), + GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT), + NULL); + m = g_list_append(m, act); + } + } + + if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) { + act = purple_menu_action_new(_("Don't Appear Permanently Offline"), + PURPLE_CALLBACK(yahoo_presence_settings), + GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT), + NULL); + m = g_list_append(m, act); + } else { + act = purple_menu_action_new(_("Appear Permanently Offline"), + PURPLE_CALLBACK(yahoo_presence_settings), + GINT_TO_POINTER(YAHOO_PRESENCE_PERM_OFFLINE), + NULL); + m = g_list_append(m, act); + } + + return m; +} + +static void yahoo_doodle_blist_node(PurpleBlistNode *node, gpointer data) +{ + PurpleBuddy *b = (PurpleBuddy *)node; + PurpleAccount *account = purple_buddy_get_account(b); + PurpleConnection *gc = purple_account_get_connection(account); + + yahoo_doodle_initiate(gc, purple_buddy_get_name(b)); +} + +#if 0 +/* XXX: it doesn't seems to work */ +static void +yahoo_userinfo_blist_node(PurpleBlistNode *node, gpointer data) +{ + PurpleBuddy *b = (PurpleBuddy *)node; + PurpleAccount *account = purple_buddy_get_account(b); + PurpleConnection *gc = purple_account_get_connection(account); + + yahoo_set_userinfo_for_buddy(gc, b); +} +#endif + +static GList *yahoo_buddy_menu(PurpleBuddy *buddy) +{ + GList *m = NULL; + PurpleMenuAction *act; + + PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + YahooData *yd = purple_connection_get_protocol_data(gc); + static char buf2[1024]; + YahooFriend *f; + + f = yahoo_friend_find(gc, purple_buddy_get_name(buddy)); + + if (!f && !yd->wm) { + act = purple_menu_action_new(_("Add Buddy"), + PURPLE_CALLBACK(yahoo_addbuddyfrommenu_cb), + NULL, NULL); + m = g_list_append(m, act); + + return m; + + } + + if (f && f->status != YAHOO_STATUS_OFFLINE && f->fed == YAHOO_FEDERATION_NONE) { + if (!yd->wm) { + act = purple_menu_action_new(_("Join in Chat"), + PURPLE_CALLBACK(yahoo_chat_goto_menu), + NULL, NULL); + m = g_list_append(m, act); + } + + act = purple_menu_action_new(_("Initiate Conference"), + PURPLE_CALLBACK(yahoo_initiate_conference), + NULL, NULL); + m = g_list_append(m, act); + + if (yahoo_friend_get_game(f)) { + const char *game = yahoo_friend_get_game(f); + char *room; + char *t; + + if ((room = strstr(game, "&follow="))) {/* skip ahead to the url */ + while (*room && *room != '\t') /* skip to the tab */ + room++; + t = room++; /* room as now at the name */ + while (*t != '\n') + t++; /* replace the \n with a space */ + *t = ' '; + g_snprintf(buf2, sizeof buf2, "%s", room); + + act = purple_menu_action_new(buf2, + PURPLE_CALLBACK(yahoo_game), + NULL, NULL); + m = g_list_append(m, act); + } + } + } + + if (f) { + act = purple_menu_action_new(_("Presence Settings"), NULL, NULL, + build_presence_submenu(f, gc)); + m = g_list_append(m, act); + + if (f->fed == YAHOO_FEDERATION_NONE) { + act = purple_menu_action_new(_("Start Doodling"), + PURPLE_CALLBACK(yahoo_doodle_blist_node), + NULL, NULL); + m = g_list_append(m, act); + } + +#if 0 + /* XXX: it doesn't seems to work */ + act = purple_menu_action_new(_("Set User Info..."), + PURPLE_CALLBACK(yahoo_userinfo_blist_node), + NULL, NULL); + m = g_list_append(m, act); +#endif + } + + return m; +} + +GList *yahoo_blist_node_menu(PurpleBlistNode *node) +{ + if(PURPLE_IS_BUDDY(node)) { + return yahoo_buddy_menu((PurpleBuddy *) node); + } else { + return NULL; + } +} + +static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + const char *name = yd->profiles[GPOINTER_TO_INT(purple_request_fields_get_choice(fields, "id"))]; + + struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash_str(pkt, 3, name); + yahoo_packet_send_and_free(pkt, yd); + + purple_connection_set_display_name(gc, name); +} + +static void +yahoo_get_inbox_token_cb(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, gpointer _unused) +{ + PurpleConnection *gc = + purple_http_conn_get_purple_connection(http_conn); + gchar *url; + YahooData *yd = purple_connection_get_protocol_data(gc); + + g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); + + if (!purple_http_response_is_successful(response)) { + purple_debug_error("yahoo", + "Requesting mail login token failed: %s\n", + purple_http_response_get_error(response)); + url = g_strdup(yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL); + } else { + /* Should we not be hardcoding the rd url? */ + gchar *token; + token = g_strdup(purple_http_response_get_data(response, NULL)); + g_strstrip(token); + url = g_strdup_printf( + "http://login.yahoo.com/config/reset_cookies_token?" + ".token=%s" + "&.done=http://us.rd.yahoo.com/messenger/client/%%3f" + "http://mail.yahoo.com/", token); + purple_str_wipe(token); + } + + /* Open the mailbox with the parsed url data */ + purple_notify_uri(gc, url); + + g_free(url); +} + + +static void yahoo_show_inbox(PurpleProtocolAction *action) +{ + /* Setup a cookie that can be used by the browser */ + /* XXX I have no idea how this will work with Yahoo! Japan. */ + + PurpleConnection *gc = action->connection; + YahooData *yd = purple_connection_get_protocol_data(gc); + PurpleHttpRequest *req; + PurpleHttpCookieJar *cookiejar; + + req = purple_http_request_new( + "https://login.yahoo.com/config/cookie_token"); + purple_http_request_set_method(req, "POST"); + purple_http_request_header_set(req, "User-Agent", + YAHOO_CLIENT_USERAGENT); + cookiejar = purple_http_request_get_cookie_jar(req); + purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t); + purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y); + purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc, + req, yahoo_get_inbox_token_cb, NULL)); + purple_http_request_unref(req); +} + +#if 0 +/* XXX: it doesn't seems to work */ +static void +yahoo_set_userinfo_fn(PurpleProtocolAction *action) +{ + yahoo_set_userinfo(action->connection); +} +#endif + +static void yahoo_show_act_id(PurpleProtocolAction *action) +{ + PurpleRequestFields *fields; + PurpleRequestFieldGroup *group; + PurpleRequestField *field; + PurpleConnection *gc = (PurpleConnection *) action->connection; + YahooData *yd = purple_connection_get_protocol_data(gc); + const char *name = purple_connection_get_display_name(gc); + int iter; + + fields = purple_request_fields_new(); + group = purple_request_field_group_new(NULL); + purple_request_fields_add_group(fields, group); + field = purple_request_field_choice_new("id", "Activate which ID?", 0); + purple_request_field_group_add_field(group, field); + + for (iter = 0; yd->profiles[iter]; iter++) { + purple_request_field_choice_add(field, yd->profiles[iter], GINT_TO_POINTER(iter)); + if (purple_strequal(yd->profiles[iter], name)) + purple_request_field_choice_set_default_value(field, GINT_TO_POINTER(iter)); + } + + purple_request_fields(gc, NULL, _("Select the ID you want to activate"), NULL, + fields, + _("OK"), G_CALLBACK(yahoo_act_id), + _("Cancel"), NULL, + purple_request_cpar_from_connection(gc), gc); +} + +static void yahoo_show_chat_goto(PurpleProtocolAction *action) +{ + PurpleConnection *gc = action->connection; + purple_request_input(gc, NULL, _("Join whom in chat?"), NULL, + "", FALSE, FALSE, NULL, + _("OK"), G_CALLBACK(yahoo_chat_goto), + _("Cancel"), NULL, + purple_request_cpar_from_connection(gc), + gc); +} + +GList *yahoo_get_actions(PurpleConnection *gc) { + GList *m = NULL; + PurpleProtocolAction *act; + +#if 0 + /* XXX: it doesn't seems to work */ + act = purple_protocol_action_new(_("Set User Info..."), + yahoo_set_userinfo_fn); + m = g_list_append(m, act); +#endif + + act = purple_protocol_action_new(_("Activate ID..."), + yahoo_show_act_id); + m = g_list_append(m, act); + + act = purple_protocol_action_new(_("Join User in Chat..."), + yahoo_show_chat_goto); + m = g_list_append(m, act); + + m = g_list_append(m, NULL); + act = purple_protocol_action_new(_("Open Inbox"), + yahoo_show_inbox); + m = g_list_append(m, act); + + return m; +} + +struct yahoo_sms_carrier_cb_data { + PurpleConnection *gc; + char *who; + char *what; +}; + +static void yahoo_get_sms_carrier_cb(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, gpointer _sms_cb_data) +{ + struct yahoo_sms_carrier_cb_data *sms_cb_data = _sms_cb_data; + PurpleConnection *gc = sms_cb_data->gc; + YahooData *yd = purple_connection_get_protocol_data(gc); + char *status = NULL; + char *carrier = NULL; + PurpleAccount *account = purple_connection_get_account(gc); + PurpleIMConversation *im = purple_conversations_find_im_with_account(sms_cb_data->who, account); + + if (!purple_http_response_is_successful(response)) { + purple_conversation_write(PURPLE_CONVERSATION(im), NULL, + _("Can't send SMS. Unable to obtain mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL)); + + g_free(sms_cb_data->who); + g_free(sms_cb_data->what); + g_free(sms_cb_data); + return ; + } else { + const gchar *got_data = purple_http_response_get_data(response, NULL); + PurpleXmlNode *validate_data_root = purple_xmlnode_from_str(got_data, -1); + PurpleXmlNode *validate_data_child = purple_xmlnode_get_child(validate_data_root, "mobile_no"); + const char *mobile_no = purple_xmlnode_get_attrib(validate_data_child, "msisdn"); + + validate_data_root = purple_xmlnode_copy(validate_data_child); + validate_data_child = purple_xmlnode_get_child(validate_data_root, "status"); + status = purple_xmlnode_get_data(validate_data_child); + + validate_data_child = purple_xmlnode_get_child(validate_data_root, "carrier"); + carrier = purple_xmlnode_get_data(validate_data_child); + + purple_debug_info("yahoo", "SMS validate data: %s\n", got_data); + + if (status && g_str_equal(status, "Valid")) { + g_hash_table_insert(yd->sms_carrier, + g_strdup_printf("+%s", mobile_no), g_strdup(carrier)); + yahoo_send_im(sms_cb_data->gc, sms_cb_data->who, + sms_cb_data->what, PURPLE_MESSAGE_SEND); + } else { + g_hash_table_insert(yd->sms_carrier, + g_strdup_printf("+%s", mobile_no), g_strdup("Unknown")); + purple_conversation_write(PURPLE_CONVERSATION(im), NULL, + _("Can't send SMS. Unknown mobile carrier."), + PURPLE_MESSAGE_SYSTEM, time(NULL)); + } + + purple_xmlnode_free(validate_data_child); + purple_xmlnode_free(validate_data_root); + g_free(sms_cb_data->who); + g_free(sms_cb_data->what); + g_free(sms_cb_data); + g_free(status); + g_free(carrier); + } +} + +static void yahoo_get_sms_carrier(PurpleConnection *gc, gpointer data) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + PurpleHttpRequest *req; + PurpleHttpCookieJar *cookiejar; + struct yahoo_sms_carrier_cb_data *sms_cb_data; + char *validate_request_str = NULL; + PurpleXmlNode *validate_request_root = NULL; + PurpleXmlNode *validate_request_child = NULL; + + if(!(sms_cb_data = data)) + return; + + validate_request_root = purple_xmlnode_new("validate"); + purple_xmlnode_set_attrib(validate_request_root, "intl", "us"); + purple_xmlnode_set_attrib(validate_request_root, "version", YAHOO_CLIENT_VERSION); + purple_xmlnode_set_attrib(validate_request_root, "qos", "0"); + + validate_request_child = purple_xmlnode_new_child(validate_request_root, "mobile_no"); + purple_xmlnode_set_attrib(validate_request_child, "msisdn", sms_cb_data->who + 1); + + validate_request_str = purple_xmlnode_to_str(validate_request_root, NULL); + + purple_xmlnode_free(validate_request_child); + purple_xmlnode_free(validate_request_root); + + req = purple_http_request_new(NULL); + purple_http_request_set_url_printf(req, "http://validate.msg.yahoo.com" + "/mobileno?intl=us&version=%s", YAHOO_CLIENT_VERSION); + purple_http_request_set_method(req, "POST"); + purple_http_request_header_set(req, "User-Agent", + YAHOO_CLIENT_USERAGENT); + cookiejar = purple_http_request_get_cookie_jar(req); + purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t); + purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y); + purple_http_request_set_contents(req, validate_request_str, -1); + purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc, + req, yahoo_get_sms_carrier_cb, data)); + purple_http_request_unref(req); + + g_free(validate_request_str); +} + +int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + struct yahoo_packet *pkt = NULL; + char *msg = yahoo_html_to_codes(what); + char *msg2; + PurpleWhiteboard *wb; + int ret = 1; + const char *fed_who; + gsize lenb = 0; + glong lenc = 0; + struct yahoo_p2p_data *p2p_data; + YahooFederation fed = YAHOO_FEDERATION_NONE; + msg2 = yahoo_string_encode(gc, msg, TRUE); + + if(msg2) { + lenb = strlen(msg2); + lenc = g_utf8_strlen(msg2, -1); + + if(lenb > YAHOO_MAX_MESSAGE_LENGTH_BYTES || lenc > YAHOO_MAX_MESSAGE_LENGTH_CHARS) { + purple_debug_info("yahoo", "Message too big. Length is %" G_GSIZE_FORMAT + " bytes, %ld characters. Max is %d bytes, %d chars." + " Message is '%s'.\n", lenb, lenc, YAHOO_MAX_MESSAGE_LENGTH_BYTES, + YAHOO_MAX_MESSAGE_LENGTH_CHARS, msg2); + g_free(msg); + g_free(msg2); + return -E2BIG; + } + } + + fed = yahoo_get_federation_from_name(who); + + if (who[0] == '+') { + /* we have an sms to be sent */ + gchar *carrier = NULL; + const char *alias = NULL; + PurpleAccount *account = purple_connection_get_account(gc); + PurpleIMConversation *im = purple_conversations_find_im_with_account(who, account); + + carrier = g_hash_table_lookup(yd->sms_carrier, who); + if (!carrier) { + struct yahoo_sms_carrier_cb_data *sms_cb_data; + sms_cb_data = g_malloc(sizeof(struct yahoo_sms_carrier_cb_data)); + sms_cb_data->gc = gc; + sms_cb_data->who = g_strdup(who); + sms_cb_data->what = g_strdup(what); + + purple_conversation_write(PURPLE_CONVERSATION(im), NULL, _("Getting mobile carrier to send the SMS."), PURPLE_MESSAGE_SYSTEM, time(NULL)); + + yahoo_get_sms_carrier(gc, sms_cb_data); + + g_free(msg); + g_free(msg2); + return ret; + } + else if( strcmp(carrier,"Unknown") == 0 ) { + purple_conversation_write(PURPLE_CONVERSATION(im), NULL, _("Can't send SMS. Unknown mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL)); + + g_free(msg); + g_free(msg2); + return -1; + } + + alias = purple_account_get_private_alias(account); + pkt = yahoo_packet_new(YAHOO_SERVICE_SMS_MSG, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, "sssss", + 1, purple_connection_get_display_name(gc), + 69, alias, + 5, who + 1, + 68, carrier, + 14, msg2); + yahoo_packet_send_and_free(pkt, yd); + + g_free(msg); + g_free(msg2); + + return ret; + } + + pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id); + fed_who = who; + switch (fed) { + case YAHOO_FEDERATION_MSN: + case YAHOO_FEDERATION_OCS: + case YAHOO_FEDERATION_IBM: + case YAHOO_FEDERATION_PBX: + fed_who += 4; + break; + case YAHOO_FEDERATION_NONE: + default: + break; + } + yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, fed_who); + if (fed) + yahoo_packet_hash_int(pkt, 241, fed); + + yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */ + yahoo_packet_hash_str(pkt, 14, msg2); + + /* + * IMVironment. + * + * If this message is to a user who is also Doodling with the local user, + * format the chat packet with the correct IMV information (thanks Yahoo!) + * + * Otherwise attempt to use the same IMVironment as the remote user, + * just so that we don't inadvertantly reset their IMVironment back + * to nothing. + * + * If they have not set an IMVironment, then use the default. + */ + wb = purple_whiteboard_get_session(purple_connection_get_account(gc), who); + if (wb) + yahoo_packet_hash_str(pkt, 63, DOODLE_IMV_KEY); + else + { + const char *imv; + imv = g_hash_table_lookup(yd->imvironments, who); + if (imv != NULL) + yahoo_packet_hash_str(pkt, 63, imv); + else + yahoo_packet_hash_str(pkt, 63, ";0"); + } + + yahoo_packet_hash_str(pkt, 64, "0"); /* no idea */ + yahoo_packet_hash_str(pkt, 1002, "1"); /* no idea, Yahoo 6 or later only it seems */ + if (!yd->picture_url) + yahoo_packet_hash_str(pkt, 206, "0"); /* 0 = no picture, 2 = picture, maybe 1 = avatar? */ + else + yahoo_packet_hash_str(pkt, 206, "2"); + + /* We may need to not send any packets over 2000 bytes, but I'm not sure yet. */ + if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000) { + /* if p2p link exists, send through it. To-do: key 15, time value to be sent in case of p2p */ + if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !fed) { + yahoo_packet_hash_int(pkt, 11, p2p_data->session_id); + yahoo_p2p_write_pkt(p2p_data->source, pkt); + } + else { + yahoo_packet_send(pkt, yd); + if(!fed) + yahoo_send_p2p_pkt(gc, who, 0); /* send p2p packet, with val_13=0 */ + } + } + else + ret = -E2BIG; + + yahoo_packet_free(pkt); + + g_free(msg); + g_free(msg2); + + return ret; +} + +unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + struct yahoo_p2p_data *p2p_data; + YahooFederation fed = YAHOO_FEDERATION_NONE; + struct yahoo_packet *pkt = NULL; + + fed = yahoo_get_federation_from_name(who); + + /* Don't do anything if sms is being typed */ + if( strncmp(who, "+", 1) == 0 ) + return 0; + + pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, yd->session_id); + + /* check to see if p2p link exists, send through it */ + if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !fed) { + yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc), + 14, " ", 13, state == PURPLE_IM_TYPING ? "1" : "0", + 5, who, 11, p2p_data->session_id, 1002, "1"); /* To-do: key 15 to be sent in case of p2p */ + yahoo_p2p_write_pkt(p2p_data->source, pkt); + yahoo_packet_free(pkt); + } + else { /* send through yahoo server */ + + const char *fed_who = who; + switch (fed) { + case YAHOO_FEDERATION_MSN: + case YAHOO_FEDERATION_OCS: + case YAHOO_FEDERATION_IBM: + case YAHOO_FEDERATION_PBX: + fed_who += 4; + break; + case YAHOO_FEDERATION_NONE: + default: + break; + } + + yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc), + 14, " ", 13, state == PURPLE_IM_TYPING ? "1" : "0", + 5, fed_who, 1002, "1"); + if (fed) + yahoo_packet_hash_int(pkt, 241, fed); + yahoo_packet_send_and_free(pkt, yd); + } + + return 0; +} + +static void yahoo_session_presence_remove(gpointer key, gpointer value, gpointer data) +{ + YahooFriend *f = value; + if (f && f->presence == YAHOO_PRESENCE_ONLINE) + f->presence = YAHOO_PRESENCE_DEFAULT; +} + +void yahoo_set_status(PurpleAccount *account, PurpleStatus *status) +{ + PurpleConnection *gc; + PurplePresence *presence; + YahooData *yd; + struct yahoo_packet *pkt; + int old_status; + const char *msg = NULL; + char *tmp = NULL; + char *conv_msg = NULL; + + if (!purple_status_is_active(status)) + return; + + gc = purple_account_get_connection(account); + presence = purple_status_get_presence(status); + yd = purple_connection_get_protocol_data(gc); + old_status = yd->current_status; + + yd->current_status = get_yahoo_status_from_purple_status(status); + + if (yd->current_status == YAHOO_STATUS_CUSTOM) + { + msg = purple_status_get_attr_string(status, "message"); + + if (purple_status_is_available(status)) { + tmp = yahoo_string_encode(gc, msg, TRUE); + conv_msg = purple_markup_strip_html(tmp); + g_free(tmp); + } else { + if ((msg == NULL) || (*msg == '\0')) + msg = _("Away"); + tmp = yahoo_string_encode(gc, msg, TRUE); + conv_msg = purple_markup_strip_html(tmp); + g_free(tmp); + } + } + + if (yd->current_status == YAHOO_STATUS_INVISIBLE) { + pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash_str(pkt, 13, "2"); + yahoo_packet_send_and_free(pkt, yd); + + return; + } + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash_int(pkt, 10, yd->current_status); + + if (yd->current_status == YAHOO_STATUS_CUSTOM) { + yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */ + yahoo_packet_hash_str(pkt, 19, conv_msg); + } else { + yahoo_packet_hash_str(pkt, 19, ""); + } + + g_free(conv_msg); + + if (purple_presence_is_idle(presence)) + yahoo_packet_hash_str(pkt, 47, "2"); + else { + if (!purple_status_is_available(status)) + yahoo_packet_hash_str(pkt, 47, "1"); + else + yahoo_packet_hash_str(pkt, 47, "0"); + } + + yahoo_packet_send_and_free(pkt, yd); + + if (old_status == YAHOO_STATUS_INVISIBLE) { + pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash_str(pkt, 13, "1"); + yahoo_packet_send_and_free(pkt, yd); + + /* Any per-session presence settings are removed */ + g_hash_table_foreach(yd->friends, yahoo_session_presence_remove, NULL); + + } +} + +void yahoo_set_idle(PurpleConnection *gc, int idle) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + struct yahoo_packet *pkt = NULL; + char *msg = NULL, *msg2 = NULL; + PurpleStatus *status = NULL; + gboolean invisible = FALSE; + + if (idle && yd->current_status != YAHOO_STATUS_CUSTOM) + yd->current_status = YAHOO_STATUS_IDLE; + else if (!idle && yd->current_status == YAHOO_STATUS_IDLE) { + status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc))); + yd->current_status = get_yahoo_status_from_purple_status(status); + } + + invisible = (yd->current_status == YAHOO_STATUS_INVISIBLE); + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, yd->session_id); + + if (!idle && invisible) + yahoo_packet_hash_int(pkt, 10, YAHOO_STATUS_AVAILABLE); + else + yahoo_packet_hash_int(pkt, 10, yd->current_status); + + if (yd->current_status == YAHOO_STATUS_CUSTOM) { + const char *tmp; + if (status == NULL) + status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc))); + tmp = purple_status_get_attr_string(status, "message"); + if (tmp != NULL) { + msg = yahoo_string_encode(gc, tmp, TRUE); + msg2 = purple_markup_strip_html(msg); + yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */ + yahoo_packet_hash_str(pkt, 19, msg2); + } else { + /* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for + * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message */ + yahoo_packet_hash_str(pkt, 19, _("Away")); + } + } else { + yahoo_packet_hash_str(pkt, 19, ""); + } + + if (idle) + yahoo_packet_hash_str(pkt, 47, "2"); + else if (yd->current_status == YAHOO_STATUS_CUSTOM && + !purple_status_is_available(status)) + /* We are still unavailable in this case. + * Make sure Yahoo knows that */ + yahoo_packet_hash_str(pkt, 47, "1"); + + yahoo_packet_send_and_free(pkt, yd); + + g_free(msg); + g_free(msg2); +} + +GList *yahoo_status_types(PurpleAccount *account) +{ + PurpleStatusType *type; + GList *types = NULL; + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, YAHOO_STATUS_TYPE_AVAILABLE, + NULL, TRUE, TRUE, FALSE, + "message", _("Message"), + purple_value_new(G_TYPE_STRING), NULL); + types = g_list_append(types, type); + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_AWAY, + NULL, TRUE, TRUE, FALSE, + "message", _("Message"), + purple_value_new(G_TYPE_STRING), NULL); + types = g_list_append(types, type); + + type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_BRB, _("Be Right Back"), TRUE); + types = g_list_append(types, type); + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_BUSY, + _("Busy"), TRUE, TRUE, FALSE, + "message", _("Message"), + purple_value_new(G_TYPE_STRING), NULL); + types = g_list_append(types, type); + + type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATHOME, _("Not at Home"), TRUE); + types = g_list_append(types, type); + + type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATDESK, _("Not at Desk"), TRUE); + types = g_list_append(types, type); + + type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTINOFFICE, _("Not in Office"), TRUE); + types = g_list_append(types, type); + + type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_ONPHONE, _("On the Phone"), TRUE); + types = g_list_append(types, type); + + type = purple_status_type_new(PURPLE_STATUS_EXTENDED_AWAY, YAHOO_STATUS_TYPE_ONVACATION, _("On Vacation"), TRUE); + types = g_list_append(types, type); + + type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_OUTTOLUNCH, _("Out to Lunch"), TRUE); + types = g_list_append(types, type); + + type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_STEPPEDOUT, _("Stepped Out"), TRUE); + types = g_list_append(types, type); + + + type = purple_status_type_new(PURPLE_STATUS_INVISIBLE, YAHOO_STATUS_TYPE_INVISIBLE, NULL, TRUE); + types = g_list_append(types, type); + + type = purple_status_type_new(PURPLE_STATUS_OFFLINE, YAHOO_STATUS_TYPE_OFFLINE, NULL, TRUE); + types = g_list_append(types, type); + + type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, YAHOO_STATUS_TYPE_MOBILE, NULL, FALSE, FALSE, TRUE); + types = g_list_append(types, type); + + return types; +} + +void yahoo_keepalive(PurpleConnection *gc) +{ + struct yahoo_packet *pkt; + YahooData *yd = purple_connection_get_protocol_data(gc); + time_t now = time(NULL); + + /* We're only allowed to send a ping once an hour or the servers will boot us */ + if ((now - yd->last_ping) >= PING_TIMEOUT) { + yd->last_ping = now; + + /* The native client will only send PING or CHATPING */ + if (yd->chat_online) { + if (yd->wm) { + ycht_chat_send_keepalive(yd->ycht); + } else { + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash_str(pkt, 109, purple_connection_get_display_name(gc)); + yahoo_packet_send_and_free(pkt, yd); + } + } else { + pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_send_and_free(pkt, yd); + } + } + + if ((now - yd->last_keepalive) >= KEEPALIVE_TIMEOUT) { + yd->last_keepalive = now; + pkt = yahoo_packet_new(YAHOO_SERVICE_KEEPALIVE, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc)); + yahoo_packet_send_and_free(pkt, yd); + } + +} + +void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + struct yahoo_packet *pkt; + const char *group = NULL; + char *group2; + const char *bname; + const char *fed_bname; + YahooFederation fed = YAHOO_FEDERATION_NONE; + + if (!yd->logged_in) + return; + + fed_bname = bname = purple_buddy_get_name(buddy); + if (!purple_account_privacy_check(purple_connection_get_account(gc), bname)) + return; + + fed = yahoo_get_federation_from_name(bname); + if (fed != YAHOO_FEDERATION_NONE) + fed_bname += 4; + + g = purple_buddy_get_group(buddy); + if (g) + group = purple_group_get_name(g); + else + group = "Buddies"; + + group2 = yahoo_string_encode(gc, group, FALSE); + pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); + if (fed) { + yahoo_packet_hash(pkt, "sssssssisss", + 14, "", + 65, group2, + 97, "1", /* UTF-8 */ + 1, purple_connection_get_display_name(gc), + 302, "319", + 300, "319", + 7, fed_bname, + 241, fed, + 334, "0", + 301, "319", + 303, "319" + ); + } + else { + yahoo_packet_hash(pkt, "ssssssssss", + 14, "", + 65, group2, + 97, "1", /* UTF-8 */ + 1, purple_connection_get_display_name(gc), + 302, "319", + 300, "319", + 7, fed_bname, + 334, "0", + 301, "319", + 303, "319" + ); + } + + yahoo_packet_send_and_free(pkt, yd); + g_free(group2); +} + +void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + struct yahoo_packet *pkt; + GSList *buddies, *l; + PurpleGroup *g; + gboolean remove = TRUE; + char *cg; + const char *bname, *gname; + YahooFriend *f = NULL; + YahooFederation fed = YAHOO_FEDERATION_NONE; + + bname = purple_buddy_get_name(buddy); + f = yahoo_friend_find(gc, bname); + if (!f) + return; + fed = f->fed; + + gname = purple_group_get_name(group); + buddies = purple_blist_find_buddies(purple_connection_get_account(gc), bname); + for (l = buddies; l; l = l->next) { + g = purple_buddy_get_group(l->data); + if (purple_utf8_strcasecmp(gname, purple_group_get_name(g))) { + remove = FALSE; + break; + } + } + + g_slist_free(buddies); + + if (remove) { + g_hash_table_remove(yd->friends, bname); + f = NULL; /* f no longer valid - Just making it clear */ + } + + cg = yahoo_string_encode(gc, gname, FALSE); + pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); + + switch (fed) { + case YAHOO_FEDERATION_MSN: + case YAHOO_FEDERATION_OCS: + case YAHOO_FEDERATION_IBM: + bname += 4; + break; + case YAHOO_FEDERATION_NONE: + default: + break; + } + + yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), + 7, bname, 65, cg); + if (fed) + yahoo_packet_hash_int(pkt, 241, fed); + yahoo_packet_send_and_free(pkt, yd); + g_free(cg); +} + +void yahoo_add_deny(PurpleConnection *gc, const char *who) { + YahooData *yd = purple_connection_get_protocol_data(gc); + struct yahoo_packet *pkt; + YahooFederation fed = YAHOO_FEDERATION_NONE; + + if (!yd->logged_in) + return; + + if (!who || who[0] == '\0') + return; + + fed = yahoo_get_federation_from_name(who); + + pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id); + + if(fed) + yahoo_packet_hash(pkt, "ssis", 1, purple_connection_get_display_name(gc), 7, who+4, 241, fed, 13, "1"); + else + yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "1"); + + yahoo_packet_send_and_free(pkt, yd); +} + +void yahoo_rem_deny(PurpleConnection *gc, const char *who) { + YahooData *yd = purple_connection_get_protocol_data(gc); + struct yahoo_packet *pkt; + YahooFederation fed = YAHOO_FEDERATION_NONE; + + if (!yd->logged_in) + return; + + if (!who || who[0] == '\0') + return; + fed = yahoo_get_federation_from_name(who); + + pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id); + + if(fed) + yahoo_packet_hash(pkt, "ssis", 1, purple_connection_get_display_name(gc), 7, who+4, 241, fed, 13, "2"); + else + yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "2"); + + yahoo_packet_send_and_free(pkt, yd); +} + +void yahoo_set_permit_deny(PurpleConnection *gc) +{ + PurpleAccount *account; + GSList *deny; + + account = purple_connection_get_account(gc); + + switch (purple_account_get_privacy_type(account)) + { + case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL: + for (deny = purple_account_privacy_get_denied(account); deny; deny = deny->next) + yahoo_rem_deny(gc, deny->data); + break; + + case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST: + case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS: + case PURPLE_ACCOUNT_PRIVACY_DENY_USERS: + case PURPLE_ACCOUNT_PRIVACY_DENY_ALL: + for (deny = purple_account_privacy_get_denied(account); deny; deny = deny->next) + yahoo_add_deny(gc, deny->data); + break; + } +} + +void yahoo_change_buddys_group(PurpleConnection *gc, const char *who, + const char *old_group, const char *new_group) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + struct yahoo_packet *pkt; + char *gpn, *gpo; + YahooFriend *f = yahoo_friend_find(gc, who); + const char *temp = NULL; + + /* Step 0: If they aren't on the server list anyway, + * don't bother letting the server know. + */ + if (!f) + return; + + if(f->fed) { + temp = who+4; + } else + temp = who; + + /* If old and new are the same, we would probably + * end up deleting the buddy, which would be bad. + * This might happen because of the charset conversation. + */ + gpn = yahoo_string_encode(gc, new_group, FALSE); + gpo = yahoo_string_encode(gc, old_group, FALSE); + if (!strcmp(gpn, gpo)) { + g_free(gpn); + g_free(gpo); + return; + } + + pkt = yahoo_packet_new(YAHOO_SERVICE_CHGRP_15, YAHOO_STATUS_AVAILABLE, yd->session_id); + if(f->fed) + yahoo_packet_hash(pkt, "ssssissss", 1, purple_connection_get_display_name(gc), + 302, "240", 300, "240", 7, temp, 241, f->fed, 224, gpo, 264, gpn, 301, + "240", 303, "240"); + else + yahoo_packet_hash(pkt, "ssssssss", 1, purple_connection_get_display_name(gc), + 302, "240", 300, "240", 7, temp, 224, gpo, 264, gpn, 301, + "240", 303, "240"); + yahoo_packet_send_and_free(pkt, yd); + + g_free(gpn); + g_free(gpo); +} + +void yahoo_rename_group(PurpleConnection *gc, const char *old_name, + PurpleGroup *group, GList *moved_buddies) +{ + YahooData *yd = purple_connection_get_protocol_data(gc); + struct yahoo_packet *pkt; + char *gpn, *gpo; + + gpn = yahoo_string_encode(gc, purple_group_get_name(group), FALSE); + gpo = yahoo_string_encode(gc, old_name, FALSE); + if (!strcmp(gpn, gpo)) { + g_free(gpn); + g_free(gpo); + return; + } + + pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), + 65, gpo, 67, gpn); + yahoo_packet_send_and_free(pkt, yd); + g_free(gpn); + g_free(gpo); +} + +/********************************* Commands **********************************/ + +PurpleCmdRet +yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data) { + PurpleAccount *account = purple_conversation_get_account(c); + + if (*args && args[0]) + return PURPLE_CMD_RET_FAILED; + + purple_protocol_send_attention(purple_account_get_connection(account), purple_conversation_get_name(c), YAHOO_BUZZ); + + return PURPLE_CMD_RET_OK; +} + +PurpleCmdRet +yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd, + char **args, char **error, void *data) +{ + GHashTable *comp; + PurpleConnection *gc; + + if (!args || !args[0]) + return PURPLE_CMD_RET_FAILED; + + gc = purple_conversation_get_connection(conv); + purple_debug_info("yahoo", "Trying to join %s \n", args[0]); + + comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_replace(comp, g_strdup("room"), g_ascii_strdown(args[0], -1)); + g_hash_table_replace(comp, g_strdup("type"), g_strdup("Chat")); + + yahoo_c_join(gc, comp); + + g_hash_table_destroy(comp); + return PURPLE_CMD_RET_OK; +} + +PurpleCmdRet +yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd, + char **args, char **error, void *data) +{ + PurpleAccount *account = purple_conversation_get_account(conv); + if (*args && args[0]) + return PURPLE_CMD_RET_FAILED; + purple_roomlist_show_with_account(account); + return PURPLE_CMD_RET_OK; +} + +gboolean yahoo_offline_message(const PurpleBuddy *buddy) +{ + return TRUE; +} + +gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type) +{ + PurpleIMConversation *im; + + im = purple_conversations_find_im_with_account(username, + purple_connection_get_account(gc)); + + g_return_val_if_fail(im != NULL, FALSE); + + purple_debug_info("yahoo", "Sending <ding> on account %s to buddy %s.\n", + username, purple_conversation_get_name(PURPLE_CONVERSATION(im))); + purple_conversation_send_with_flags(PURPLE_CONVERSATION(im), "<ding>", PURPLE_MESSAGE_INVISIBLE); + + return TRUE; +} + +GList *yahoo_attention_types(PurpleAccount *account) +{ + static GList *list = NULL; + + if (!list) { + /* Yahoo only supports one attention command: the 'buzz'. */ + /* This is index number YAHOO_BUZZ. */ + list = g_list_append(list, purple_attention_type_new("Buzz", _("Buzz"), + _("%s has buzzed you!"), _("Buzzing %s..."))); + } + + return list; +} + +gssize +yahoo_get_max_message_size(PurpleConversation *conv) +{ + return YAHOO_MAX_MESSAGE_LENGTH_CHARS; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/yahoo/ymsg.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,399 @@ +/** + * @file ymsg.h The Yahoo! and Yahoo! JAPAN Protocols + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _YMSG_H_ +#define _YMSG_H_ + +#include "circularbuffer.h" +#include "cmds.h" +#include "http.h" +#include "protocol.h" +#include "network.h" + +#define YAHOO_PAGER_HOST_REQ_URL "http://vcs1.msg.yahoo.com/capacity" +#define YAHOO_PAGER_HOST_FALLBACK "scsa.msg.yahoo.com" +#define YAHOO_PAGER_PORT 5050 +#define YAHOO_PAGER_PORT_P2P 5101 +#define YAHOO_LOGIN_URL "https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=%s" +#define YAHOO_TOKEN_URL "https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s" +#define YAHOO_P2P_KEEPALIVE_SECS 300 +#define YAHOO_P2P_SERVER_TIMEOUT 10 +#define YAHOO_PROFILE_URL "http://profiles.yahoo.com/" +#define YAHOO_MAIL_URL "http://rd.yahoo.com/messenger/client/?http://mail.yahoo.com/" +#define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com" +#define YAHOO_XFER_RELAY_HOST "relay.msg.yahoo.com" +#define YAHOO_XFER_RELAY_PORT 80 +#define YAHOO_ROOMLIST_URL "http://insider.msg.yahoo.com/ycontent/" +#define YAHOO_ROOMLIST_LOCALE "us" + +/* Yahoo! JAPAN stuff */ +#define YAHOOJP_PAGER_HOST_REQ_URL "http://cs1.yahoo.co.jp/capacity" +#define YAHOOJP_TOKEN_URL "https://login.yahoo.co.jp/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s" +#define YAHOOJP_LOGIN_URL "https://login.yahoo.co.jp/config/pwtoken_login?src=ymsgr&ts=&token=%s" +#define YAHOOJP_PROFILE_URL "http://profiles.yahoo.co.jp/" +#define YAHOOJP_MAIL_URL "http://mail.yahoo.co.jp/" +#define YAHOOJP_XFER_HOST "filetransfer.msg.yahoo.co.jp" +#define YAHOOJP_WEBCAM_HOST "wc.yahoo.co.jp" +/* not sure, must test: */ +#define YAHOOJP_XFER_RELAY_HOST "relay.msg.yahoo.co.jp" +#define YAHOOJP_XFER_RELAY_PORT 80 +#define YAHOOJP_ROOMLIST_URL "http://insider.msg.yahoo.co.jp/ycontent/" +#define YAHOOJP_ROOMLIST_LOCALE "ja" + +#define YAHOO_AUDIBLE_URL "http://l.yimg.com/pu/dl/aud" + +#define WEBMESSENGER_URL "http://login.yahoo.com/config/login?.src=pg" + +#define YAHOO_SMS_CARRIER_URL "http://validate.msg.yahoo.com" + +#define YAHOO_USERINFO_URL "http://address.yahoo.com/yab/us?v=XM&sync=1&tags=short&useutf8=1&noclear=1&legenc=codepage-1252" +#define YAHOOJP_USERINFO_URL "http://address.yahoo.co.jp/yab/jp?v=XM&sync=1&tags=short&useutf8=1&noclear=1&legenc=codepage-1252" + +#define YAHOO_PICURL_SETTING "picture_url" +#define YAHOO_PICCKSUM_SETTING "picture_checksum" +#define YAHOO_PICEXPIRE_SETTING "picture_expire" + +#define YAHOO_STATUS_TYPE_OFFLINE "offline" +#define YAHOO_STATUS_TYPE_AVAILABLE "available" +#define YAHOO_STATUS_TYPE_BRB "brb" +#define YAHOO_STATUS_TYPE_BUSY "busy" +#define YAHOO_STATUS_TYPE_NOTATHOME "notathome" +#define YAHOO_STATUS_TYPE_NOTATDESK "notatdesk" +#define YAHOO_STATUS_TYPE_NOTINOFFICE "notinoffice" +#define YAHOO_STATUS_TYPE_ONPHONE "onphone" +#define YAHOO_STATUS_TYPE_ONVACATION "onvacation" +#define YAHOO_STATUS_TYPE_OUTTOLUNCH "outtolunch" +#define YAHOO_STATUS_TYPE_STEPPEDOUT "steppedout" +#define YAHOO_STATUS_TYPE_AWAY "away" +#define YAHOO_STATUS_TYPE_INVISIBLE "invisible" +#define YAHOO_STATUS_TYPE_MOBILE "mobile" + +#define YAHOO_CLIENT_VERSION_ID "4194239" +#define YAHOO_CLIENT_VERSION "9.0.0.2162" + +#define YAHOOJP_CLIENT_VERSION_ID "4186047" +#define YAHOOJP_CLIENT_VERSION "9.0.0.1727" + +#define YAHOO_CLIENT_USERAGENT "Mozilla/5.0" +#define YAHOO_CLIENT_USERAGENT_ALIAS "Mozilla/4.0 (compatible; MSIE 5.5)" + +/* Index into attention types list. */ +#define YAHOO_BUZZ 0 + +typedef enum { + YAHOO_PKT_TYPE_SERVER = 0, + YAHOO_PKT_TYPE_P2P +} yahoo_pkt_type; + +typedef enum { + YAHOO_P2P_WE_ARE_CLIENT =0, + YAHOO_P2P_WE_ARE_SERVER +} yahoo_p2p_connection_type; + +enum yahoo_status { + YAHOO_STATUS_AVAILABLE = 0, + YAHOO_STATUS_BRB, + YAHOO_STATUS_BUSY, + YAHOO_STATUS_NOTATHOME, + YAHOO_STATUS_NOTATDESK, + YAHOO_STATUS_NOTINOFFICE, + YAHOO_STATUS_ONPHONE, + YAHOO_STATUS_ONVACATION, + YAHOO_STATUS_OUTTOLUNCH, + YAHOO_STATUS_STEPPEDOUT, + YAHOO_STATUS_P2P = 11, + YAHOO_STATUS_INVISIBLE = 12, + YAHOO_STATUS_CUSTOM = 99, + YAHOO_STATUS_IDLE = 999, + YAHOO_STATUS_WEBLOGIN = 0x5a55aa55, + YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */ + YAHOO_STATUS_TYPING = 0x16, + YAHOO_STATUS_DISCONNECTED = -1 /* 0xffffffff; in ymsg 15. doesnt mean the normal sense of 'disconnected' */ +}; + +/* + * Yahoo federated networks. Key 241 in ymsg. + * If it doesn't exist, it is on Yahoo's netowrk. + * It if does exist, send to another IM network. + */ + +typedef enum { + YAHOO_FEDERATION_NONE = 0, /* No federation - Yahoo! network */ + YAHOO_FEDERATION_OCS = 1, /* LCS or OCS private networks */ + YAHOO_FEDERATION_MSN = 2, /* MSN or Windows Live network */ + YAHOO_FEDERATION_IBM = 9, /* IBM/Sametime network */ + YAHOO_FEDERATION_PBX = 100 /* Yahoo! Pingbox service */ +} YahooFederation; + + +struct yahoo_buddy_icon_upload_data { + PurpleConnection *gc; + char *filename; + GString *picture_data; +}; + +struct yahoo_p2p_data { + PurpleConnection *gc; + char *host_ip; + char *host_username; + int val_13; + guint input_event; + gint source; + int session_id; + yahoo_p2p_connection_type connection_type; +}; + +struct _YchtConn; + +typedef struct _YahooPersonalDetails { + char *id; + + struct { + char *first; + char *last; + char *middle; + char *nick; + } names; + + struct { + char *work; + char *home; + char *mobile; + } phone; +} YahooPersonalDetails; + +typedef struct { + PurpleConnection *gc; + int fd; + guint inpa; + guchar *rxqueue; + int rxlen; + PurpleCircularBuffer *txbuf; + guint txhandler; + GHashTable *friends; + + char **profiles; /* Multiple profiles can be associated with an account */ + YahooPersonalDetails ypd; + + /** + * This is used to keep track of the IMVironment chosen + * by people you talk to. We don't do very much with + * this right now... but at least now if the remote user + * selects an IMVironment we won't reset it back to the + * default of nothing. + */ + GHashTable *imvironments; + + int current_status; + gboolean logged_in; + GString *tmp_serv_blist, *tmp_serv_ilist, *tmp_serv_plist; + GSList *confs; + unsigned int conf_id; /* just a counter */ + gboolean chat_online; + gboolean in_chat; + char *chat_name; + char *pending_chat_room; + char *pending_chat_id; + char *pending_chat_topic; + char *pending_chat_goto; + char *auth; + char *cookie_y; + char *cookie_t; + char *cookie_b; + int session_id; + gboolean jp; + gboolean wm; /* connected w/ web messenger method */ + /* picture aka buddy icon stuff */ + char *picture_url; + int picture_checksum; + + /* ew. we have to check the icon before we connect, + * but can't upload it til we're connected. */ + struct yahoo_buddy_icon_upload_data *picture_upload_todo; + PurpleHttpConnection *picture_upload_hc; + + struct _YchtConn *ycht; + + /** + * This set contains HTTP connections + * for when we lookup people profile or photo information. + */ + PurpleHttpConnectionSet *http_reqs; + + GHashTable *xfer_peer_idstring_map;/* Hey, i dont know, but putting this HashTable next to friends gives a run time fault... */ + GSList *cookies;/* contains all cookies, including _y and _t */ + PurpleNetworkListenData *listen_data; + + /** + * We may receive a list15 in multiple packets with no prior warning as to how many we'll be getting; + * the server expects us to keep track of the group for which it is sending us contact names. + */ + char *current_list15_grp; + time_t last_ping; + time_t last_keepalive; + GHashTable *peers; /* information about p2p data */ + int yahoo_p2p_timer; + int yahoo_local_p2p_server_fd; + int yahoo_p2p_server_watcher; + GHashTable *sms_carrier; /* sms carrier data */ + guint yahoo_p2p_server_timeout_handle; +} YahooData; + +#define YAHOO_MAX_STATUS_MESSAGE_LENGTH (255) + +/* + * Current Maximum Length for Instant Messages + * + * This was found by experiment. + * + * The YMSG protocol allows a message of up to 948 bytes, but the official client + * limits to 800 characters. According to experiments I conducted, it seems that + * the discrepancy is to allow some leeway for messages with mixed single- and + * multi-byte characters, as I was able to send messages of 840 and 932 bytes + * by using some multibyte characters (some random Chinese or Japanese characters, + * to be precise). - rekkanoryo + */ +#define YAHOO_MAX_MESSAGE_LENGTH_BYTES 948 +#define YAHOO_MAX_MESSAGE_LENGTH_CHARS 800 + +/* sometimes i wish prpls could #include things from other prpls. then i could just + * use the routines from libfaim and not have to admit to knowing how they work. */ +#define yahoo_put16(buf, data) ( \ + (*(buf) = (unsigned char)((data)>>8)&0xff), \ + (*((buf)+1) = (unsigned char)(data)&0xff), \ + 2) +#define yahoo_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff)) +#define yahoo_put32(buf, data) ( \ + (*((buf)) = (unsigned char)((data)>>24)&0xff), \ + (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \ + (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \ + (*((buf)+3) = (unsigned char)(data)&0xff), \ + 4) +#define yahoo_get32(buf) ((((*(buf))<<24)&0xff000000) + \ + (((*((buf)+1))<<16)&0x00ff0000) + \ + (((*((buf)+2))<< 8)&0x0000ff00) + \ + (((*((buf)+3) )&0x000000ff))) + +/* util.c */ +void yahoo_init_colorht(void); +void yahoo_dest_colorht(void); +char *yahoo_codes_to_html(const char *x); + +/** + * This function takes a normal HTML message and converts it to the message + * format used by Yahoo, which uses a frankensteinish combination of ANSI + * escape codes and broken HTML. + * + * It results in slightly different output than would be sent by official + * Yahoo clients. The two main differences are: + * + * 1. We always close all tags, whereas official Yahoo clients leave tags + * dangling open at the end of each message (and the client treats them + * as closed). + * 2. We always close inner tags first before closing outter tags. + * + * For example, if you want to send this message: + * <b> bold <i> bolditalic </i></b><i> italic </i> + * Official Yahoo clients would send: + * ESC[1m bold ESC[2m bolditalic ESC[x1m italic + * But we will send: + * ESC[1m bold ESC[2m bolditalic ESC[x2mESC[x1mESC[2m italic ESC[x2m + */ +char *yahoo_html_to_codes(const char *src); + +gboolean +yahoo_account_use_http_proxy(PurpleConnection *conn); + +/** + * Encode some text to send to the yahoo server. + * + * @param gc The connection handle. + * @param str The null terminated utf8 string to encode. + * @param utf8 Whether to return a UTF-8 string. + * @return A g_malloc'ed string in the appropriate encoding. If jd->jp or + * utf8 is true then the string is copied verbatim. Otherwise the + * encoding from account settings is used. + */ +gchar *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean utf8); + +/** + * Decode some text received from the server. + * + * @param gc The gc handle. + * @param str The null terminated string to decode. + * @param utf8 Did the server tell us it was supposed to be utf8? + * @return The decoded, utf-8 string, which must be g_free()'d. + */ +char *yahoo_string_decode(PurpleConnection *gc, const char *str, gboolean utf8); + +char *yahoo_convert_to_numeric(const char *str); + +YahooFederation yahoo_get_federation_from_name(const char *who); + +/* yahoo_profile.c */ +void yahoo_get_info(PurpleConnection *gc, const char *name); + +/* ymsg.h - these functions were formerly static but need not to be for the + * new two-protocol model. */ +const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b); +const char *yahoo_list_emblem(PurpleBuddy *b); +char *yahoo_status_text(PurpleBuddy *b); +void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full); +GList *yahoo_status_types(PurpleAccount *account); +GList *yahoo_blist_node_menu(PurpleBlistNode *node); +void yahoo_login(PurpleAccount *account); +void yahoo_close(PurpleConnection *gc); +int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags); +unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state); +void yahoo_set_status(PurpleAccount *account, PurpleStatus *status); +void yahoo_set_idle(PurpleConnection *gc, int idle); +void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message); +void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); +void yahoo_add_deny(PurpleConnection *gc, const char *who); +void yahoo_rem_deny(PurpleConnection *gc, const char *who); +void yahoo_set_permit_deny(PurpleConnection *gc); +void yahoo_keepalive(PurpleConnection *gc); +void yahoo_change_buddys_group(PurpleConnection *gc, const char *who, const char *old_group, const char *new_group); +void yahoo_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies); +gboolean yahoo_offline_message(const PurpleBuddy *buddy); +gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type); +GList *yahoo_attention_types(PurpleAccount *account); + +GList *yahoo_get_actions(PurpleConnection *gc); +void yahoopurple_register_commands(void); +gssize yahoo_get_max_message_size(PurpleConversation *conv); + +PurpleCmdRet yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data); +PurpleCmdRet yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data); +PurpleCmdRet yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data); +/* needed for xfer, thought theyd be useful for other enhancements later on + Returns list of cookies stored in yahoo_data formatted as a single null terminated string + returned value must be g_freed +*/ +gchar* yahoo_get_cookies(PurpleConnection *gc); + +/* send p2p pkt containing our encoded ip, asking peer to connect to us */ +void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13); + +#endif /* _YMSG_H_ */
--- a/libpurple/protocols/zephyr/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/zephyr/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -62,9 +62,10 @@ zephyr_err.c \ zephyr_err.h \ zephyr_internal.h \ - zephyr.c + zephyr.c \ + zephyr.h -ZEPHYRSOURCESEXT = zephyr.c +ZEPHYRSOURCESEXT = zephyr.c zephyr.h AM_CFLAGS = $(st) @@ -105,5 +106,6 @@ -I$(top_srcdir)/libpurple/protocols \ -DCONFDIR=\"$(sysconfdir)\" \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(KRB4_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/zephyr/zephyr.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/protocols/zephyr/zephyr.c Fri Jan 31 18:02:20 2014 +0530 @@ -31,13 +31,14 @@ #include "accountopt.h" #include "debug.h" #include "notify.h" -#include "prpl.h" +#include "plugins.h" #include "server.h" #include "util.h" #include "cmds.h" #include "version.h" #include "internal.h" +#include "zephyr.h" #include <strings.h> @@ -49,6 +50,9 @@ #define ZEPHYR_FD_READ 0 #define ZEPHYR_FD_WRITE 1 +static PurpleProtocol *my_protocol = NULL; +static GSList *cmds = NULL; + extern Code_t ZGetLocations(ZLocations_t *, int *); extern Code_t ZSetLocation(char *); extern Code_t ZUnsetLocation(void); @@ -817,9 +821,9 @@ purple_notify_user_info_destroy(user_info); } else { if (nlocs>0) - purple_prpl_got_user_status(purple_connection_get_account(gc), b ? bname : user, "available", NULL); + purple_protocol_got_user_status(purple_connection_get_account(gc), b ? bname : user, "available", NULL); else - purple_prpl_got_user_status(purple_connection_get_account(gc), b ? bname : user, "offline", NULL); + purple_protocol_got_user_status(purple_connection_get_account(gc), b ? bname : user, "offline", NULL); } g_free(user); @@ -1202,9 +1206,9 @@ purple_notify_user_info_destroy(user_info); } else { if (nlocs>0) - purple_prpl_got_user_status(purple_connection_get_account(gc), b ? bname : user, "available", NULL); + purple_protocol_got_user_status(purple_connection_get_account(gc), b ? bname : user, "available", NULL); else - purple_prpl_got_user_status(purple_connection_get_account(gc), b ? bname : user, "offline", NULL); + purple_protocol_got_user_status(purple_connection_get_account(gc), b ? bname : user, "offline", NULL); } } else if (!g_ascii_strncasecmp(spewtype,"subscribed",10)) { @@ -1328,9 +1332,9 @@ for(i=0;i<numlocs;i++) { ZGetLocations(&locations,&one); if (nlocs>0) - purple_prpl_got_user_status(account,name,"available",NULL); + purple_protocol_got_user_status(account,name,"available",NULL); else - purple_prpl_got_user_status(account,name,"offline",NULL); + purple_protocol_got_user_status(account,name,"offline",NULL); } } #else @@ -2369,21 +2373,21 @@ static GList *zephyr_chat_info(PurpleConnection * gc) { GList *m = NULL; - struct proto_chat_entry *pce; - - pce = g_new0(struct proto_chat_entry, 1); + PurpleProtocolChatEntry *pce; + + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Class:"); pce->identifier = "class"; m = g_list_append(m, pce); - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Instance:"); pce->identifier = "instance"; m = g_list_append(m, pce); - pce = g_new0(struct proto_chat_entry, 1); + pce = g_new0(PurpleProtocolChatEntry, 1); pce->label = _("_Recipient:"); pce->identifier = "recipient"; @@ -2713,71 +2717,93 @@ static void zephyr_register_slash_commands(void) { - - purple_cmd_register("msg","ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + PurpleCmdId id; + + id = purple_cmd_register("msg","ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_msg, _("msg <nick> <message>: Send a private message to a user"), NULL); - - purple_cmd_register("zlocate","w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("zlocate","w", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_zlocate, _("zlocate <nick>: Locate user"), NULL); - - purple_cmd_register("zl","w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("zl","w", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_zlocate, _("zl <nick>: Locate user"), NULL); - - purple_cmd_register("instance","s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("instance","s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_instance, _("instance <instance>: Set the instance to be used on this class"), NULL); - - purple_cmd_register("inst","s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("inst","s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_instance, _("inst <instance>: Set the instance to be used on this class"), NULL); - - purple_cmd_register("topic","s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("topic","s", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_instance, _("topic <instance>: Set the instance to be used on this class"), NULL); - - purple_cmd_register("sub", "www", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("sub", "www", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_joinchat_cir, _("sub <class> <instance> <recipient>: Join a new chat"), NULL); - - purple_cmd_register("zi","ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("zi","ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_zi, _("zi <instance>: Send a message to <message,<i>instance</i>,*>"), NULL); - - purple_cmd_register("zci","wws",PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("zci","wws",PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_zci, _("zci <class> <instance>: Send a message to <<i>class</i>,<i>instance</i>,*>"), NULL); - - purple_cmd_register("zcir","wwws",PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("zcir","wwws",PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_zcir, _("zcir <class> <instance> <recipient>: Send a message to <<i>class</i>,<i>instance</i>,<i>recipient</i>>"), NULL); - - purple_cmd_register("zir","wws",PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("zir","wws",PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_zir, _("zir <instance> <recipient>: Send a message to <MESSAGE,<i>instance</i>,<i>recipient</i>>"), NULL); - - purple_cmd_register("zc","ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("zc","ws", PURPLE_CMD_P_PROTOCOL, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, "prpl-zephyr", zephyr_purple_cmd_zc, _("zc <class>: Send a message to <<i>class</i>,PERSONAL,*>"), NULL); - + cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id)); +} + + +static void zephyr_unregister_slash_commands(void) +{ + while (cmds) { + PurpleCmdId id = GPOINTER_TO_UINT(cmds->data); + purple_cmd_unregister(id); + cmds = g_slist_delete_link(cmds, cmds); + } } @@ -2799,17 +2825,17 @@ } -static void zephyr_action_resubscribe(PurplePluginAction *action) +static void zephyr_action_resubscribe(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; zephyr_resubscribe(gc); } -static void zephyr_action_get_subs_from_server(PurplePluginAction *action) +static void zephyr_action_get_subs_from_server(PurpleProtocolAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleConnection *gc = action->connection; zephyr_account *zephyr = purple_connection_get_protocol_data(gc); gchar *title; int retval, nsubs, one,i; @@ -2853,168 +2879,172 @@ } -static GList *zephyr_actions(PurplePlugin *plugin, gpointer context) +static GList *zephyr_get_actions(PurpleConnection *gc) { GList *list = NULL; - PurplePluginAction *act = NULL; - - act = purple_plugin_action_new(_("Resubscribe"), zephyr_action_resubscribe); + PurpleProtocolAction *act = NULL; + + act = purple_protocol_action_new(_("Resubscribe"), zephyr_action_resubscribe); list = g_list_append(list, act); - act = purple_plugin_action_new(_("Retrieve subscriptions from server"), zephyr_action_get_subs_from_server); + act = purple_protocol_action_new(_("Retrieve subscriptions from server"), zephyr_action_get_subs_from_server); list = g_list_append(list,act); return list; } -static PurplePlugin *my_protocol = NULL; - -static PurplePluginProtocolInfo prpl_info = { - sizeof(PurplePluginProtocolInfo), /* struct_size */ - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_NO_PASSWORD, - NULL, /* ??? user_splits */ - NULL, /* ??? protocol_options */ - NO_BUDDY_ICONS, - zephyr_list_icon, - NULL, /* ??? list_emblems */ - NULL, /* ??? status_text */ - NULL, /* ??? tooltip_text */ - zephyr_status_types, /* status_types */ - NULL, /* ??? blist_node_menu - probably all useful actions are already handled*/ - zephyr_chat_info, /* chat_info */ - NULL, /* chat_info_defaults */ - zephyr_login, /* login */ - zephyr_close, /* close */ - zephyr_send_im, /* send_im */ - NULL, /* XXX set info (Location?) */ - zephyr_send_typing, /* send_typing */ - zephyr_zloc, /* get_info */ - zephyr_set_status, /* set_status */ - NULL, /* ??? set idle */ - NULL, /* change password */ - NULL, /* add_buddy */ - NULL, /* add_buddies */ - NULL, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* remove_permit */ - NULL, /* remove_deny */ - NULL, /* set_permit_deny */ - zephyr_join_chat, /* join_chat */ - NULL, /* reject_chat -- No chat invites*/ - zephyr_get_chat_name, /* get_chat_name */ - NULL, /* chat_invite -- No chat invites*/ - zephyr_chat_leave, /* chat_leave */ - NULL, /* chat_whisper -- No "whispering"*/ - zephyr_chat_send, /* chat_send */ - NULL, /* keepalive -- Not necessary*/ - NULL, /* register_user -- Not supported*/ - NULL, /* XXX get_cb_info */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - NULL, /* buddy_free */ - NULL, /* convo_closed */ - zephyr_normalize, /* normalize */ - NULL, /* XXX set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* XXX get_cb_real_name */ - zephyr_chat_set_topic, /* set_chat_topic */ - zephyr_find_blist_chat, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ - NULL, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - - NULL, - NULL, - NULL, - NULL, /* get_account_text_table */ - NULL, /* initate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL /* get_max_message_size */ -}; - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-zephyr", /**< id */ - "Zephyr", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Zephyr Protocol Plugin"), - /** description */ - N_("Zephyr Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, - zephyr_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void init_plugin(PurplePlugin * plugin) + +static void +zephyr_protocol_init(PurpleProtocol *protocol) { PurpleAccountOption *option; char *tmp = get_exposure_level(); + protocol->id = "prpl-zephyr"; + protocol->name = "Zephyr"; + protocol->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_NO_PASSWORD; + option = purple_account_option_bool_new(_("Use tzc"), "use_tzc", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("tzc command"), "tzc_command", "/usr/bin/tzc -e %s"); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Export to .anyone"), "write_anyone", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Export to .zephyr.subs"), "write_zsubs", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Import from .anyone"), "read_anyone", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_bool_new(_("Import from .zephyr.subs"), "read_zsubs", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Realm"), "realm", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Exposure"), "exposure_level", tmp?tmp: EXPOSE_REALMVIS); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + protocol->protocol_options = g_list_append(protocol->protocol_options, option); option = purple_account_option_string_new(_("Encoding"), "encoding", ZEPHYR_FALLBACK_CHARSET); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - my_protocol = plugin; + protocol->protocol_options = g_list_append(protocol->protocol_options, option); +} + + +static void +zephyr_protocol_class_init(PurpleProtocolClass *klass) +{ + klass->login = zephyr_login; + klass->close = zephyr_close; + klass->status_types = zephyr_status_types; + klass->list_icon = zephyr_list_icon; +} + + +static void +zephyr_protocol_client_iface_init(PurpleProtocolClientIface *client_iface) +{ + client_iface->get_actions = zephyr_get_actions; + client_iface->normalize = zephyr_normalize; + client_iface->find_blist_chat = zephyr_find_blist_chat; +} + + +static void +zephyr_protocol_server_iface_init(PurpleProtocolServerIface *server_iface) +{ + server_iface->get_info = zephyr_zloc; + server_iface->set_status = zephyr_set_status; + + server_iface->set_info = NULL; /* XXX Location? */ + server_iface->set_buddy_icon = NULL; /* XXX */ +} + + +static void +zephyr_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) +{ + im_iface->send = zephyr_send_im; + im_iface->send_typing = zephyr_send_typing; +} + + +static void +zephyr_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->info = zephyr_chat_info; + chat_iface->join = zephyr_join_chat; + chat_iface->get_name = zephyr_get_chat_name; + chat_iface->leave = zephyr_chat_leave; + chat_iface->send = zephyr_chat_send; + chat_iface->set_topic = zephyr_chat_set_topic; + + chat_iface->get_user_real_name = NULL; /* XXX */ +} + + +PURPLE_DEFINE_TYPE_EXTENDED( + ZephyrProtocol, zephyr_protocol, PURPLE_TYPE_PROTOCOL, 0, + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, + zephyr_protocol_client_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, + zephyr_protocol_server_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, + zephyr_protocol_im_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, + zephyr_protocol_chat_iface_init) +); + + +static PurplePluginInfo *plugin_query(GError **error) +{ + return purple_plugin_info_new( + "id", "prpl-zephyr", + "name", "Zephyr Protocol", + "version", DISPLAY_VERSION, + "category", N_("Protocol"), + "summary", N_("Zephyr Protocol Plugin"), + "description", N_("Zephyr Protocol Plugin"), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + ); +} + + +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + zephyr_protocol_register_type(plugin); + + my_protocol = purple_protocols_add(ZEPHYR_TYPE_PROTOCOL, error); + if (!my_protocol) + return FALSE; + zephyr_register_slash_commands(); + + return TRUE; } -PURPLE_INIT_PLUGIN(zephyr, init_plugin, info); + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + zephyr_unregister_slash_commands(); + + if (!purple_protocols_remove(my_protocol, error)) + return FALSE; + + return TRUE; +} + + +PURPLE_PLUGIN_INIT(zephyr, plugin_query, plugin_load, plugin_unload);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/zephyr/zephyr.h Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,49 @@ +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#ifndef _ZEPHYR_H_ +#define _ZEPHYR_H_ + +#include "protocol.h" + +#define ZEPHYR_TYPE_PROTOCOL (zephyr_protocol_get_type()) +#define ZEPHYR_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ZEPHYR_TYPE_PROTOCOL, ZephyrProtocol)) +#define ZEPHYR_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ZEPHYR_TYPE_PROTOCOL, ZephyrProtocolClass)) +#define ZEPHYR_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZEPHYR_TYPE_PROTOCOL)) +#define ZEPHYR_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ZEPHYR_TYPE_PROTOCOL)) +#define ZEPHYR_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), ZEPHYR_TYPE_PROTOCOL, ZephyrProtocolClass)) + +typedef struct _ZephyrProtocol +{ + PurpleProtocol parent; +} ZephyrProtocol; + +typedef struct _ZephyrProtocolClass +{ + PurpleProtocolClass parent_class; +} ZephyrProtocolClass; + +/** + * Returns the GType for the ZephyrProtocol object. + */ +G_MODULE_EXPORT GType zephyr_protocol_get_type(void); + +#endif /* _ZEPHYR_H_ */
--- a/libpurple/proxy.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/proxy.h Fri Jan 31 18:02:20 2014 +0530 @@ -306,10 +306,10 @@ /** * Cancel an in-progress connection attempt. This should be called - * by the PRPL if the user disables an account while it is still + * by the protocol if the user disables an account while it is still * performing the initial sign on. Or when establishing a file * transfer, if we attempt to connect to a remote user but they - * are behind a firewall then the PRPL can cancel the connection + * are behind a firewall then the protocol can cancel the connection * attempt early rather than just letting the OS's TCP/IP stack * time-out the connection. */
--- a/libpurple/prpl.c Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,666 +0,0 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include "internal.h" -#include "conversation.h" -#include "debug.h" -#include "network.h" -#include "notify.h" -#include "prpl.h" -#include "request.h" -#include "util.h" - -/**************************************************************************/ -/** @name Attention Type API */ -/**************************************************************************/ - -struct _PurpleAttentionType -{ - const char *name; /**< Shown in GUI elements */ - const char *incoming_description; /**< Shown when sent */ - const char *outgoing_description; /**< Shown when receied */ - const char *icon_name; /**< Icon to display (optional) */ - const char *unlocalized_name; /**< Unlocalized name for UIs needing it */ -}; - - -PurpleAttentionType * -purple_attention_type_new(const char *ulname, const char *name, - const char *inc_desc, const char *out_desc) -{ - PurpleAttentionType *attn = g_new0(PurpleAttentionType, 1); - - purple_attention_type_set_name(attn, name); - purple_attention_type_set_incoming_desc(attn, inc_desc); - purple_attention_type_set_outgoing_desc(attn, out_desc); - purple_attention_type_set_unlocalized_name(attn, ulname); - - return attn; -} - - -void -purple_attention_type_set_name(PurpleAttentionType *type, const char *name) -{ - g_return_if_fail(type != NULL); - - type->name = name; -} - -void -purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc) -{ - g_return_if_fail(type != NULL); - - type->incoming_description = desc; -} - -void -purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc) -{ - g_return_if_fail(type != NULL); - - type->outgoing_description = desc; -} - -void -purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name) -{ - g_return_if_fail(type != NULL); - - type->icon_name = name; -} - -void -purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname) -{ - g_return_if_fail(type != NULL); - - type->unlocalized_name = ulname; -} - -const char * -purple_attention_type_get_name(const PurpleAttentionType *type) -{ - g_return_val_if_fail(type != NULL, NULL); - - return type->name; -} - -const char * -purple_attention_type_get_incoming_desc(const PurpleAttentionType *type) -{ - g_return_val_if_fail(type != NULL, NULL); - - return type->incoming_description; -} - -const char * -purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type) -{ - g_return_val_if_fail(type != NULL, NULL); - - return type->outgoing_description; -} - -const char * -purple_attention_type_get_icon_name(const PurpleAttentionType *type) -{ - g_return_val_if_fail(type != NULL, NULL); - - if(type->icon_name == NULL || *(type->icon_name) == '\0') - return NULL; - - return type->icon_name; -} - -const char * -purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type) -{ - g_return_val_if_fail(type != NULL, NULL); - - return type->unlocalized_name; -} - -/**************************************************************************/ -/** @name Protocol Plugin API */ -/**************************************************************************/ -void -purple_prpl_got_account_idle(PurpleAccount *account, gboolean idle, - time_t idle_time) -{ - g_return_if_fail(account != NULL); - g_return_if_fail(purple_account_is_connected(account)); - - purple_presence_set_idle(purple_account_get_presence(account), - idle, idle_time); -} - -void -purple_prpl_got_account_login_time(PurpleAccount *account, time_t login_time) -{ - PurplePresence *presence; - - g_return_if_fail(account != NULL); - g_return_if_fail(purple_account_is_connected(account)); - - if (login_time == 0) - login_time = time(NULL); - - presence = purple_account_get_presence(account); - - purple_presence_set_login_time(presence, login_time); -} - -void -purple_prpl_got_account_status(PurpleAccount *account, const char *status_id, ...) -{ - PurplePresence *presence; - PurpleStatus *status; - va_list args; - - g_return_if_fail(account != NULL); - g_return_if_fail(status_id != NULL); - g_return_if_fail(purple_account_is_connected(account)); - - presence = purple_account_get_presence(account); - status = purple_presence_get_status(presence, status_id); - - g_return_if_fail(status != NULL); - - va_start(args, status_id); - purple_status_set_active_with_attrs(status, TRUE, args); - va_end(args); -} - -void -purple_prpl_got_account_actions(PurpleAccount *account) -{ - - g_return_if_fail(account != NULL); - g_return_if_fail(purple_account_is_connected(account)); - - purple_signal_emit(purple_accounts_get_handle(), "account-actions-changed", - account); -} - -void -purple_prpl_got_user_idle(PurpleAccount *account, const char *name, - gboolean idle, time_t idle_time) -{ - PurplePresence *presence; - GSList *list; - - g_return_if_fail(account != NULL); - g_return_if_fail(name != NULL); - g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account)); - - if ((list = purple_blist_find_buddies(account, name)) == NULL) - return; - - while (list) { - presence = purple_buddy_get_presence(list->data); - list = g_slist_delete_link(list, list); - purple_presence_set_idle(presence, idle, idle_time); - } -} - -void -purple_prpl_got_user_login_time(PurpleAccount *account, const char *name, - time_t login_time) -{ - GSList *list; - PurplePresence *presence; - - g_return_if_fail(account != NULL); - g_return_if_fail(name != NULL); - - if ((list = purple_blist_find_buddies(account, name)) == NULL) - return; - - if (login_time == 0) - login_time = time(NULL); - - while (list) { - PurpleBuddy *buddy = list->data; - presence = purple_buddy_get_presence(buddy); - list = g_slist_delete_link(list, list); - - if (purple_presence_get_login_time(presence) != login_time) - { - purple_presence_set_login_time(presence, login_time); - - purple_signal_emit(purple_blist_get_handle(), "buddy-got-login-time", buddy); - } - } -} - -void -purple_prpl_got_user_status(PurpleAccount *account, const char *name, - const char *status_id, ...) -{ - GSList *list, *l; - PurpleBuddy *buddy; - PurplePresence *presence; - PurpleStatus *status; - PurpleStatus *old_status; - va_list args; - - g_return_if_fail(account != NULL); - g_return_if_fail(name != NULL); - g_return_if_fail(status_id != NULL); - g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account)); - - if((list = purple_blist_find_buddies(account, name)) == NULL) - return; - - for(l = list; l != NULL; l = l->next) { - buddy = l->data; - - presence = purple_buddy_get_presence(buddy); - status = purple_presence_get_status(presence, status_id); - - if(NULL == status) - /* - * TODO: This should never happen, right? We should call - * g_warning() or something. - */ - continue; - - old_status = purple_presence_get_active_status(presence); - - va_start(args, status_id); - purple_status_set_active_with_attrs(status, TRUE, args); - va_end(args); - - purple_buddy_update_status(buddy, old_status); - } - - g_slist_free(list); - - /* The buddy is no longer online, they are therefore by definition not - * still typing to us. */ - if (!purple_status_is_online(status)) { - serv_got_typing_stopped(purple_account_get_connection(account), name); - purple_prpl_got_media_caps(account, name); - } -} - -void purple_prpl_got_user_status_deactive(PurpleAccount *account, const char *name, - const char *status_id) -{ - GSList *list, *l; - PurpleBuddy *buddy; - PurplePresence *presence; - PurpleStatus *status; - - g_return_if_fail(account != NULL); - g_return_if_fail(name != NULL); - g_return_if_fail(status_id != NULL); - g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account)); - - if((list = purple_blist_find_buddies(account, name)) == NULL) - return; - - for(l = list; l != NULL; l = l->next) { - buddy = l->data; - - presence = purple_buddy_get_presence(buddy); - status = purple_presence_get_status(presence, status_id); - - if(NULL == status) - continue; - - if (purple_status_is_active(status)) { - purple_status_set_active(status, FALSE); - purple_buddy_update_status(buddy, status); - } - } - - g_slist_free(list); -} - -static void -do_prpl_change_account_status(PurpleAccount *account, - PurpleStatus *old_status, PurpleStatus *new_status) -{ - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; - - if (purple_status_is_online(new_status) && - purple_account_is_disconnected(account) && - purple_network_is_available()) - { - purple_account_connect(account); - return; - } - - if (!purple_status_is_online(new_status)) - { - if (!purple_account_is_disconnected(account)) - purple_account_disconnect(account); - /* Clear out the unsaved password if we switch to offline status */ - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL, NULL, NULL); - - return; - } - - if (purple_account_is_connecting(account)) - /* - * We don't need to call the set_status PRPL function because - * the PRPL will take care of setting its status during the - * connection process. - */ - return; - - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - - if (prpl == NULL) - return; - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (!purple_account_is_disconnected(account) && prpl_info->set_status != NULL) - { - prpl_info->set_status(account, new_status); - } -} - -void -purple_prpl_change_account_status(PurpleAccount *account, - PurpleStatus *old_status, PurpleStatus *new_status) -{ - g_return_if_fail(account != NULL); - g_return_if_fail(new_status != NULL); - g_return_if_fail(!purple_status_is_exclusive(new_status) || old_status != NULL); - - do_prpl_change_account_status(account, old_status, new_status); - - purple_signal_emit(purple_accounts_get_handle(), "account-status-changed", - account, old_status, new_status); -} - -GList * -purple_prpl_get_statuses(PurpleAccount *account, PurplePresence *presence) -{ - GList *statuses = NULL; - GList *l; - PurpleStatus *status; - - g_return_val_if_fail(account != NULL, NULL); - g_return_val_if_fail(presence != NULL, NULL); - - for (l = purple_account_get_status_types(account); l != NULL; l = l->next) - { - status = purple_status_new((PurpleStatusType *)l->data, presence); - statuses = g_list_prepend(statuses, status); - } - - statuses = g_list_reverse(statuses); - - return statuses; -} - -static void -purple_prpl_attention(PurpleConversation *conv, const char *who, - guint type, PurpleMessageFlags flags, time_t mtime) -{ - PurpleAccount *account = purple_conversation_get_account(conv); - purple_signal_emit(purple_conversations_get_handle(), - flags == PURPLE_MESSAGE_SEND ? "sent-attention" : "got-attention", - account, who, conv, type); -} - -void -purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code) -{ - PurpleAttentionType *attn; - PurpleMessageFlags flags; - PurplePlugin *prpl; - PurpleIMConversation *im; - gboolean (*send_attention)(PurpleConnection *, const char *, guint); - PurpleBuddy *buddy; - const char *alias; - gchar *description; - time_t mtime; - - g_return_if_fail(gc != NULL); - g_return_if_fail(who != NULL); - - prpl = purple_find_prpl(purple_account_get_protocol_id(purple_connection_get_account(gc))); - send_attention = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention; - g_return_if_fail(send_attention != NULL); - - mtime = time(NULL); - - attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code); - - if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who)) != NULL) - alias = purple_buddy_get_contact_alias(buddy); - else - alias = who; - - if (attn && purple_attention_type_get_outgoing_desc(attn)) { - description = g_strdup_printf(purple_attention_type_get_outgoing_desc(attn), alias); - } else { - description = g_strdup_printf(_("Requesting %s's attention..."), alias); - } - - flags = PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM; - - purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n", - description, who); - - if (!send_attention(gc, who, type_code)) - return; - - im = purple_im_conversation_new(purple_connection_get_account(gc), who); - purple_conversation_write_message(PURPLE_CONVERSATION(im), NULL, description, flags, mtime); - purple_prpl_attention(PURPLE_CONVERSATION(im), who, type_code, PURPLE_MESSAGE_SEND, time(NULL)); - - g_free(description); -} - -static void -got_attention(PurpleConnection *gc, int id, const char *who, guint type_code) -{ - PurpleMessageFlags flags; - PurpleAttentionType *attn; - PurpleBuddy *buddy; - const char *alias; - gchar *description; - time_t mtime; - - mtime = time(NULL); - - attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code); - - /* PURPLE_MESSAGE_NOTIFY is for attention messages. */ - flags = PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_RECV; - - /* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display - * it next to the attention command. And if it is null, display a generic icon. */ - - if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who)) != NULL) - alias = purple_buddy_get_contact_alias(buddy); - else - alias = who; - - if (attn && purple_attention_type_get_incoming_desc(attn)) { - description = g_strdup_printf(purple_attention_type_get_incoming_desc(attn), alias); - } else { - description = g_strdup_printf(_("%s has requested your attention!"), alias); - } - - purple_debug_info("server", "got_attention: got '%s' from %s\n", - description, who); - - if (id == -1) - serv_got_im(gc, who, description, flags, mtime); - else - serv_got_chat_in(gc, id, who, flags, description, mtime); - - /* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */ - - g_free(description); -} - -void -purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code) -{ - PurpleConversation *conv = NULL; - PurpleAccount *account = purple_connection_get_account(gc); - - got_attention(gc, -1, who, type_code); - conv = - purple_conversations_find_with_account(who, account); - if (conv) - purple_prpl_attention(conv, who, type_code, PURPLE_MESSAGE_RECV, - time(NULL)); -} - -void -purple_prpl_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code) -{ - got_attention(gc, id, who, type_code); -} - -gboolean -purple_prpl_initiate_media(PurpleAccount *account, - const char *who, - PurpleMediaSessionType type) -{ -#ifdef USE_VV - PurpleConnection *gc = NULL; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; - - if (account) - gc = purple_account_get_connection(account); - if (gc) - prpl = purple_connection_get_prpl(gc); - if (prpl) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, initiate_media)) { - /* should check that the protocol supports this media type here? */ - return prpl_info->initiate_media(account, who, type); - } else -#endif - return FALSE; -} - -PurpleMediaCaps -purple_prpl_get_media_caps(PurpleAccount *account, const char *who) -{ -#ifdef USE_VV - PurpleConnection *gc = NULL; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; - - if (account) - gc = purple_account_get_connection(account); - if (gc) - prpl = purple_connection_get_prpl(gc); - if (prpl) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, - get_media_caps)) { - return prpl_info->get_media_caps(account, who); - } -#endif - return PURPLE_MEDIA_CAPS_NONE; -} - -void -purple_prpl_got_media_caps(PurpleAccount *account, const char *name) -{ -#ifdef USE_VV - GSList *list; - - g_return_if_fail(account != NULL); - g_return_if_fail(name != NULL); - - if ((list = purple_blist_find_buddies(account, name)) == NULL) - return; - - while (list) { - PurpleBuddy *buddy = list->data; - PurpleMediaCaps oldcaps = purple_buddy_get_media_caps(buddy); - PurpleMediaCaps newcaps = 0; - const gchar *bname = purple_buddy_get_name(buddy); - list = g_slist_delete_link(list, list); - - - newcaps = purple_prpl_get_media_caps(account, bname); - purple_buddy_set_media_caps(buddy, newcaps); - - if (oldcaps == newcaps) - continue; - - purple_signal_emit(purple_blist_get_handle(), - "buddy-caps-changed", buddy, - newcaps, oldcaps); - } -#endif -} - -gssize -purple_prpl_get_max_message_size(PurplePlugin *prpl) -{ - PurplePluginProtocolInfo *prpl_info; - - g_return_val_if_fail(prpl != NULL, 0); - g_return_val_if_fail(PURPLE_IS_PROTOCOL_PLUGIN(prpl), 0); - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - g_return_val_if_fail(prpl_info != NULL, 0); - - if (!PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_max_message_size)) - return 0; - - return prpl_info->get_max_message_size(NULL); -} - -/************************************************************************** - * Protocol Plugin Subsystem API - **************************************************************************/ - -PurplePlugin * -purple_find_prpl(const char *id) -{ - GList *l; - PurplePlugin *plugin; - - g_return_val_if_fail(id != NULL, NULL); - - for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) { - plugin = (PurplePlugin *)l->data; - - if (purple_strequal(plugin->info->id, id)) - return plugin; - } - - return NULL; -}
--- a/libpurple/prpl.h Fri Jan 31 17:56:27 2014 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1005 +0,0 @@ -/** - * @file prpl.h Protocol Plugin functions - * @ingroup core - */ - -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -/* this file should be all that prpls need to include. therefore, by including - * this file, they should get glib, proxy, purple_connection, prpl, etc. */ - -#ifndef _PURPLE_PRPL_H_ -#define _PURPLE_PRPL_H_ - -typedef struct _PurplePluginProtocolInfo PurplePluginProtocolInfo; - -/** Represents "nudges" and "buzzes" that you may send to a buddy to attract - * their attention (or vice-versa). - */ -typedef struct _PurpleAttentionType PurpleAttentionType; - -/**************************************************************************/ -/** @name Basic Protocol Information */ -/**************************************************************************/ - -typedef enum { - PURPLE_ICON_SCALE_DISPLAY = 0x01, /**< We scale the icon when we display it */ - PURPLE_ICON_SCALE_SEND = 0x02 /**< We scale the icon before we send it to the server */ -} PurpleIconScaleRules; - - -/** - * A description of a Buddy Icon specification. This tells Purple what kind of image file - * it should give this prpl, and what kind of image file it should expect back. - * Dimensions less than 1 should be ignored and the image not scaled. - */ -typedef struct _PurpleBuddyIconSpec PurpleBuddyIconSpec; - -/** - * A description of a file transfer thumbnail specification. - * This tells the UI if and what image formats the prpl support for file - * transfer thumbnails. - */ -typedef struct _PurpleThumbnailSpec PurpleThumbnailSpec; - -/** - * This \#define exists just to make it easier to fill out the buddy icon - * field in the prpl info struct for protocols that couldn't care less. - */ -#define NO_BUDDY_ICONS {NULL, 0, 0, 0, 0, 0, 0} - -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -#include "buddylist.h" -#include "conversations.h" -#include "xfer.h" -#include "imgstore.h" -#include "media.h" -#include "notify.h" -#include "proxy.h" -#include "plugin.h" -#include "roomlist.h" -#include "status.h" -#include "whiteboard.h" - - -/** @copydoc PurpleBuddyIconSpec */ -struct _PurpleBuddyIconSpec { - /** This is a comma-delimited list of image formats or @c NULL if icons - * are not supported. Neither the core nor the prpl will actually - * check to see if the data it's given matches this; it's entirely up - * to the UI to do what it wants - */ - char *format; - - int min_width; /**< Minimum width of this icon */ - int min_height; /**< Minimum height of this icon */ - int max_width; /**< Maximum width of this icon */ - int max_height; /**< Maximum height of this icon */ - size_t max_filesize; /**< Maximum size in bytes */ - PurpleIconScaleRules scale_rules; /**< How to stretch this icon */ -}; - -/** Represents an entry containing information that must be supplied by the - * user when joining a chat. - */ -struct proto_chat_entry { - const char *label; /**< User-friendly name of the entry */ - const char *identifier; /**< Used by the PRPL to identify the option */ - gboolean required; /**< True if it's required */ - gboolean is_int; /**< True if the entry expects an integer */ - int min; /**< Minimum value in case of integer */ - int max; /**< Maximum value in case of integer */ - gboolean secret; /**< True if the entry is secret (password) */ -}; - -/** - * Protocol options - * - * These should all be stuff that some plugins can do and others can't. - */ -typedef enum -{ - /** - * User names are unique to a chat and are not shared between rooms. - * - * XMPP lets you choose what name you want in chats, so it shouldn't - * be pulling the aliases from the buddy list for the chat list; - * it gets annoying. - */ - OPT_PROTO_UNIQUE_CHATNAME = 0x00000004, - - /** - * Chat rooms have topics. - * - * IRC and XMPP support this. - */ - OPT_PROTO_CHAT_TOPIC = 0x00000008, - - /** - * Don't require passwords for sign-in. - * - * Zephyr doesn't require passwords, so there's no - * need for a password prompt. - */ - OPT_PROTO_NO_PASSWORD = 0x00000010, - - /** - * Notify on new mail. - * - * MSN and Yahoo notify you when you have new mail. - */ - OPT_PROTO_MAIL_CHECK = 0x00000020, - - /** - * Images in IMs. - * - * Oscar lets you send images in direct IMs. - */ - OPT_PROTO_IM_IMAGE = 0x00000040, - - /** - * Allow passwords to be optional. - * - * Passwords in IRC are optional, and are needed for certain - * functionality. - */ - OPT_PROTO_PASSWORD_OPTIONAL = 0x00000080, - - /** - * Allows font size to be specified in sane point size - * - * Probably just XMPP and Y!M - */ - OPT_PROTO_USE_POINTSIZE = 0x00000100, - - /** - * Set the Register button active even when the username has not - * been specified. - * - * Gadu-Gadu doesn't need a username to register new account (because - * usernames are assigned by the server). - */ - OPT_PROTO_REGISTER_NOSCREENNAME = 0x00000200, - - /** - * Indicates that slash commands are native to this protocol. - * Used as a hint that unknown commands should not be sent as messages. - */ - OPT_PROTO_SLASH_COMMANDS_NATIVE = 0x00000400, - - /** - * Indicates that this protocol supports sending a user-supplied message - * along with an invitation. - */ - OPT_PROTO_INVITE_MESSAGE = 0x00000800, - - /** - * Indicates that this protocol supports sending a user-supplied message - * along with an authorization acceptance. - */ - OPT_PROTO_AUTHORIZATION_GRANTED_MESSAGE = 0x00001000, - - /** - * Indicates that this protocol supports sending a user-supplied message - * along with an authorization denial. - */ - OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE = 0x00002000 - -} PurpleProtocolOptions; - -/** - * A protocol plugin information structure. - * - * Every protocol plugin initializes this structure. It is the gateway - * between purple and the protocol plugin. Many of these callbacks can be - * NULL. If a callback must be implemented, it has a comment indicating so. - */ -struct _PurplePluginProtocolInfo -{ - /** - * The size of the PurplePluginProtocolInfo. This should always be sizeof(PurplePluginProtocolInfo). - * This allows adding more functions to this struct without requiring a major version bump. - */ - unsigned long struct_size; - - /* NOTE: - * If more functions are added, they should accessed using the following syntax: - * - * if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, new_function)) - * prpl->new_function(...); - * - * instead of - * - * if (prpl->new_function != NULL) - * prpl->new_function(...); - * - * The PURPLE_PROTOCOL_PLUGIN_HAS_FUNC macro can be used for the older member - * functions (e.g. login, send_im etc.) too. - */ - - PurpleProtocolOptions options; /**< Protocol options. */ - - GList *user_splits; /**< A GList of PurpleAccountUserSplit */ - GList *protocol_options; /**< A GList of PurpleAccountOption */ - - PurpleBuddyIconSpec icon_spec; /**< The icon spec. */ - - /** - * Returns the base icon name for the given buddy and account. - * If buddy is NULL and the account is non-NULL, it will return the - * name to use for the account's icon. If both are NULL, it will - * return the name to use for the protocol's icon. - * - * This must be implemented. - */ - const char *(*list_icon)(PurpleAccount *account, PurpleBuddy *buddy); - - /** - * Fills the four char**'s with string identifiers for "emblems" - * that the UI will interpret and display as relevant - */ - const char *(*list_emblem)(PurpleBuddy *buddy); - - /** - * Gets a short string representing this buddy's status. This will - * be shown on the buddy list. - */ - char *(*status_text)(PurpleBuddy *buddy); - - /** - * Allows the prpl to add text to a buddy's tooltip. - */ - void (*tooltip_text)(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full); - - /** - * Returns a list of #PurpleStatusType which exist for this account; - * this must be implemented, and must add at least the offline and - * online states. - */ - GList *(*status_types)(PurpleAccount *account); - - /** - * Returns a list of #PurpleMenuAction structs, which represent extra - * actions to be shown in (for example) the right-click menu for @a - * node. - */ - GList *(*blist_node_menu)(PurpleBlistNode *node); - - /** - * Returns a list of #proto_chat_entry structs, which represent - * information required by the PRPL to join a chat. libpurple will - * call join_chat along with the information filled by the user. - * - * @return A list of #proto_chat_entry structs - */ - GList *(*chat_info)(PurpleConnection *); - - /** - * Returns a hashtable which maps #proto_chat_entry struct identifiers - * to default options as strings based on chat_name. The resulting - * hashtable should be created with g_hash_table_new_full(g_str_hash, - * g_str_equal, NULL, g_free);. Use #get_chat_name if you instead need - * to extract a chat name from a hashtable. - * - * @param chat_name The chat name to be turned into components - * @return Hashtable containing the information extracted from chat_name - */ - GHashTable *(*chat_info_defaults)(PurpleConnection *, const char *chat_name); - - /* All the server-related functions */ - - /** This must be implemented. */ - void (*login)(PurpleAccount *); - - /** This must be implemented. */ - void (*close)(PurpleConnection *); - - /** - * This PRPL function should return a positive value on success. - * If the message is too big to be sent, return -E2BIG. If - * the account is not connected, return -ENOTCONN. If the - * PRPL is unable to send the message for another reason, return - * some other negative value. You can use one of the valid - * errno values, or just big something. If the message should - * not be echoed to the conversation window, return 0. - */ - int (*send_im)(PurpleConnection *, const char *who, - const char *message, - PurpleMessageFlags flags); - - void (*set_info)(PurpleConnection *, const char *info); - - /** - * @return If this protocol requires the PURPLE_IM_TYPING message to - * be sent repeatedly to signify that the user is still - * typing, then the PRPL should return the number of - * seconds to wait before sending a subsequent notification. - * Otherwise the PRPL should return 0. - */ - unsigned int (*send_typing)(PurpleConnection *, const char *name, PurpleIMTypingState state); - - /** - * Should arrange for purple_notify_userinfo() to be called with - * @a who's user info. - */ - void (*get_info)(PurpleConnection *, const char *who); - void (*set_status)(PurpleAccount *account, PurpleStatus *status); - - void (*set_idle)(PurpleConnection *, int idletime); - void (*change_passwd)(PurpleConnection *, const char *old_pass, - const char *new_pass); - - /** - * Add a buddy to a group on the server. - * - * This PRPL function may be called in situations in which the buddy is - * already in the specified group. If the protocol supports - * authorization and the user is not already authorized to see the - * status of \a buddy, \a add_buddy should request authorization. - * - * If authorization is required, then use the supplied invite message. - */ - void (*add_buddy)(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message); - void (*add_buddies)(PurpleConnection *pc, GList *buddies, GList *groups, const char *message); - void (*remove_buddy)(PurpleConnection *, PurpleBuddy *buddy, PurpleGroup *group); - void (*remove_buddies)(PurpleConnection *, GList *buddies, GList *groups); - void (*add_permit)(PurpleConnection *, const char *name); - void (*add_deny)(PurpleConnection *, const char *name); - void (*rem_permit)(PurpleConnection *, const char *name); - void (*rem_deny)(PurpleConnection *, const char *name); - void (*set_permit_deny)(PurpleConnection *); - - /** - * Called when the user requests joining a chat. Should arrange for - * #serv_got_joined_chat to be called. - * - * @param components A hashtable containing information required to - * join the chat as described by the entries returned - * by #chat_info. It may also be called when accepting - * an invitation, in which case this matches the - * data parameter passed to #serv_got_chat_invite. - */ - void (*join_chat)(PurpleConnection *, GHashTable *components); - - /** - * Called when the user refuses a chat invitation. - * - * @param components A hashtable containing information required to - * join the chat as passed to #serv_got_chat_invite. - */ - void (*reject_chat)(PurpleConnection *, GHashTable *components); - - /** - * Returns a chat name based on the information in components. Use - * #chat_info_defaults if you instead need to generate a hashtable - * from a chat name. - * - * @param components A hashtable containing information about the chat. - */ - char *(*get_chat_name)(GHashTable *components); - - /** - * Invite a user to join a chat. - * - * @param id The id of the chat to invite the user to. - * @param message A message displayed to the user when the invitation - * is received. - * @param who The name of the user to send the invation to. - */ - void (*chat_invite)(PurpleConnection *, int id, - const char *message, const char *who); - /** - * Called when the user requests leaving a chat. - * - * @param id The id of the chat to leave - */ - void (*chat_leave)(PurpleConnection *, int id); - - /** - * Send a whisper to a user in a chat. - * - * @param id The id of the chat. - * @param who The name of the user to send the whisper to. - * @param message The message of the whisper. - */ - void (*chat_whisper)(PurpleConnection *, int id, - const char *who, const char *message); - - /** - * Send a message to a chat. - * This PRPL function should return a positive value on success. - * If the message is too big to be sent, return -E2BIG. If - * the account is not connected, return -ENOTCONN. If the - * PRPL is unable to send the message for another reason, return - * some other negative value. You can use one of the valid - * errno values, or just big something. - * - * @param id The id of the chat to send the message to. - * @param message The message to send to the chat. - * @param flags A bitwise OR of #PurpleMessageFlags representing - * message flags. - * @return A positive number or 0 in case of success, - * a negative error number in case of failure. - */ - int (*chat_send)(PurpleConnection *, int id, const char *message, PurpleMessageFlags flags); - - /** If implemented, this will be called regularly for this prpl's - * active connections. You'd want to do this if you need to repeatedly - * send some kind of keepalive packet to the server to avoid being - * disconnected. ("Regularly" is defined by - * <code>KEEPALIVE_INTERVAL</code> in <tt>libpurple/connection.c</tt>.) - */ - void (*keepalive)(PurpleConnection *); - - /** new user registration */ - void (*register_user)(PurpleAccount *); - - /** - * @deprecated Use #PurplePluginProtocolInfo.get_info instead. - */ - void (*get_cb_info)(PurpleConnection *, int, const char *who); - - /** save/store buddy's alias on server list/roster */ - void (*alias_buddy)(PurpleConnection *, const char *who, - const char *alias); - - /** change a buddy's group on a server list/roster */ - void (*group_buddy)(PurpleConnection *, const char *who, - const char *old_group, const char *new_group); - - /** rename a group on a server list/roster */ - void (*rename_group)(PurpleConnection *, const char *old_name, - PurpleGroup *group, GList *moved_buddies); - - void (*buddy_free)(PurpleBuddy *); - - void (*convo_closed)(PurpleConnection *, const char *who); - - /** - * Convert the username @a who to its canonical form. Also checks for - * validity. - * - * For example, AIM treats "fOo BaR" and "foobar" as the same user; this - * function should return the same normalized string for both of those. - * On the other hand, both of these are invalid for protocols with - * number-based usernames, so function should return NULL in such case. - * - * @param account The account the username is related to. Can - * be NULL. - * @param who The username to convert. - * @return Normalized username, or NULL, if it's invalid. - */ - const char *(*normalize)(const PurpleAccount *account, const char *who); - - /** - * Set the buddy icon for the given connection to @a img. The prpl - * does NOT own a reference to @a img; if it needs one, it must - * #purple_imgstore_ref(@a img) itself. - */ - void (*set_buddy_icon)(PurpleConnection *, PurpleStoredImage *img); - - void (*remove_group)(PurpleConnection *gc, PurpleGroup *group); - - /** Gets the real name of a participant in a chat. For example, on - * XMPP this turns a chat room nick <tt>foo</tt> into - * <tt>room\@server/foo</tt> - * @param gc the connection on which the room is. - * @param id the ID of the chat room. - * @param who the nickname of the chat participant. - * @return the real name of the participant. This string must be - * freed by the caller. - */ - char *(*get_cb_real_name)(PurpleConnection *gc, int id, const char *who); - - void (*set_chat_topic)(PurpleConnection *gc, int id, const char *topic); - - PurpleChat *(*find_blist_chat)(PurpleAccount *account, const char *name); - - /* room listing prpl callbacks */ - PurpleRoomlist *(*roomlist_get_list)(PurpleConnection *gc); - void (*roomlist_cancel)(PurpleRoomlist *list); - void (*roomlist_expand_category)(PurpleRoomlist *list, PurpleRoomlistRoom *category); - - /* file transfer callbacks */ - gboolean (*can_receive_file)(PurpleConnection *, const char *who); - void (*send_file)(PurpleConnection *, const char *who, const char *filename); - PurpleXfer *(*new_xfer)(PurpleConnection *, const char *who); - - /** Checks whether offline messages to @a buddy are supported. - * @return @c TRUE if @a buddy can be sent messages while they are - * offline, or @c FALSE if not. - */ - gboolean (*offline_message)(const PurpleBuddy *buddy); - - PurpleWhiteboardPrplOps *whiteboard_prpl_ops; - - /** For use in plugins that may understand the underlying protocol */ - int (*send_raw)(PurpleConnection *gc, const char *buf, int len); - - /* room list serialize */ - char *(*roomlist_room_serialize)(PurpleRoomlistRoom *room); - - /** Remove the user from the server. The account can either be - * connected or disconnected. After the removal is finished, the - * connection will stay open and has to be closed! - */ - /* This is here rather than next to register_user for API compatibility - * reasons. - */ - void (*unregister_user)(PurpleAccount *, PurpleAccountUnregistrationCb cb, void *user_data); - - /* Attention API for sending & receiving zaps/nudges/buzzes etc. */ - gboolean (*send_attention)(PurpleConnection *gc, const char *username, guint type); - GList *(*get_attention_types)(PurpleAccount *acct); - - /** This allows protocols to specify additional strings to be used for - * various purposes. The idea is to stuff a bunch of strings in this hash - * table instead of expanding the struct for every addition. This hash - * table is allocated every call and MUST be unrefed by the caller. - * - * @param account The account to specify. This can be NULL. - * @return The protocol's string hash table. The hash table should be - * destroyed by the caller when it's no longer needed. - */ - GHashTable *(*get_account_text_table)(PurpleAccount *account); - - /** - * Initiate a media session with the given contact. - * - * @param account The account to initiate the media session on. - * @param who The remote user to initiate the session with. - * @param type The type of media session to initiate. - * @return TRUE if the call succeeded else FALSE. (Doesn't imply the media session or stream will be successfully created) - */ - gboolean (*initiate_media)(PurpleAccount *account, const char *who, - PurpleMediaSessionType type); - - /** - * Checks to see if the given contact supports the given type of media session. - * - * @param account The account the contact is on. - * @param who The remote user to check for media capability with. - * @return The media caps the contact supports. - */ - PurpleMediaCaps (*get_media_caps)(PurpleAccount *account, - const char *who); - - /** - * Returns an array of "PurpleMood"s, with the last one having - * "mood" set to @c NULL. - */ - PurpleMood *(*get_moods)(PurpleAccount *account); - - /** - * Set the user's "friendly name" (or alias or nickname or - * whatever term you want to call it) on the server. The - * protocol plugin should call success_cb or failure_cb - * *asynchronously* (if it knows immediately that the set will fail, - * call one of the callbacks from an idle/0-second timeout) depending - * on if the nickname is set successfully. - * - * @param gc The connection for which to set an alias - * @param alias The new server-side alias/nickname for this account, - * or NULL to unset the alias/nickname (or return it to - * a protocol-specific "default"). - * @param success_cb Callback to be called if the public alias is set - * @param failure_cb Callback to be called if setting the public alias - * fails - * @see purple_account_set_public_alias - */ - void (*set_public_alias)(PurpleConnection *gc, const char *alias, - PurpleSetPublicAliasSuccessCallback success_cb, - PurpleSetPublicAliasFailureCallback failure_cb); - /** - * Retrieve the user's "friendly name" as set on the server. - * The protocol plugin should call success_cb or failure_cb - * *asynchronously* (even if it knows immediately that the get will fail, - * call one of the callbacks from an idle/0-second timeout) depending - * on if the nickname is retrieved. - * - * @param gc The connection for which to retireve the alias - * @param success_cb Callback to be called with the retrieved alias - * @param failure_cb Callback to be called if the prpl is unable to - * retrieve the alias - * @see purple_account_get_public_alias - */ - void (*get_public_alias)(PurpleConnection *gc, - PurpleGetPublicAliasSuccessCallback success_cb, - PurpleGetPublicAliasFailureCallback failure_cb); - - /** - * Gets the maximum message size in bytes for the conversation. - * - * It may depend on connection-specific or conversation-specific - * variables, like channel or buddy's name length. - * - * This value is intended for plaintext message, the exact value may be - * lower because of: - * - used newlines (some protocols count them as more than one byte), - * - formatting, - * - used special characters. - * - * @param conv The conversation to query, or NULL to get safe minimum - * for the protocol. - * - * @return Maximum message size, 0 if unspecified, -1 for infinite. - */ - gssize (*get_max_message_size)(PurpleConversation *conv); -}; - -#define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \ - (G_STRUCT_OFFSET(PurplePluginProtocolInfo, member) < prpl->struct_size && \ - prpl->member != NULL) - - -#define PURPLE_IS_PROTOCOL_PLUGIN(plugin) \ - ((plugin)->info->type == PURPLE_PLUGIN_PROTOCOL) - -#define PURPLE_PLUGIN_PROTOCOL_INFO(plugin) \ - ((PurplePluginProtocolInfo *)(plugin)->info->extra_info) - -G_BEGIN_DECLS - -/**************************************************************************/ -/** @name Attention Type API */ -/**************************************************************************/ -/*@{*/ - -/** - * Creates a new #PurpleAttentionType object and sets its mandatory parameters. - * - * @param ulname A non-localized string that can be used by UIs in need of such - * non-localized strings. This should be the same as @a name, - * without localization. - * @param name A localized string that the UI may display for the event. This - * should be the same string as @a ulname, with localization. - * @param inc_desc A localized description shown when the event is received. - * @param out_desc A localized description shown when the event is sent. - * - * @return A pointer to the new object. - */ -PurpleAttentionType *purple_attention_type_new(const char *ulname, const char *name, - const char *inc_desc, const char *out_desc); - -/** - * Sets the displayed name of the attention-demanding event. - * - * @param type The attention type. - * @param name The localized name that will be displayed by UIs. This should be - * the same string given as the unlocalized name, but with - * localization. - */ -void purple_attention_type_set_name(PurpleAttentionType *type, const char *name); - -/** - * Sets the description of the attention-demanding event shown in conversations - * when the event is received. - * - * @param type The attention type. - * @param desc The localized description for incoming events. - */ -void purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc); - -/** - * Sets the description of the attention-demanding event shown in conversations - * when the event is sent. - * - * @param type The attention type. - * @param desc The localized description for outgoing events. - */ -void purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc); - -/** - * Sets the name of the icon to display for the attention event; this is optional. - * - * @param type The attention type. - * @param name The icon's name. - * @note Icons are optional for attention events. - */ -void purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name); - -/** - * Sets the unlocalized name of the attention event; some UIs may need this, - * thus it is required. - * - * @param type The attention type. - * @param ulname The unlocalized name. This should be the same string given as - * the localized name, but without localization. - */ -void purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname); - -/** - * Get the attention type's name as displayed by the UI. - * - * @param type The attention type. - * - * @return The name. - */ -const char *purple_attention_type_get_name(const PurpleAttentionType *type); - -/** - * Get the attention type's description shown when the event is received. - * - * @param type The attention type. - * @return The description. - */ -const char *purple_attention_type_get_incoming_desc(const PurpleAttentionType *type); - -/** - * Get the attention type's description shown when the event is sent. - * - * @param type The attention type. - * @return The description. - */ -const char *purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type); - -/** - * Get the attention type's icon name. - * - * @param type The attention type. - * @return The icon name or @c NULL if unset/empty. - * @note Icons are optional for attention events. - */ -const char *purple_attention_type_get_icon_name(const PurpleAttentionType *type); - -/** - * Get the attention type's unlocalized name; this is useful for some UIs. - * - * @param type The attention type - * @return The unlocalized name. - */ -const char *purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type); - -/*@}*/ - -/**************************************************************************/ -/** @name Protocol Plugin API */ -/**************************************************************************/ -/*@{*/ - -/** - * Notifies Purple that our account's idle state and time have changed. - * - * This is meant to be called from protocol plugins. - * - * @param account The account. - * @param idle The user's idle state. - * @param idle_time The user's idle time. - */ -void purple_prpl_got_account_idle(PurpleAccount *account, gboolean idle, - time_t idle_time); - -/** - * Notifies Purple of our account's log-in time. - * - * This is meant to be called from protocol plugins. - * - * @param account The account the user is on. - * @param login_time The user's log-in time. - */ -void purple_prpl_got_account_login_time(PurpleAccount *account, time_t login_time); - -/** - * Notifies Purple that our account's status has changed. - * - * This is meant to be called from protocol plugins. - * - * @param account The account the user is on. - * @param status_id The status ID. - * @param ... A NULL-terminated list of attribute IDs and values, - * beginning with the value for @a attr_id. - */ -void purple_prpl_got_account_status(PurpleAccount *account, - const char *status_id, ...) G_GNUC_NULL_TERMINATED; - -/** - * Notifies Purple that our account's actions have changed. This is only - * called after the initial connection. Emits the account-actions-changed - * signal. - * - * This is meant to be called from protocol plugins. - * - * @param account The account. - * - * @see account-actions-changed - */ -void purple_prpl_got_account_actions(PurpleAccount *account); - -/** - * Notifies Purple that a buddy's idle state and time have changed. - * - * This is meant to be called from protocol plugins. - * - * @param account The account the user is on. - * @param name The name of the buddy. - * @param idle The user's idle state. - * @param idle_time The user's idle time. This is the time at - * which the user became idle, in seconds since - * the epoch. If the PRPL does not know this value - * then it should pass 0. - */ -void purple_prpl_got_user_idle(PurpleAccount *account, const char *name, - gboolean idle, time_t idle_time); - -/** - * Notifies Purple of a buddy's log-in time. - * - * This is meant to be called from protocol plugins. - * - * @param account The account the user is on. - * @param name The name of the buddy. - * @param login_time The user's log-in time. - */ -void purple_prpl_got_user_login_time(PurpleAccount *account, const char *name, - time_t login_time); - -/** - * Notifies Purple that a buddy's status has been activated. - * - * This is meant to be called from protocol plugins. - * - * @param account The account the user is on. - * @param name The name of the buddy. - * @param status_id The status ID. - * @param ... A NULL-terminated list of attribute IDs and values, - * beginning with the value for @a attr_id. - */ -void purple_prpl_got_user_status(PurpleAccount *account, const char *name, - const char *status_id, ...) G_GNUC_NULL_TERMINATED; - -/** - * Notifies libpurple that a buddy's status has been deactivated - * - * This is meant to be called from protocol plugins. - * - * @param account The account the user is on. - * @param name The name of the buddy. - * @param status_id The status ID. - */ -void purple_prpl_got_user_status_deactive(PurpleAccount *account, const char *name, - const char *status_id); - -/** - * Informs the server that our account's status changed. - * - * @param account The account the user is on. - * @param old_status The previous status. - * @param new_status The status that was activated, or deactivated - * (in the case of independent statuses). - */ -void purple_prpl_change_account_status(PurpleAccount *account, - PurpleStatus *old_status, - PurpleStatus *new_status); - -/** - * Retrieves the list of stock status types from a prpl. - * - * @param account The account the user is on. - * @param presence The presence for which we're going to get statuses - * - * @return List of statuses - */ -GList *purple_prpl_get_statuses(PurpleAccount *account, PurplePresence *presence); - -/** - * Send an attention request message. - * - * @param gc The connection to send the message on. - * @param who Whose attention to request. - * @param type_code An index into the prpl's attention_types list determining the type - * of the attention request command to send. 0 if prpl only defines one - * (for example, Yahoo and MSN), but some protocols define more (MySpaceIM). - * - * Note that you can't send arbitrary PurpleAttentionType's, because there is - * only a fixed set of attention commands. - */ -void purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code); - -/** - * Process an incoming attention message. - * - * @param gc The connection that received the attention message. - * @param who Who requested your attention. - * @param type_code An index into the prpl's attention_types list determining the type - * of the attention request command to send. - */ -void purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code); - -/** - * Process an incoming attention message in a chat. - * - * @param gc The connection that received the attention message. - * @param id The chat id. - * @param who Who requested your attention. - * @param type_code An index into the prpl's attention_types list determining the type - * of the attention request command to send. - */ -void purple_prpl_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code); - -/** - * Determines if the contact supports the given media session type. - * - * @param account The account the user is on. - * @param who The name of the contact to check capabilities for. - * - * @return The media caps the contact supports. - */ -PurpleMediaCaps purple_prpl_get_media_caps(PurpleAccount *account, - const char *who); - -/** - * Initiates a media session with the given contact. - * - * @param account The account the user is on. - * @param who The name of the contact to start a session with. - * @param type The type of media session to start. - * - * @return TRUE if the call succeeded else FALSE. (Doesn't imply the media session or stream will be successfully created) - */ -gboolean purple_prpl_initiate_media(PurpleAccount *account, - const char *who, - PurpleMediaSessionType type); - -/** - * Signals that the prpl received capabilities for the given contact. - * - * This function is intended to be used only by prpls. - * - * @param account The account the user is on. - * @param who The name of the contact for which capabilities have been received. - */ -void purple_prpl_got_media_caps(PurpleAccount *account, const char *who); - -/** - * Gets the safe maximum message size in bytes for the protocol plugin. - * - * @see PurplePluginProtocolInfo#get_max_message_size - * - * @param prpl The protocol plugin to query. - * - * @return Maximum message size, 0 if unspecified, -1 for infinite. - */ -gssize -purple_prpl_get_max_message_size(PurplePlugin *prpl); - -/*@}*/ - -/**************************************************************************/ -/** @name Protocol Plugin Subsystem API */ -/**************************************************************************/ -/*@{*/ - -/** - * Finds a protocol plugin structure of the specified type. - * - * @param id The protocol plugin; - */ -PurplePlugin *purple_find_prpl(const char *id); - -/*@}*/ - -G_END_DECLS - -#endif /* _PRPL_H_ */
--- a/libpurple/purple-remote Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/purple-remote Fri Jan 31 18:02:20 2014 +0530 @@ -70,8 +70,8 @@ getstatusmessage quit - PurpleAccountsFindConnected?name=&protocol=prpl-jabber - PurpleAccountsFindConnected(,prpl-jabber) + PurpleAccountsFindConnected?name=&protocol=jabber + PurpleAccountsFindConnected(,jabber) """ % sys.argv[0] if (requested): sys.exit(0)
--- a/libpurple/purple-send Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/purple-send Fri Jan 31 18:02:20 2014 +0530 @@ -17,7 +17,7 @@ Examples: - $0 PurpleAccountsFindConnected string: string:prpl-jabber + $0 PurpleAccountsFindConnected string: string:jabber $0 PurpleAccountsGetAll $0 PurpleCoreQuit
--- a/libpurple/purple.h.in Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/purple.h.in Fri Jan 31 18:02:20 2014 +0530 @@ -75,13 +75,13 @@ #include <network.h> #include <notify.h> #include <ntlm.h> -#include <plugin.h> +#include <plugins.h> #include <pluginpref.h> #include <pounce.h> #include <prefs.h> #include <presence.h> #include <proxy.h> -#include <prpl.h> +#include <protocols.h> #include <request.h> #include <roomlist.h> #include <savedstatuses.h>
--- a/libpurple/request.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/request.h Fri Jan 31 18:02:20 2014 +0530 @@ -31,6 +31,7 @@ #include <glib.h> #include "certificate.h" +#include "conversation.h" #include "request-datasheet.h" /** @@ -654,7 +655,7 @@ * * @return The UI data associated with this object. This is a * convenience field provided to the UIs--it is not - * used by the libuprple core. + * used by the libpurple core. */ gpointer purple_request_fields_get_ui_data(const PurpleRequestFields *fields); @@ -1746,7 +1747,7 @@ * @param handle The plugin or connection handle. For some * things this is <em>extremely</em> important. The * handle is used to programmatically close the request - * dialog when it is no longer needed. For PRPLs this + * dialog when it is no longer needed. For protocols this * is often a pointer to the #PurpleConnection * instance. For plugins this should be a similar, * unique memory location. This value is important
--- a/libpurple/roomlist.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/roomlist.c Fri Jan 31 18:02:20 2014 +0530 @@ -59,7 +59,7 @@ gchar *name; /**< The name of the room. */ GList *fields; /**< Other fields. */ PurpleRoomlistRoom *parent; /**< The parent room, or NULL. */ - gboolean expanded_once; /**< A flag the UI uses to avoid multiple expand prpl cbs. */ + gboolean expanded_once; /**< A flag the UI uses to avoid multiple expand protocol cbs. */ }; /** @@ -161,19 +161,15 @@ PurpleRoomlist *purple_roomlist_get_list(PurpleConnection *gc) { - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL); g_return_val_if_fail(PURPLE_CONNECTION_IS_CONNECTED(gc), NULL); - prpl = purple_connection_get_prpl(gc); + protocol = purple_connection_get_protocol(gc); - if(prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if(prpl_info && prpl_info->roomlist_get_list) - return prpl_info->roomlist_get_list(gc); + if(protocol) + return purple_protocol_roomlist_iface_get_list(protocol, gc); return NULL; } @@ -181,8 +177,7 @@ void purple_roomlist_cancel_get_list(PurpleRoomlist *list) { PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list); - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc; g_return_if_fail(priv != NULL); @@ -192,20 +187,16 @@ g_return_if_fail(PURPLE_IS_CONNECTION(gc)); if(gc) - prpl = purple_connection_get_prpl(gc); + protocol = purple_connection_get_protocol(gc); - if(prpl) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if(prpl_info && prpl_info->roomlist_cancel) - prpl_info->roomlist_cancel(list); + if(protocol) + purple_protocol_roomlist_iface_cancel(protocol, list); } void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category) { PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list); - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc; g_return_if_fail(priv != NULL); @@ -216,13 +207,10 @@ g_return_if_fail(PURPLE_IS_CONNECTION(gc)); if(gc) - prpl = purple_connection_get_prpl(gc); + protocol = purple_connection_get_protocol(gc); - if(prpl) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if(prpl_info && prpl_info->roomlist_expand_category) - prpl_info->roomlist_expand_category(list, category); + if(protocol) + purple_protocol_roomlist_iface_expand_category(protocol, list, category); } GList * purple_roomlist_get_fields(PurpleRoomlist *list) @@ -420,12 +408,26 @@ PurpleRoomlist *purple_roomlist_new(PurpleAccount *account) { + PurpleRoomlist *list; + PurpleProtocol *protocol; + g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); - return g_object_new(PURPLE_TYPE_ROOMLIST, - "account", account, - NULL - ); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL); + + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, roomlist_new)) + list = purple_protocol_factory_iface_roomlist_new(protocol, account); + else + list = g_object_new(PURPLE_TYPE_ROOMLIST, + "account", account, + NULL + ); + + g_return_val_if_fail(list != NULL, NULL); + + return list; } /*@}*/
--- a/libpurple/roomlist.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/roomlist.h Fri Jan 31 18:02:20 2014 +0530 @@ -203,8 +203,8 @@ void purple_roomlist_room_add(PurpleRoomlist *list, PurpleRoomlistRoom *room); /** - * Returns a PurpleRoomlist structure from the prpl, and - * instructs the prpl to start fetching the list. + * Returns a PurpleRoomlist structure from the protocol, and + * instructs the protocol to start fetching the list. * * @param gc The PurpleConnection to have get a list. * @@ -214,8 +214,8 @@ PurpleRoomlist *purple_roomlist_get_list(PurpleConnection *gc); /** - * Tells the prpl to stop fetching the list. - * If this is possible and done, the prpl will + * Tells the protocol to stop fetching the list. + * If this is possible and done, the protocol will * call set_in_progress with @c FALSE and possibly * unref the list if it took a reference. * @@ -224,7 +224,7 @@ void purple_roomlist_cancel_get_list(PurpleRoomlist *list); /** - * Tells the prpl that a category was expanded. + * Tells the protocol that a category was expanded. * * On some protocols, the rooms in the category * won't be fetched until this is called. @@ -250,7 +250,7 @@ * @param list The roomlist, which must not be @c NULL. * * @return The protocol data associated with this room list. This is a - * convenience field provided to the protocol plugin--it is not + * convenience field provided to the protocol -- it is not * used the libpurple core. */ gpointer purple_roomlist_get_protocol_data(PurpleRoomlist *list);
--- a/libpurple/savedstatuses.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/savedstatuses.c Fri Jan 31 18:02:20 2014 +0530 @@ -435,12 +435,12 @@ * And I can always make them smile * From White Castle to the Nile</message> * <substatus> - * <account protocol='prpl-aim'>markdoliner</account> + * <account protocol='aim'>markdoliner</account> * <state>available</state> * <message>The ladies man is here to answer your queries.</message> * </substatus> * <substatus> - * <account protocol='prpl-aim'>giantgraypanda</account> + * <account protocol='aim'>giantgraypanda</account> * <state>away</state> * <message>A.C. ain't in charge no more.</message> * </substatus>
--- a/libpurple/server.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/server.c Fri Jan 31 18:02:20 2014 +0530 @@ -30,7 +30,7 @@ #include "log.h" #include "notify.h" #include "prefs.h" -#include "prpl.h" +#include "protocol.h" #include "request.h" #include "signals.h" #include "server.h" @@ -43,15 +43,11 @@ unsigned int serv_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->send_typing) - return prpl_info->send_typing(gc, name, state); + protocol = purple_connection_get_protocol(gc); + return purple_protocol_im_iface_send_typing(protocol, gc, name, state); } return 0; @@ -122,26 +118,24 @@ PurpleIMConversation *im = NULL; PurpleAccount *account = NULL; PurplePresence *presence = NULL; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; int val = -EINVAL; const gchar *auto_reply_pref = NULL; g_return_val_if_fail(gc != NULL, val); - prpl = purple_connection_get_prpl(gc); + protocol = purple_connection_get_protocol(gc); - g_return_val_if_fail(prpl != NULL, val); - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + g_return_val_if_fail(protocol != NULL, val); + g_return_val_if_fail(PURPLE_PROTOCOL_HAS_IM_IFACE(protocol), val); account = purple_connection_get_account(gc); presence = purple_account_get_presence(account); im = purple_conversations_find_im_with_account(name, account); - if (prpl_info->send_im) - val = prpl_info->send_im(gc, name, message, flags); + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, IM_IFACE, send)) + val = purple_protocol_im_iface_send(protocol, gc, name, message, flags); /* * XXX - If "only auto-reply when away & idle" is set, then shouldn't @@ -165,35 +159,29 @@ void serv_get_info(PurpleConnection *gc, const char *name) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->get_info) - prpl_info->get_info(gc, name); + protocol = purple_connection_get_protocol(gc); + purple_protocol_server_iface_get_info(protocol, gc, name); } } void serv_set_info(PurpleConnection *gc, const char *info) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleAccount *account; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + protocol = purple_connection_get_protocol(gc); - if (prpl_info->set_info) { + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, set_info)) { account = purple_connection_get_account(gc); purple_signal_emit(purple_accounts_get_handle(), "account-setting-info", account, info); - prpl_info->set_info(gc, info); + purple_protocol_server_iface_set_info(protocol, gc, info); purple_signal_emit(purple_accounts_get_handle(), "account-set-info", account, info); @@ -208,8 +196,7 @@ { PurpleAccount *account; PurpleConnection *gc; - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (b) { account = purple_buddy_get_account(b); @@ -218,13 +205,10 @@ gc = purple_account_get_connection(account); if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->alias_buddy) - prpl_info->alias_buddy(gc, - purple_buddy_get_name(b), - purple_buddy_get_local_alias(b)); + protocol = purple_connection_get_protocol(gc); + purple_protocol_server_iface_alias_buddy(protocol, gc, + purple_buddy_get_name(b), + purple_buddy_get_local_alias(b)); } } } @@ -301,20 +285,18 @@ PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code) { - PurplePlugin *prpl; + PurpleProtocol *protocol; PurpleAttentionType* attn; - GList *(*get_attention_types)(PurpleAccount *); g_return_val_if_fail(account != NULL, NULL); - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); /* Lookup the attention type in the protocol's attention_types list, if any. */ - get_attention_types = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->get_attention_types; - if (get_attention_types) { + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, ATTENTION_IFACE, get_types)) { GList *attention_types; - attention_types = get_attention_types(account); + attention_types = purple_protocol_attention_iface_get_types(protocol, account); attn = (PurpleAttentionType *)g_list_nth_data(attention_types, type_code); } else { attn = NULL; @@ -335,8 +317,7 @@ { PurpleAccount *account; PurpleConnection *gc; - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; g_return_if_fail(b != NULL); g_return_if_fail(og != NULL); @@ -346,80 +327,59 @@ gc = purple_account_get_connection(account); if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->group_buddy) - prpl_info->group_buddy(gc, purple_buddy_get_name(b), - purple_group_get_name(og), - purple_group_get_name(ng)); + protocol = purple_connection_get_protocol(gc); + purple_protocol_server_iface_group_buddy(protocol, gc, purple_buddy_get_name(b), + purple_group_get_name(og), + purple_group_get_name(ng)); } } void serv_add_permit(PurpleConnection *gc, const char *name) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->add_permit) - prpl_info->add_permit(gc, name); + protocol = purple_connection_get_protocol(gc); + purple_protocol_privacy_iface_add_permit(protocol, gc, name); } } void serv_add_deny(PurpleConnection *gc, const char *name) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->add_deny) - prpl_info->add_deny(gc, name); + protocol = purple_connection_get_protocol(gc); + purple_protocol_privacy_iface_add_deny(protocol, gc, name); } } void serv_rem_permit(PurpleConnection *gc, const char *name) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->rem_permit) - prpl_info->rem_permit(gc, name); + protocol = purple_connection_get_protocol(gc); + purple_protocol_privacy_iface_rem_permit(protocol, gc, name); } } void serv_rem_deny(PurpleConnection *gc, const char *name) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->rem_deny) - prpl_info->rem_deny(gc, name); + protocol = purple_connection_get_protocol(gc); + purple_protocol_privacy_iface_rem_deny(protocol, gc, name); } } void serv_set_permit_deny(PurpleConnection *gc) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + protocol = purple_connection_get_protocol(gc); /* * this is called when either you import a buddy list, and make lots @@ -427,44 +387,34 @@ * in the prefs. In either case you should probably be resetting and * resending the permit/deny info when you get this. */ - if (prpl_info->set_permit_deny) - prpl_info->set_permit_deny(gc); + purple_protocol_privacy_iface_set_permit_deny(protocol, gc); } } void serv_join_chat(PurpleConnection *gc, GHashTable *data) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->join_chat) - prpl_info->join_chat(gc, data); + protocol = purple_connection_get_protocol(gc); + purple_protocol_chat_iface_join(protocol, gc, data); } } void serv_reject_chat(PurpleConnection *gc, GHashTable *data) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->reject_chat) - prpl_info->reject_chat(gc, data); + protocol = purple_connection_get_protocol(gc); + purple_protocol_chat_iface_reject(protocol, gc, data); } } void serv_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name) { - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleChatConversation *chat; char *buffy = message && *message ? g_strdup(message) : NULL; @@ -474,16 +424,13 @@ return; if(gc) - prpl = purple_connection_get_prpl(gc); - - if(prpl) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + protocol = purple_connection_get_protocol(gc); purple_signal_emit(purple_conversations_get_handle(), "chat-inviting-user", chat, name, &buffy); - if (prpl_info && prpl_info->chat_invite) - prpl_info->chat_invite(gc, id, buffy, name); + if (protocol) + purple_protocol_chat_iface_invite(protocol, gc, id, buffy, name); purple_signal_emit(purple_conversations_get_handle(), "chat-invited-user", chat, name, buffy); @@ -493,45 +440,35 @@ /* Ya know, nothing uses this except purple_chat_conversation_finalize(), * I think I'll just merge it into that later... - * Then again, something might want to use this, from outside prpl-land + * Then again, something might want to use this, from outside protocol-land * to leave a chat without destroying the conversation. */ void serv_chat_leave(PurpleConnection *gc, int id) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->chat_leave) - prpl_info->chat_leave(gc, id); + protocol = purple_connection_get_protocol(gc); + purple_protocol_chat_iface_leave(protocol, gc, id); } void serv_chat_whisper(PurpleConnection *gc, int id, const char *who, const char *message) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->chat_whisper) - prpl_info->chat_whisper(gc, id, who, message); + protocol = purple_connection_get_protocol(gc); + purple_protocol_chat_iface_whisper(protocol, gc, id, who, message); } } int serv_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + protocol = purple_connection_get_protocol(gc); - if (prpl_info->chat_send) - return prpl_info->chat_send(gc, id, message, flags); + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, send)) + return purple_protocol_chat_iface_send(protocol, gc, id, message, flags); return -EINVAL; } @@ -562,7 +499,7 @@ } /* - * XXX: Should we be setting this here, or relying on prpls to set it? + * XXX: Should we be setting this here, or relying on protocols to set it? */ flags |= PURPLE_MESSAGE_RECV; @@ -911,7 +848,7 @@ purple_normalize(purple_conversation_get_account( PURPLE_CONVERSATION(chat)), who))) { flags |= PURPLE_MESSAGE_SEND; - flags &= ~PURPLE_MESSAGE_RECV; /* Just in case some prpl sets it! */ + flags &= ~PURPLE_MESSAGE_RECV; /* Just in case some protocol sets it! */ } else { flags |= PURPLE_MESSAGE_RECV; } @@ -948,16 +885,14 @@ void serv_send_file(PurpleConnection *gc, const char *who, const char *file) { - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; if (gc) { - prpl = purple_connection_get_prpl(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + protocol = purple_connection_get_protocol(gc); - if (prpl_info->send_file && - (!prpl_info->can_receive_file - || prpl_info->can_receive_file(gc, who))) - prpl_info->send_file(gc, who, file); + if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, can_receive) || + purple_protocol_xfer_iface_can_receive(protocol, gc, who)) + + purple_protocol_xfer_iface_send(protocol, gc, who, file); } }
--- a/libpurple/server.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/server.h Fri Jan 31 18:02:20 2014 +0530 @@ -28,7 +28,7 @@ #include "accounts.h" #include "conversations.h" -#include "prpl.h" +#include "protocols.h" G_BEGIN_DECLS @@ -53,9 +53,9 @@ void serv_move_buddy(PurpleBuddy *, PurpleGroup *, PurpleGroup *); int serv_send_im(PurpleConnection *, const char *, const char *, PurpleMessageFlags flags); -/** Get information about an account's attention commands, from the prpl. +/** Get information about an account's attention commands, from the protocol. * - * @return The attention command numbered 'code' from the prpl's attention_types, or NULL. + * @return The attention command numbered 'code' from the protocol's attention_types, or NULL. */ PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code); @@ -75,7 +75,7 @@ void serv_got_alias(PurpleConnection *gc, const char *who, const char *alias); /** - * A protocol plugin should call this when it retrieves a private alias from + * A protocol should call this when it retrieves a private alias from * the server. Private aliases are the aliases the user sets, while public * aliases are the aliases or display names that buddies set for themselves. * @@ -126,7 +126,7 @@ void serv_reject_chat(PurpleConnection *, GHashTable *data); /** - * Called by a prpl when an account is invited into a chat. + * Called by a protocol when an account is invited into a chat. * * @param gc The connection on which the invite arrived. * @param name The name of the chat you're being invited to. @@ -141,17 +141,17 @@ GHashTable *data); /** - * Called by a prpl when an account has joined a chat. + * Called by a protocol when an account has joined a chat. * * @param gc The connection on which the chat was joined. - * @param id The id of the chat, assigned by the prpl. + * @param id The id of the chat, assigned by the protocol. * @param name The name of the chat. * @return The resulting conversation */ PurpleChatConversation *serv_got_joined_chat(PurpleConnection *gc, int id, const char *name); /** - * Called by a prpl when an attempt to join a chat via serv_join_chat() + * Called by a protocol when an attempt to join a chat via serv_join_chat() * fails. * * @param gc The connection on which chat joining failed @@ -162,18 +162,18 @@ void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data); /** - * Called by a prpl when an account has left a chat. + * Called by a protocol when an account has left a chat. * * @param g The connection on which the chat was left. - * @param id The id of the chat, as assigned by the prpl. + * @param id The id of the chat, as assigned by the protocol. */ void serv_got_chat_left(PurpleConnection *g, int id); /** - * Called by a prpl when a message has been received in a chat. + * Called by a protocol when a message has been received in a chat. * * @param g The connection on which the message was received. - * @param id The id of the chat, as assigned by the prpl. + * @param id The id of the chat, as assigned by the protocol. * @param who The name of the user who sent the message. * @param flags The flags of the message. * @param message The message received in the chat.
--- a/libpurple/signals.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/signals.c Fri Jan 31 18:02:20 2014 +0530 @@ -177,17 +177,15 @@ void purple_signals_unregister_by_instance(void *instance) { - gboolean found; - g_return_if_fail(instance != NULL); - found = g_hash_table_remove(instance_table, instance); + g_hash_table_remove(instance_table, instance); /* * Makes things easier (more annoying?) for developers who don't have * things registering and unregistering in the right order :) */ - g_return_if_fail(found); + /* g_return_if_fail(found); */ } void
--- a/libpurple/sslconn.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/sslconn.c Fri Jan 31 18:02:20 2014 +0530 @@ -29,6 +29,7 @@ #include "certificate.h" #include "debug.h" +#include "plugins.h" #include "request.h" #include "sslconn.h" @@ -44,10 +45,10 @@ if (_ssl_initialized) return FALSE; - plugin = purple_plugins_find_with_id("core-ssl"); + plugin = purple_plugins_find_plugin("core-ssl"); - if (plugin != NULL && !purple_plugin_is_loaded(plugin)) - purple_plugin_load(plugin); + if (plugin && !purple_plugin_is_loaded(plugin)) + purple_plugin_load(plugin, NULL); ops = purple_ssl_get_ops(); if ((ops == NULL) || (ops->init == NULL) || (ops->uninit == NULL) ||
--- a/libpurple/status.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/status.c Fri Jan 31 18:02:20 2014 +0530 @@ -560,7 +560,7 @@ PurpleAccountUiOps *ops = purple_accounts_get_ui_ops(); if (purple_account_get_enabled(account, purple_core_get_ui())) - purple_prpl_change_account_status(account, old_status, new_status); + purple_protocol_change_account_status(account, old_status, new_status); if (ops != NULL && ops->status_changed != NULL) {
--- a/libpurple/status.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/status.h Fri Jan 31 18:02:20 2014 +0530 @@ -41,13 +41,13 @@ #define PURPLE_TYPE_STATUS_TYPE (purple_status_type_get_type()) /** - * PurpleStatusType's are created by each PRPL. They outline the + * PurpleStatusType's are created by each protocol. They outline the * available statuses of the protocol. AIM, for example, supports * an available state with an optional available message, an away * state with a mandatory message, and an invisible state (which is * technically "independent" of the other two, but we'll get into * that later). PurpleStatusTypes are very permanent. They are - * hardcoded in each PRPL and will not change often. And because + * hardcoded in each protocol and will not change often. And because * they are hardcoded, they do not need to be saved to any XML file. */ typedef struct _PurpleStatusType PurpleStatusType; @@ -110,7 +110,7 @@ * PurpleBuddy node for this person in your buddy list. Purple wants * to mark this buddy as "away," so it creates a new PurpleStatus. * The PurpleStatus has its PurpleStatusType set to the "away" state - * for the oscar PRPL. The PurpleStatus also contains the buddy's + * for the oscar protocol. The PurpleStatus also contains the buddy's * away message. PurpleStatuses are sometimes saved, depending on * the context. The current PurpleStatuses associated with each of * your accounts are saved so that the next time you start Purple,
--- a/libpurple/tests/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/tests/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -23,6 +23,7 @@ check_libpurple_CFLAGS=\ @CHECK_CFLAGS@ \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DEBUG_CFLAGS) \ $(LIBXML_CFLAGS) \ -I.. \ @@ -32,9 +33,10 @@ check_libpurple_LDADD=\ $(top_builddir)/libpurple/protocols/jabber/libjabber.la \ $(top_builddir)/libpurple/protocols/oscar/liboscar.la \ - $(top_builddir)/libpurple/protocols/yahoo/libymsg.la \ + $(top_builddir)/libpurple/protocols/yahoo/libyahoo.la \ $(top_builddir)/libpurple/libpurple.la \ @CHECK_LIBS@ \ - $(GLIB_LIBS) + $(GLIB_LIBS) \ + $(GPLUGIN_LIBS) endif
--- a/libpurple/tests/test_yahoo_util.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/tests/test_yahoo_util.c Fri Jan 31 18:02:20 2014 +0530 @@ -1,7 +1,7 @@ #include <string.h> #include "tests.h" -#include "../protocols/yahoo/libymsg.h" +#include "../protocols/yahoo/ymsg.h" static void setup_codes_to_html(void) {
--- a/libpurple/util.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/util.c Fri Jan 31 18:02:20 2014 +0530 @@ -29,7 +29,7 @@ #include "debug.h" #include "glibcompat.h" #include "notify.h" -#include "prpl.h" +#include "protocol.h" #include "prefs.h" #include "util.h" @@ -3407,15 +3407,11 @@ if (account != NULL) { - PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - - if (prpl != NULL) - { - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->normalize) - ret = prpl_info->normalize(account, str); - } + PurpleProtocol *protocol = + purple_protocols_find(purple_account_get_protocol_id(account)); + + if (protocol != NULL) + ret = purple_protocol_client_iface_normalize(protocol, account, str); } if (ret == NULL) @@ -3434,7 +3430,7 @@ /* * You probably don't want to call this directly, it is - * mainly for use as a PRPL callback function. See the + * mainly for use as a protocol callback function. See the * comments in util.h. */ const char * @@ -3455,23 +3451,21 @@ } gboolean -purple_validate(const PurplePlugin *prpl, const char *str) +purple_validate(const PurpleProtocol *protocol, const char *str) { - PurplePluginProtocolInfo *prpl_info; const char *normalized; - g_return_val_if_fail(prpl != NULL, FALSE); + g_return_val_if_fail(protocol != NULL, FALSE); g_return_val_if_fail(str != NULL, FALSE); if (str[0] == '\0') return FALSE; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (!prpl_info->normalize) + if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, normalize)) return TRUE; - normalized = prpl_info->normalize(NULL, str); + normalized = purple_protocol_client_iface_normalize(PURPLE_PROTOCOL(protocol), + NULL, str); return (NULL != normalized); }
--- a/libpurple/util.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/util.h Fri Jan 31 18:02:20 2014 +0530 @@ -33,7 +33,7 @@ /** * A generic structure that contains information about an "action." One - * place this is is used is by PRPLs to tell the core the list of available + * place this is is used is by protocols to tell the core the list of available * right-click actions for a buddy list row. */ typedef struct _PurpleMenuAction PurpleMenuAction; @@ -50,7 +50,7 @@ #include "signals.h" #include "xmlnode.h" #include "notify.h" -#include "plugin.h" +#include "protocols.h" typedef char *(*PurpleInfoFieldFormatCallback)(const char *field, size_t len); @@ -949,7 +949,7 @@ * * @param account The account the string belongs to, or NULL if you do * not know the account. If you use NULL, the string - * will still be normalized, but if the PRPL uses a + * will still be normalized, but if the protocol uses a * custom normalization function then the string may * not be normalized correctly. * @param str The string to normalize. @@ -961,7 +961,7 @@ /** * Normalizes a string, so that it is suitable for comparison. * - * This is one possible implementation for the PRPL callback + * This is one possible implementation for the protocol callback * function "normalize." It returns a lowercase and UTF-8 * normalized version of the string. * @@ -975,12 +975,12 @@ /** * Checks, if a string is valid. * - * @param prpl The protocol plugin the string belongs to. + * @param protocol The protocol the string belongs to. * @param str The string to validate. * * @return TRUE, if string is valid, otherwise FALSE. */ -gboolean purple_validate(const PurplePlugin *prpl, const char *str); +gboolean purple_validate(const PurpleProtocol *protocol, const char *str); /** * Compares two strings to see if the first contains the second as
--- a/libpurple/whiteboard.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/whiteboard.c Fri Jan 31 18:02:20 2014 +0530 @@ -24,7 +24,7 @@ #include "internal.h" #include "glibcompat.h" #include "whiteboard.h" -#include "prpl.h" +#include "protocol.h" #define PURPLE_WHITEBOARD_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_WHITEBOARD, PurpleWhiteboardPrivate)) @@ -43,7 +43,7 @@ /* TODO Remove this and use protocol-specific subclasses. */ void *proto_data; /**< Protocol specific data */ - PurpleWhiteboardPrplOps *prpl_ops; /**< Protocol-plugin operations */ + PurpleWhiteboardOps *protocol_ops; /**< Protocol operations */ GList *draw_list; /**< List of drawing elements/deltas to send */ @@ -67,7 +67,7 @@ static GParamSpec *properties[PROP_LAST]; static PurpleWhiteboardUiOps *whiteboard_ui_ops = NULL; -/* static PurpleWhiteboardPrplOps *whiteboard_prpl_ops = NULL; */ +/* static PurpleWhiteboardOps *whiteboard_protocol_ops = NULL; */ static GList *wb_list = NULL; @@ -81,13 +81,13 @@ whiteboard_ui_ops = ops; } -void purple_whiteboard_set_prpl_ops(PurpleWhiteboard *wb, PurpleWhiteboardPrplOps *ops) +void purple_whiteboard_set_protocol_ops(PurpleWhiteboard *wb, PurpleWhiteboardOps *ops) { PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb); g_return_if_fail(priv != NULL); - priv->prpl_ops = ops; + priv->protocol_ops = ops; } PurpleAccount *purple_whiteboard_get_account(const PurpleWhiteboard *wb) @@ -170,15 +170,15 @@ gboolean purple_whiteboard_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height) { PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb); - PurpleWhiteboardPrplOps *prpl_ops; + PurpleWhiteboardOps *protocol_ops; g_return_val_if_fail(priv != NULL, FALSE); - prpl_ops = priv->prpl_ops; + protocol_ops = priv->protocol_ops; - if (prpl_ops && prpl_ops->get_dimensions) + if (protocol_ops && protocol_ops->get_dimensions) { - prpl_ops->get_dimensions(wb, width, height); + protocol_ops->get_dimensions(wb, width, height); return TRUE; } @@ -194,14 +194,14 @@ void purple_whiteboard_send_draw_list(PurpleWhiteboard *wb, GList *list) { PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb); - PurpleWhiteboardPrplOps *prpl_ops; + PurpleWhiteboardOps *protocol_ops; g_return_if_fail(priv != NULL); - prpl_ops = priv->prpl_ops; + protocol_ops = priv->protocol_ops; - if (prpl_ops && prpl_ops->send_draw_list) - prpl_ops->send_draw_list(wb, list); + if (protocol_ops && protocol_ops->send_draw_list) + protocol_ops->send_draw_list(wb, list); } void purple_whiteboard_draw_point(PurpleWhiteboard *wb, int x, int y, int color, int size) @@ -225,41 +225,41 @@ void purple_whiteboard_send_clear(PurpleWhiteboard *wb) { PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb); - PurpleWhiteboardPrplOps *prpl_ops; + PurpleWhiteboardOps *protocol_ops; g_return_if_fail(priv != NULL); - prpl_ops = priv->prpl_ops; + protocol_ops = priv->protocol_ops; - if (prpl_ops && prpl_ops->clear) - prpl_ops->clear(wb); + if (protocol_ops && protocol_ops->clear) + protocol_ops->clear(wb); } void purple_whiteboard_send_brush(PurpleWhiteboard *wb, int size, int color) { PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb); - PurpleWhiteboardPrplOps *prpl_ops; + PurpleWhiteboardOps *protocol_ops; g_return_if_fail(priv != NULL); - prpl_ops = priv->prpl_ops; + protocol_ops = priv->protocol_ops; - if (prpl_ops && prpl_ops->set_brush) - prpl_ops->set_brush(wb, size, color); + if (protocol_ops && protocol_ops->set_brush) + protocol_ops->set_brush(wb, size, color); } gboolean purple_whiteboard_get_brush(const PurpleWhiteboard *wb, int *size, int *color) { PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb); - PurpleWhiteboardPrplOps *prpl_ops; + PurpleWhiteboardOps *protocol_ops; g_return_val_if_fail(priv != NULL, FALSE); - prpl_ops = priv->prpl_ops; + protocol_ops = priv->protocol_ops; - if (prpl_ops && prpl_ops->get_brush) + if (protocol_ops && protocol_ops->get_brush) { - prpl_ops->get_brush(wb, size, color); + protocol_ops->get_brush(wb, size, color); return TRUE; } return FALSE; @@ -385,17 +385,18 @@ { PurpleWhiteboard *wb = PURPLE_WHITEBOARD(object); PurpleWhiteboardPrivate *priv = PURPLE_WHITEBOARD_GET_PRIVATE(wb); - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; parent_class->constructed(object); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl( - purple_account_get_connection(priv->account))); - purple_whiteboard_set_prpl_ops(wb, prpl_info->whiteboard_prpl_ops); + protocol = purple_connection_get_protocol( + purple_account_get_connection(priv->account)); + purple_whiteboard_set_protocol_ops(wb, + purple_protocol_get_whiteboard_ops(protocol)); /* Start up protocol specifics */ - if(priv->prpl_ops && priv->prpl_ops->start) - priv->prpl_ops->start(wb); + if(priv->protocol_ops && priv->protocol_ops->start) + priv->protocol_ops->start(wb); wb_list = g_list_append(wb_list, wb); } @@ -415,8 +416,8 @@ } /* Do protocol specific session ending procedures */ - if(priv->prpl_ops && priv->prpl_ops->end) - priv->prpl_ops->end(wb); + if(priv->protocol_ops && priv->protocol_ops->end) + priv->protocol_ops->end(wb); wb_list = g_list_remove(wb_list, wb); @@ -492,13 +493,28 @@ PurpleWhiteboard *purple_whiteboard_new(PurpleAccount *account, const char *who, int state) { + PurpleWhiteboard *wb; + PurpleProtocol *protocol; + g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); g_return_val_if_fail(who != NULL, NULL); - return g_object_new(PURPLE_TYPE_WHITEBOARD, - "account", account, - "who", who, - "state", state, - NULL - ); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL); + + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, whiteboard_new)) + wb = purple_protocol_factory_iface_whiteboard_new(protocol, account, + who, state); + else + wb = g_object_new(PURPLE_TYPE_WHITEBOARD, + "account", account, + "who", who, + "state", state, + NULL + ); + + g_return_val_if_fail(wb != NULL, NULL); + + return wb; }
--- a/libpurple/whiteboard.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/whiteboard.h Fri Jan 31 18:02:20 2014 +0530 @@ -38,9 +38,9 @@ typedef struct _PurpleWhiteboardClass PurpleWhiteboardClass; /** - * Whiteboard PRPL Operations + * Whiteboard protocol operations */ -typedef struct _PurpleWhiteboardPrplOps PurpleWhiteboardPrplOps; +typedef struct _PurpleWhiteboardOps PurpleWhiteboardOps; #include "account.h" @@ -68,9 +68,9 @@ } PurpleWhiteboardUiOps; /** - * PurpleWhiteboard PRPL Operations + * PurpleWhiteboard Protocol Operations */ -struct _PurpleWhiteboardPrplOps +struct _PurpleWhiteboardOps { void (*start)(PurpleWhiteboard *wb); /**< start function */ void (*end)(PurpleWhiteboard *wb); /**< end function */ @@ -132,12 +132,12 @@ void purple_whiteboard_set_ui_ops(PurpleWhiteboardUiOps *ops); /** - * Sets the prpl operations for a whiteboard + * Sets the protocol operations for a whiteboard * - * @param wb The whiteboard for which to set the prpl operations - * @param ops The prpl operations to set + * @param wb The whiteboard for which to set the protocol operations + * @param ops The protocol operations to set */ -void purple_whiteboard_set_prpl_ops(PurpleWhiteboard *wb, PurpleWhiteboardPrplOps *ops); +void purple_whiteboard_set_protocol_ops(PurpleWhiteboard *wb, PurpleWhiteboardOps *ops); /** * Creates a new whiteboard
--- a/libpurple/xfer.c Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/xfer.c Fri Jan 31 18:02:20 2014 +0530 @@ -81,7 +81,7 @@ PurpleXferStatus status; /**< File Transfer's status. */ - /** I/O operations, which should be set by the prpl using + /** I/O operations, which should be set by the protocol using * purple_xfer_set_init_fnc() and friends. Setting #init is * mandatory; all others are optional. */ @@ -101,17 +101,17 @@ PurpleXferUiOps *ui_ops; /**< UI-specific operations. */ /* TODO Remove this and use protocol-specific subclasses. */ - void *proto_data; /**< prpl-specific data. */ + void *proto_data; /**< Protocol-specific data. */ /* * Used to moderate the file transfer when either the read/write ui_ops are - * set or fd is not set. In those cases, the UI/prpl call the respective + * set or fd is not set. In those cases, the UI/protocol call the respective * function, which is somewhat akin to a fd watch being triggered. */ enum { PURPLE_XFER_READY_NONE = 0x0, PURPLE_XFER_READY_UI = 0x1, - PURPLE_XFER_READY_PRPL = 0x2, + PURPLE_XFER_READY_PROTOCOL = 0x2, } ready; /* TODO: Should really use a PurpleCircBuffer for this. */ @@ -1365,7 +1365,7 @@ gsize s = MIN((gsize)purple_xfer_get_bytes_remaining(xfer), (gsize)priv->current_buffer_size); gboolean read = TRUE; - /* this is so the prpl can keep the connection open + /* this is so the protocol can keep the connection open if it needs to for some odd reason. */ if (s == 0) { if (priv->watcher) { @@ -1398,8 +1398,8 @@ purple_xfer_set_watcher(xfer, 0); } - /* Need to indicate the prpl is still ready... */ - priv->ready |= PURPLE_XFER_READY_PRPL; + /* Need to indicate the protocol is still ready... */ + priv->ready |= PURPLE_XFER_READY_PROTOCOL; g_return_if_reached(); } @@ -1472,12 +1472,12 @@ if (priv->dest_fp == NULL) { /* The UI is moderating its side manually */ if (0 == (priv->ready & PURPLE_XFER_READY_UI)) { - priv->ready |= PURPLE_XFER_READY_PRPL; + priv->ready |= PURPLE_XFER_READY_PROTOCOL; purple_input_remove(priv->watcher); purple_xfer_set_watcher(xfer, 0); - purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer); + purple_debug_misc("xfer", "Protocol is ready on ft %p, waiting for UI\n", xfer); return; } @@ -1549,12 +1549,12 @@ priv->ready |= PURPLE_XFER_READY_UI; - if (0 == (priv->ready & PURPLE_XFER_READY_PRPL)) { - purple_debug_misc("xfer", "UI is ready on ft %p, waiting for prpl\n", xfer); + if (0 == (priv->ready & PURPLE_XFER_READY_PROTOCOL)) { + purple_debug_misc("xfer", "UI is ready on ft %p, waiting for protocol\n", xfer); return; } - purple_debug_misc("xfer", "UI (and prpl) ready on ft %p, so proceeding\n", xfer); + purple_debug_misc("xfer", "UI (and protocol) ready on ft %p, so proceeding\n", xfer); type = purple_xfer_get_xfer_type(xfer); if (type == PURPLE_XFER_TYPE_SEND) @@ -1572,21 +1572,21 @@ } void -purple_xfer_prpl_ready(PurpleXfer *xfer) +purple_xfer_protocol_ready(PurpleXfer *xfer) { PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); - priv->ready |= PURPLE_XFER_READY_PRPL; + priv->ready |= PURPLE_XFER_READY_PROTOCOL; /* I don't think fwrite/fread are ever *not* ready */ if (priv->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI)) { - purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer); + purple_debug_misc("xfer", "Protocol is ready on ft %p, waiting for UI\n", xfer); return; } - purple_debug_misc("xfer", "Prpl (and UI) ready on ft %p, so proceeding\n", xfer); + purple_debug_misc("xfer", "Protocol (and UI) ready on ft %p, so proceeding\n", xfer); priv->ready = PURPLE_XFER_READY_NONE; @@ -2322,16 +2322,31 @@ PurpleXfer * purple_xfer_new(PurpleAccount *account, PurpleXferType type, const char *who) { + PurpleXfer *xfer; + PurpleProtocol *protocol; + g_return_val_if_fail(type != PURPLE_XFER_TYPE_UNKNOWN, NULL); g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); g_return_val_if_fail(who != NULL, NULL); - return g_object_new(PURPLE_TYPE_XFER, - "type", type, - "account", account, - "remote-user", who, - NULL - ); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + + g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL); + + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, xfer_new)) + xfer = purple_protocol_factory_iface_xfer_new(protocol, account, type, + who); + else + xfer = g_object_new(PURPLE_TYPE_XFER, + "account", account, + "type", type, + "remote-user", who, + NULL + ); + + g_return_val_if_fail(xfer != NULL, NULL); + + return xfer; } /**************************************************************************
--- a/libpurple/xfer.h Fri Jan 31 17:56:27 2014 +0530 +++ b/libpurple/xfer.h Fri Jan 31 18:02:20 2014 +0530 @@ -88,7 +88,7 @@ void (*cancel_remote)(PurpleXfer *xfer); /** - * UI op to write data received from the prpl. The UI must deal with the + * UI op to write data received from the protocol. The UI must deal with the * entire buffer and return size, or it is treated as an error. * * @param xfer The file transfer structure @@ -101,7 +101,7 @@ gssize (*ui_write)(PurpleXfer *xfer, const guchar *buffer, gssize size); /** - * UI op to read data to send to the prpl for a file transfer. + * UI op to read data to send to the protocol for a file transfer. * * @param xfer The file transfer structure * @param buffer A pointer to a buffer. The UI must allocate this buffer. @@ -173,7 +173,7 @@ /** * Creates a new file transfer handle. - * This is called by prpls. + * This is called by protocols. * * @param account The account sending or receiving the file. * @param type The type of file transfer. @@ -527,7 +527,7 @@ * Sets the function to be called if the request is denied. * * @param xfer The file transfer. - * @param fnc The request denied prpl callback. + * @param fnc The request denied protocol callback. */ void purple_xfer_set_request_denied_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *)); @@ -629,7 +629,7 @@ * @a ip and @a port are ignored. * * Passing @a fd as '-1' is a special-case and indicates to the - * protocol plugin to facilitate the file transfer itself. + * protocol to facilitate the file transfer itself. * * @param xfer The file transfer. * @param fd The file descriptor for the socket. @@ -709,13 +709,13 @@ void purple_xfer_ui_ready(PurpleXfer *xfer); /** - * Allows the prpl to signal it's ready to send/receive data (depending on - * the direction of the file transfer. Used when the prpl provides read/write + * Allows the protocol to signal it's ready to send/receive data (depending on + * the direction of the file transfer. Used when the protocol provides read/write * ops and cannot/does not provide a raw fd to the core. * * @param xfer The file transfer which is ready. */ -void purple_xfer_prpl_ready(PurpleXfer *xfer); +void purple_xfer_protocol_ready(PurpleXfer *xfer); /** * Gets the thumbnail data for a transfer
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/m4macros/introspection.m4 Fri Jan 31 18:02:20 2014 +0530 @@ -0,0 +1,96 @@ +dnl -*- mode: autoconf -*- +dnl Copyright 2009 Johan Dahlin +dnl +dnl This file is free software; the author(s) gives unlimited +dnl permission to copy and/or distribute it, with or without +dnl modifications, as long as this notice is preserved. +dnl + +# serial 1 + +m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL], +[ + AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([LT_INIT],[$0])dnl setup libtool first + + dnl enable/disable introspection + m4_if([$2], [require], + [dnl + enable_introspection=yes + ],[dnl + AC_ARG_ENABLE(introspection, + AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]], + [Enable introspection for this build]),, + [enable_introspection=auto]) + ])dnl + + AC_MSG_CHECKING([for gobject-introspection]) + + dnl presence/version checking + AS_CASE([$enable_introspection], + [no], [dnl + found_introspection="no (disabled, use --enable-introspection to enable)" + ],dnl + [yes],[dnl + PKG_CHECK_EXISTS([gobject-introspection-1.0],, + AC_MSG_ERROR([gobject-introspection-1.0 is not installed])) + PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], + found_introspection=yes, + AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME])) + ],dnl + [auto],[dnl + PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no) + dnl Canonicalize enable_introspection + enable_introspection=$found_introspection + ],dnl + [dnl + AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@]) + ])dnl + + AC_MSG_RESULT([$found_introspection]) + + INTROSPECTION_SCANNER= + INTROSPECTION_COMPILER= + INTROSPECTION_GENERATE= + INTROSPECTION_GIRDIR= + INTROSPECTION_TYPELIBDIR= + if test "x$found_introspection" = "xyes"; then + INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` + INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0` + INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0` + INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0` + INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)" + INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0` + INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0` + INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection + fi + AC_SUBST(INTROSPECTION_SCANNER) + AC_SUBST(INTROSPECTION_COMPILER) + AC_SUBST(INTROSPECTION_GENERATE) + AC_SUBST(INTROSPECTION_GIRDIR) + AC_SUBST(INTROSPECTION_TYPELIBDIR) + AC_SUBST(INTROSPECTION_CFLAGS) + AC_SUBST(INTROSPECTION_LIBS) + AC_SUBST(INTROSPECTION_MAKEFILE) + + AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes") +]) + + +dnl Usage: +dnl GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version]) + +AC_DEFUN([GOBJECT_INTROSPECTION_CHECK], +[ + _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1]) +]) + +dnl Usage: +dnl GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version]) + + +AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE], +[ + _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require]) +])
--- a/pidgin/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -172,6 +172,7 @@ libpidgin_la_LIBADD = \ @LIBOBJS@ \ $(GLIB_LIBS) \ + $(GPLUGIN_LIBS) \ $(GCR_LIBS) \ $(DBUS_LIBS) \ $(GSTREAMER_LIBS) \ @@ -185,6 +186,7 @@ $(WEBKIT_LIBS) \ $(GTK_LIBS) \ $(X11_LIBS) \ + $(INTROSPECTION_LIBS) \ $(top_builddir)/libpurple/libpurple.la pidgin_DEPENDENCIES = $(builddir)/libpidgin.la @@ -201,6 +203,7 @@ -I$(top_builddir) \ -I$(top_srcdir) \ $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(GCR_CFLAGS) \ $(GSTREAMER_CFLAGS) \ $(GSTVIDEO_CFLAGS) \ @@ -211,6 +214,54 @@ $(DBUS_CFLAGS) \ $(GTKSPELL_CFLAGS) \ $(LIBXML_CFLAGS) \ - $(WEBKIT_CFLAGS) + $(WEBKIT_CFLAGS) \ + $(INTROSPECTION_CFLAGS) + +-include $(INTROSPECTION_MAKEFILE) +INTROSPECTION_GIRS = +INTROSPECTION_SCANNER_ARGS = --add-include-path=$(builddir) --add-include-path=$(top_builddir)/libpurple +INTROSPECTION_COMPILER_ARGS = --includedir=$(top_builddir)/libpurple + +if HAVE_INTROSPECTION +introspection_sources = $(libpidgininclude_HEADERS) + +Pidgin-$(PURPLE_MAJOR_VERSION).$(PURPLE_MINOR_VERSION).gir: $(builddir)/libpidgin.la +Pidgin_3_0_gir_INCLUDES = GObject-2.0 Gtk-$(GTK_VERSION).0 Purple-$(PURPLE_MAJOR_VERSION).$(PURPLE_MINOR_VERSION) +Pidgin_3_0_gir_CFLAGS = \ + $(INCLUDES) \ + -DDATADIR=\"$(datadir)\" \ + -DLIBDIR=\"$(libdir)/pidgin/\" \ + -DLOCALEDIR=\"$(datadir)/locale\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -I$(top_builddir)/libpurple \ + -I$(top_srcdir)/libpurple/ \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + $(GLIB_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ + $(GCR_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(GSTVIDEO_CFLAGS) \ + $(GSTINTERFACES_CFLAGS) \ + $(GTK_CFLAGS) \ + $(X11_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(GTKSPELL_CFLAGS) \ + $(LIBXML_CFLAGS) \ + $(WEBKIT_CFLAGS) \ + $(INTROSPECTION_CFLAGS) + +Pidgin_3_0_gir_LIBS = $(builddir)/libpidgin.la +Pidgin_3_0_gir_FILES = $(introspection_sources) +INTROSPECTION_GIRS += Pidgin-$(PURPLE_MAJOR_VERSION).$(PURPLE_MINOR_VERSION).gir + +girdir = $(INTROSPECTION_GIRDIR) +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(INTROSPECTION_TYPELIBDIR) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES = $(gir_DATA) $(typelib_DATA) +endif + endif # ENABLE_GTK -
--- a/pidgin/gtkaccount.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkaccount.c Fri Jan 31 18:02:20 2014 +0530 @@ -32,9 +32,9 @@ #include "core.h" #include "debug.h" #include "notify.h" -#include "plugin.h" +#include "plugins.h" #include "prefs.h" -#include "prpl.h" +#include "protocol.h" #include "request.h" #include "savedstatuses.h" #include "signals.h" @@ -98,8 +98,7 @@ PurpleAccount *account; char *protocol_id; - PurplePlugin *plugin; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleProxyType new_proxy_type; @@ -185,6 +184,7 @@ set_dialog_icon(AccountPrefsDialog *dialog, gpointer data, size_t len, gchar *new_icon_path) { GdkPixbuf *pixbuf = NULL; + PurpleBuddyIconSpec *icon_spec = NULL; dialog->icon_img = purple_imgstore_unref(dialog->icon_img); if (data != NULL) @@ -199,14 +199,16 @@ pixbuf = pidgin_pixbuf_from_imgstore(dialog->icon_img); } - if (pixbuf && dialog->prpl_info && - (dialog->prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_DISPLAY)) + if (dialog->protocol) + icon_spec = purple_protocol_get_icon_spec(dialog->protocol); + + if (pixbuf && icon_spec && (icon_spec->scale_rules & PURPLE_ICON_SCALE_DISPLAY)) { /* Scale the icon to something reasonable */ int width, height; GdkPixbuf *scale; - pidgin_buddy_icon_get_scale_size(pixbuf, &dialog->prpl_info->icon_spec, + pidgin_buddy_icon_get_scale_size(pixbuf, icon_spec, PURPLE_ICON_SCALE_DISPLAY, &width, &height); scale = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR); @@ -231,19 +233,13 @@ set_account_protocol_cb(GtkWidget *widget, const char *id, AccountPrefsDialog *dialog) { - PurplePlugin *new_plugin; - - new_plugin = purple_find_prpl(id); - - dialog->plugin = new_plugin; - - if (dialog->plugin != NULL) - { - dialog->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin); - - g_free(dialog->protocol_id); - dialog->protocol_id = g_strdup(dialog->plugin->info->id); - } + PurpleProtocol *new_protocol; + + new_protocol = purple_protocols_find(id); + + dialog->protocol = new_protocol; + g_free(dialog->protocol_id); + dialog->protocol_id = dialog->protocol ? g_strdup(purple_protocol_get_id(dialog->protocol)) : NULL; if (dialog->account != NULL) purple_account_clear_settings(dialog->account); @@ -255,13 +251,13 @@ gtk_widget_grab_focus(dialog->protocol_menu); - if (!dialog->prpl_info || !dialog->prpl_info->register_user) { + if (!dialog->protocol || !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, SERVER_IFACE, register_user)) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( dialog->register_button), FALSE); gtk_widget_hide(dialog->register_button); } else { - if (dialog->prpl_info != NULL && - (dialog->prpl_info->options & OPT_PROTO_REGISTER_NOSCREENNAME)) { + if (dialog->protocol != NULL && + (purple_protocol_get_options(dialog->protocol) & OPT_PROTO_REGISTER_NOSCREENNAME)) { gtk_widget_set_sensitive(dialog->register_button, TRUE); } else { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( @@ -275,9 +271,9 @@ static void username_changed_cb(GtkEntry *entry, AccountPrefsDialog *dialog) { - gboolean opt_noscreenname = (dialog->prpl_info != NULL && - (dialog->prpl_info->options & OPT_PROTO_REGISTER_NOSCREENNAME)); - gboolean username_valid = purple_validate(dialog->plugin, + gboolean opt_noscreenname = (dialog->protocol != NULL && + (purple_protocol_get_options(dialog->protocol) & OPT_PROTO_REGISTER_NOSCREENNAME)); + gboolean username_valid = purple_validate(dialog->protocol, gtk_entry_get_text(entry)); if (dialog->ok_button) { @@ -306,12 +302,12 @@ GHashTable *table; const char *label; - if (!dialog->prpl_info || ! PURPLE_PROTOCOL_PLUGIN_HAS_FUNC( - dialog->prpl_info, get_account_text_table)) { + if (!dialog->protocol || ! PURPLE_PROTOCOL_IMPLEMENTS( + dialog->protocol, CLIENT_IFACE, get_account_text_table)) { return FALSE; } - table = dialog->prpl_info->get_account_text_table(NULL); + table = purple_protocol_client_iface_get_account_text_table(dialog->protocol, NULL); label = g_hash_table_lookup(table, "login_label"); if(!strcmp(gtk_entry_get_text(GTK_ENTRY(widget)), label)) { @@ -334,8 +330,8 @@ GHashTable *table = NULL; const char *label = NULL; - if(PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, get_account_text_table)) { - table = dialog->prpl_info->get_account_text_table(NULL); + if(PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, CLIENT_IFACE, get_account_text_table)) { + table = purple_protocol_client_iface_get_account_text_table(dialog->protocol, NULL); label = g_hash_table_lookup(table, "login_label"); if (*gtk_entry_get_text(GTK_ENTRY(widget)) == '\0') { @@ -374,7 +370,7 @@ #endif gint xsize; - table = dialog->prpl_info->get_account_text_table(NULL); + table = purple_protocol_client_iface_get_account_text_table(dialog->protocol, NULL); label = g_hash_table_lookup(table, "login_label"); text = gtk_entry_get_text(GTK_ENTRY(widget)); @@ -442,8 +438,8 @@ { int register_checked = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(dialog->register_button)); - int opt_noscreenname = (dialog->prpl_info != NULL && - (dialog->prpl_info->options & OPT_PROTO_REGISTER_NOSCREENNAME)); + int opt_noscreenname = (dialog->protocol != NULL && + (purple_protocol_get_options(dialog->protocol) & OPT_PROTO_REGISTER_NOSCREENNAME)); int register_noscreenname = (opt_noscreenname && register_checked); #if !GTK_CHECK_VERSION(3,2,0) @@ -480,7 +476,7 @@ if (filename != NULL) { size_t len = 0; - gpointer data = pidgin_convert_buddy_icon(dialog->plugin, filename, &len); + gpointer data = pidgin_convert_buddy_icon(dialog->protocol, filename, &len); set_dialog_icon(dialog, data, len, g_strdup(filename)); } @@ -527,7 +523,7 @@ if ((rtmp = strchr(tmp, '\r')) || (rtmp = strchr(tmp, '\n'))) *rtmp = '\0'; - data = pidgin_convert_buddy_icon(dialog->plugin, tmp, &len); + data = pidgin_convert_buddy_icon(dialog->protocol, tmp, &len); /* This takes ownership of tmp */ set_dialog_icon(dialog, data, len, tmp); } @@ -625,11 +621,11 @@ if (dialog->account != NULL) username = g_strdup(purple_account_get_username(dialog->account)); - if (!username && dialog->prpl_info - && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, get_account_text_table)) { + if (!username && dialog->protocol + && PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, CLIENT_IFACE, get_account_text_table)) { GHashTable *table; const char *label; - table = dialog->prpl_info->get_account_text_table(NULL); + table = purple_protocol_client_iface_get_account_text_table(dialog->protocol, NULL); label = g_hash_table_lookup(table, "login_label"); #if GTK_CHECK_VERSION(3,2,0) @@ -652,10 +648,10 @@ G_CALLBACK(username_changed_cb), dialog); /* Do the user split thang */ - if (dialog->prpl_info == NULL) + if (dialog->protocol == NULL) user_splits = NULL; else - user_splits = dialog->prpl_info->user_splits; + user_splits = purple_protocol_get_user_splits(dialog->protocol); if (dialog->user_split_entries != NULL) { g_list_free(dialog->user_split_entries); @@ -744,8 +740,8 @@ purple_account_get_remember_password(dialog->account)); } - if (dialog->prpl_info != NULL && - (dialog->prpl_info->options & OPT_PROTO_NO_PASSWORD)) { + if (dialog->protocol != NULL && + (purple_protocol_get_options(dialog->protocol) & OPT_PROTO_NO_PASSWORD)) { gtk_widget_hide(dialog->password_box); gtk_widget_hide(dialog->remember_pass_check); @@ -844,11 +840,12 @@ gtk_box_pack_start(GTK_BOX(hbox2), button, FALSE, FALSE, 0); gtk_widget_show(button); - if (dialog->prpl_info != NULL) { - if (!(dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK)) + if (dialog->protocol != NULL) { + PurpleBuddyIconSpec *icon_spec = purple_protocol_get_icon_spec(dialog->protocol); + if (!(purple_protocol_get_options(dialog->protocol) & OPT_PROTO_MAIL_CHECK)) gtk_widget_hide(dialog->new_mail_check); - if (dialog->prpl_info->icon_spec.format == NULL) { + if (!icon_spec || icon_spec->format == NULL) { gtk_widget_hide(dialog->icon_check); gtk_widget_hide(dialog->icon_hbox); } @@ -883,9 +880,9 @@ } #if 0 - if (!dialog->prpl_info || - (!(dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK) && - (dialog->prpl_info->icon_spec.format == NULL))) { + if (!dialog->protocol || + (!(purple_protocol_get_options(dialog->protocol) & OPT_PROTO_MAIL_CHECK) && + (purple_protocol_get_icon_spec(dialog->protocol).format == NULL))) { /* Nothing to see :( aww. */ gtk_widget_hide(dialog->user_frame); @@ -925,8 +922,8 @@ dialog->protocol_opt_entries = g_list_delete_link(dialog->protocol_opt_entries, dialog->protocol_opt_entries); } - if (dialog->prpl_info == NULL || - dialog->prpl_info->protocol_options == NULL) + if (dialog->protocol == NULL || + purple_protocol_get_protocol_options(dialog->protocol) == NULL) return; account = dialog->account; @@ -938,7 +935,7 @@ gtk_label_new_with_mnemonic(_("Ad_vanced")), 1); gtk_widget_show(vbox); - for (l = dialog->prpl_info->protocol_options; l != NULL; l = l->next) + for (l = purple_protocol_get_protocol_options(dialog->protocol); l != NULL; l = l->next) { option = (PurpleAccountOption *)l->data; @@ -1325,7 +1322,7 @@ add_voice_options(AccountPrefsDialog *dialog) { #ifdef USE_VV - if (!dialog->prpl_info || !PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, initiate_media)) { + if (!dialog->protocol || !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, MEDIA_IFACE, initiate_session)) { if (dialog->voice_frame) { gtk_widget_destroy(dialog->voice_frame); dialog->voice_frame = NULL; @@ -1423,13 +1420,14 @@ gboolean new_acct = FALSE, icon_change = FALSE; PurpleAccount *account; gboolean remember; + PurpleBuddyIconSpec *icon_spec = NULL; /* Build the username string. */ username = g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->username_entry))); - if (dialog->prpl_info != NULL) + if (dialog->protocol != NULL) { - for (l = dialog->prpl_info->user_splits, + for (l = purple_protocol_get_user_splits(dialog->protocol), l2 = dialog->user_split_entries; l != NULL && l2 != NULL; l = l->next, l2 = l2->next) @@ -1490,7 +1488,10 @@ purple_account_set_private_alias(account, NULL); /* Buddy Icon */ - if (dialog->prpl_info != NULL && dialog->prpl_info->icon_spec.format != NULL) + if (dialog->protocol != NULL) + icon_spec = purple_protocol_get_icon_spec(dialog->protocol); + + if (icon_spec && icon_spec->format != NULL) { const char *filename; @@ -1520,7 +1521,7 @@ else if ((filename = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon")) && icon_change) { size_t len = 0; - gpointer data = pidgin_convert_buddy_icon(dialog->plugin, filename, &len); + gpointer data = pidgin_convert_buddy_icon(dialog->protocol, filename, &len); purple_account_set_buddy_icon_path(account, filename); purple_buddy_icons_set_account_icon(account, data, len); } @@ -1536,7 +1537,7 @@ purple_account_set_remember_password(account, remember); /* Check Mail */ - if (dialog->prpl_info && dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK) + if (dialog->protocol && purple_protocol_get_options(dialog->protocol) & OPT_PROTO_MAIL_CHECK) purple_account_set_check_mail(account, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(dialog->new_mail_check))); @@ -1559,7 +1560,7 @@ g_free(username); /* Add the protocol settings */ - if (dialog->prpl_info) { + if (dialog->protocol) { ProtocolOptEntry *opt_entry; GtkTreeIter iter; char *value2; @@ -1728,10 +1729,12 @@ dialog->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); if (dialog->account == NULL) { - /* Select the first prpl in the list*/ - GList *prpl_list = purple_plugins_get_protocols(); - if (prpl_list != NULL) - dialog->protocol_id = g_strdup(((PurplePlugin *) prpl_list->data)->info->id); + /* Select the first protocol in the list*/ + GList *protocol_list = purple_protocols_get_all(); + if (protocol_list != NULL) { + dialog->protocol_id = g_strdup(purple_protocol_get_id(PURPLE_PROTOCOL(protocol_list->data))); + g_list_free(protocol_list); + } } else { @@ -1739,8 +1742,12 @@ g_strdup(purple_account_get_protocol_id(dialog->account)); } - if ((dialog->plugin = purple_find_prpl(dialog->protocol_id)) != NULL) - dialog->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin); + /* TODO if no protocols are loaded, this should inform the user that + protocols need to be loaded instead of just doing nothing */ + if (!dialog->protocol_id) + return; + + dialog->protocol = purple_protocols_find(dialog->protocol_id); dialog->window = win = pidgin_create_dialog((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"), PIDGIN_HIG_BOX_SPACE, "account", FALSE); @@ -1775,7 +1782,7 @@ if (dialog->account == NULL) gtk_widget_set_sensitive(button, FALSE); - if (!dialog->prpl_info || !dialog->prpl_info->register_user) + if (!dialog->protocol || !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, SERVER_IFACE, register_user)) gtk_widget_hide(button); /* Setup the page with 'Advanced' (protocol options). */ @@ -1850,7 +1857,7 @@ if (gtk_tree_model_iter_nth_child(model, &iter, NULL, index)) { - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_MEDIUM); if ((pixbuf != NULL) && purple_account_is_disconnected(account)) gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE); @@ -2238,17 +2245,18 @@ { GdkPixbuf *pixbuf, *buddyicon = NULL; PurpleStoredImage *img = NULL; - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info = NULL; - - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM); + PurpleProtocol *protocol = NULL; + PurpleBuddyIconSpec *icon_spec = NULL; + + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_MEDIUM); if ((pixbuf != NULL) && purple_account_is_disconnected(account)) gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE); - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info != NULL && prpl_info->icon_spec.format != NULL) { + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if (protocol != NULL) + icon_spec = purple_protocol_get_icon_spec(protocol); + + if (icon_spec != NULL && icon_spec->format != NULL) { if (purple_account_get_bool(account, "use-global-buddyicon", TRUE)) { if (global_buddyicon != NULL) buddyicon = g_object_ref(G_OBJECT(global_buddyicon)); @@ -2707,14 +2715,12 @@ authorize_reason_cb(struct auth_request *ar) { const char *protocol_id; - PurplePlugin *plugin; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; protocol_id = purple_account_get_protocol_id(ar->account); - if ((plugin = purple_find_prpl(protocol_id)) != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - - if (prpl_info && (prpl_info->options & OPT_PROTO_AUTHORIZATION_GRANTED_MESSAGE)) { + protocol = purple_protocols_find(protocol_id); + + if (protocol && (purple_protocol_get_options(protocol) & OPT_PROTO_AUTHORIZATION_GRANTED_MESSAGE)) { /* Duplicate information because ar is freed by closing minidialog */ struct auth_request *aa = g_new0(struct auth_request, 1); aa->auth_cb = ar->auth_cb; @@ -2752,14 +2758,12 @@ deny_reason_cb(struct auth_request *ar) { const char *protocol_id; - PurplePlugin *plugin; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; protocol_id = purple_account_get_protocol_id(ar->account); - if ((plugin = purple_find_prpl(protocol_id)) != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - - if (prpl_info && (prpl_info->options & OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE)) { + protocol = purple_protocols_find(protocol_id); + + if (protocol && (purple_protocol_get_options(protocol) & OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE)) { /* Duplicate information because ar is freed by closing minidialog */ struct auth_request *aa = g_new0(struct auth_request, 1); aa->auth_cb = ar->auth_cb; @@ -2815,7 +2819,7 @@ PurpleConnection *gc; GtkWidget *alert; PidginMiniDialog *dialog; - GdkPixbuf *prpl_icon; + GdkPixbuf *protocol_icon; struct auth_request *aa; const char *our_name; gboolean have_valid_alias; @@ -2854,7 +2858,7 @@ g_free(escaped_our_name); g_free(escaped_message); - prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + protocol_icon = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); aa = g_new0(struct auth_request, 1); aa->auth_cb = auth_cb; @@ -2866,7 +2870,7 @@ aa->add_buddy_after_auth = !on_list; alert = pidgin_make_mini_dialog_with_custom_icon( - gc, prpl_icon, + gc, protocol_icon, _("Authorize buddy?"), NULL, aa, _("Authorize"), authorize_reason_cb, _("Deny"), deny_reason_cb,
--- a/pidgin/gtkblist.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkblist.c Fri Jan 31 18:02:20 2014 +0530 @@ -32,9 +32,9 @@ #include "core.h" #include "debug.h" #include "notify.h" -#include "prpl.h" +#include "protocol.h" #include "prefs.h" -#include "plugin.h" +#include "plugins.h" #include "request.h" #include "signals.h" #include "pidginstock.h" @@ -322,7 +322,7 @@ #ifdef USE_VV static void gtk_blist_menu_audio_call_cb(GtkWidget *w, PurpleBuddy *b) { - purple_prpl_initiate_media(purple_buddy_get_account(b), + purple_protocol_initiate_media(purple_buddy_get_account(b), purple_buddy_get_name(b), PURPLE_MEDIA_AUDIO); } @@ -330,13 +330,13 @@ { /* if the buddy supports both audio and video, start a combined call, otherwise start a pure video session */ - if (purple_prpl_get_media_caps(purple_buddy_get_account(b), + if (purple_protocol_get_media_caps(purple_buddy_get_account(b), purple_buddy_get_name(b)) & PURPLE_MEDIA_CAPS_AUDIO_VIDEO) { - purple_prpl_initiate_media(purple_buddy_get_account(b), + purple_protocol_initiate_media(purple_buddy_get_account(b), purple_buddy_get_name(b), PURPLE_MEDIA_AUDIO | PURPLE_MEDIA_VIDEO); } else { - purple_prpl_initiate_media(purple_buddy_get_account(b), + purple_protocol_initiate_media(purple_buddy_get_account(b), purple_buddy_get_name(b), PURPLE_MEDIA_VIDEO); } } @@ -384,20 +384,18 @@ { PurpleAccount *account; PurpleConversation *conv; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; GHashTable *components; const char *name; - char *chat_name; + char *chat_name = NULL; account = purple_chat_get_account(chat); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account))); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); components = purple_chat_get_components(chat); - if (prpl_info && prpl_info->get_chat_name) - chat_name = prpl_info->get_chat_name(components); - else - chat_name = NULL; + if (protocol) + chat_name = purple_protocol_chat_iface_get_name(protocol, components); if (chat_name) name = chat_name; @@ -664,14 +662,14 @@ PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL); PurpleRequestField *field; GList *parts, *iter; - struct proto_chat_entry *pce; + PurpleProtocolChatEntry *pce; PurpleConnection *gc; PurpleChat *chat = (PurpleChat*)node; purple_request_fields_add_group(fields, group); gc = purple_account_get_connection(purple_chat_get_account(chat)); - parts = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->chat_info(gc); + parts = purple_protocol_chat_iface_info(purple_connection_get_protocol(gc), gc); for (iter = parts; iter; iter = iter->next) { pce = iter->data; @@ -747,12 +745,12 @@ account = purple_buddy_get_account(b); } else if (PURPLE_IS_CHAT(node)) { PurpleChat *c = PURPLE_CHAT(node); - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; type = PURPLE_LOG_CHAT; account = purple_chat_get_account(c); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account))); - if (prpl_info && prpl_info->get_chat_name) { - name = prpl_info->get_chat_name(purple_chat_get_components(c)); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if (protocol) { + name = purple_protocol_chat_iface_get_name(protocol, purple_chat_get_components(c)); } } else if (PURPLE_IS_CONTACT(node)) { pidgin_log_show_contact(PURPLE_CONTACT(node)); @@ -887,7 +885,7 @@ static void set_sensitive_if_input_chat_cb(GtkWidget *entry, gpointer user_data) { - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleConnection *gc; PidginChatData *data; GList *tmp; @@ -911,8 +909,8 @@ gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), GTK_RESPONSE_OK, sensitive); gc = purple_account_get_connection(data->rq_data.account); - prpl_info = (gc != NULL) ? PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)) : NULL; - sensitive = (prpl_info != NULL && prpl_info->roomlist_get_list != NULL); + protocol = (gc != NULL) ? purple_connection_get_protocol(gc) : NULL; + sensitive = (protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST_IFACE, get_list)); gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), 1, sensitive); } @@ -920,16 +918,16 @@ static void set_sensitive_if_input_buddy_cb(GtkWidget *entry, gpointer user_data) { - PurplePlugin *prpl; + PurpleProtocol *protocol; PidginAddBuddyData *data = user_data; const char *text; - prpl = purple_find_prpl(purple_account_get_protocol_id( + protocol = purple_protocols_find(purple_account_get_protocol_id( data->rq_data.account)); text = gtk_entry_get_text(GTK_ENTRY(entry)); gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), - GTK_RESPONSE_OK, purple_validate(prpl, text)); + GTK_RESPONSE_OK, purple_validate(protocol, text)); } static void @@ -945,14 +943,14 @@ chat_account_filter_func(PurpleAccount *account) { PurpleConnection *gc = purple_account_get_connection(account); - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; if (gc == NULL) return FALSE; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - return (prpl_info->chat_info != NULL); + protocol = purple_connection_get_protocol(gc); + + return (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, info)); } gboolean @@ -1041,27 +1039,24 @@ rebuild_chat_entries(PidginChatData *data, const char *default_chat_name) { PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; GList *list = NULL, *tmp; GHashTable *defaults = NULL; - struct proto_chat_entry *pce; + PurpleProtocolChatEntry *pce; gboolean focus = TRUE; g_return_if_fail(data->rq_data.account != NULL); gc = purple_account_get_connection(data->rq_data.account); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + protocol = purple_connection_get_protocol(gc); gtk_container_foreach(GTK_CONTAINER(data->rq_data.vbox), (GtkCallback)gtk_widget_destroy, NULL); g_list_free(data->entries); data->entries = NULL; - if (prpl_info->chat_info != NULL) - list = prpl_info->chat_info(gc); - - if (prpl_info->chat_info_defaults != NULL) - defaults = prpl_info->chat_info_defaults(gc, default_chat_name); + list = purple_protocol_chat_iface_info(protocol, gc); + defaults = purple_protocol_chat_iface_info_defaults(protocol, gc, default_chat_name); for (tmp = list; tmp; tmp = tmp->next) { @@ -1425,12 +1420,12 @@ PurpleBlistNode *node) { GList *l, *ll; - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - if(!prpl_info || !prpl_info->blist_node_menu) + PurpleProtocol *protocol = purple_connection_get_protocol(gc); + + if(!protocol || !PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, blist_node_menu)) return; - for(l = ll = prpl_info->blist_node_menu(node); l; l = l->next) { + for(l = ll = purple_protocol_client_iface_blist_node_menu(protocol, node); l; l = l->next) { PurpleMenuAction *act = (PurpleMenuAction *) l->data; pidgin_append_menu_action(menu, act, node); } @@ -1481,7 +1476,7 @@ pidgin_blist_make_buddy_menu(GtkWidget *menu, PurpleBuddy *buddy, gboolean sub) { PurpleAccount *account = NULL; PurpleConnection *pc = NULL; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleContact *contact; PurpleBlistNode *node; gboolean contact_expanded = FALSE; @@ -1491,7 +1486,7 @@ account = purple_buddy_get_account(buddy); pc = purple_account_get_connection(account); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(pc)); + protocol = purple_connection_get_protocol(pc); node = PURPLE_BLIST_NODE(buddy); @@ -1501,7 +1496,7 @@ contact_expanded = node->contact_expanded; } - if (prpl_info && prpl_info->get_info) { + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info)) { pidgin_new_item_from_stock(menu, _("Get _Info"), PIDGIN_STOCK_TOOLBAR_USER_INFO, G_CALLBACK(gtk_blist_menu_info_cb), buddy, 0, 0, NULL); } @@ -1509,10 +1504,10 @@ G_CALLBACK(gtk_blist_menu_im_cb), buddy, 0, 0, NULL); #ifdef USE_VV - if (prpl_info && prpl_info->get_media_caps) { + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, MEDIA_IFACE, get_caps)) { PurpleAccount *account = purple_buddy_get_account(buddy); const gchar *who = purple_buddy_get_name(buddy); - PurpleMediaCaps caps = purple_prpl_get_media_caps(account, who); + PurpleMediaCaps caps = purple_protocol_get_media_caps(account, who); if (caps & PURPLE_MEDIA_CAPS_AUDIO) { pidgin_new_item_from_stock(menu, _("_Audio Call"), PIDGIN_STOCK_TOOLBAR_AUDIO_CALL, @@ -1531,9 +1526,10 @@ #endif - if (prpl_info && prpl_info->send_file) { - if (!prpl_info->can_receive_file || - prpl_info->can_receive_file(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy))) + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, send)) { + if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, can_receive) || + purple_protocol_xfer_iface_can_receive(protocol, + purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy))) { pidgin_new_item_from_stock(menu, _("_Send File..."), PIDGIN_STOCK_TOOLBAR_SEND_FILE, @@ -1858,7 +1854,7 @@ continue; menuitem = gtk_image_menu_item_new_with_label(purple_buddy_get_name(buddy)); - buf = pidgin_create_prpl_icon(purple_buddy_get_account(buddy), PIDGIN_PRPL_ICON_SMALL); + buf = pidgin_create_protocol_icon(purple_buddy_get_account(buddy), PIDGIN_PROTOCOL_ICON_SMALL); image = gtk_image_new_from_pixbuf(buf); g_object_unref(G_OBJECT(buf)); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), @@ -1940,8 +1936,7 @@ PurpleBlistNode *node; GtkTreeIter iter; GtkTreeSelection *sel; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; struct _pidgin_blist_node *gtknode; gboolean handled = FALSE; @@ -1974,11 +1969,9 @@ else b = (PurpleBuddy *)node; - prpl = purple_find_prpl(purple_account_get_protocol_id(purple_buddy_get_account(b))); - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl && prpl_info->get_info) + protocol = purple_protocols_find(purple_account_get_protocol_id(purple_buddy_get_account(b))); + + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info)) pidgin_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(b)), purple_buddy_get_name(b)); handled = TRUE; } @@ -2103,7 +2096,7 @@ } static void -add_buddies_from_vcard(const char *prpl_id, PurpleGroup *group, GList *list, +add_buddies_from_vcard(const char *protocol_id, PurpleGroup *group, GList *list, const char *alias) { GList *l; @@ -2118,7 +2111,7 @@ gc = (PurpleConnection *)l->data; account = purple_connection_get_account(gc); - if (!strcmp(purple_account_get_protocol_id(account), prpl_id)) + if (!strcmp(purple_account_get_protocol_id(account), protocol_id)) break; account = NULL; @@ -2317,8 +2310,8 @@ } protocol = - PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->list_icon(purple_buddy_get_account(buddy), - buddy); + purple_protocol_class_list_icon(purple_connection_get_protocol(gc), + purple_buddy_get_account(buddy), buddy); str = g_string_new(NULL); g_string_printf(str, @@ -2671,7 +2664,8 @@ PurpleAccount *account = NULL; PurpleContact *contact = NULL; PurpleStoredImage *custom_img; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; + PurpleBuddyIconSpec *icon_spec = NULL; gint orig_width, orig_height, scale_width, scale_height; if (PURPLE_IS_CONTACT(node)) { @@ -2694,7 +2688,7 @@ } if(account && purple_account_get_connection(account)) { - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(purple_account_get_connection(account))); + protocol = purple_connection_get_protocol(purple_account_get_connection(account)); } #if 0 @@ -2770,8 +2764,11 @@ scale_width = orig_width = gdk_pixbuf_get_width(buf); scale_height = orig_height = gdk_pixbuf_get_height(buf); - if (prpl_info && prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_DISPLAY) - purple_buddy_icon_get_scale_size(&prpl_info->icon_spec, &scale_width, &scale_height); + if (protocol) + icon_spec = purple_protocol_get_icon_spec(protocol); + + if (icon_spec && icon_spec->scale_rules & PURPLE_ICON_SCALE_DISPLAY) + purple_buddy_icon_spec_get_scaled_size(purple_protocol_get_icon_spec(protocol), &scale_width, &scale_height); if (scaled || scale_height > 200 || scale_width > 200) { GdkPixbuf *tmpbuf; @@ -2836,14 +2833,14 @@ #define TOOLTIP_BORDER 12 #define SMALL_SPACE 6 #define LARGE_SPACE 12 -#define PRPL_SIZE 16 +#define PROTOCOL_SIZE 16 struct tooltip_data { PangoLayout *layout; PangoLayout *name_layout; - GdkPixbuf *prpl_icon; + GdkPixbuf *protocol_icon; GdkPixbuf *status_icon; GdkPixbuf *avatar; - gboolean avatar_is_prpl_icon; + gboolean avatar_is_protocol_icon; int avatar_width; int avatar_height; int name_height; @@ -2874,8 +2871,8 @@ static struct tooltip_data * create_tip_for_account(PurpleAccount *account) { struct tooltip_data *td = g_new0(struct tooltip_data, 1); - td->status_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); - /* Yes, status_icon, not prpl_icon */ + td->status_icon = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); + /* Yes, status_icon, not protocol_icon */ if (purple_account_is_disconnected(account)) gdk_pixbuf_saturate_and_pixelate(td->status_icon, td->status_icon, 0.0, FALSE); td->layout = create_pango_layout(purple_account_get_username(account), &td->width, &td->height); @@ -2899,7 +2896,7 @@ td->status_icon = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_LARGE); td->avatar = pidgin_blist_get_buddy_icon(node, !full, FALSE); if (account != NULL) { - td->prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + td->protocol_icon = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); } tooltip_text = pidgin_get_tooltip_text(node, full); if (tooltip_text && *tooltip_text) { @@ -2923,12 +2920,12 @@ g_free(tmp); td->name_layout = create_pango_layout(node_name, &td->name_width, &td->name_height); - td->name_width += SMALL_SPACE + PRPL_SIZE; - td->name_height = MAX(td->name_height, PRPL_SIZE + SMALL_SPACE); -#if 0 /* PRPL Icon as avatar */ + td->name_width += SMALL_SPACE + PROTOCOL_SIZE; + td->name_height = MAX(td->name_height, PROTOCOL_SIZE + SMALL_SPACE); +#if 0 /* Protocol Icon as avatar */ if(!td->avatar && full) { - td->avatar = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_LARGE); - td->avatar_is_prpl_icon = TRUE; + td->avatar = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_LARGE); + td->avatar_is_protocol_icon = TRUE; } #endif @@ -2954,7 +2951,7 @@ int max_text_width; int max_avatar_width; GList *l; - int prpl_col = 0; + int protocol_col = 0; GtkTextDirection dir = gtk_widget_get_direction(widget); int status_size = 0; @@ -2984,9 +2981,9 @@ max_width = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; if (dir == GTK_TEXT_DIR_RTL) - prpl_col = TOOLTIP_BORDER + max_avatar_width + SMALL_SPACE; + protocol_col = TOOLTIP_BORDER + max_avatar_width + SMALL_SPACE; else - prpl_col = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width - PRPL_SIZE; + protocol_col = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width - PROTOCOL_SIZE; current_height = 12; for(l = gtkblist->tooltipdata; l; l = l->next) @@ -3053,10 +3050,10 @@ } } - if (!td->avatar_is_prpl_icon && td->prpl_icon) { - gdk_cairo_set_source_pixbuf(cr, td->prpl_icon, prpl_col, + if (!td->avatar_is_protocol_icon && td->protocol_icon) { + gdk_cairo_set_source_pixbuf(cr, td->protocol_icon, protocol_col, current_height + - (td->name_height - PRPL_SIZE) / 2); + (td->name_height - PROTOCOL_SIZE) / 2); cairo_paint(cr); } @@ -3130,8 +3127,8 @@ g_object_unref(td->avatar); if(td->status_icon) g_object_unref(td->status_icon); - if(td->prpl_icon) - g_object_unref(td->prpl_icon); + if(td->protocol_icon) + g_object_unref(td->protocol_icon); if (td->layout) g_object_unref(td->layout); if (td->name_layout) @@ -3520,14 +3517,10 @@ PurpleConnection *gc = purple_account_get_connection(account); if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS) { - PurplePluginProtocolInfo *prpl_info = - PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + PurpleProtocol *protocol = purple_connection_get_protocol(gc); PurpleMood *mood = NULL; - /* PURPLE_CONNECTION_FLAG_SUPPORT_MOODS would not be set if the prpl doesn't - * have get_moods, so using PURPLE_PROTOCOL_PLUGIN_HAS_FUNC isn't necessary - * here */ - for (mood = prpl_info->get_moods(account) ; + for (mood = purple_protocol_client_iface_get_moods(protocol, account) ; mood->mood != NULL ; mood++) { int mood_count = GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood)); @@ -3606,7 +3599,7 @@ PurpleRequestFieldGroup *g; PurpleRequestField *f; PurpleConnection *gc = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleMood *mood; PurpleMood *global_moods = get_global_moods(); @@ -3614,8 +3607,8 @@ PurplePresence *presence = purple_account_get_presence(account); PurpleStatus *status = purple_presence_get_status(presence, "mood"); gc = purple_account_get_connection(account); - g_return_if_fail(purple_connection_get_prpl(gc) != NULL); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + g_return_if_fail(purple_connection_get_protocol(gc) != NULL); + protocol = purple_connection_get_protocol(gc); current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); } else { current_mood = get_global_mood_status(); @@ -3631,8 +3624,8 @@ /* TODO: rlaager wants this sorted. */ /* TODO: darkrain wants it sorted post-translation */ - if (account && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods)) - mood = prpl_info->get_moods(account); + if (account && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_moods)) + mood = purple_protocol_client_iface_get_moods(protocol, account); else mood = global_moods; for ( ; mood->mood != NULL ; mood++) { @@ -3802,23 +3795,21 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full) { GString *str = g_string_new(""); - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; char *tmp; if (PURPLE_IS_CHAT(node)) { PurpleChat *chat; GList *connections; - GList *cur; - struct proto_chat_entry *pce; + GList *cur = NULL; + PurpleProtocolChatEntry *pce; char *name, *value; PurpleChatConversation *conv; PidginBlistNode *bnode = purple_blist_node_get_ui_data(node); chat = (PurpleChat *)node; - prpl = purple_find_prpl(purple_account_get_protocol_id(purple_chat_get_account(chat))); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + protocol = purple_protocols_find(purple_account_get_protocol_id(purple_chat_get_account(chat))); connections = purple_connections_get_all(); if (connections && connections->next) @@ -3832,8 +3823,8 @@ conv = PURPLE_CHAT_CONVERSATION(bnode->conv.conv); } else { char *chat_name; - if (prpl_info && prpl_info->get_chat_name) - chat_name = prpl_info->get_chat_name(purple_chat_get_components(chat)); + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, get_name)) + chat_name = purple_protocol_chat_iface_get_name(protocol, purple_chat_get_components(chat)); else chat_name = g_strdup(purple_chat_get_name(chat)); @@ -3846,7 +3837,7 @@ g_string_append_printf(str, _("\n<b>Occupants:</b> %d"), g_list_length(purple_chat_conversation_get_users(conv))); - if (prpl_info && (prpl_info->options & OPT_PROTO_CHAT_TOPIC)) { + if (protocol && (purple_protocol_get_options(protocol) & OPT_PROTO_CHAT_TOPIC)) { const char *chattopic = purple_chat_conversation_get_topic(conv); char *topic = chattopic ? g_markup_escape_text(chattopic, -1) : NULL; g_string_append_printf(str, _("\n<b>Topic:</b> %s"), topic ? topic : _("(no topic set)")); @@ -3854,10 +3845,8 @@ } } - if (prpl_info && prpl_info->chat_info != NULL) - cur = prpl_info->chat_info(purple_account_get_connection(purple_chat_get_account(chat))); - else - cur = NULL; + if (protocol) + cur = purple_protocol_chat_iface_info(protocol, purple_account_get_connection(purple_chat_get_account(chat))); while (cur != NULL) { @@ -3906,8 +3895,7 @@ c = purple_buddy_get_contact(b); } - prpl = purple_find_prpl(purple_account_get_protocol_id(purple_buddy_get_account(b))); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + protocol = purple_protocols_find(purple_account_get_protocol_id(purple_buddy_get_account(b))); presence = purple_buddy_get_presence(b); user_info = purple_notify_user_info_new(); @@ -4016,10 +4004,10 @@ } if (purple_account_is_connected(purple_buddy_get_account(b)) && - prpl_info && prpl_info->tooltip_text) + protocol) { - /* Additional text from the PRPL */ - prpl_info->tooltip_text(b, user_info, full); + /* Additional text from the protocol */ + purple_protocol_client_iface_tooltip_text(protocol, b, user_info, full); } /* These are Easter Eggs. Patches to remove them will be rejected. */ @@ -4106,8 +4094,7 @@ { PurpleBuddy *buddy = NULL; struct _pidgin_blist_node *gtknode = purple_blist_node_get_ui_data(node); - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; const char *name = NULL; char *filename, *path; PurplePresence *p = NULL; @@ -4131,7 +4118,7 @@ if (((struct _pidgin_blist_node*)purple_blist_node_get_ui_data(node->parent))->contact_expanded) { if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons")) return NULL; - return pidgin_create_prpl_icon(purple_buddy_get_account((PurpleBuddy*)node), PIDGIN_PRPL_ICON_SMALL); + return pidgin_create_protocol_icon(purple_buddy_get_account((PurpleBuddy*)node), PIDGIN_PROTOCOL_ICON_SMALL); } } else { return NULL; @@ -4175,13 +4162,11 @@ return _pidgin_blist_get_cached_emblem(path); } - prpl = purple_find_prpl(purple_account_get_protocol_id(purple_buddy_get_account(buddy))); - if (!prpl) + protocol = purple_protocols_find(purple_account_get_protocol_id(purple_buddy_get_account(buddy))); + if (!protocol) return NULL; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info && prpl_info->list_emblem) - name = prpl_info->list_emblem(buddy); + name = purple_protocol_client_iface_list_emblem(protocol, buddy); if (name == NULL) { PurpleStatus *status; @@ -4236,15 +4221,15 @@ if(buddy || chat) { PurpleAccount *account; - PurplePlugin *prpl; + PurpleProtocol *protocol; if(buddy) account = purple_buddy_get_account(buddy); else account = purple_chat_get_account(chat); - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - if(!prpl) + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if(!protocol) return NULL; } @@ -4327,8 +4312,7 @@ { const char *name, *name_color, *name_font, *status_color, *status_font; char *text = NULL; - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleContact *contact; PurplePresence *presence; struct _pidgin_blist_node *gtkcontactnode = NULL; @@ -4377,13 +4361,11 @@ if (!aliased || biglist) { /* Status Info */ - prpl = purple_find_prpl(purple_account_get_protocol_id(purple_buddy_get_account(b))); - - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && prpl_info->status_text && purple_account_get_connection(purple_buddy_get_account(b))) { - char *tmp = prpl_info->status_text(b); + protocol = purple_protocols_find(purple_account_get_protocol_id(purple_buddy_get_account(b))); + + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, status_text) && + purple_account_get_connection(purple_buddy_get_account(b))) { + char *tmp = purple_protocol_client_iface_status_text(protocol, b); const char *end; if(tmp && !g_utf8_validate(tmp, -1, &end)) { @@ -5181,13 +5163,13 @@ } static void -pack_prpl_icon_start(GtkWidget *box, +pack_protocol_icon_start(GtkWidget *box, PurpleAccount *account) { GdkPixbuf *pixbuf; GtkWidget *image; - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); if (pixbuf != NULL) { image = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(pixbuf); @@ -5447,7 +5429,7 @@ hbox = gtk_hbox_new(FALSE, 6); g_object_set_data(G_OBJECT(hbox), OBJECT_DATA_KEY_ACCOUNT, account); - pack_prpl_icon_start(hbox, account); + pack_protocol_icon_start(hbox, account); label = gtk_label_new(NULL); markup = g_strdup_printf("<span size=\"smaller\">%s</span>", username); @@ -6687,7 +6669,7 @@ static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *node) { PurplePresence *presence = purple_buddy_get_presence(buddy); - GdkPixbuf *status, *avatar, *emblem, *prpl_icon; + GdkPixbuf *status, *avatar, *emblem, *protocol_icon; GdkColor *color = NULL; char *mark; char *idle = NULL; @@ -6756,7 +6738,7 @@ } } - prpl_icon = pidgin_create_prpl_icon(purple_buddy_get_account(buddy), PIDGIN_PRPL_ICON_SMALL); + protocol_icon = pidgin_create_protocol_icon(purple_buddy_get_account(buddy), PIDGIN_PROTOCOL_ICON_SMALL); if (theme != NULL) color = pidgin_blist_theme_get_contact_color(theme); @@ -6771,7 +6753,7 @@ BUDDY_ICON_VISIBLE_COLUMN, biglist, EMBLEM_COLUMN, emblem, EMBLEM_VISIBLE_COLUMN, (emblem != NULL), - PROTOCOL_ICON_COLUMN, prpl_icon, + PROTOCOL_ICON_COLUMN, protocol_icon, PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"), BGCOLOR_COLUMN, color, CONTACT_EXPANDER_COLUMN, NULL, @@ -6787,8 +6769,8 @@ g_object_unref(status); if(avatar) g_object_unref(avatar); - if(prpl_icon) - g_object_unref(prpl_icon); + if(protocol_icon) + g_object_unref(protocol_icon); } /* This is a variation on the original gtk_blist_update_contact. Here we @@ -6937,7 +6919,7 @@ if(purple_account_is_connected(purple_chat_get_account(chat))) { GtkTreeIter iter; - GdkPixbuf *status, *avatar, *emblem, *prpl_icon; + GdkPixbuf *status, *avatar, *emblem, *protocol_icon; const gchar *color, *font; gchar *mark, *tmp; gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); @@ -6999,7 +6981,7 @@ g_free(mark); mark = tmp; - prpl_icon = pidgin_create_prpl_icon(purple_chat_get_account(chat), PIDGIN_PRPL_ICON_SMALL); + protocol_icon = pidgin_create_protocol_icon(purple_chat_get_account(chat), PIDGIN_PROTOCOL_ICON_SMALL); if (theme != NULL) bgcolor = pidgin_blist_theme_get_contact_color(theme); @@ -7011,7 +6993,7 @@ BUDDY_ICON_VISIBLE_COLUMN, showicons, EMBLEM_COLUMN, emblem, EMBLEM_VISIBLE_COLUMN, emblem != NULL, - PROTOCOL_ICON_COLUMN, prpl_icon, + PROTOCOL_ICON_COLUMN, protocol_icon, PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"), NAME_COLUMN, mark, BGCOLOR_COLUMN, bgcolor, @@ -7025,8 +7007,8 @@ g_object_unref(status); if(avatar) g_object_unref(avatar); - if(prpl_icon) - g_object_unref(prpl_icon); + if(protocol_icon) + g_object_unref(protocol_icon); } else { pidgin_blist_hide_node(list, node, TRUE); @@ -7164,8 +7146,7 @@ PidginAddBuddyData *data) { PurpleConnection *pc = NULL; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; gboolean invite_enabled = TRUE; /* Save our account */ @@ -7174,10 +7155,8 @@ if (account) pc = purple_account_get_connection(account); if (pc) - prpl = purple_connection_get_prpl(pc); - if (prpl) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info && !(prpl_info->options & OPT_PROTO_INVITE_MESSAGE)) + protocol = purple_connection_get_protocol(pc); + if (protocol && !(purple_protocol_get_options(protocol) & OPT_PROTO_INVITE_MESSAGE)) invite_enabled = FALSE; gtk_widget_set_sensitive(data->entry_for_invite, invite_enabled); @@ -7427,13 +7406,13 @@ GList *l; PurpleConnection *gc; GtkBox *vbox; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; if (account != NULL) { gc = purple_account_get_connection(account); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - if (prpl_info->join_chat == NULL) { + protocol = purple_connection_get_protocol(gc); + + if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, join)) { purple_notify_error(gc, NULL, _("This protocol does not" " support chat rooms."), NULL, purple_request_cpar_from_account(account)); @@ -7443,9 +7422,9 @@ /* Find an account with chat capabilities */ for (l = purple_connections_get_all(); l != NULL; l = l->next) { gc = (PurpleConnection *)l->data; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - if (prpl_info->join_chat != NULL) { + protocol = purple_connection_get_protocol(gc); + + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, join)) { account = purple_connection_get_account(gc); break; } @@ -8123,21 +8102,24 @@ static void build_plugin_actions(GtkActionGroup *action_group, GString *ui, char *parent, - PurplePlugin *plugin, gpointer context) + PurplePlugin *plugin) { GtkAction *menuaction; + PurplePluginActionsCb actions_cb; PurplePluginAction *action = NULL; GList *actions, *l; char *name; int count = 0; - actions = PURPLE_PLUGIN_ACTIONS(plugin, context); + actions_cb = + purple_plugin_info_get_actions_cb(purple_plugin_get_info(plugin)); + + actions = actions_cb(plugin); for (l = actions; l != NULL; l = l->next) { if (l->data) { action = (PurplePluginAction *)l->data; action->plugin = plugin; - action->context = context; name = g_strdup_printf("%s-action-%d", parent, count++); menuaction = gtk_action_new(name, action->label, NULL, NULL); @@ -8185,6 +8167,13 @@ purple_account_set_enabled(account, PIDGIN_UI, FALSE); } +static void +protocol_act(GtkWidget *obj, PurpleProtocolAction *pam) +{ + if (pam && pam->callback) + pam->callback(pam); +} + void pidgin_blist_update_accounts_menu(void) { @@ -8233,7 +8222,7 @@ menuitem = gtk_image_menu_item_new_with_label(buf); g_free(buf); - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); if (pixbuf != NULL) { if (!purple_account_is_connected(account)) gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE); @@ -8266,8 +8255,7 @@ PurpleConnection *gc = NULL; PurpleAccount *account = NULL; GdkPixbuf *pixbuf = NULL; - PurplePlugin *plugin = NULL; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; account = accounts->data; @@ -8280,7 +8268,7 @@ accel_path_buf = g_strconcat("<Actions>/AccountActions/", buf, NULL); g_free(buf); - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); if (pixbuf != NULL) { if (!purple_account_is_connected(account)) gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, @@ -8307,13 +8295,13 @@ pidgin_separator(submenu); gc = purple_account_get_connection(account); - plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? purple_connection_get_prpl(gc) : NULL; - prpl_info = plugin ? PURPLE_PLUGIN_PROTOCOL_INFO(plugin) : NULL; - - if (prpl_info && - (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) || - PURPLE_PLUGIN_HAS_ACTIONS(plugin))) { - if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) && + protocol = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? + purple_connection_get_protocol(gc) : NULL; + + if (protocol && + (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_moods) || + PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_actions))) { + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_moods) && (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) { if (purple_account_get_status(account, "mood")) { @@ -8324,29 +8312,28 @@ } } - if (PURPLE_PLUGIN_HAS_ACTIONS(plugin)) { + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_actions)) { GtkWidget *menuitem; - PurplePluginAction *action = NULL; + PurpleProtocolAction *action = NULL; GList *actions, *l; - actions = PURPLE_PLUGIN_ACTIONS(plugin, gc); + actions = purple_protocol_client_iface_get_actions(protocol, gc); for (l = actions; l != NULL; l = l->next) { if (l->data) { - action = (PurplePluginAction *) l->data; - action->plugin = plugin; - action->context = gc; + action = (PurpleProtocolAction *) l->data; + action->connection = gc; menuitem = gtk_menu_item_new_with_label(action->label); gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); g_signal_connect(G_OBJECT(menuitem), "activate", - G_CALLBACK(plugin_act), action); - g_object_set_data_full(G_OBJECT(menuitem), "plugin_action", + G_CALLBACK(protocol_act), action); + g_object_set_data_full(G_OBJECT(menuitem), "protocol_action", action, - (GDestroyNotify)purple_plugin_action_free); + (GDestroyNotify)purple_protocol_action_free); gtk_widget_show(menuitem); } else @@ -8379,6 +8366,7 @@ pidgin_blist_update_plugin_actions(void) { PurplePlugin *plugin = NULL; + PurplePluginInfo *info; GList *l; GtkAction *action; @@ -8407,19 +8395,17 @@ char *name; plugin = (PurplePlugin *)l->data; - - if (PURPLE_IS_PROTOCOL_PLUGIN(plugin)) - continue; - - if (!PURPLE_PLUGIN_HAS_ACTIONS(plugin)) + info = purple_plugin_get_info(plugin); + + if (!purple_plugin_info_get_actions_cb(info)) continue; name = g_strdup_printf("plugin%d", count); - action = gtk_action_new(name, plugin->info->name, NULL, NULL); + action = gtk_action_new(name, purple_plugin_info_get_name(info), NULL, NULL); gtk_action_group_add_action(plugins_action_group, action); g_string_append_printf(plugins_ui, "<menu action='%s'>", name); - build_plugin_actions(plugins_action_group, plugins_ui, name, plugin, NULL); + build_plugin_actions(plugins_action_group, plugins_ui, name, plugin); g_string_append(plugins_ui, "</menu>"); count++;
--- a/pidgin/gtkconv-theme-loader.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkconv-theme-loader.c Fri Jan 31 18:02:20 2014 +0530 @@ -26,6 +26,7 @@ #include "xmlnode.h" #include "debug.h" +#include "prefs.h" /***************************************************************************** * Conversation Theme Builder
--- a/pidgin/gtkconv.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkconv.c Fri Jan 31 18:02:20 2014 +0530 @@ -44,7 +44,8 @@ #include "imgstore.h" #include "log.h" #include "notify.h" -#include "prpl.h" +#include "plugins.h" +#include "protocol.h" #include "request.h" #include "theme-loader.h" #include "theme-manager.h" @@ -189,7 +190,7 @@ static GList *busy_list = NULL; static GList *xa_list = NULL; static GList *offline_list = NULL; -static GHashTable *prpl_lists = NULL; +static GHashTable *protocol_lists = NULL; static GHashTable *e2ee_stock = NULL; static PurpleTheme *default_conv_theme = NULL; @@ -378,15 +379,17 @@ tmp = g_strdup_printf("Using Pidgin v%s with libpurple v%s.", DISPLAY_VERSION, purple_core_get_version()); } else if (!g_ascii_strcasecmp(args[0], "plugins")) { - /* Show all the loaded plugins, including the protocol plugins and plugin loaders. - * This is intentional, since third party prpls are often sources of bugs, and some - * plugin loaders (e.g. mono) can also be buggy. + /* Show all the loaded plugins, including plugins marked internal. + * This is intentional, since third party protocols are often sources of bugs, and some + * plugin loaders can also be buggy. */ GString *str = g_string_new("Loaded Plugins: "); const GList *plugins = purple_plugins_get_loaded(); if (plugins) { for (; plugins; plugins = plugins->next) { - str = g_string_append(str, purple_plugin_get_name(plugins->data)); + PurplePluginInfo *info = purple_plugin_get_info(PURPLE_PLUGIN(plugins->data)); + str = g_string_append(str, purple_plugin_info_get_name(info)); + if (plugins->next) str = g_string_append(str, ", "); } @@ -554,13 +557,13 @@ break; case PURPLE_CMD_STATUS_NOT_FOUND: { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc; if ((gc = purple_conversation_get_connection(conv))) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - if ((prpl_info != NULL) && (prpl_info->options & OPT_PROTO_SLASH_COMMANDS_NATIVE)) { + protocol = purple_connection_get_protocol(gc); + + if ((protocol != NULL) && (purple_protocol_get_options(protocol) & OPT_PROTO_SLASH_COMMANDS_NATIVE)) { char *spaceslash; /* If the first word in the entered text has a '/' in it, then the user @@ -597,7 +600,7 @@ PURPLE_MESSAGE_NO_LOG, time(NULL)); retval = TRUE; break; - case PURPLE_CMD_STATUS_WRONG_PRPL: + case PURPLE_CMD_STATUS_WRONG_PROTOCOL: purple_conversation_write(conv, "", _("That command doesn't work on this protocol."), PURPLE_MESSAGE_NO_LOG, time(NULL)); retval = TRUE; @@ -1196,7 +1199,7 @@ PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win); PurpleAccount *account = purple_conversation_get_account(conv); - purple_prpl_initiate_media(account, + purple_protocol_initiate_media(account, purple_conversation_get_name(conv), action == win->menu->audio_call ? PURPLE_MEDIA_AUDIO : action == win->menu->video_call ? PURPLE_MEDIA_VIDEO : @@ -1229,7 +1232,7 @@ index = 0; else index = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(obj), "index")); - purple_prpl_send_attention(purple_conversation_get_connection(conv), + purple_protocol_send_attention(purple_conversation_get_connection(conv), purple_conversation_get_name(conv), index); } } @@ -1486,7 +1489,7 @@ PurpleConversation *conv = gtkconv->active_conv; PurpleAccount *account; PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; gchar *real_who = NULL; account = purple_conversation_get_account(conv); @@ -1495,10 +1498,10 @@ gc = purple_account_get_connection(account); g_return_if_fail(gc != NULL); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - if (prpl_info && prpl_info->get_cb_real_name) - real_who = prpl_info->get_cb_real_name(gc, + protocol = purple_connection_get_protocol(gc); + + if (protocol) + real_who = purple_protocol_chat_iface_get_user_real_name(protocol, gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)), who); if(!who && !real_who) @@ -1541,7 +1544,7 @@ static void menu_chat_send_file_cb(GtkWidget *w, PidginConversation *gtkconv) { - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleConversation *conv = gtkconv->active_conv; const char *who = g_object_get_data(G_OBJECT(w), "user_data"); PurpleConnection *gc = purple_conversation_get_connection(conv); @@ -1549,10 +1552,10 @@ g_return_if_fail(gc != NULL); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - if (prpl_info && prpl_info->get_cb_real_name) - real_who = prpl_info->get_cb_real_name(gc, + protocol = purple_connection_get_protocol(gc); + + if (protocol) + real_who = purple_protocol_chat_iface_get_user_real_name(protocol, gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)), who); serv_send_file(gc, real_who ? real_who : who, NULL); @@ -1640,7 +1643,7 @@ create_chat_menu(PurpleChatConversation *chat, const char *who, PurpleConnection *gc) { static GtkWidget *menu = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConversation *conv = PURPLE_CONVERSATION(chat); PurpleAccount *account = purple_conversation_get_account(conv); gboolean is_me = FALSE; @@ -1648,7 +1651,7 @@ PurpleBuddy *buddy = NULL; if (gc != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + protocol = purple_connection_get_protocol(gc); /* * If a menu already exists, destroy it before creating a new one, @@ -1672,7 +1675,7 @@ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); - if (prpl_info && prpl_info->send_file) + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, send)) { gboolean can_receive_file = TRUE; @@ -1680,14 +1683,14 @@ PIDGIN_STOCK_TOOLBAR_SEND_FILE, G_CALLBACK(menu_chat_send_file_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL); - if (gc == NULL || prpl_info == NULL) + if (gc == NULL || protocol == NULL) can_receive_file = FALSE; else { gchar *real_who = NULL; - if (prpl_info->get_cb_real_name) - real_who = prpl_info->get_cb_real_name(gc, - purple_chat_conversation_get_id(chat), who); - if (!(!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, real_who ? real_who : who))) + real_who = purple_protocol_chat_iface_get_user_real_name(protocol, gc, + purple_chat_conversation_get_id(chat), who); + if (!(!PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, can_receive) || + purple_protocol_xfer_iface_can_receive(protocol, gc, real_who ? real_who : who))) can_receive_file = FALSE; g_free(real_who); } @@ -1712,7 +1715,7 @@ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); } - if (prpl_info && (prpl_info->get_info || prpl_info->get_cb_info)) { + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info)) { button = pidgin_new_item_from_stock(menu, _("Info"), PIDGIN_STOCK_TOOLBAR_USER_INFO, G_CALLBACK(menu_chat_info_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL); @@ -1722,7 +1725,7 @@ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); } - if (!is_me && prpl_info && !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { + if (!is_me && protocol && !(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) { if ((buddy = purple_blist_find_buddy(account, who)) != NULL) button = pidgin_new_item_from_stock(menu, _("Remove"), GTK_STOCK_REMOVE, G_CALLBACK(menu_chat_add_remove_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL); @@ -2480,21 +2483,21 @@ * A bunch of buddy icon functions **************************************************************************/ -static GList *get_prpl_icon_list(PurpleAccount *account) +static GList *get_protocol_icon_list(PurpleAccount *account) { GList *l = NULL; - PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - const char *prplname = prpl_info->list_icon(account, NULL); - l = g_hash_table_lookup(prpl_lists, prplname); + PurpleProtocol *protocol = + purple_protocols_find(purple_account_get_protocol_id(account)); + const char *protoname = purple_protocol_class_list_icon(protocol, account, NULL); + l = g_hash_table_lookup(protocol_lists, protoname); if (l) return l; - l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_LARGE)); - l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM)); - l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL)); - - g_hash_table_insert(prpl_lists, g_strdup(prplname), l); + l = g_list_prepend(l, pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_LARGE)); + l = g_list_prepend(l, pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_MEDIUM)); + l = g_list_prepend(l, pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL)); + + g_hash_table_insert(protocol_lists, g_strdup(protoname), l); return l; } @@ -2531,7 +2534,7 @@ } } - return get_prpl_icon_list(account); + return get_protocol_icon_list(account); } static const char * @@ -2652,7 +2655,7 @@ g_object_unref(emblem); if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons")) { - emblem = pidgin_create_prpl_icon(purple_conversation_get_account(gtkconv->active_conv), PIDGIN_PRPL_ICON_SMALL); + emblem = pidgin_create_protocol_icon(purple_conversation_get_account(gtkconv->active_conv), PIDGIN_PROTOCOL_ICON_SMALL); } else { emblem = NULL; } @@ -3298,11 +3301,11 @@ if ((chat == NULL) && (gtkconv->webview != NULL)) { GHashTable *components; PurpleAccount *account = purple_conversation_get_account(conv); - PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + PurpleProtocol *protocol = + purple_protocols_find(purple_account_get_protocol_id(account)); if (purple_account_get_connection(account) != NULL && - PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_info_defaults)) { - components = prpl_info->chat_info_defaults(purple_account_get_connection(account), + PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, info_defaults)) { + components = purple_protocol_chat_iface_info_defaults(protocol, purple_account_get_connection(account), purple_conversation_get_name(conv)); } else { components = g_hash_table_new_full(g_str_hash, g_str_equal, @@ -3388,7 +3391,7 @@ */ if (account != NULL && PURPLE_IS_IM_CONVERSATION(conv)) { PurpleMediaCaps caps = - purple_prpl_get_media_caps(account, + purple_protocol_get_media_caps(account, purple_conversation_get_name(conv)); gtk_action_set_sensitive(win->menu->audio_call, @@ -3420,8 +3423,7 @@ GtkWidget *menu; PurpleConversation *conv; PurpleConnection *pc; - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; GList *list; conv = pidgin_conv_window_get_active_conversation(win); @@ -3436,12 +3438,10 @@ pc = purple_conversation_get_connection(conv); if (pc != NULL) - prpl = purple_connection_get_prpl(pc); - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_attention_types)) { - list = prpl_info->get_attention_types(purple_connection_get_account(pc)); + protocol = purple_connection_get_protocol(pc); + + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ATTENTION_IFACE, get_types)) { + list = purple_protocol_attention_iface_get_types(protocol, purple_connection_get_account(pc)); /* Multiple attention types */ if (list && list->next) { @@ -3969,7 +3969,7 @@ gchar *text; /* Create a pixmap for the protocol icon. */ - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); /* Now convert it to GtkImage */ if (pixbuf == NULL) @@ -4286,7 +4286,7 @@ PurpleConversation *conv; PidginChatPane *gtkchat; PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; GtkTreeModel *tm; GtkListStore *ls; GtkTreePath *newpath; @@ -4308,7 +4308,7 @@ gtkchat = gtkconv->u.chat; gc = purple_conversation_get_connection(conv); - if (!gc || !(prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)))) + if (!gc || !(protocol = purple_connection_get_protocol(gc))) return; tm = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); @@ -4659,7 +4659,7 @@ static void topic_callback(GtkWidget *w, PidginConversation *gtkconv) { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc; PurpleConversation *conv = gtkconv->active_conv; PidginChatPane *gtkchat; @@ -4668,10 +4668,10 @@ gc = purple_conversation_get_connection(conv); - if(!gc || !(prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)))) + if(!gc || !(protocol = purple_connection_get_protocol(gc))) return; - if(prpl_info->set_chat_topic == NULL) + if(!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, set_topic)) return; gtkconv = PIDGIN_CONVERSATION(conv); @@ -4689,7 +4689,7 @@ else gtk_entry_set_text(GTK_ENTRY(gtkchat->topic_text), ""); - prpl_info->set_chat_topic(gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)), + purple_protocol_chat_iface_set_topic(protocol, gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)), new_topic); g_free(new_topic); @@ -4739,7 +4739,7 @@ } static void -update_chat_alias(PurpleBuddy *buddy, PurpleChatConversation *chat, PurpleConnection *gc, PurplePluginProtocolInfo *prpl_info) +update_chat_alias(PurpleBuddy *buddy, PurpleChatConversation *chat, PurpleConnection *gc, PurpleProtocol *protocol) { PidginConversation *gtkconv = PIDGIN_CONVERSATION(PURPLE_CONVERSATION(chat)); PurpleAccount *account = purple_conversation_get_account(PURPLE_CONVERSATION(chat)); @@ -4803,7 +4803,7 @@ blist_node_aliased_cb(PurpleBlistNode *node, const char *old_alias, PurpleChatConversation *chat) { PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleConversation *conv = PURPLE_CONVERSATION(chat); g_return_if_fail(node != NULL); @@ -4811,10 +4811,10 @@ gc = purple_conversation_get_connection(conv); g_return_if_fail(gc != NULL); - g_return_if_fail(purple_connection_get_prpl(gc) != NULL); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - if (prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME) + g_return_if_fail(purple_connection_get_protocol(gc) != NULL); + protocol = purple_connection_get_protocol(gc); + + if (purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME) return; if (PURPLE_IS_CONTACT(node)) @@ -4826,11 +4826,11 @@ if(!PURPLE_IS_BUDDY(bnode)) continue; - update_chat_alias((PurpleBuddy *)bnode, chat, gc, prpl_info); + update_chat_alias((PurpleBuddy *)bnode, chat, gc, protocol); } } else if (PURPLE_IS_BUDDY(node)) - update_chat_alias((PurpleBuddy *)node, chat, gc, prpl_info); + update_chat_alias((PurpleBuddy *)node, chat, gc, protocol); else if (PURPLE_IS_CHAT(node) && purple_conversation_get_account(conv) == purple_chat_get_account((PurpleChat*)node)) { @@ -5036,8 +5036,8 @@ { PurpleConversation *conv = gtkconv->active_conv; PurpleConnection *gc = purple_conversation_get_connection(conv); - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - if (prpl_info->options & OPT_PROTO_CHAT_TOPIC) + PurpleProtocol *protocol = purple_connection_get_protocol(gc); + if (purple_protocol_get_options(protocol) & OPT_PROTO_CHAT_TOPIC) { GtkWidget *hbox, *label; PidginChatPane *gtkchat = gtkconv->u.chat; @@ -5051,7 +5051,7 @@ gtkchat->topic_text = gtk_entry_new(); gtk_widget_set_size_request(gtkchat->topic_text, -1, BUDDYICON_SIZE_MIN); - if(prpl_info->set_chat_topic == NULL) { + if(!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, set_topic)) { gtk_editable_set_editable(GTK_EDITABLE(gtkchat->topic_text), FALSE); } else { g_signal_connect(G_OBJECT(gtkchat->topic_text), "activate", @@ -5073,7 +5073,7 @@ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkconv->u.chat->list)); PurpleConversation *conv = gtkconv->active_conv; PurpleBlistNode *node; - PurplePluginProtocolInfo *prpl_info; + PurpleProtocol *protocol; PurpleAccount *account = purple_conversation_get_account(conv); char *who = NULL; @@ -5085,9 +5085,9 @@ gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(purple_account_get_connection(account))); + protocol = purple_connection_get_protocol(purple_account_get_connection(account)); node = (PurpleBlistNode*)(purple_blist_find_buddy(purple_conversation_get_account(conv), who)); - if (node && prpl_info && (prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) + if (node && protocol && (purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) pidgin_blist_draw_tooltip(node, gtkconv->infopane); g_free(who); @@ -5710,7 +5710,7 @@ PurpleIMConversation *im; PurpleAccount *convaccount = purple_conversation_get_account(conv); PurpleConnection *gc = purple_account_get_connection(convaccount); - PurplePluginProtocolInfo *prpl_info = gc ? PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)) : NULL; + PurpleProtocol *protocol = gc ? purple_connection_get_protocol(gc) : NULL; const guchar *data = gtk_selection_data_get_data(sd); if (info == PIDGIN_DRAG_BLIST_NODE) @@ -5737,7 +5737,7 @@ * invite him to the chat. */ if (PURPLE_IS_CHAT_CONVERSATION(conv) && - prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_invite) && + protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, invite) && strcmp(purple_account_get_protocol_id(convaccount), purple_account_get_protocol_id(buddyaccount)) == 0) { purple_chat_conversation_invite_user(PURPLE_CHAT_CONVERSATION(conv), buddyname, NULL, TRUE); @@ -5774,13 +5774,13 @@ } else if (info == PIDGIN_DRAG_IM_CONTACT) { - char *protocol = NULL; + char *protocol_id = NULL; char *username = NULL; PurpleAccount *account; PidginConversation *gtkconv; if (pidgin_parse_x_im_contact((const char *) data, FALSE, &account, - &protocol, &username, NULL)) + &protocol_id, &username, NULL)) { if (account == NULL) { @@ -5793,8 +5793,8 @@ * invite him to the chat. */ if (PURPLE_IS_CHAT_CONVERSATION(conv) && - prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_invite) && - strcmp(purple_account_get_protocol_id(convaccount), protocol) == 0) { + protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, invite) && + strcmp(purple_account_get_protocol_id(convaccount), protocol_id) == 0) { purple_chat_conversation_invite_user(PURPLE_CHAT_CONVERSATION(conv), username, NULL, TRUE); } else { im = purple_im_conversation_new(account, username); @@ -5808,7 +5808,7 @@ } g_free(username); - g_free(protocol); + g_free(protocol_id); gtk_drag_finish(dc, TRUE, gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t); @@ -6722,7 +6722,7 @@ gtk_font_options |= GTK_IMHTML_NO_COLOURS | GTK_IMHTML_NO_FONTS | GTK_IMHTML_NO_SIZES | GTK_IMHTML_NO_FORMATTING; /* this is gonna crash one day, I can feel it. */ - if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(purple_conversation_get_account(conv))))->options & + if (purple_protocols_find(purple_account_get_protocol_id(purple_conversation_get_account(conv)))->options & OPT_PROTO_USE_POINTSIZE) { gtk_font_options |= GTK_IMHTML_USE_POINTSIZE; } @@ -7322,7 +7322,7 @@ PidginWindow *win; PurpleConversation *conv = gtkconv->active_conv; PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; GdkPixbuf *window_icon = NULL; GtkWebViewButtons buttons; PurpleAccount *account; @@ -7332,7 +7332,7 @@ account = purple_conversation_get_account(conv); if (gc != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + protocol = purple_connection_get_protocol(gc); if (win->menu->send_to != NULL) update_send_to_selection(win); @@ -7341,7 +7341,7 @@ * Handle hiding and showing stuff based on what type of conv this is. * Stuff that Purple IMs support in general should be shown for IM * conversations. Stuff that Purple chats support in general should be - * shown for chat conversations. It doesn't matter whether the PRPL + * shown for chat conversations. It doesn't matter whether the protocol * supports it or not--that only affects if the button or menu item * is sensitive or not. */ @@ -7429,7 +7429,7 @@ buttons = GTK_WEBVIEW_SMILEY | GTK_WEBVIEW_IMAGE; } - if (!(prpl_info->options & OPT_PROTO_IM_IMAGE) + if (!(purple_protocol_get_options(protocol) & OPT_PROTO_IM_IMAGE) && !(features & PURPLE_CONNECTION_FLAG_NO_IMAGES)) { features |= PURPLE_CONNECTION_FLAG_NO_IMAGES; purple_conversation_set_features(conv, features); @@ -7450,27 +7450,28 @@ /* Deal with menu items */ gtk_action_set_sensitive(win->menu->view_log, TRUE); gtk_action_set_sensitive(win->menu->add_pounce, TRUE); - gtk_action_set_sensitive(win->menu->get_info, (prpl_info->get_info != NULL)); - gtk_action_set_sensitive(win->menu->invite, (prpl_info->chat_invite != NULL)); + gtk_action_set_sensitive(win->menu->get_info, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info))); + gtk_action_set_sensitive(win->menu->invite, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, invite))); gtk_action_set_sensitive(win->menu->insert_link, (features & PURPLE_CONNECTION_FLAG_HTML)); gtk_action_set_sensitive(win->menu->insert_image, !(features & PURPLE_CONNECTION_FLAG_NO_IMAGES)); if (PURPLE_IS_IM_CONVERSATION(conv)) { - gtk_action_set_sensitive(win->menu->add, (prpl_info->add_buddy != NULL)); - gtk_action_set_sensitive(win->menu->remove, (prpl_info->remove_buddy != NULL)); + gtk_action_set_sensitive(win->menu->add, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, add_buddy))); + gtk_action_set_sensitive(win->menu->remove, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, remove_buddy))); gtk_action_set_sensitive(win->menu->send_file, - (prpl_info->send_file != NULL && (!prpl_info->can_receive_file || - prpl_info->can_receive_file(gc, purple_conversation_get_name(conv))))); - gtk_action_set_sensitive(win->menu->get_attention, (prpl_info->send_attention != NULL)); + (PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, send) && + (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, can_receive) || + purple_protocol_xfer_iface_can_receive(protocol, gc, purple_conversation_get_name(conv))))); + gtk_action_set_sensitive(win->menu->get_attention, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, ATTENTION_IFACE, send))); gtk_action_set_sensitive(win->menu->alias, (account != NULL) && (purple_blist_find_buddy(account, purple_conversation_get_name(conv)) != NULL)); } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) { - gtk_action_set_sensitive(win->menu->add, (prpl_info->join_chat != NULL)); - gtk_action_set_sensitive(win->menu->remove, (prpl_info->join_chat != NULL)); + gtk_action_set_sensitive(win->menu->add, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, join))); + gtk_action_set_sensitive(win->menu->remove, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, join))); gtk_action_set_sensitive(win->menu->alias, (account != NULL) && (purple_blist_find_chat(account, purple_conversation_get_name(conv)) != NULL)); @@ -8388,10 +8389,8 @@ GHashTable *comps = NULL; PurpleChat *chat = purple_blist_find_chat(purple_conversation_get_account(conv), purple_conversation_get_name(conv)); if (chat == NULL) { - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - if (prpl_info->chat_info_defaults != NULL) - comps = prpl_info->chat_info_defaults(gc, purple_conversation_get_name(conv)); + PurpleProtocol *protocol = purple_connection_get_protocol(gc); + comps = purple_protocol_chat_iface_info_defaults(protocol, gc, purple_conversation_get_name(conv)); } else { comps = purple_chat_get_components(chat); } @@ -9093,7 +9092,7 @@ #include "imgstore.h" #include "log.h" #include "notify.h" -#include "prpl.h" +#include "protocol.h" #include "request.h" #include "util.h" @@ -9968,12 +9967,12 @@ text = purple_buddy_get_contact_alias(buddy); } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) { PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; gc = purple_conversation_get_connection(conv); if (gc != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - if (prpl_info && prpl_info->set_chat_topic == NULL) + protocol = purple_connection_get_protocol(gc); + if (protocol && !PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, set_topic)) /* This protocol doesn't support setting the chat room topic */ return FALSE; @@ -10099,7 +10098,7 @@ xa_list = make_status_icon_list(PIDGIN_STOCK_STATUS_XA, w); offline_list = make_status_icon_list(PIDGIN_STOCK_STATUS_OFFLINE, w); away_list = make_status_icon_list(PIDGIN_STOCK_STATUS_AWAY, w); - prpl_lists = g_hash_table_new(g_str_hash, g_str_equal); + protocol_lists = g_hash_table_new(g_str_hash, g_str_equal); } static void
--- a/pidgin/gtkdialogs.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkdialogs.c Fri Jan 31 18:02:20 2014 +0530 @@ -31,8 +31,8 @@ #include "debug.h" #include "notify.h" -#include "plugin.h" -#include "prpl.h" +#include "plugins.h" +#include "protocol.h" #include "request.h" #include "util.h" #include "core.h" @@ -691,12 +691,14 @@ g_string_append(str, "<dt>Network Security Services (NSS):</dt><dd>Disabled</dd>"); #endif - if (purple_plugins_find_with_id("core-perl") != NULL) +#warning TODO: Check for perl. + if (purple_plugins_find_plugin("core-perl") != NULL) g_string_append(str, "<dt>Perl:</dt><dd>Enabled</dd>"); else g_string_append(str, "<dt>Perl:</dt><dd>Disabled</dd>"); - if (purple_plugins_find_with_id("core-tcl") != NULL) { +#warning TODO: Check for tcl. + if (purple_plugins_find_plugin("core-tcl") != NULL) { g_string_append(str, "<dt>Tcl:</dt><dd>Enabled</dd>"); #ifdef HAVE_TK g_string_append(str, "<dt>Tk:</dt><dd>Enabled</dd>"); @@ -833,47 +835,85 @@ void pidgin_dialogs_plugins_info(void) { GString *str; - GList *l = NULL; + GList *plugins, *l = NULL; PurplePlugin *plugin = NULL; + PurplePluginInfo *info; + PurplePluginExtraCb extra_cb; char *title = g_strdup_printf(_("%s Plugin Information"), PIDGIN_NAME); - char *pname = NULL, *pauthor = NULL; - const char *pver, *pwebsite, *pid; - gboolean ploaded, punloadable; + char *pname = NULL, *authors, *pauthors, *pextra; + const char *pver, *plicense, *pwebsite, *pid; + gboolean ploaded, ploadable; + const char * const *authorlist; + guint n_authors; static GtkWidget *plugins_info = NULL; str = g_string_sized_new(4096); g_string_append_printf(str, "<h2>%s</h2><dl>", _("Plugin Information")); - for(l = purple_plugins_get_all(); l; l = l->next) { - plugin = (PurplePlugin *)l->data; + plugins = purple_plugins_find_all(); + + for(l = plugins; l; l = l->next) { + plugin = PURPLE_PLUGIN(l->data); + info = purple_plugin_get_info(plugin); + extra_cb = purple_plugin_info_get_extra_cb(info); + + pname = g_markup_escape_text(purple_plugin_info_get_name(info), -1); + authorlist = purple_plugin_info_get_authors(info); - pname = g_markup_escape_text(purple_plugin_get_name(plugin), -1); - if ((pauthor = (char *)purple_plugin_get_author(plugin)) != NULL) - pauthor = g_markup_escape_text(pauthor, -1); - pver = purple_plugin_get_version(plugin); - pwebsite = purple_plugin_get_homepage(plugin); - pid = purple_plugin_get_id(plugin); - punloadable = purple_plugin_is_unloadable(plugin); + if (authorlist) { + authors = g_strjoinv(", ", (gchar **)authorlist); + n_authors = g_strv_length((gchar **)authorlist); + } else { + authors = NULL; + n_authors = 0; + } + + if (authors) + pauthors = g_markup_escape_text(authors, -1); + else + pauthors = NULL; + + pver = purple_plugin_info_get_version(info); + plicense = purple_plugin_info_get_license_id(info); + pwebsite = purple_plugin_info_get_website(info); + pid = purple_plugin_info_get_id(info); + ploadable = !purple_plugin_info_get_error(info); ploaded = purple_plugin_is_loaded(plugin); + if (ploaded && extra_cb) + pextra = extra_cb(plugin); + else + pextra = NULL; + g_string_append_printf(str, "<dt>%s</dt><dd>" - "<b>Author:</b> %s<br/>" + "<b>%s:</b> %s<br/>" "<b>Version:</b> %s<br/>" + "<b>License:</b> %s<br/>" "<b>Website:</b> %s<br/>" "<b>ID String:</b> %s<br/>" + "<b>Extra:</b> %s<br/>" "<b>Loadable:</b> %s<br/>" "<b>Loaded:</b> %s" "</dd><br/>", - pname, pauthor ? pauthor : "(null)", - pver, pwebsite, pid, - punloadable ? "<span style=\"color: #FF0000;\"><b>No</b></span>" : "Yes", - ploaded ? "Yes" : "No"); + pname ? pname : "", + (n_authors > 1 ? "Authors" : "Author"), + pauthors ? pauthors : "", + pver ? pver : "", + plicense ? plicense : "", + pwebsite ? pwebsite : "", + pid, + pextra ? pextra : "", + ploadable ? "Yes" : "<span style=\"color: #FF0000;\"><b>No</b></span>", + ploaded ? "Yes" : "No"); g_free(pname); - g_free(pauthor); + g_free(pextra); + g_free(pauthors); + g_free(authors); } + g_list_free(plugins); g_string_append(str, "</dl>"); @@ -901,15 +941,15 @@ { PurpleRequestFields *fields = _fields; PurpleAccount *account; - PurplePlugin *prpl; + PurpleProtocol *protocol; const char *username; gboolean valid; account = purple_request_fields_get_account(fields, "account"); - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); username = purple_request_fields_get_string(fields, "screenname"); - valid = purple_validate(prpl, username); + valid = purple_validate(protocol, username); if (errmsg && !valid) *errmsg = g_strdup(_("Invalid username"));
--- a/pidgin/gtkdocklet.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkdocklet.c Fri Jan 31 18:02:20 2014 +0530 @@ -260,8 +260,8 @@ while(c != NULL) { PurpleConnection *gc = c->data; - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - if (prpl_info != NULL && prpl_info->chat_info != NULL) + PurpleProtocol *protocol = purple_connection_get_protocol(gc); + if (protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, info)) return TRUE; c = c->next; } @@ -297,7 +297,7 @@ docklet_signed_on_cb(PurpleConnection *gc) { if (!enable_join_chat) { - if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->chat_info != NULL) + if (PURPLE_PROTOCOL_IMPLEMENTS(purple_connection_get_protocol(gc), CHAT_IFACE, info)) enable_join_chat = TRUE; } docklet_update_status(); @@ -307,7 +307,7 @@ docklet_signed_off_cb(PurpleConnection *gc) { if (enable_join_chat) { - if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->chat_info != NULL) + if (PURPLE_PROTOCOL_IMPLEMENTS(purple_connection_get_protocol(gc), CHAT_IFACE, info)) enable_join_chat = online_account_supports_chat(); } docklet_update_status(); @@ -623,7 +623,6 @@ } - static void plugin_act(GtkWidget *widget, PurplePluginAction *pam) { @@ -632,14 +631,16 @@ } static void -build_plugin_actions(GtkWidget *menu, PurplePlugin *plugin, - gpointer context) +build_plugin_actions(GtkWidget *menu, PurplePlugin *plugin) { GtkWidget *menuitem; + PurplePluginActionsCb actions_cb; PurplePluginAction *action = NULL; GList *actions, *l; - actions = PURPLE_PLUGIN_ACTIONS(plugin, context); + actions_cb = + purple_plugin_info_get_actions_cb(purple_plugin_get_info(plugin)); + actions = actions_cb(plugin); for (l = actions; l != NULL; l = l->next) { @@ -647,7 +648,6 @@ { action = (PurplePluginAction *) l->data; action->plugin = plugin; - action->context = context; menuitem = gtk_menu_item_new_with_label(action->label); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); @@ -672,6 +672,7 @@ { GtkWidget *menuitem, *submenu; PurplePlugin *plugin = NULL; + PurplePluginInfo *info; GList *l; int c = 0; @@ -679,21 +680,20 @@ /* Add a submenu for each plugin with custom actions */ for (l = purple_plugins_get_loaded(); l; l = l->next) { - plugin = (PurplePlugin *) l->data; + plugin = PURPLE_PLUGIN(l->data); + info = purple_plugin_get_info(plugin); - if (PURPLE_IS_PROTOCOL_PLUGIN(plugin)) + if (!purple_plugin_info_get_actions_cb(info)) continue; - if (!PURPLE_PLUGIN_HAS_ACTIONS(plugin)) - continue; - - menuitem = gtk_image_menu_item_new_with_label(_(plugin->info->name)); + menuitem = + gtk_image_menu_item_new_with_label(_(purple_plugin_info_get_name(info))); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); submenu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); - build_plugin_actions(submenu, plugin, NULL); + build_plugin_actions(submenu, plugin); c++; }
--- a/pidgin/gtkimhtmltoolbar.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkimhtmltoolbar.c Fri Jan 31 18:02:20 2014 +0530 @@ -927,7 +927,7 @@ PurpleConnection *gc = purple_conversation_get_connection(conv); toggle_button_set_active_block(GTK_TOGGLE_BUTTON(attention), FALSE, toolbar); - purple_prpl_send_attention(gc, who, 0); + purple_protocol_send_attention(gc, who, 0); gtk_widget_grab_focus(toolbar->imhtml); } @@ -1593,14 +1593,14 @@ PurpleConversation *conv) { PurpleConnection *gc = purple_conversation_get_connection(conv); - PurplePlugin *prpl = purple_connection_get_prpl(gc); + PurpleProtocol *protocol = purple_connection_get_protocol(gc); g_object_set_data(G_OBJECT(toolbar), "active_conv", conv); /* gray out attention button on protocols that don't support it for the time being it is always disabled for chats */ gtk_widget_set_sensitive(toolbar->attention, - conv && prpl && PURPLE_IS_IM_CONVERSATION(conv) && - PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention != NULL); + conv && protocol && PURPLE_IS_IM_CONVERSATION(conv) && + PURPLE_PROTOCOL_IMPLEMENTS(protocol, ATTENTION_IFACE, send)); }
--- a/pidgin/gtklog.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtklog.c Fri Jan 31 18:02:20 2014 +0530 @@ -689,7 +689,7 @@ PidginLogViewer *lv = NULL; const char *name = buddyname; char *title; - GdkPixbuf *prpl_icon; + GdkPixbuf *protocol_icon; g_return_if_fail(account != NULL); g_return_if_fail(buddyname != NULL); @@ -727,14 +727,14 @@ title = g_strdup_printf(_("Conversations with %s"), name); } - prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM); + protocol_icon = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_MEDIUM); display_log_viewer(ht, purple_log_get_logs(type, buddyname, account), - title, gtk_image_new_from_pixbuf(prpl_icon), + title, gtk_image_new_from_pixbuf(protocol_icon), purple_log_get_total_size(type, buddyname, account)); - if (prpl_icon) - g_object_unref(prpl_icon); + if (protocol_icon) + g_object_unref(protocol_icon); g_free(title); } @@ -822,7 +822,7 @@ for(accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) { PurpleAccount *account = (PurpleAccount *)accounts->data; - if(purple_find_prpl(purple_account_get_protocol_id(account)) == NULL) + if(purple_protocols_find(purple_account_get_protocol_id(account)) == NULL) continue; logs = g_list_concat(purple_log_get_system_logs(account), logs);
--- a/pidgin/gtknotify.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtknotify.c Fri Jan 31 18:02:20 2014 +0530 @@ -516,7 +516,7 @@ if (!account) return; - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); image = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(G_OBJECT(pixbuf)); @@ -709,7 +709,7 @@ if (clear) return NULL; - icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM); + icon = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_MEDIUM); if (new_n) { data = g_new0(PidginNotifyMailData, 1); @@ -966,7 +966,7 @@ gtk_list_store_clear(data->model); - pixbuf = pidgin_create_prpl_icon(purple_connection_get_account(gc), PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(purple_connection_get_account(gc), PIDGIN_PROTOCOL_ICON_SMALL); for (row = results->rows; row != NULL; row = row->next) { @@ -1539,7 +1539,7 @@ if (pounce_dialog == NULL) pounce_dialog = pidgin_create_notification_dialog(PIDGIN_NOTIFY_POUNCE); - icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + icon = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); pounce_data = g_new(PidginNotifyPounceData, 1);
--- a/pidgin/gtkplugin.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkplugin.c Fri Jan 31 18:02:20 2014 +0530 @@ -37,10 +37,25 @@ #include "gtk3compat.h" +#define PIDGIN_PLUGIN_INFO_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_PLUGIN_INFO, PidginPluginInfoPrivate)) + #define PIDGIN_RESPONSE_CONFIGURE 98121 typedef struct { + PidginPluginConfigFrameCb config_frame_cb; +} PidginPluginInfoPrivate; + +enum +{ + PROP_0, + PROP_GTK_CONFIG_FRAME_CB, + PROP_LAST +}; + +typedef struct +{ enum { PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME, @@ -60,7 +75,7 @@ } PidginPluginUiData; static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model, - GtkTreeIter *iter, gboolean unload); + GtkTreeIter *iter, GError *error, gboolean unload); static GtkWidget *expander = NULL; static GtkWidget *plugin_dialog = NULL; @@ -68,35 +83,125 @@ static GtkLabel *plugin_name = NULL; static GtkTextBuffer *plugin_desc = NULL; static GtkLabel *plugin_error = NULL; -static GtkLabel *plugin_author = NULL; +static GtkLabel *plugin_authors = NULL; static GtkLabel *plugin_website = NULL; static gchar *plugin_website_uri = NULL; static GtkLabel *plugin_filename = NULL; static GtkWidget *pref_button = NULL; +/* Set method for GObject properties */ +static void +pidgin_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value, + GParamSpec *pspec) +{ + PidginPluginInfoPrivate *priv = PIDGIN_PLUGIN_INFO_GET_PRIVATE(obj); + + switch (param_id) { + case PROP_GTK_CONFIG_FRAME_CB: + priv->config_frame_cb = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +/* Get method for GObject properties */ +static void +pidgin_plugin_info_get_property(GObject *obj, guint param_id, GValue *value, + GParamSpec *pspec) +{ + PidginPluginInfoPrivate *priv = PIDGIN_PLUGIN_INFO_GET_PRIVATE(obj); + + switch (param_id) { + case PROP_GTK_CONFIG_FRAME_CB: + g_value_set_pointer(value, priv->config_frame_cb); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +/* Class initializer function */ +static void pidgin_plugin_info_class_init(PidginPluginInfoClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(PidginPluginInfoPrivate)); + + /* Setup properties */ + obj_class->get_property = pidgin_plugin_info_get_property; + obj_class->set_property = pidgin_plugin_info_set_property; + + g_object_class_install_property(obj_class, PROP_GTK_CONFIG_FRAME_CB, + g_param_spec_pointer("gtk-config-frame-cb", + "GTK configuration frame callback", + "Callback that returns a GTK configuration frame", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +GType +pidgin_plugin_info_get_type(void) +{ + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PidginPluginInfoClass), + .class_init = (GClassInitFunc)pidgin_plugin_info_class_init, + .instance_size = sizeof(PidginPluginInfo), + }; + + type = g_type_register_static(PURPLE_TYPE_PLUGIN_INFO, + "PidginPluginInfo", &info, 0); + } + + return type; +} + +PidginPluginInfo * +pidgin_plugin_info_new(const char *first_property, ...) +{ + GObject *info; + va_list var_args; + + /* at least ID is required */ + if (!first_property) + return NULL; + + va_start(var_args, first_property); + info = g_object_new_valist(PIDGIN_TYPE_PLUGIN_INFO, first_property, + var_args); + va_end(var_args); + + g_object_set(info, "ui-requirement", PIDGIN_UI, NULL); + + return PIDGIN_PLUGIN_INFO(info); +} + static gboolean pidgin_plugin_has_prefs(PurplePlugin *plugin) { - PurplePluginUiInfo *pinfo; + PurplePluginInfo *info = purple_plugin_get_info(plugin); + PidginPluginInfoPrivate *priv = NULL; + gboolean ret; g_return_val_if_fail(plugin != NULL, FALSE); if (!purple_plugin_is_loaded(plugin)) return FALSE; - if (PIDGIN_IS_PIDGIN_PLUGIN(plugin) && plugin->info->ui_info && - PIDGIN_PLUGIN_UI_INFO(plugin)->get_config_frame) - { - return TRUE; - } + if (PIDGIN_IS_PLUGIN_INFO(info)) + priv = PIDGIN_PLUGIN_INFO_GET_PRIVATE(info); - pinfo = plugin->info->prefs_info; + ret = ((priv && priv->config_frame_cb) || + purple_plugin_info_get_pref_frame_cb(info) || + purple_plugin_info_get_pref_request_cb(info)); - if (!pinfo) - return FALSE; - - return (pinfo->get_plugin_pref_frame || pinfo->get_plugin_pref_request); + return ret; } static GtkWidget * @@ -104,25 +209,25 @@ PurplePluginPrefFrame **purple_pref_frame) { GtkWidget *config = NULL; + PurplePluginInfo *info; + PidginPluginInfoPrivate *priv = NULL; g_return_val_if_fail(plugin != NULL, NULL); - if (PIDGIN_IS_PIDGIN_PLUGIN(plugin) && plugin->info->ui_info - && PIDGIN_PLUGIN_UI_INFO(plugin)->get_config_frame) - { - PidginPluginUiInfo *ui_info; + info = purple_plugin_get_info(plugin); + if (PIDGIN_IS_PLUGIN_INFO(info)) + priv = PIDGIN_PLUGIN_INFO_GET_PRIVATE(info); - ui_info = PIDGIN_PLUGIN_UI_INFO(plugin); + if (priv) + config = priv->config_frame_cb(plugin); - config = ui_info->get_config_frame(plugin); - } - - if (config == NULL && plugin->info->prefs_info - && plugin->info->prefs_info->get_plugin_pref_frame) + if (!config && purple_plugin_info_get_pref_frame_cb(info)) { PurplePluginPrefFrame *frame; + PurplePluginPrefFrameCb pref_frame_cb = + purple_plugin_info_get_pref_frame_cb(info); - frame = plugin->info->prefs_info->get_plugin_pref_frame(plugin); + frame = pref_frame_cb(plugin); config = pidgin_plugin_pref_create_frame(frame); @@ -135,11 +240,14 @@ static void pref_dialog_close(PurplePlugin *plugin) { + PurplePluginInfo *info; PidginPluginUiData *ui_data; g_return_if_fail(plugin != NULL); - ui_data = plugin->ui_data; + info = purple_plugin_get_info(plugin); + + ui_data = purple_plugin_info_get_ui_data(info); if (ui_data == NULL) return; @@ -157,7 +265,7 @@ purple_plugin_pref_frame_destroy(ui_data->u.frame.pref_frame); g_free(ui_data); - plugin->ui_data = NULL; + purple_plugin_info_set_ui_data(info, NULL); } @@ -174,58 +282,61 @@ static void pidgin_plugin_open_config(PurplePlugin *plugin, GtkWindow *parent) { - PurplePluginUiInfo *pinfo; + PurplePluginInfo *info; + PidginPluginInfoPrivate *priv = NULL; PidginPluginUiData *ui_data; - gboolean has_purple_frame, has_purple_request, has_pidgin_frame; + PurplePluginPrefFrameCb pref_frame_cb; + PurplePluginPrefRequestCb pref_request_cb; + PidginPluginConfigFrameCb get_pidgin_frame = NULL; gint prefs_count; g_return_if_fail(plugin != NULL); + info = purple_plugin_get_info(plugin); + if (!pidgin_plugin_has_prefs(plugin)) { purple_debug_warning("gtkplugin", "Plugin has no prefs"); return; } - if (plugin->ui_data != NULL) + if (purple_plugin_info_get_ui_data(info)) return; - pinfo = plugin->info->prefs_info; - if (pinfo == NULL) - has_purple_frame = has_purple_request = FALSE; - else { - has_purple_frame = (pinfo->get_plugin_pref_frame != NULL); - has_purple_request = (pinfo->get_plugin_pref_request != NULL); - } - has_pidgin_frame = (PIDGIN_IS_PIDGIN_PLUGIN(plugin) && - plugin->info->ui_info && - PIDGIN_PLUGIN_UI_INFO(plugin)->get_config_frame); + if (PIDGIN_IS_PLUGIN_INFO(info)) + priv = PIDGIN_PLUGIN_INFO_GET_PRIVATE(info); + + pref_frame_cb = purple_plugin_info_get_pref_frame_cb(info); + pref_request_cb = purple_plugin_info_get_pref_request_cb(info); + + if (priv) + get_pidgin_frame = priv->config_frame_cb; prefs_count = 0; - if (has_purple_frame) + if (pref_frame_cb) prefs_count++; - if (has_purple_request) + if (pref_request_cb) prefs_count++; - if (has_pidgin_frame) + if (get_pidgin_frame) prefs_count++; if (prefs_count > 1) { purple_debug_warning("gtkplugin", "Plugin %s contains more than" " one prefs callback, some will be ignored.", - plugin->info->name); + purple_plugin_info_get_name(info)); } g_return_if_fail(prefs_count > 0); - plugin->ui_data = ui_data = g_new0(PidginPluginUiData, 1); + ui_data = g_new0(PidginPluginUiData, 1); + purple_plugin_info_set_ui_data(info, ui_data); /* Priority: pidgin frame > purple request > purple frame * Purple frame could be replaced with purple request some day. */ - if (has_purple_request && !has_pidgin_frame) { + if (pref_request_cb && !get_pidgin_frame) { ui_data->type = PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST; - ui_data->u.request_handle = - pinfo->get_plugin_pref_request(plugin); + ui_data->u.request_handle = pref_request_cb(plugin); purple_request_add_close_notify(ui_data->u.request_handle, - purple_callback_set_zero, &plugin->ui_data); + purple_callback_set_zero, &info->ui_data); purple_request_add_close_notify(ui_data->u.request_handle, g_free, ui_data); } else { @@ -239,7 +350,7 @@ purple_debug_error("gtkplugin", "Failed to display prefs frame"); g_free(ui_data); - plugin->ui_data = NULL; + purple_plugin_info_set_ui_data(info, NULL); return; } @@ -254,7 +365,7 @@ gtk_dialog_get_content_area(GTK_DIALOG(dialog))), box); gtk_window_set_role(GTK_WINDOW(dialog), "plugin_config"); gtk_window_set_title(GTK_WINDOW(dialog), - _(purple_plugin_get_name(plugin))); + _(purple_plugin_info_get_name(info))); gtk_widget_show_all(dialog); } } @@ -270,44 +381,38 @@ { GtkListStore *ls = GTK_LIST_STORE(data); GtkTreeIter iter; - GList *probes; + GList *plugins, *l; PurplePlugin *plug; + PurplePluginInfo *info; gtk_list_store_clear(ls); - purple_plugins_probe(G_MODULE_SUFFIX); + purple_plugins_refresh(); - for (probes = purple_plugins_get_all(); - probes != NULL; - probes = probes->next) + plugins = purple_plugins_find_all(); + + for (l = plugins; l != NULL; l = l->next) { char *name; char *version; char *summary; char *desc; - plug = probes->data; + plug = PURPLE_PLUGIN(l->data); + info = purple_plugin_get_info(plug); - if (plug->info->type == PURPLE_PLUGIN_LOADER) { - GList *cur; - for (cur = PURPLE_PLUGIN_LOADER_INFO(plug)->exts; cur != NULL; - cur = cur->next) - purple_plugins_probe(cur->data); + if (purple_plugin_is_internal(plug)) continue; - } else if (plug->info->type != PURPLE_PLUGIN_STANDARD || - (plug->info->flags & PURPLE_PLUGIN_FLAG_INVISIBLE)) { - continue; - } gtk_list_store_append (ls, &iter); - if (plug->info->name) { - name = g_markup_escape_text(_(plug->info->name), -1); + if (purple_plugin_info_get_name(info)) { + name = g_markup_escape_text(_(purple_plugin_info_get_name(info)), -1); } else { - char *tmp = g_path_get_basename(plug->path); + char *tmp = g_path_get_basename(purple_plugin_get_filename(plug)); name = g_markup_escape_text(tmp, -1); g_free(tmp); } - version = g_markup_escape_text(purple_plugin_get_version(plug), -1); - summary = g_markup_escape_text(purple_plugin_get_summary(plug), -1); + version = g_markup_escape_text(purple_plugin_info_get_version(info), -1); + summary = g_markup_escape_text(purple_plugin_info_get_summary(info), -1); desc = g_strdup_printf("<b>%s</b> %s\n%s", name, version, @@ -320,10 +425,29 @@ 0, purple_plugin_is_loaded(plug), 1, desc, 2, plug, - 3, purple_plugin_is_unloadable(plug), + 3, purple_plugin_info_get_error(info), -1); g_free(desc); } + + g_list_free(plugins); +} + +static gboolean +check_if_loaded(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + PurplePlugin *plugin; + gtk_tree_model_get(model, iter, 2, &plugin, -1); + gtk_list_store_set(GTK_LIST_STORE(model), iter, + 0, purple_plugin_is_loaded(plugin), + -1); + return FALSE; +} + +static void +update_loaded_plugins(GtkTreeModel *model) +{ + gtk_tree_model_foreach(model, check_if_loaded, NULL); } static void plugin_loading_common(PurplePlugin *plugin, GtkTreeView *view, gboolean loaded) @@ -379,7 +503,7 @@ GtkTreeModel *model = (GtkTreeModel *)data[1]; GtkTreeIter *iter = (GtkTreeIter *)data[2]; - plugin_toggled_stage_two(plugin, model, iter, TRUE); + plugin_toggled_stage_two(plugin, model, iter, NULL, TRUE); g_free(data); } @@ -390,24 +514,18 @@ GtkTreeIter *iter = g_new(GtkTreeIter, 1); GtkTreePath *path = gtk_tree_path_new_from_string(pth); PurplePlugin *plug; + GError *error = NULL; gtk_tree_model_get_iter(model, iter, path); gtk_tree_path_free(path); gtk_tree_model_get(model, iter, 2, &plug, -1); - /* Apparently, GTK+ won't honor the sensitive flag on cell renderers for booleans. */ - if (purple_plugin_is_unloadable(plug)) - { - g_free(iter); - return; - } - if (!purple_plugin_is_loaded(plug)) { pidgin_set_cursor(plugin_dialog, GDK_WATCH); - purple_plugin_load(plug); - plugin_toggled_stage_two(plug, model, iter, FALSE); + purple_plugin_load(plug, &error); + plugin_toggled_stage_two(plug, model, iter, error, FALSE); pidgin_clear_cursor(plugin_dialog); } @@ -415,19 +533,22 @@ { pref_dialog_close(plug); - if (plug->dependent_plugins != NULL) + if (purple_plugin_get_dependent_plugins(plug) != NULL) { GString *tmp = g_string_new(_("The following plugins will be unloaded.")); - GList *l; + GSList *l; gpointer *cb_data; - for (l = plug->dependent_plugins ; l != NULL ; l = l->next) + for (l = purple_plugin_get_dependent_plugins(plug); l != NULL ; l = l->next) { const char *dep_name = (const char *)l->data; - PurplePlugin *dep_plugin = purple_plugins_find_with_id(dep_name); + PurplePlugin *dep_plugin = purple_plugins_find_plugin(dep_name); + PurplePluginInfo *dep_info; + g_return_if_fail(dep_plugin != NULL); - g_string_append_printf(tmp, "\n\t%s\n", purple_plugin_get_name(dep_plugin)); + dep_info = purple_plugin_get_info(dep_plugin); + g_string_append_printf(tmp, "\n\t%s\n", purple_plugin_info_get_name(dep_info)); } cb_data = g_new(gpointer, 3); @@ -444,50 +565,50 @@ g_string_free(tmp, TRUE); } else - plugin_toggled_stage_two(plug, model, iter, TRUE); + plugin_toggled_stage_two(plug, model, iter, NULL, TRUE); } } -static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model, GtkTreeIter *iter, gboolean unload) +static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model, GtkTreeIter *iter, GError *error, gboolean unload) { + PurplePluginInfo *info = purple_plugin_get_info(plug); + if (unload) { pidgin_set_cursor(plugin_dialog, GDK_WATCH); - if (!purple_plugin_unload(plug)) + if (!purple_plugin_unload(plug, &error)) { const char *primary = _("Could not unload plugin"); const char *reload = _("The plugin could not be unloaded now, but will be disabled at the next startup."); - if (!plug->error) - { - purple_notify_warning(NULL, NULL, primary, reload, NULL); - } - else - { - char *tmp = g_strdup_printf("%s\n\n%s", reload, plug->error); - purple_notify_warning(NULL, NULL, primary, tmp, NULL); - g_free(tmp); - } + char *tmp = g_strdup_printf("%s\n\n%s", reload, error->message); + purple_notify_warning(NULL, NULL, primary, tmp, NULL); + g_free(tmp); purple_plugin_disable(plug); } pidgin_clear_cursor(plugin_dialog); } + else if (!unload && error) + { + purple_notify_warning(NULL, NULL, _("Could not load plugin"), error->message, NULL); + } gtk_widget_set_sensitive(pref_button, pidgin_plugin_has_prefs(plug)); - if (plug->error != NULL) + if (error != NULL) { - gchar *name = g_markup_escape_text(purple_plugin_get_name(plug), -1); + gchar *name = g_markup_escape_text(purple_plugin_info_get_name(info), -1); - gchar *error = g_markup_escape_text(plug->error, -1); + gchar *disp_error = g_markup_escape_text(error->message, -1); gchar *text; text = g_strdup_printf( "<b>%s</b> %s\n<span weight=\"bold\" color=\"red\"%s</span>", - purple_plugin_get_name(plug), purple_plugin_get_version(plug), error); + purple_plugin_info_get_name(info), + purple_plugin_info_get_version(info), disp_error); gtk_list_store_set(GTK_LIST_STORE (model), iter, 1, text, -1); @@ -495,17 +616,23 @@ text = g_strdup_printf( "<span weight=\"bold\" color=\"red\">%s</span>", - error); + disp_error); gtk_label_set_markup(plugin_error, text); g_free(text); - g_free(error); + g_free(disp_error); g_free(name); + + g_error_free(error); } - gtk_list_store_set(GTK_LIST_STORE (model), iter, - 0, purple_plugin_is_loaded(plug), - -1); + if ((unload && purple_plugin_get_dependent_plugins(plug)) || + (!unload && purple_plugin_info_get_dependencies(info))) + update_loaded_plugins(model); + else + gtk_list_store_set(GTK_LIST_STORE (model), iter, + 0, purple_plugin_is_loaded(plug), + -1); g_free(iter); pidgin_plugins_save(); @@ -529,9 +656,12 @@ static void prefs_plugin_sel (GtkTreeSelection *sel, GtkTreeModel *model) { gchar *buf, *tmp, *name, *version; + gchar *authors = NULL; + const gchar * const *authorlist; GtkTreeIter iter; GValue val; PurplePlugin *plug; + PurplePluginInfo *info; if (!gtk_tree_selection_get_selected (sel, &model, &iter)) { @@ -549,9 +679,10 @@ val.g_type = 0; gtk_tree_model_get_value (model, &iter, 2, &val); plug = g_value_get_pointer(&val); + info = purple_plugin_get_info(plug); - name = g_markup_escape_text(purple_plugin_get_name(plug), -1); - version = g_markup_escape_text(purple_plugin_get_version(plug), -1); + name = g_markup_escape_text(purple_plugin_info_get_name(info), -1); + version = g_markup_escape_text(purple_plugin_info_get_version(info), -1); buf = g_strdup_printf( "<span size=\"larger\" weight=\"bold\">%s</span> " "<span size=\"smaller\">%s</span>", @@ -561,12 +692,19 @@ g_free(version); g_free(buf); - gtk_text_buffer_set_text(plugin_desc, purple_plugin_get_description(plug), -1); - gtk_label_set_text(plugin_author, purple_plugin_get_author(plug)); - gtk_label_set_text(plugin_filename, plug->path); + gtk_text_buffer_set_text(plugin_desc, purple_plugin_info_get_description(info), -1); + + authorlist = purple_plugin_info_get_authors(info); + if (authorlist) + authors = g_strjoinv(",\n", (gchar **)authorlist); + gtk_label_set_text(plugin_authors, authors); + g_free(authors); + + gtk_label_set_text(plugin_filename, purple_plugin_get_filename(plug)); g_free(plugin_website_uri); - plugin_website_uri = g_strdup(purple_plugin_get_homepage(plug)); + plugin_website_uri = g_strdup(purple_plugin_info_get_website(info)); + if (plugin_website_uri) { tmp = g_markup_escape_text(plugin_website_uri, -1); @@ -581,13 +719,13 @@ gtk_label_set_text(plugin_website, NULL); } - if (plug->error == NULL) + if (purple_plugin_info_get_error(info) == NULL) { gtk_label_set_text(plugin_error, NULL); } else { - tmp = g_markup_escape_text(plug->error, -1); + tmp = g_markup_escape_text(purple_plugin_info_get_error(info), -1); buf = g_strdup_printf( _("<span foreground=\"red\" weight=\"bold\">" "Error: %s\n" @@ -613,15 +751,17 @@ GtkTreeModel *model; GValue val; GtkTreeIter iter; - GList *it; + GList *list, *it; switch (response) { case GTK_RESPONSE_CLOSE: case GTK_RESPONSE_DELETE_EVENT: purple_request_close_with_handle(plugin_dialog); purple_signals_disconnect_by_handle(plugin_dialog); - for (it = purple_plugins_get_all(); it; it = g_list_next(it)) + list = purple_plugins_find_all(); + for (it = list; it; it = g_list_next(it)) pref_dialog_close(it->data); + g_list_free(list); gtk_widget_destroy(d); plugin_dialog = NULL; break; @@ -686,20 +826,30 @@ GtkTreeIter iter; GtkTreeView *treeview = GTK_TREE_VIEW(data); PurplePlugin *plugin = NULL; + PurplePluginInfo *info; GtkTreeModel *model = gtk_tree_view_get_model(treeview); PangoLayout *layout; int width, height; - char *markup, *name, *desc, *author; + const char * const *authorlist; + char *markup, *name, *desc; + char *authors = NULL, *pauthors = NULL; if (!gtk_tree_model_get_iter(model, &iter, path)) return FALSE; gtk_tree_model_get(model, &iter, 2, &plugin, -1); + info = purple_plugin_get_info(plugin); + authorlist = purple_plugin_info_get_authors(info); + + if (authorlist) + authors = g_strjoinv(", ", (gchar **)authorlist); + if (authors) + pauthors = g_markup_escape_text(authors, -1); markup = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> %s\n<b>%s:</b> %s", - name = g_markup_escape_text(purple_plugin_get_name(plugin), -1), - _("Description"), desc = g_markup_escape_text(purple_plugin_get_description(plugin), -1), - _("Author"), author = g_markup_escape_text(purple_plugin_get_author(plugin), -1)); + name = g_markup_escape_text(purple_plugin_info_get_name(info), -1), + _("Description"), desc = g_markup_escape_text(purple_plugin_info_get_description(info), -1), + (g_strv_length((gchar **)authorlist) > 1 ? _("Authors") : _("Author")), pauthors); layout = gtk_widget_create_pango_layout(tipwindow, NULL); pango_layout_set_markup(layout, markup, -1); @@ -716,7 +866,8 @@ g_free(markup); g_free(name); g_free(desc); - g_free(author); + g_free(pauthors); + g_free(authors); return TRUE; } @@ -771,12 +922,12 @@ gtk_label_set_selectable(plugin_error, TRUE); gtk_box_pack_start(vbox, GTK_WIDGET(plugin_error), FALSE, FALSE, 0); - plugin_author = GTK_LABEL(gtk_label_new(NULL)); - gtk_label_set_line_wrap(plugin_author, FALSE); - gtk_misc_set_alignment(GTK_MISC(plugin_author), 0, 0); - gtk_label_set_selectable(plugin_author, TRUE); + plugin_authors = GTK_LABEL(gtk_label_new(NULL)); + gtk_label_set_line_wrap(plugin_authors, FALSE); + gtk_misc_set_alignment(GTK_MISC(plugin_authors), 0, 0); + gtk_label_set_selectable(plugin_authors, TRUE); pidgin_add_widget_to_vbox(vbox, "", sg, - GTK_WIDGET(plugin_author), TRUE, &label); + GTK_WIDGET(plugin_authors), TRUE, &label); gtk_label_set_markup(GTK_LABEL(label), _("<b>Written by:</b>")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
--- a/pidgin/gtkplugin.h Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkplugin.h Fri Jan 31 18:02:20 2014 +0530 @@ -27,16 +27,36 @@ #define _PIDGINPLUGIN_H_ #include "pidgin.h" -#include "plugin.h" +#include "plugins.h" -typedef struct _PidginPluginUiInfo PidginPluginUiInfo; +#define PIDGIN_TYPE_PLUGIN_INFO (pidgin_plugin_info_get_type()) +#define PIDGIN_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_PLUGIN_INFO, PidginPluginInfo)) +#define PIDGIN_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_PLUGIN_INFO, PidginPluginInfoClass)) +#define PIDGIN_IS_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_PLUGIN_INFO)) +#define PIDGIN_IS_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_PLUGIN_INFO)) +#define PIDGIN_PLUGIN_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_PLUGIN_INFO, PidginPluginInfoClass)) + +/** @copydoc _PidginPluginInfo */ +typedef struct _PidginPluginInfo PidginPluginInfo; +/** @copydoc _PidginPluginInfoClass */ +typedef struct _PidginPluginInfoClass PidginPluginInfoClass; + +typedef GtkWidget *(*PidginPluginConfigFrameCb)(PurplePlugin *); /** - * A GTK+ UI structure for plugins. + * Extends #PurplePluginInfo to hold UI information for pidgin. */ -struct _PidginPluginUiInfo -{ - GtkWidget *(*get_config_frame)(PurplePlugin *plugin); +struct _PidginPluginInfo { + PurplePluginInfo parent; +}; + +/** + * PidginPluginInfoClass: + * + * The base class for all #PidginPluginInfo's. + */ +struct _PidginPluginInfoClass { + PurplePluginInfoClass parent_class; /*< private >*/ void (*_pidgin_reserved1)(void); @@ -45,16 +65,33 @@ void (*_pidgin_reserved4)(void); }; -#define PIDGIN_PLUGIN_TYPE PIDGIN_UI +G_BEGIN_DECLS + +/** + * Returns the GType for the PidginPluginInfo object. + */ +GType pidgin_plugin_info_get_type(void); -#define PIDGIN_IS_PIDGIN_PLUGIN(plugin) \ - ((plugin)->info != NULL && (plugin)->info->ui_info != NULL && \ - !strcmp((plugin)->info->ui_requirement, PIDGIN_PLUGIN_TYPE)) - -#define PIDGIN_PLUGIN_UI_INFO(plugin) \ - ((PidginPluginUiInfo *)(plugin)->info->ui_info) - -G_BEGIN_DECLS +/** + * Creates a new #PidginPluginInfo instance to be returned from + * gplugin_plugin_query() of a pidgin plugin, using the provided name/value + * pairs. + * + * See purple_plugin_info_new() for a list of available property names. + * Additionally, you can provide the property "gtk-config-frame-cb", + * which should be a callback that returns a GtkWidget for the plugin's + * configuration (see PidginPluginConfigFrameCb). + * + * @param first_property The first property name + * @param ... The value of the first property, followed optionally by more + * name/value pairs, followed by @c NULL + * + * @return A new #PidginPluginInfo instance. + * + * @see purple_plugin_info_new() + */ +PidginPluginInfo *pidgin_plugin_info_new(const char *first_property, ...) + G_GNUC_NULL_TERMINATED; /** * Saves all loaded plugins.
--- a/pidgin/gtkpounce.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkpounce.c Fri Jan 31 18:02:20 2014 +0530 @@ -30,7 +30,7 @@ #include "account.h" #include "conversation.h" #include "debug.h" -#include "prpl.h" +#include "protocol.h" #include "request.h" #include "server.h" #include "sound.h" @@ -201,7 +201,7 @@ account = purple_pounce_get_pouncer(pounce); - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_MEDIUM); pouncer = purple_account_get_username(account); pouncee = purple_pounce_get_pouncee(pounce);
--- a/pidgin/gtkprefs.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkprefs.c Fri Jan 31 18:02:20 2014 +0530 @@ -34,7 +34,7 @@ #include "notify.h" #include "prefs.h" #include "proxy.h" -#include "prpl.h" +#include "protocol.h" #include "request.h" #include "savedstatuses.h" #include "sound.h"
--- a/pidgin/gtkrequest.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkrequest.c Fri Jan 31 18:02:20 2014 +0530 @@ -107,7 +107,7 @@ if (!account) return; - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); image = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(G_OBJECT(pixbuf)); @@ -1600,11 +1600,11 @@ id[0] = '\0'; id++; - if (g_strcmp0(domain, "prpl") == 0) { + if (g_strcmp0(domain, "protocol") == 0) { PurpleAccount *account; - gchar *prpl, *accountname; - - prpl = id; + gchar *protocol_id, *accountname; + + protocol_id = id; accountname = strchr(id, ':'); if (!accountname) { @@ -1615,10 +1615,10 @@ accountname[0] = '\0'; accountname++; - account = purple_accounts_find(accountname, prpl); + account = purple_accounts_find(accountname, protocol_id); if (account) { - image = pidgin_create_prpl_icon(account, - PIDGIN_PRPL_ICON_SMALL); + image = pidgin_create_protocol_icon(account, + PIDGIN_PROTOCOL_ICON_SMALL); } } else if (g_strcmp0(domain, "e2ee") == 0) { image = pidgin_pixbuf_from_imgstore(
--- a/pidgin/gtkroomlist.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkroomlist.c Fri Jan 31 18:02:20 2014 +0530 @@ -230,13 +230,13 @@ char *name; PurpleAccount *account = purple_roomlist_get_account(info->list); PurpleConnection *gc = purple_account_get_connection(account); - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; if(gc != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + protocol = purple_connection_get_protocol(gc); - if(prpl_info != NULL && prpl_info->roomlist_room_serialize) - name = prpl_info->roomlist_room_serialize(info->room); + if(protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST_IFACE, room_serialize)) + name = purple_protocol_roomlist_iface_room_serialize(protocol, info->room); else name = g_strdup(purple_roomlist_room_get_name(info->room)); @@ -537,12 +537,12 @@ static gboolean account_filter_func(PurpleAccount *account) { PurpleConnection *conn = purple_account_get_connection(account); - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; if (conn && PURPLE_CONNECTION_IS_CONNECTED(conn)) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(conn)); + protocol = purple_connection_get_protocol(conn); - return (prpl_info && prpl_info->roomlist_get_list != NULL); + return (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST_IFACE, get_list)); } gboolean
--- a/pidgin/gtksavedstatuses.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtksavedstatuses.c Fri Jan 31 18:02:20 2014 +0530 @@ -1002,7 +1002,7 @@ const char *id = NULL, *name = NULL, *message = NULL; PurpleStatusPrimitive prim = PURPLE_STATUS_UNSET; - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_MEDIUM); if ((pixbuf != NULL) && !purple_account_is_connected(account)) { gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
--- a/pidgin/gtkstatusbox.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkstatusbox.c Fri Jan 31 18:02:20 2014 +0530 @@ -128,7 +128,7 @@ /** * This column stores the GdkPixbuf for the status emblem. Currently only 'saved' is stored. * In the GtkTreeModel for the dropdown, this is the stock-id (gchararray), and for the - * GtkTreeModel for the cell_view (for the account-specific statusbox), this is the prpl-icon + * GtkTreeModel for the cell_view (for the account-specific statusbox), this is the protocol icon * (GdkPixbuf) of the account. */ EMBLEM_COLUMN, @@ -494,12 +494,13 @@ case PROP_ICON_SEL: if (g_value_get_boolean(value)) { if (statusbox->account) { - PurplePlugin *plug = purple_plugins_find_with_id(purple_account_get_protocol_id(statusbox->account)); - if (plug) { - PurplePluginProtocolInfo *prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plug); - if (prplinfo && prplinfo->icon_spec.format != NULL) - setup_icon_box(statusbox); - } + PurpleBuddyIconSpec *icon_spec = NULL; + PurpleProtocol *protocol = + purple_protocols_find(purple_account_get_protocol_id(statusbox->account)); + if (protocol) + icon_spec = purple_protocol_get_icon_spec(protocol); + if (icon_spec && icon_spec->format != NULL) + setup_icon_box(statusbox); } else { setup_icon_box(statusbox); } @@ -715,7 +716,7 @@ text = g_strdup_printf("%s - <span size=\"smaller\" color=\"%s\">%s</span>", purple_account_get_username(status_box->account), aa_color, secondary ? secondary : primary); - emblem = pidgin_create_prpl_icon(status_box->account, PIDGIN_PRPL_ICON_SMALL); + emblem = pidgin_create_protocol_icon(status_box->account, PIDGIN_PROTOCOL_ICON_SMALL); } else if (secondary != NULL) { text = g_strdup_printf("%s<span size=\"smaller\" color=\"%s\"> - %s</span>", primary, aa_color, secondary); @@ -962,11 +963,11 @@ { GList *iter, *active_accts = purple_accounts_get_all_active(); PurpleAccount *acct1 = NULL; - const char *prpl1 = NULL; + const char *proto1 = NULL; if (active_accts) { acct1 = active_accts->data; - prpl1 = purple_account_get_protocol_id(acct1); + proto1 = purple_account_get_protocol_id(acct1); } else { /* there's no enabled account */ return NULL; @@ -977,7 +978,7 @@ PurpleAccount *acct2 = iter->data; GList *s1, *s2; - if (!g_str_equal(prpl1, purple_account_get_protocol_id(acct2))) { + if (!g_str_equal(proto1, purple_account_get_protocol_id(acct2))) { acct1 = NULL; break; } @@ -1450,46 +1451,46 @@ buddy_icon_set_cb(const char *filename, PidginStatusBox *box) { PurpleStoredImage *img = NULL; + PurpleBuddyIconSpec *icon_spec = NULL; if (box->account) { - PurplePlugin *plug = purple_find_prpl(purple_account_get_protocol_id(box->account)); - if (plug) { - PurplePluginProtocolInfo *prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plug); - if (prplinfo && prplinfo->icon_spec.format) { - gpointer data = NULL; - size_t len = 0; - if (filename) - data = pidgin_convert_buddy_icon(plug, filename, &len); - img = purple_buddy_icons_set_account_icon(box->account, data, len); - if (img) - /* - * set_account_icon doesn't give us a reference, but we - * unref one below (for the other code path) - */ - purple_imgstore_ref(img); - - purple_account_set_buddy_icon_path(box->account, filename); - - purple_account_set_bool(box->account, "use-global-buddyicon", (filename != NULL)); - } + PurpleProtocol *protocol = + purple_protocols_find(purple_account_get_protocol_id(box->account)); + if (protocol) + icon_spec = purple_protocol_get_icon_spec(protocol); + if (icon_spec && icon_spec->format) { + gpointer data = NULL; + size_t len = 0; + if (filename) + data = pidgin_convert_buddy_icon(protocol, filename, &len); + img = purple_buddy_icons_set_account_icon(box->account, data, len); + if (img) + /* + * set_account_icon doesn't give us a reference, but we + * unref one below (for the other code path) + */ + purple_imgstore_ref(img); + + purple_account_set_buddy_icon_path(box->account, filename); + + purple_account_set_bool(box->account, "use-global-buddyicon", (filename != NULL)); } } else { GList *accounts; for (accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) { PurpleAccount *account = accounts->data; - PurplePlugin *plug = purple_find_prpl(purple_account_get_protocol_id(account)); - if (plug) { - PurplePluginProtocolInfo *prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plug); - if (prplinfo != NULL && - purple_account_get_bool(account, "use-global-buddyicon", TRUE) && - prplinfo->icon_spec.format) { - gpointer data = NULL; - size_t len = 0; - if (filename) - data = pidgin_convert_buddy_icon(plug, filename, &len); - purple_buddy_icons_set_account_icon(account, data, len); - purple_account_set_buddy_icon_path(account, filename); - } + PurpleProtocol *protocol = + purple_protocols_find(purple_account_get_protocol_id(account)); + if (protocol) + icon_spec = purple_protocol_get_icon_spec(protocol); + if (icon_spec && icon_spec->format && + purple_account_get_bool(account, "use-global-buddyicon", TRUE)) { + gpointer data = NULL; + size_t len = 0; + if (filename) + data = pidgin_convert_buddy_icon(protocol, filename, &len); + purple_buddy_icons_set_account_icon(account, data, len); + purple_account_set_buddy_icon_path(account, filename); } }
--- a/pidgin/gtkthemes.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkthemes.c Fri Jan 31 18:02:20 2014 +0530 @@ -25,7 +25,7 @@ #include "conversation.h" #include "debug.h" -#include "prpl.h" +#include "protocol.h" #include "util.h" #include "gtkconv.h" @@ -409,7 +409,7 @@ } GSList *pidgin_themes_get_proto_smileys(const char *id) { - PurplePlugin *proto; + PurpleProtocol *protocol; struct smiley_list *list, *def; if ((current_smiley_theme == NULL) || (current_smiley_theme->list == NULL)) @@ -420,12 +420,12 @@ if (id == NULL) return def->smileys; - proto = purple_find_prpl(id); + protocol = purple_protocols_find(id); while (list) { if (!strcmp(list->sml, "default")) def = list; - else if (proto && !strcmp(proto->info->name, list->sml)) + else if (protocol && !strcmp(purple_protocol_get_name(protocol), list->sml)) break; list = list->next;
--- a/pidgin/gtkutils.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkutils.c Fri Jan 31 18:02:20 2014 +0530 @@ -50,7 +50,7 @@ #include "imgstore.h" #include "notify.h" #include "prefs.h" -#include "prpl.h" +#include "protocol.h" #include "request.h" #include "signals.h" #include "sound.h" @@ -551,19 +551,14 @@ } static GdkPixbuf * -pidgin_create_prpl_icon_from_prpl(PurplePlugin *prpl, PidginPrplIconSize size, PurpleAccount *account) +pidgin_create_icon_from_protocol(PurpleProtocol *protocol, PidginProtocolIconSize size, PurpleAccount *account) { - PurplePluginProtocolInfo *prpl_info; const char *protoname = NULL; char *tmp; char *filename = NULL; GdkPixbuf *pixbuf; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info->list_icon == NULL) - return NULL; - - protoname = prpl_info->list_icon(account, NULL); + protoname = purple_protocol_class_list_icon(protocol, account, NULL); if (protoname == NULL) return NULL; @@ -574,8 +569,8 @@ tmp = g_strconcat(protoname, ".png", NULL); filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", - size == PIDGIN_PRPL_ICON_SMALL ? "16" : - size == PIDGIN_PRPL_ICON_MEDIUM ? "22" : "48", + size == PIDGIN_PROTOCOL_ICON_SMALL ? "16" : + size == PIDGIN_PROTOCOL_ICON_MEDIUM ? "22" : "48", tmp, NULL); g_free(tmp); @@ -628,11 +623,11 @@ create_protocols_menu(const char *default_proto_id) { AopMenu *aop_menu = NULL; - PurplePlugin *plugin; + PurpleProtocol *protocol; GdkPixbuf *pixbuf = NULL; GtkTreeIter iter; GtkListStore *ls; - GList *p; + GList *list, *p; int i; ls = gtk_list_store_new(AOP_COLUMN_COUNT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER); @@ -641,27 +636,30 @@ aop_menu->default_item = 0; aop_menu->model = GTK_TREE_MODEL(ls); - for (p = purple_plugins_get_protocols(), i = 0; + list = purple_protocols_get_all(); + + for (p = list, i = 0; p != NULL; p = p->next, i++) { - plugin = (PurplePlugin *)p->data; - - pixbuf = pidgin_create_prpl_icon_from_prpl(plugin, PIDGIN_PRPL_ICON_SMALL, NULL); + protocol = PURPLE_PROTOCOL(p->data); + + pixbuf = pidgin_create_icon_from_protocol(protocol, PIDGIN_PROTOCOL_ICON_SMALL, NULL); gtk_list_store_append(ls, &iter); gtk_list_store_set(ls, &iter, AOP_ICON_COLUMN, pixbuf, - AOP_NAME_COLUMN, plugin->info->name, - AOP_DATA_COLUMN, plugin->info->id, + AOP_NAME_COLUMN, purple_protocol_get_name(protocol), + AOP_DATA_COLUMN, purple_protocol_get_id(protocol), -1); if (pixbuf) g_object_unref(pixbuf); - if (default_proto_id != NULL && !strcmp(plugin->info->id, default_proto_id)) + if (default_proto_id != NULL && !strcmp(purple_protocol_get_id(protocol), default_proto_id)) aop_menu->default_item = i; } + g_list_free(list); return aop_menu; } @@ -724,7 +722,7 @@ continue; } - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); if (pixbuf) { if (purple_account_is_disconnected(account) && show_all && @@ -913,24 +911,18 @@ void pidgin_retrieve_user_info_in_chat(PurpleConnection *conn, const char *name, int chat) { char *who = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; if (chat < 0) { pidgin_retrieve_user_info(conn, name); return; } - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(conn)); - if (prpl_info != NULL && prpl_info->get_cb_real_name) - who = prpl_info->get_cb_real_name(conn, chat, name); - if (prpl_info == NULL || prpl_info->get_cb_info == NULL) { - pidgin_retrieve_user_info(conn, who ? who : name); - g_free(who); - return; - } - - show_retrieveing_info(conn, who ? who : name); - prpl_info->get_cb_info(conn, chat, name); + protocol = purple_connection_get_protocol(conn); + if (protocol != NULL) + who = purple_protocol_chat_iface_get_user_real_name(protocol, conn, chat, name); + + pidgin_retrieve_user_info(conn, who ? who : name); g_free(who); } @@ -1022,34 +1014,31 @@ for (l = list; l != NULL; l = l->next) { PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info = NULL; - PurplePlugin *plugin; + PurpleProtocol *proto = NULL; if (all_accounts) { account = (PurpleAccount *)l->data; - plugin = purple_plugins_find_with_id( + proto = purple_protocols_find( purple_account_get_protocol_id(account)); - if (plugin == NULL) + if (proto == NULL) { account = NULL; continue; } - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); } else { gc = (PurpleConnection *)l->data; account = purple_connection_get_account(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + proto = purple_connection_get_protocol(gc); } - protoname = prpl_info->list_icon(account, NULL); + protoname = purple_protocol_class_list_icon(proto, account, NULL); if (!strcmp(protoname, protocol)) break; @@ -1064,34 +1053,31 @@ for (l = list; l != NULL; l = l->next) { PurpleConnection *gc; - PurplePluginProtocolInfo *prpl_info = NULL; - PurplePlugin *plugin; + PurpleProtocol *proto = NULL; if (all_accounts) { account = (PurpleAccount *)l->data; - plugin = purple_plugins_find_with_id( + proto = purple_protocols_find( purple_account_get_protocol_id(account)); - if (plugin == NULL) + if (proto == NULL) { account = NULL; continue; } - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); } else { gc = (PurpleConnection *)l->data; account = purple_connection_get_account(gc); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + proto = purple_connection_get_protocol(gc); } - protoname = prpl_info->list_icon(account, NULL); + protoname = purple_protocol_class_list_icon(proto, account, NULL); if (!strcmp(protoname, "aim") || !strcmp(protoname, "icq")) break; @@ -1446,7 +1432,7 @@ GdkPixbuf *pb; GList *files = purple_uri_list_extract_filenames((const gchar *) gtk_selection_data_get_data(sd)); PurpleConnection *gc = purple_account_get_connection(account); - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; #ifndef _WIN32 PurpleDesktopItem *item; #endif @@ -1495,14 +1481,14 @@ data->account = account; if (gc) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); - - if (prpl_info && prpl_info->options & OPT_PROTO_IM_IMAGE) + protocol = purple_connection_get_protocol(gc); + + if (protocol && purple_protocol_get_options(protocol) & OPT_PROTO_IM_IMAGE) im = TRUE; - if (prpl_info && prpl_info->can_receive_file) - ft = prpl_info->can_receive_file(gc, who); - else if (prpl_info && prpl_info->send_file) + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, can_receive)) + ft = purple_protocol_xfer_iface_can_receive(protocol, gc, who); + else if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, send)) ft = TRUE; if (im && ft) @@ -1618,7 +1604,7 @@ if ((spec == NULL) || !(spec->scale_rules & rules)) return; - purple_buddy_icon_get_scale_size(spec, width, height); + purple_buddy_icon_spec_get_scaled_size(spec, width, height); /* and now for some arbitrary sanity checks */ if(*width > 100) @@ -1694,16 +1680,16 @@ } GdkPixbuf * -pidgin_create_prpl_icon(PurpleAccount *account, PidginPrplIconSize size) +pidgin_create_protocol_icon(PurpleAccount *account, PidginProtocolIconSize size) { - PurplePlugin *prpl; + PurpleProtocol *protocol; g_return_val_if_fail(account != NULL, NULL); - prpl = purple_find_prpl(purple_account_get_protocol_id(account)); - if (prpl == NULL) + protocol = purple_protocols_find(purple_account_get_protocol_id(account)); + if (protocol == NULL) return NULL; - return pidgin_create_prpl_icon_from_prpl(prpl, size, account); + return pidgin_create_icon_from_protocol(protocol, size, account); } static void @@ -2284,14 +2270,13 @@ } gpointer -pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len) +pidgin_convert_buddy_icon(PurpleProtocol *protocol, const char *path, size_t *len) { - PurplePluginProtocolInfo *prpl_info; PurpleBuddyIconSpec *spec; int orig_width, orig_height, new_width, new_height; GdkPixbufFormat *format; char **pixbuf_formats; - char **prpl_formats; + char **protocol_formats; GError *error = NULL; gchar *contents; gsize length; @@ -2300,8 +2285,7 @@ int i; gchar *tmp; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - spec = &prpl_info->icon_spec; + spec = purple_protocol_get_icon_spec(protocol); g_return_val_if_fail(spec->format != NULL, NULL); format = gdk_pixbuf_get_file_info(path, &orig_width, &orig_height); @@ -2311,10 +2295,10 @@ } pixbuf_formats = gdk_pixbuf_format_get_extensions(format); - prpl_formats = g_strsplit(spec->format, ",", 0); - - if (str_array_match(pixbuf_formats, prpl_formats) && /* This is an acceptable format AND */ - (!(spec->scale_rules & PURPLE_ICON_SCALE_SEND) || /* The prpl doesn't scale before it sends OR */ + protocol_formats = g_strsplit(spec->format, ",", 0); + + if (str_array_match(pixbuf_formats, protocol_formats) && /* This is an acceptable format AND */ + (!(spec->scale_rules & PURPLE_ICON_SCALE_SEND) || /* The protocol doesn't scale before it sends OR */ (spec->min_width <= orig_width && spec->max_width >= orig_width && spec->min_height <= orig_height && spec->max_height >= orig_height))) /* The icon is the correct size */ { @@ -2323,7 +2307,7 @@ if (!g_file_get_contents(path, &contents, &length, &error)) { purple_debug_warning("buddyicon", "Could not get file contents " "of %s: %s\n", path, error->message); - g_strfreev(prpl_formats); + g_strfreev(protocol_formats); return NULL; } @@ -2332,7 +2316,7 @@ constraints. Great! Return it without making any changes. */ if (len) *len = length; - g_strfreev(prpl_formats); + g_strfreev(protocol_formats); return contents; } @@ -2348,7 +2332,7 @@ purple_debug_warning("buddyicon", "Could not open icon '%s' for " "conversion: %s\n", path, error->message); g_error_free(error); - g_strfreev(prpl_formats); + g_strfreev(protocol_formats); return NULL; } original = g_object_ref(G_OBJECT(pixbuf)); @@ -2361,7 +2345,7 @@ (orig_width < spec->min_width || orig_width > spec->max_width || orig_height < spec->min_height || orig_height > spec->max_height)) { - purple_buddy_icon_get_scale_size(spec, &new_width, &new_height); + purple_buddy_icon_spec_get_scaled_size(spec, &new_width, &new_height); g_object_unref(G_OBJECT(pixbuf)); pixbuf = gdk_pixbuf_scale_simple(original, new_width, new_height, GDK_INTERP_HYPER); @@ -2369,31 +2353,31 @@ scale_factor = 1; do { - for (i = 0; prpl_formats[i]; i++) { + for (i = 0; protocol_formats[i]; i++) { int quality = 100; do { const char *key = NULL; const char *value = NULL; gchar tmp_buf[4]; - purple_debug_info("buddyicon", "Converting buddy icon to %s\n", prpl_formats[i]); - - if (g_str_equal(prpl_formats[i], "png")) { + purple_debug_info("buddyicon", "Converting buddy icon to %s\n", protocol_formats[i]); + + if (g_str_equal(protocol_formats[i], "png")) { key = "compression"; value = "9"; - } else if (g_str_equal(prpl_formats[i], "jpeg")) { + } else if (g_str_equal(protocol_formats[i], "jpeg")) { sprintf(tmp_buf, "%u", quality); key = "quality"; value = tmp_buf; } if (!gdk_pixbuf_save_to_buffer(pixbuf, &contents, &length, - prpl_formats[i], &error, key, value, NULL)) + protocol_formats[i], &error, key, value, NULL)) { /* The NULL checking of error is necessary due to this bug: * http://bugzilla.gnome.org/show_bug.cgi?id=405539 */ purple_debug_warning("buddyicon", - "Could not convert to %s: %s\n", prpl_formats[i], + "Could not convert to %s: %s\n", protocol_formats[i], (error && error->message) ? error->message : "Unknown error"); g_error_free(error); error = NULL; @@ -2411,10 +2395,10 @@ "%dx%d to %dx%d, format=%s, quality=%u, " "filesize=%" G_GSIZE_FORMAT "\n", orig_width, orig_height, new_width, new_height, - prpl_formats[i], quality, length); + protocol_formats[i], quality, length); if (len) *len = length; - g_strfreev(prpl_formats); + g_strfreev(protocol_formats); g_object_unref(G_OBJECT(pixbuf)); g_object_unref(G_OBJECT(original)); return contents; @@ -2422,7 +2406,7 @@ g_free(contents); - if (!g_str_equal(prpl_formats[i], "jpeg")) { + if (!g_str_equal(protocol_formats[i], "jpeg")) { /* File size was too big and we can't lower the quality, so skip to the next image type. */ break; @@ -2442,12 +2426,12 @@ g_object_unref(G_OBJECT(pixbuf)); pixbuf = gdk_pixbuf_scale_simple(original, new_width, new_height, GDK_INTERP_HYPER); } while ((new_width > 10 || new_height > 10) && new_width > spec->min_width && new_height > spec->min_height); - g_strfreev(prpl_formats); + g_strfreev(protocol_formats); g_object_unref(G_OBJECT(pixbuf)); g_object_unref(G_OBJECT(original)); tmp = g_strdup_printf(_("The file '%s' is too large for %s. Please try a smaller image.\n"), - path, plugin->info->name); + path, purple_protocol_get_name(protocol)); purple_notify_error(NULL, _("Icon Error"), _("Could not set icon"), tmp, NULL); g_free(tmp);
--- a/pidgin/gtkutils.h Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkutils.h Fri Jan 31 18:02:20 2014 +0530 @@ -28,7 +28,7 @@ #include "gtkconv.h" #include "pidgin.h" -#include "prpl.h" +#include "protocol.h" #include "util.h" typedef enum @@ -49,10 +49,10 @@ typedef enum { - PIDGIN_PRPL_ICON_SMALL, - PIDGIN_PRPL_ICON_MEDIUM, - PIDGIN_PRPL_ICON_LARGE -} PidginPrplIconSize; + PIDGIN_PROTOCOL_ICON_SMALL, + PIDGIN_PROTOCOL_ICON_MEDIUM, + PIDGIN_PROTOCOL_ICON_LARGE +} PidginProtocolIconSize; #ifndef _WIN32 typedef enum @@ -496,7 +496,7 @@ void pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char *who); /** - * Convenience wrapper for purple_buddy_icon_get_scale_size + * Convenience wrapper for purple_buddy_icon_spec_get_scaled_size */ void pidgin_buddy_icon_get_scale_size(GdkPixbuf *buf, PurpleBuddyIconSpec *spec, PurpleIconScaleRules rules, int *width, int *height); @@ -514,7 +514,7 @@ * to allocate the image buffer, or the image file * contained invalid data. */ -GdkPixbuf *pidgin_create_prpl_icon(PurpleAccount *account, PidginPrplIconSize size); +GdkPixbuf *pidgin_create_protocol_icon(PurpleAccount *account, PidginProtocolIconSize size); /** * Creates a status icon for a given primitve @@ -593,13 +593,13 @@ /** * Converts a buddy icon to the required size and format * - * @param plugin The prpl to convert the icon + * @param protocol The protocol to convert the icon * @param path The path of a file to convert * @param len If not @c NULL, the length of the returned data will be set here. * * @return The converted image data, or @c NULL if an error occurred. */ -gpointer pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len); +gpointer pidgin_convert_buddy_icon(PurpleProtocol *protocol, const char *path, size_t *len); /** * Converts "->" and "<-" in strings to Unicode arrow characters, for use in referencing
--- a/pidgin/gtkwebviewtoolbar.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkwebviewtoolbar.c Fri Jan 31 18:02:20 2014 +0530 @@ -590,7 +590,7 @@ g_free(filename); gtk_webview_insert_image(GTK_WEBVIEW(toolbar->webview), id); - /* TODO: do it after passing an image to prpl, not before + /* TODO: do it after passing an image to protocol, not before * purple_imgstore_unref_by_id(id); */ } @@ -975,7 +975,7 @@ const gchar *who = purple_conversation_get_name(conv); PurpleConnection *gc = purple_conversation_get_connection(conv); - purple_prpl_send_attention(gc, who, 0); + purple_protocol_send_attention(gc, who, 0); gtk_widget_grab_focus(toolbar->webview); } @@ -1621,15 +1621,15 @@ { GtkWebViewToolbarPriv *priv = GTK_WEBVIEWTOOLBAR_GET_PRIVATE(toolbar); PurpleConnection *gc = purple_conversation_get_connection(conv); - PurplePlugin *prpl = purple_connection_get_prpl(gc); + PurpleProtocol *protocol = purple_connection_get_protocol(gc); priv->active_conv = conv; /* gray out attention button on protocols that don't support it for the time being it is always disabled for chats */ gtk_action_set_sensitive(priv->attention, - conv && prpl && PURPLE_IS_IM_CONVERSATION(conv) && - PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention != NULL); + conv && protocol && PURPLE_IS_IM_CONVERSATION(conv) && + PURPLE_PROTOCOL_IMPLEMENTS(protocol, ATTENTION_IFACE, send)); gtk_action_set_sensitive(priv->smiley, pidgin_themes_get_proto_smileys(priv->sml) != NULL);
--- a/pidgin/gtkwhiteboard.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkwhiteboard.c Fri Jan 31 18:02:20 2014 +0530 @@ -330,7 +330,7 @@ /* * Whiteboard start button on conversation window (move this code to gtkconv? - * and use new prpl_info member?) + * and use new protocol member?) */ #if 0 static void pidginwhiteboard_button_start_press(GtkButton *button, gpointer data) @@ -607,7 +607,7 @@ MotionCount = 0; */ - /* Send draw list to prpl draw_list handler */ + /* Send draw list to protocol draw_list handler */ purple_whiteboard_send_draw_list(gtkwb->wb, draw_list); pidgin_whiteboard_set_canvas_as_icon(gtkwb);
--- a/pidgin/gtkwhiteboard.h Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkwhiteboard.h Fri Jan 31 18:02:20 2014 +0530 @@ -37,7 +37,7 @@ #define BRUSH_STATE_DOWN 1 #define BRUSH_STATE_MOTION 2 -/* XXX: This seems duplicated with the Yahoo! Doodle prpl code. +/* XXX: This seems duplicated with the Yahoo! Doodle protocol code. * XXX: How should they work together? */ #define PALETTE_NUM_COLORS 7
--- a/pidgin/gtkxfer.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/gtkxfer.c Fri Jan 31 18:02:20 2014 +0530 @@ -29,7 +29,7 @@ #include "debug.h" #include "notify.h" #include "xfer.h" -#include "prpl.h" +#include "protocol.h" #include "util.h" #include "gtkxfer.h" @@ -1171,10 +1171,10 @@ } } - /* Try the first format given by the PRPL without options */ + /* Try the first format given by the protocol without options */ if (format == NULL) { purple_debug_info("xfer", - "creating thumbnail of format %s as demanded by PRPL\n", + "creating thumbnail of format %s as demanded by protocol\n", formats_split[0]); format = formats_split[0]; }
--- a/pidgin/libpidgin.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/libpidgin.c Fri Jan 31 18:02:20 2014 +0530 @@ -35,7 +35,7 @@ #include "network.h" #include "notify.h" #include "prefs.h" -#include "prpl.h" +#include "protocol.h" #include "pounce.h" #include "sound.h" #include "status.h" @@ -742,17 +742,6 @@ purple_core_set_ui_ops(pidgin_core_get_ui_ops()); purple_eventloop_set_ui_ops(pidgin_eventloop_get_ui_ops()); - /* - * Set plugin search directories. Give priority to the plugins - * in user's home directory. - */ - search_path = g_build_filename(purple_user_dir(), "plugins", NULL); - if (!g_stat(search_path, &st)) - g_mkdir(search_path, S_IRUSR | S_IWUSR | S_IXUSR); - purple_plugins_add_search_path(search_path); - g_free(search_path); - purple_plugins_add_search_path(LIBDIR); - if (!purple_core_init(PIDGIN_UI)) { fprintf(stderr, "Initialization of the libpurple core failed. Dumping core.\n" @@ -763,6 +752,15 @@ abort(); } + search_path = g_build_filename(purple_user_dir(), "plugins", NULL); + if (!g_stat(search_path, &st)) + g_mkdir(search_path, S_IRUSR | S_IWUSR | S_IXUSR); + purple_plugins_add_search_path(search_path); + g_free(search_path); + + purple_plugins_add_search_path(LIBDIR); + purple_plugins_refresh(); + if (opt_si && !purple_core_ensure_single_instance()) { #ifdef HAVE_DBUS DBusConnection *conn = purple_dbus_get_connection();
--- a/pidgin/plugins/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -1,4 +1,4 @@ -DIST_SUBDIRS = cap disco gestures gevolution musicmessaging perl ticker +DIST_SUBDIRS = cap disco gestures gevolution musicmessaging ticker if BUILD_GEVOLUTION GEVOLUTION_DIR = gevolution @@ -12,10 +12,6 @@ CAP_DIR = cap endif -if USE_PERL -PERL_DIR = perl -endif - if ENABLE_GESTURES GESTURE_DIR = gestures endif @@ -25,7 +21,6 @@ $(GESTURE_DIR) \ $(GEVOLUTION_DIR) \ $(MUSICMESSAGING_DIR) \ - $(PERL_DIR) \ disco \ ticker @@ -132,6 +127,7 @@ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ $(GTK_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(UNITY_CFLAGS) \ $(WEBKIT_CFLAGS) \ $(GSTREAMER_CFLAGS) \
--- a/pidgin/plugins/cap/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/cap/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -24,6 +24,7 @@ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ $(GTK_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(SQLITE3_CFLAGS) EXTRA_DIST = Makefile.mingw
--- a/pidgin/plugins/cap/cap.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/cap/cap.c Fri Jan 31 18:02:20 2014 +0530 @@ -33,9 +33,9 @@ static double generate_prediction_for(PurpleBuddy *buddy) { double prediction = 1.0f; gboolean generated = FALSE; - gchar *buddy_name = buddy->name; - const gchar *protocol_id = purple_account_get_protocol_id(buddy->account); - const gchar *account_id = purple_account_get_username(buddy->account); + const gchar *buddy_name = purple_buddy_get_name(buddy); + const gchar *protocol_id = purple_account_get_protocol_id(purple_buddy_get_account(buddy)); + const gchar *account_id = purple_account_get_username(purple_buddy_get_account(buddy)); const gchar *status_id = purple_status_get_id(get_status_for(buddy)); time_t t = time(NULL); struct tm *current_time = localtime(&t); @@ -113,7 +113,7 @@ g_return_val_if_fail(buddy != NULL, NULL); - stats = g_hash_table_lookup(_buddy_stats, buddy->name); + stats = g_hash_table_lookup(_buddy_stats, purple_buddy_get_name(buddy)); if(!stats) { stats = g_malloc0(sizeof(CapStatistics)); stats->last_message = -1; @@ -121,7 +121,7 @@ stats->last_seen = -1; stats->last_status_id = ""; - g_hash_table_insert(_buddy_stats, g_strdup(buddy->name), stats); + g_hash_table_insert(_buddy_stats, g_strdup(purple_buddy_get_name(buddy)), stats); } else { /* This may actually be a different PurpleBuddy than what is in stats. * We replace stats->buddy to make sure we're looking at a valid pointer. */ @@ -290,9 +290,9 @@ } static void insert_cap_success(CapStatistics *stats) { - gchar *buddy_name = stats->buddy->name; - const gchar *protocol_id = purple_account_get_protocol_id(stats->buddy->account); - const gchar *account_id = purple_account_get_username(stats->buddy->account); + gchar *buddy_name = purple_buddy_get_name(stats->buddy); + const gchar *protocol_id = purple_account_get_protocol_id(purple_buddy_get_account(stats->buddy)); + const gchar *account_id = purple_account_get_username(purple_buddy_get_account(stats->buddy)); const gchar *status_id = (stats->last_message_status_id) ? stats->last_message_status_id : purple_status_get_id(get_status_for(stats->buddy)); @@ -316,9 +316,9 @@ } static void insert_cap_failure(CapStatistics *stats) { - gchar *buddy_name = stats->buddy->name; - const gchar *protocol_id = purple_account_get_protocol_id(stats->buddy->account); - const gchar *account_id = purple_account_get_username(stats->buddy->account); + gchar *buddy_name = purple_buddy_get_name(stats->buddy); + const gchar *protocol_id = purple_account_get_protocol_id(purple_buddy_get_account(stats->buddy)); + const gchar *account_id = purple_account_get_username(purple_buddy_get_account(stats->buddy)); const gchar *status_id = (stats->last_message_status_id) ? stats->last_message_status_id : purple_status_get_id(get_status_for(stats->buddy)); @@ -438,7 +438,7 @@ /* drawing-tooltip */ static void drawing_tooltip(PurpleBlistNode *node, GString *text, gboolean full) { - if(node->type == PURPLE_BLIST_BUDDY_NODE) { + if(PURPLE_IS_BUDDY(node)) { PurpleBuddy *buddy = PURPLE_BUDDY(node); CapStatistics *stats = get_stats_for(buddy); /* get the probability that this buddy will respond and add to the tooltip */ @@ -625,9 +625,9 @@ return; status_id = purple_status_get_id(status); - buddy_name = statistics->buddy->name; - protocol_id = purple_account_get_protocol_id(statistics->buddy->account); - account_id = purple_account_get_username(statistics->buddy->account); + buddy_name = purple_buddy_get_name(statistics->buddy); + protocol_id = purple_account_get_protocol_id(purple_buddy_get_account(statistics->buddy)); + account_id = purple_account_get_username(purple_buddy_get_account(statistics->buddy)); statistics->last_status_id = purple_status_get_id(status); @@ -646,25 +646,6 @@ /* Purple plugin specific code */ -static gboolean plugin_load(PurplePlugin *plugin) { - _plugin_pointer = plugin; - _signals_connected = FALSE; - - /* buddy_stats is a hashtable where strings are keys - * and the keys are a buddies account id (PurpleBuddy.name). - * keys/values are automatically deleted */ - _buddy_stats = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_stats); - - /* ? - Can't remember at the moment - */ - _my_offline_times = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - - if(create_database_connection()) { - add_plugin_functionality(plugin); - } - return TRUE; -} - static void add_plugin_functionality(PurplePlugin *plugin) { if(_signals_connected) return; @@ -751,21 +732,6 @@ } } -static gboolean plugin_unload(PurplePlugin *plugin) { - purple_debug_info("cap", "CAP plugin unloading\n"); - - /* clean up memory allocations */ - if(_buddy_stats) { - g_hash_table_foreach(_buddy_stats, write_stats_on_unload, NULL); - g_hash_table_destroy(_buddy_stats); - } - - /* close database connection */ - destroy_database_connection(); - - return TRUE; -} - static CapPrefsUI * create_cap_prefs_ui() { CapPrefsUI *ui = g_malloc(sizeof(CapPrefsUI)); @@ -868,7 +834,7 @@ return ui; } -static void cap_prefs_ui_destroy_cb(GtkObject *object, gpointer user_data) { +static void cap_prefs_ui_destroy_cb(GObject *object, gpointer user_data) { CapPrefsUI *ui = user_data; if(_db) { add_plugin_functionality(_plugin_pointer); @@ -880,39 +846,6 @@ purple_prefs_set_int(user_data, gtk_spin_button_get_value_as_int(spinbutton)); } -static PidginPluginUiInfo ui_info = { - get_config_frame, - 0 /* page_num (reserved) */, - NULL,NULL,NULL,NULL -}; - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - CAP_PLUGIN_ID, /**< id */ - N_("Contact Availability Prediction"), /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Contact Availability Prediction plugin."), /** summary */ - N_("Displays statistical information about your buddies' availability"), - /** description */ - "Geoffrey Foster <geoffrey.foster@gmail.com>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, - NULL,NULL,NULL,NULL -}; - static GtkWidget * get_config_frame(PurplePlugin *plugin) { CapPrefsUI *ui = create_cap_prefs_ui(); @@ -924,11 +857,67 @@ return ui->ret; } -static void init_plugin(PurplePlugin *plugin) { +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Geoffrey Foster <geoffrey.foster@gmail.com>", + NULL + }; + + return pidgin_plugin_info_new( + "id", CAP_PLUGIN_ID, + "name", N_("Contact Availability Prediction"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Contact Availability Prediction plugin."), + "description", N_("Displays statistical information about " + "your buddies' availability"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); +} + +static gboolean plugin_load(PurplePlugin *plugin, GError **error) { purple_prefs_add_none("/plugins/gtk/cap"); purple_prefs_add_int("/plugins/gtk/cap/max_seen_difference", 1); purple_prefs_add_int("/plugins/gtk/cap/max_msg_difference", 10); purple_prefs_add_int("/plugins/gtk/cap/threshold", 5); + + _plugin_pointer = plugin; + _signals_connected = FALSE; + + /* buddy_stats is a hashtable where strings are keys + * and the keys are a buddies account id (PurpleBuddy.name). + * keys/values are automatically deleted */ + _buddy_stats = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_stats); + + /* ? - Can't remember at the moment + */ + _my_offline_times = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + if(create_database_connection()) { + add_plugin_functionality(plugin); + } + return TRUE; } -PURPLE_INIT_PLUGIN(cap, init_plugin, info); +static gboolean plugin_unload(PurplePlugin *plugin, GError **error) { + purple_debug_info("cap", "CAP plugin unloading\n"); + + /* clean up memory allocations */ + if(_buddy_stats) { + g_hash_table_foreach(_buddy_stats, write_stats_on_unload, NULL); + g_hash_table_destroy(_buddy_stats); + } + + /* close database connection */ + destroy_database_connection(); + + return TRUE; +} + +PURPLE_PLUGIN_INIT(cap, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/cap/cap.h Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/cap/cap.h Fri Jan 31 18:02:20 2014 +0530 @@ -113,16 +113,15 @@ static void insert_status_change(CapStatistics *statistics); static void insert_status_change_from_purple_status(CapStatistics *statistics, PurpleStatus *status); static void insert_word_count(const char *sender, const char *receiver, guint count); -static gboolean plugin_load(PurplePlugin *plugin); +static gboolean plugin_load(PurplePlugin *plugin, GError **error); static void add_plugin_functionality(PurplePlugin *plugin); static void cancel_conversation_timeouts(gpointer key, gpointer value, gpointer user_data); static void remove_plugin_functionality(PurplePlugin *plugin); static void write_stats_on_unload(gpointer key, gpointer value, gpointer user_data); -static gboolean plugin_unload(PurplePlugin *plugin); +static gboolean plugin_unload(PurplePlugin *plugin, GError **error); static CapPrefsUI * create_cap_prefs_ui(void); -static void cap_prefs_ui_destroy_cb(GtkObject *object, gpointer user_data); +static void cap_prefs_ui_destroy_cb(GObject *object, gpointer user_data); static void numeric_spinner_prefs_cb(GtkSpinButton *spinbutton, gpointer user_data); static GtkWidget * get_config_frame(PurplePlugin *plugin); -static void init_plugin(PurplePlugin *plugin); #endif
--- a/pidgin/plugins/contact_priority.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/contact_priority.c Fri Jan 31 18:02:20 2014 +0530 @@ -162,55 +162,39 @@ return ret; } -static PidginPluginUiInfo ui_info = +static PidginPluginInfo * +plugin_query(GError **error) { - get_config_frame, - /* Padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ + const gchar * const authors[] = { + "Etan Reisner <deryni@eden.rutgers.edu>", + NULL + }; - CONTACT_PRIORITY_PLUGIN_ID, /**< id */ - N_("Contact Priority"), /**< name */ - DISPLAY_VERSION, /**< version */ - /**< summary */ - N_("Allows for controlling the values associated with different buddy states."), - /**< description */ - N_("Allows for changing the point values of idle/away/offline states for buddies in contact priority computations."), - "Etan Reisner <deryni@eden.rutgers.edu>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ + return pidgin_plugin_info_new( + "id", CONTACT_PRIORITY_PLUGIN_ID, + "name", N_("Contact Priority"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Allows for controlling the values associated with different buddy states."), + "description", N_("Allows for changing the point values of idle/away/offline states for buddies in contact priority computations."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); } -PURPLE_INIT_PLUGIN(contactpriority, init_plugin, info) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +PURPLE_PLUGIN_INIT(contactpriority, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/convcolors.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/convcolors.c Fri Jan 31 18:02:20 2014 +0530 @@ -21,10 +21,11 @@ #define PLUGIN_ID "gtk-plugin_pack-convcolors" #define PLUGIN_NAME N_("Conversation Colors") +#define PLUGIN_CATEGORY N_("User interface") #define PLUGIN_STATIC_NAME ConversationColors #define PLUGIN_SUMMARY N_("Customize colors in the conversation window") #define PLUGIN_DESCRIPTION N_("Customize colors in the conversation window") -#define PLUGIN_AUTHOR "Sadrul H Chowdhury <sadrul@users.sourceforge.net>" +#define PLUGIN_AUTHORS {"Sadrul H Chowdhury <sadrul@users.sourceforge.net>", NULL} /* System headers */ #include <gdk/gdk.h> @@ -172,24 +173,6 @@ return FALSE; } -static gboolean -plugin_load(PurplePlugin *plugin) -{ - purple_signal_connect(pidgin_conversations_get_handle(), - "displaying-im-msg", plugin, - PURPLE_CALLBACK(displaying_msg), NULL); - purple_signal_connect(pidgin_conversations_get_handle(), - "displaying-chat-msg", plugin, - PURPLE_CALLBACK(displaying_msg), NULL); - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - return TRUE; -} - /* Ripped from PurpleRC */ static void color_response(GtkDialog *color_dialog, gint response, const char *data) @@ -381,54 +364,28 @@ return ret; } -static PidginPluginUiInfo ui_info = +static PidginPluginInfo * +plugin_query(GError **error) { - get_config_frame, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, /* Magic */ - PURPLE_MAJOR_VERSION, /* Purple Major Version */ - PURPLE_MINOR_VERSION, /* Purple Minor Version */ - PURPLE_PLUGIN_STANDARD, /* plugin type */ - PIDGIN_PLUGIN_TYPE, /* ui requirement */ - 0, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ + const gchar * const authors[] = PLUGIN_AUTHORS; - PLUGIN_ID, /* plugin id */ - PLUGIN_NAME, /* name */ - DISPLAY_VERSION, /* version */ - PLUGIN_SUMMARY, /* summary */ - PLUGIN_DESCRIPTION, /* description */ - PLUGIN_AUTHOR, /* author */ - PURPLE_WEBSITE, /* website */ - - plugin_load, /* load */ - plugin_unload, /* unload */ - NULL, /* destroy */ + return pidgin_plugin_info_new( + "id", PLUGIN_ID, + "name", PLUGIN_NAME, + "version", DISPLAY_VERSION, + "category", PLUGIN_CATEGORY, + "summary", PLUGIN_SUMMARY, + "description", PLUGIN_DESCRIPTION, + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); +} - &ui_info, /* ui_info */ - NULL, /* extra_info */ - NULL, /* prefs_info */ - NULL, /* actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) { purple_prefs_add_none(PREF_PREFIX); @@ -459,6 +416,20 @@ purple_prefs_add_bool(PREF_SYSTEM_E, TRUE); purple_prefs_add_bool(PREF_ERROR_E, TRUE); purple_prefs_add_bool(PREF_NICK_E, TRUE); + + purple_signal_connect(pidgin_conversations_get_handle(), + "displaying-im-msg", plugin, + PURPLE_CALLBACK(displaying_msg), NULL); + purple_signal_connect(pidgin_conversations_get_handle(), + "displaying-chat-msg", plugin, + PURPLE_CALLBACK(displaying_msg), NULL); + return TRUE; } -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +PURPLE_PLUGIN_INIT(PLUGIN_STATIC_NAME, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/crazychat/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/crazychat/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -36,6 +36,7 @@ -I$(top_builddir)/libpurple \ -I$(top_srcdir)/pidgin \ $(GTK_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(GTKGLEXT_CFLAGS) \ $(DEBUG_CPPFLAGS) \ $(DEBUG_CFLAGS) \
--- a/pidgin/plugins/crazychat/cc_pidgin_plugin.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/crazychat/cc_pidgin_plugin.c Fri Jan 31 18:02:20 2014 +0530 @@ -2,7 +2,7 @@ #include <assert.h> #include "internal.h" -#include "plugin.h" +#include "plugins.h" #include "gtkplugin.h" #include "gtkblist.h" #include "gtkutils.h"
--- a/pidgin/plugins/disco/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/disco/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -25,4 +25,5 @@ -I$(top_builddir)/libpurple \ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ - $(GTK_CFLAGS) + $(GTK_CFLAGS) \ + $(GPLUGIN_CFLAGS)
--- a/pidgin/plugins/disco/gtkdisco.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/disco/gtkdisco.c Fri Jan 31 18:02:20 2014 +0530 @@ -423,7 +423,7 @@ static gboolean account_filter_func(PurpleAccount *account) { - return purple_strequal(purple_account_get_protocol_id(account), XMPP_PLUGIN_ID); + return purple_strequal(purple_account_get_protocol_id(account), XMPP_PROTOCOL_ID); } static gboolean
--- a/pidgin/plugins/disco/xmppdisco.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/disco/xmppdisco.c Fri Jan 31 18:02:20 2014 +0530 @@ -128,9 +128,9 @@ g_hash_table_remove(iq_callbacks, id); if (g_hash_table_size(iq_callbacks) == 0) { - PurplePlugin *prpl = purple_connection_get_prpl(pc); + PurpleProtocol *protocol = purple_connection_get_protocol(pc); iq_listening = FALSE; - purple_signal_disconnect(prpl, "jabber-receiving-iq", my_plugin, + purple_signal_disconnect(protocol, "jabber-receiving-iq", my_plugin, PURPLE_CALLBACK(xmpp_iq_received)); } @@ -151,9 +151,9 @@ g_hash_table_insert(iq_callbacks, id, cbdata); if (!iq_listening) { - PurplePlugin *prpl = purple_plugins_find_with_id(XMPP_PLUGIN_ID); + PurpleProtocol *protocol = purple_protocols_find(XMPP_PROTOCOL_ID); iq_listening = TRUE; - purple_signal_connect(prpl, "jabber-receiving-iq", my_plugin, + purple_signal_connect(protocol, "jabber-receiving-iq", my_plugin, PURPLE_CALLBACK(xmpp_iq_received), NULL); } } @@ -178,7 +178,7 @@ /* Steals id */ xmpp_iq_register_callback(pc, id, cbdata, cb); - purple_signal_emit(purple_connection_get_prpl(pc), "jabber-sending-xmlnode", + purple_signal_emit(purple_connection_get_protocol(pc), "jabber-sending-xmlnode", pc, &iq); if (iq != NULL) purple_xmlnode_free(iq); @@ -204,7 +204,7 @@ /* Steals id */ xmpp_iq_register_callback(pc, id, cbdata, cb); - purple_signal_emit(purple_connection_get_prpl(pc), "jabber-sending-xmlnode", + purple_signal_emit(purple_connection_get_protocol(pc), "jabber-sending-xmlnode", pc, &iq); if (iq != NULL) purple_xmlnode_free(iq); @@ -250,10 +250,10 @@ const char *from; const char *to; } disco_type_mappings[] = { - { "gadu-gadu", "gadu-gadu" }, /* the prpl is prpl-gg, but list_icon returns "gadu-gadu" */ + { "gadu-gadu", "gadu-gadu" }, /* the protocol is gg, but list_icon returns "gadu-gadu" */ { "sametime", "meanwhile" }, { "myspaceim", "myspace" }, - { "xmpp", "jabber" }, /* prpl-jabber (mentioned in case the prpl is renamed so this line will match) */ + { "xmpp", "jabber" }, /* jabber (mentioned in case the protocol is renamed so this line will match) */ { NULL, NULL } }; @@ -578,7 +578,7 @@ query = purple_xmlnode_new_child(iq, "query"); purple_xmlnode_set_namespace(query, NS_REGISTER); - purple_signal_emit(purple_connection_get_prpl(service->list->pc), + purple_signal_emit(purple_connection_get_protocol(service->list->pc), "jabber-sending-xmlnode", service->list->pc, &iq); if (iq != NULL) purple_xmlnode_free(iq); @@ -592,7 +592,7 @@ } static GList * -actions(PurplePlugin *plugin, gpointer context) +actions(PurplePlugin *plugin) { GList *l = NULL; PurplePluginAction *action = NULL; @@ -614,16 +614,42 @@ g_hash_table_foreach_remove(iq_callbacks, remove_iq_callbacks_by_pc, pc); } +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Paul Aurich <paul@darkrain42.org>", + NULL + }; + + return pidgin_plugin_info_new( + "id", PLUGIN_ID, + "name", N_("XMPP Service Discovery"), + "version", DISPLAY_VERSION, + "category", N_("Protocol utility"), + "summary", N_("Allows browsing and registering services."), + "description", N_("This plugin is useful for registering with legacy " + "transports or other XMPP services."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "actions-cb", actions, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { - PurplePlugin *xmpp_prpl; + PurpleProtocol *xmpp_protocol; my_plugin = plugin; - xmpp_prpl = purple_plugins_find_with_id(XMPP_PLUGIN_ID); - if (NULL == xmpp_prpl) + xmpp_protocol = purple_protocols_find(XMPP_PROTOCOL_ID); + if (NULL == xmpp_protocol) { + g_set_error(error, PLUGIN_DOMAIN, 0, _("XMPP protocol is not loaded.")); return FALSE; + } purple_signal_connect(purple_connections_get_handle(), "signing-off", plugin, PURPLE_CALLBACK(signed_off_cb), NULL); @@ -634,7 +660,7 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { g_hash_table_destroy(iq_callbacks); iq_callbacks = NULL; @@ -645,42 +671,4 @@ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - PIDGIN_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - "gtk-xmppdisco", - N_("XMPP Service Discovery"), - DISPLAY_VERSION, - N_("Allows browsing and registering services."), - N_("This plugin is useful for registering with legacy transports or other " - "XMPP services."), - "Paul Aurich <paul@darkrain42.org>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, /**< destroy */ - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(xmppdisco, init_plugin, info) +PURPLE_PLUGIN_INIT(xmppdisco, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/disco/xmppdisco.h Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/disco/xmppdisco.h Fri Jan 31 18:02:20 2014 +0530 @@ -26,13 +26,16 @@ #include "gtkdisco.h" -#define XMPP_PLUGIN_ID "prpl-jabber" +#define XMPP_PROTOCOL_ID "prpl-jabber" #define NS_DISCO_INFO "http://jabber.org/protocol/disco#info" #define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" #define NS_MUC "http://jabber.org/protocol/muc" #define NS_REGISTER "jabber:iq:register" -#include "plugin.h" +#define PLUGIN_ID "gtk-xmppdisco" +#define PLUGIN_DOMAIN (g_quark_from_static_string(PLUGIN_ID)) + +#include "plugins.h" extern PurplePlugin *my_plugin; /**
--- a/pidgin/plugins/extplacement.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/extplacement.c Fri Jan 31 18:02:20 2014 +0530 @@ -75,23 +75,6 @@ } } -static gboolean -plugin_load(PurplePlugin *plugin) -{ - pidgin_conv_placement_add_fnc("number", _("By conversation count"), - &conv_placement_by_number); - purple_prefs_trigger_callback(PIDGIN_PREFS_ROOT "/conversations/placement"); - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - pidgin_conv_placement_remove_fnc("number"); - purple_prefs_trigger_callback(PIDGIN_PREFS_ROOT "/conversations/placement"); - return TRUE; -} - static PurplePluginPrefFrame * get_plugin_pref_frame(PurplePlugin *plugin) { PurplePluginPrefFrame *frame; @@ -121,57 +104,50 @@ return frame; } -static PurplePluginUiInfo prefs_info = { - get_plugin_pref_frame, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = +static PidginPluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - "gtk-extplacement", /**< id */ - N_("ExtPlacement"), /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Extra conversation placement options."), /**< summary */ - /** description */ - N_("Restrict the number of conversations per windows," - " optionally separating IMs and Chats"), - "Stu Tomlinson <stu@nosnilmot.com>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - NULL, /**< extra_info */ - &prefs_info, /**< prefs_info */ - NULL, /**< actions */ + const gchar * const authors[] = { + "Stu Tomlinson <stu@nosnilmot.com>", + NULL + }; - /* padding */ - NULL, - NULL, - NULL, - NULL -}; + return pidgin_plugin_info_new( + "id", "gtk-extplacement", + "name", N_("ExtPlacement"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Extra conversation placement options."), + "description", N_("Restrict the number of conversations per " + "windows, optionally separating IMs and " + "Chats"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} -static void -init_plugin(PurplePlugin *plugin) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) { purple_prefs_add_none("/plugins/gtk/extplacement"); purple_prefs_add_int("/plugins/gtk/extplacement/placement_number", 4); purple_prefs_add_bool("/plugins/gtk/extplacement/placement_number_separate", FALSE); + + pidgin_conv_placement_add_fnc("number", _("By conversation count"), + &conv_placement_by_number); + purple_prefs_trigger_callback(PIDGIN_PREFS_ROOT "/conversations/placement"); + return TRUE; } -PURPLE_INIT_PLUGIN(extplacement, init_plugin, info) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + pidgin_conv_placement_remove_fnc("number"); + purple_prefs_trigger_callback(PIDGIN_PREFS_ROOT "/conversations/placement"); + return TRUE; +} + +PURPLE_PLUGIN_INIT(extplacement, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/gestures/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gestures/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -23,4 +23,5 @@ -I$(top_builddir)/libpurple \ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ - $(GTK_CFLAGS) + $(GTK_CFLAGS) \ + $(GPLUGIN_CFLAGS)
--- a/pidgin/plugins/gestures/gestures.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gestures/gestures.c Fri Jan 31 18:02:20 2014 +0530 @@ -168,50 +168,6 @@ gstroke_set_draw_strokes((gboolean) GPOINTER_TO_INT(value) ); } -static gboolean -plugin_load(PurplePlugin *plugin) -{ - PurpleConversation *conv; - GList *l; - - for (l = purple_conversations_get_all(); l != NULL; l = l->next) { - conv = (PurpleConversation *)l->data; - - if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv)) - continue; - - attach_signals(conv); - } - - purple_signal_connect(purple_conversations_get_handle(), - "conversation-created", - plugin, PURPLE_CALLBACK(new_conv_cb), NULL); - - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - PurpleConversation *conv; - PidginConversation *gtkconv; - GList *l; - - for (l = purple_conversations_get_all(); l != NULL; l = l->next) { - conv = (PurpleConversation *)l->data; - - if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv)) - continue; - - gtkconv = PIDGIN_CONVERSATION(conv); - - gstroke_cleanup(gtkconv->webview); - gstroke_disable(gtkconv->webview); - } - - return TRUE; -} - static GtkWidget * get_config_frame(PurplePlugin *plugin) { @@ -256,63 +212,44 @@ return ret; } -static PidginPluginUiInfo ui_info = +static PidginPluginInfo * +plugin_query(GError **error) { - get_config_frame, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ + const gchar * const authors[] = { + "Christian Hammond <chipx86@gnupdate.org>", + NULL + }; - GESTURES_PLUGIN_ID, /**< id */ - N_("Mouse Gestures"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Provides support for mouse gestures"), - /** description */ - N_("Allows support for mouse gestures in conversation windows. " - "Drag the middle mouse button to perform certain actions:\n" - " • Drag down and then to the right to close a conversation.\n" - " • Drag up and then to the left to switch to the previous " - "conversation.\n" - " • Drag up and then to the right to switch to the next " - "conversation."), - "Christian Hammond <chipx86@gnupdate.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ + return pidgin_plugin_info_new( + "id", GESTURES_PLUGIN_ID, + "name", N_("Mouse Gestures"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Provides support for mouse gestures"), + "description", N_("Allows support for mouse gestures in " + "conversation windows. Drag the middle " + "mouse button to perform certain " + "actions:\n" + " • Drag down and then to the right to " + "close a conversation.\n" + " • Drag up and then to the left to " + "switch to the previous conversation.\n" + " • Drag up and then to the right to " + "switch to the next conversation."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); +} - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + PurpleConversation *conv; + GList *l; - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ purple_prefs_add_none("/plugins/gtk"); purple_prefs_add_none("/plugins/gtk/X11"); purple_prefs_add_none("/plugins/gtk/X11/gestures"); @@ -320,6 +257,43 @@ purple_prefs_connect_callback(plugin, "/plugins/gtk/X11/gestures/visual", visual_pref_cb, NULL); + + for (l = purple_conversations_get_all(); l != NULL; l = l->next) { + conv = (PurpleConversation *)l->data; + + if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv)) + continue; + + attach_signals(conv); + } + + purple_signal_connect(purple_conversations_get_handle(), + "conversation-created", + plugin, PURPLE_CALLBACK(new_conv_cb), NULL); + + return TRUE; } -PURPLE_INIT_PLUGIN(gestures, init_plugin, info) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + PurpleConversation *conv; + PidginConversation *gtkconv; + GList *l; + + for (l = purple_conversations_get_all(); l != NULL; l = l->next) { + conv = (PurpleConversation *)l->data; + + if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv)) + continue; + + gtkconv = PIDGIN_CONVERSATION(conv); + + gstroke_cleanup(gtkconv->webview); + gstroke_disable(gtkconv->webview); + } + + return TRUE; +} + +PURPLE_PLUGIN_INIT(gestures, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/gevolution/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gevolution/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -15,7 +15,7 @@ new_person_dialog.c \ eds-utils.c -gevolution_la_LIBADD = $(EVOLUTION_ADDRESSBOOK_LIBS) $(GTK_LIBS) +gevolution_la_LIBADD = $(EVOLUTION_ADDRESSBOOK_LIBS) $(GPLUGIN_LIBS) $(GTK_LIBS) endif @@ -26,4 +26,5 @@ -I$(top_srcdir)/pidgin \ $(EVOLUTION_ADDRESSBOOK_CFLAGS) \ $(DEBUG_CFLAGS) \ - $(GTK_CFLAGS) + $(GTK_CFLAGS) \ + $(GPLUGIN_CFLAGS)
--- a/pidgin/plugins/gevolution/add_buddy_dialog.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gevolution/add_buddy_dialog.c Fri Jan 31 18:02:20 2014 +0530 @@ -32,7 +32,7 @@ enum { COLUMN_NAME, - COLUMN_PRPL_ICON, + COLUMN_PROTOCOL_ICON, COLUMN_USERNAME, COLUMN_DATA, NUM_COLUMNS @@ -147,7 +147,7 @@ renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(column, renderer, FALSE); gtk_tree_view_column_add_attribute(column, renderer, - "pixbuf", COLUMN_PRPL_ICON); + "pixbuf", COLUMN_PROTOCOL_ICON); /* Account name */ renderer = gtk_cell_renderer_text_new(); @@ -183,7 +183,7 @@ if (account == NULL) return; - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); for (l = list; l != NULL; l = l->next) { @@ -199,7 +199,7 @@ gtk_list_store_set(dialog->model, &iter, COLUMN_NAME, name, - COLUMN_PRPL_ICON, pixbuf, + COLUMN_PROTOCOL_ICON, pixbuf, COLUMN_USERNAME, account_name, COLUMN_DATA, contact, -1); @@ -602,13 +602,13 @@ GdkPixbuf *pixbuf; GtkTreeIter iter; - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); gtk_list_store_append(dialog->model, &iter); gtk_list_store_set(dialog->model, &iter, COLUMN_NAME, name, - COLUMN_PRPL_ICON, pixbuf, + COLUMN_PROTOCOL_ICON, pixbuf, COLUMN_DATA, contact, COLUMN_USERNAME, screenname, -1);
--- a/pidgin/plugins/gevolution/assoc-buddy.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gevolution/assoc-buddy.c Fri Jan 31 18:02:20 2014 +0530 @@ -134,7 +134,6 @@ { EBook *book; EBookQuery *query; - const char *prpl_id; gboolean status; GList *cards, *c; GError *err = NULL; @@ -188,8 +187,6 @@ return; } - prpl_id = purple_account_get_protocol_id(dialog->buddy->account); - for (c = cards; c != NULL; c = c->next) { EContact *contact = E_CONTACT(c->data); @@ -207,7 +204,7 @@ -1); /* See if this user has the buddy in its list. */ - protocol_field = gevo_prpl_get_field(dialog->buddy->account, + protocol_field = gevo_protocol_get_field(purple_buddy_get_account(dialog->buddy), dialog->buddy); if (protocol_field > 0) @@ -218,7 +215,7 @@ for (l = ims; l != NULL; l = l->next) { - if (!strcmp(l->data, dialog->buddy->name)) + if (!strcmp(l->data, purple_buddy_get_name(dialog->buddy))) { GtkTreeSelection *selection; @@ -256,8 +253,8 @@ static void new_person_cb(GtkWidget *w, GevoAssociateBuddyDialog *dialog) { - gevo_new_person_dialog_show(dialog->book, NULL, dialog->buddy->account, - dialog->buddy->name, NULL, dialog->buddy, + gevo_new_person_dialog_show(dialog->book, NULL, purple_buddy_get_account(dialog->buddy), + purple_buddy_get_name(dialog->buddy), NULL, dialog->buddy, TRUE); delete_win_cb(NULL, NULL, dialog); @@ -289,13 +286,13 @@ COLUMN_DATA, &contact, -1); - protocol_field = gevo_prpl_get_field(dialog->buddy->account, dialog->buddy); + protocol_field = gevo_protocol_get_field(purple_buddy_get_account(dialog->buddy), dialog->buddy); if (protocol_field == 0) return; /* XXX */ list = e_contact_get(contact, protocol_field); - list = g_list_append(list, g_strdup(dialog->buddy->name)); + list = g_list_append(list, g_strdup(purple_buddy_get_name(dialog->buddy))); e_contact_set(contact, protocol_field, list);
--- a/pidgin/plugins/gevolution/eds-utils.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gevolution/eds-utils.c Fri Jan 31 18:02:20 2014 +0530 @@ -177,7 +177,7 @@ EBookQuery *full_query; GSList *groups, *g; EContact *result; - EContactField protocol_field = gevo_prpl_get_field(buddy->account, buddy); + EContactField protocol_field = gevo_protocol_get_field(purple_buddy_get_account(buddy), buddy); if (protocol_field == 0) return NULL; @@ -187,7 +187,7 @@ EBookQuery *queries[2]; queries[0] = query; - queries[1] = e_book_query_field_test(protocol_field, E_BOOK_QUERY_IS, buddy->name); + queries[1] = e_book_query_field_test(protocol_field, E_BOOK_QUERY_IS, purple_buddy_get_name(buddy)); if (queries[1] == NULL) { purple_debug_error("evolution", "Error in creating protocol query\n"); @@ -199,7 +199,7 @@ } else { - full_query = e_book_query_field_test(protocol_field, E_BOOK_QUERY_IS, buddy->name); + full_query = e_book_query_field_test(protocol_field, E_BOOK_QUERY_IS, purple_buddy_get_name(buddy)); if (full_query == NULL) { purple_debug_error("evolution", "Error in creating protocol query\n");
--- a/pidgin/plugins/gevolution/gevo-util.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gevolution/gevo-util.c Fri Jan 31 18:02:20 2014 +0530 @@ -29,11 +29,11 @@ gevo_add_buddy(PurpleAccount *account, const char *group_name, const char *buddy_name, const char *alias) { - PurpleConversation *conv = NULL; + PurpleIMConversation *im = NULL; PurpleBuddy *buddy; PurpleGroup *group; - conv = purple_conversations_find_im_with_account(buddy_name, account); + im = purple_conversations_find_im_with_account(buddy_name, account); group = purple_blist_find_group(group_name); if (group == NULL) @@ -51,10 +51,10 @@ purple_account_add_buddy(account, buddy, NULL); - if (conv != NULL) + if (im != NULL) { - purple_buddy_icon_update(purple_im_conversation_get_icon(PURPLE_CONV_IM(conv))); - purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_ADD); + purple_buddy_icon_update(purple_im_conversation_get_icon(im)); + purple_conversation_update(PURPLE_CONVERSATION(im), PURPLE_CONVERSATION_UPDATE_ADD); } } @@ -81,7 +81,7 @@ if (PURPLE_IS_GROUP(gnode)) { g = PURPLE_GROUP(gnode); - list = g_list_append(list, g->name); + list = g_list_append(list, (gpointer)purple_group_get_name(g)); } } } @@ -90,7 +90,7 @@ } EContactField -gevo_prpl_get_field(PurpleAccount *account, PurpleBuddy *buddy) +gevo_protocol_get_field(PurpleAccount *account, PurpleBuddy *buddy) { EContactField protocol_field = 0; const char *protocol_id; @@ -118,9 +118,9 @@ } gboolean -gevo_prpl_is_supported(PurpleAccount *account, PurpleBuddy *buddy) +gevo_protocol_is_supported(PurpleAccount *account, PurpleBuddy *buddy) { - return (gevo_prpl_get_field(account, buddy) != 0); + return (gevo_protocol_get_field(account, buddy) != 0); } gboolean @@ -168,14 +168,14 @@ if (mail == NULL) { PurpleAccount *account = purple_buddy_get_account(buddy); - const char *prpl_id = purple_account_get_protocol_id(account); + const char *protocol_id = purple_account_get_protocol_id(account); - if (!strcmp(prpl_id, "prpl-msn")) + if (!strcmp(protocol_id, "prpl-msn")) { mail = g_strdup(purple_normalize(account, purple_buddy_get_name(buddy))); } - else if (!strcmp(prpl_id, "prpl-yahoo")) + else if (!strcmp(protocol_id, "prpl-yahoo")) { mail = g_strdup_printf("%s@yahoo.com", purple_normalize(account,
--- a/pidgin/plugins/gevolution/gevolution.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gevolution/gevolution.c Fri Jan 31 18:02:20 2014 +0530 @@ -76,7 +76,7 @@ static void update_ims_from_contact(EContact *contact, const char *name, - const char *prpl_id, EContactField field) + const char *protocol_id, EContactField field) { GList *ims = e_contact_get(contact, field); GList *l, *l2; @@ -90,7 +90,7 @@ PurpleAccount *account = purple_connection_get_account(gc); char *me; - if (strcmp(purple_account_get_protocol_id(account), prpl_id)) + if (strcmp(purple_account_get_protocol_id(account), protocol_id)) continue; if (!purple_account_get_bool(account, "gevo-autoadd", FALSE)) @@ -274,7 +274,7 @@ buddy = PURPLE_BUDDY(node); account = purple_buddy_get_account(buddy); - if (!gevo_prpl_is_supported(account, buddy)) + if (!gevo_protocol_is_supported(account, buddy)) return; contact = gevo_search_buddy_in_contacts(buddy, NULL); @@ -333,62 +333,6 @@ return FALSE; } -static gboolean -plugin_load(PurplePlugin *plugin) -{ -#if 0 - bonobo_activate(); -#endif - - backup_blist_ui_ops = purple_blist_get_ui_ops(); - - blist_ui_ops = g_memdup(backup_blist_ui_ops, sizeof(PurpleBlistUiOps)); - blist_ui_ops->request_add_buddy = request_add_buddy; - - purple_blist_set_ui_ops(blist_ui_ops); - - purple_signal_connect(purple_connections_get_handle(), "signed-on", - plugin, PURPLE_CALLBACK(signed_on_cb), NULL); - - timer = g_timeout_add(1, load_timeout, plugin); - - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - purple_blist_set_ui_ops(backup_blist_ui_ops); - - g_free(blist_ui_ops); - - backup_blist_ui_ops = NULL; - blist_ui_ops = NULL; - - if (book_view != NULL) - { - e_book_view_stop(book_view); - g_object_unref(book_view); - book_view = NULL; - } - - if (book != NULL) - { - g_object_unref(book); - book = NULL; - } - - return TRUE; -} - -static void -plugin_destroy(PurplePlugin *plugin) -{ -#if 0 - bonobo_debug_shutdown(); -#endif -} - static void autoadd_toggled_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data) @@ -489,7 +433,7 @@ gtk_list_store_append(model, &iter); - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); if ((pixbuf != NULL) && (!purple_account_is_connected(account))) gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE); @@ -512,56 +456,31 @@ return ret; } -static PidginPluginUiInfo ui_info = +static PidginPluginInfo * +plugin_query(GError **error) { - get_config_frame, /**< get_config_frame */ - 0, /**< page_num */ - /* Padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ + const gchar * const authors[] = { + "Christian Hammond <chipx86@chipx86.com>", + NULL + }; - GEVOLUTION_PLUGIN_ID, /**< id */ - N_("Evolution Integration"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Provides integration with Evolution."), - /** description */ - N_("Provides integration with Evolution."), - "Christian Hammond <chipx86@chipx86.com>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - plugin_destroy, /**< destroy */ + return pidgin_plugin_info_new( + "id", GEVOLUTION_PLUGIN_ID, + "name", N_("Evolution Integration"), + "version", DISPLAY_VERSION, + "category", N_("Integration"), + "summary", N_("Provides integration with Evolution."), + "description", N_("Provides integration with Evolution."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); +} - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - - /* Padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) { /* TODO: Change to core-remote when possible. */ /* info.dependencies = g_list_append(info.dependencies, "gtk-remote"); */ @@ -584,7 +503,8 @@ * at all, so the above explanation is suspect. This is required even with * e-d-s >= 2.29.1 where bonobo is no longer in the picture. */ - g_module_make_resident(plugin->handle); + g_module_make_resident(gplugin_native_plugin_get_module( + GPLUGIN_NATIVE_PLUGIN(plugin))); #if 0 if (!bonobo_init_full(NULL, NULL, bonobo_activation_orb_get(), @@ -593,6 +513,53 @@ purple_debug_error("evolution", "Unable to initialize bonobo.\n"); } #endif +#if 0 + bonobo_activate(); +#endif + + backup_blist_ui_ops = purple_blist_get_ui_ops(); + + blist_ui_ops = g_memdup(backup_blist_ui_ops, sizeof(PurpleBlistUiOps)); + blist_ui_ops->request_add_buddy = request_add_buddy; + + purple_blist_set_ui_ops(blist_ui_ops); + + purple_signal_connect(purple_connections_get_handle(), "signed-on", + plugin, PURPLE_CALLBACK(signed_on_cb), NULL); + + timer = g_timeout_add(1, load_timeout, plugin); + + return TRUE; } -PURPLE_INIT_PLUGIN(gevolution, init_plugin, info) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + purple_blist_set_ui_ops(backup_blist_ui_ops); + + g_free(blist_ui_ops); + + backup_blist_ui_ops = NULL; + blist_ui_ops = NULL; + + if (book_view != NULL) + { + e_book_view_stop(book_view); + g_object_unref(book_view); + book_view = NULL; + } + + if (book != NULL) + { + g_object_unref(book); + book = NULL; + } + +#if 0 + bonobo_debug_shutdown(); +#endif + + return TRUE; +} + +PURPLE_PLUGIN_INIT(gevolution, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/gevolution/gevolution.h Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gevolution/gevolution.h Fri Jan 31 18:02:20 2014 +0530 @@ -121,8 +121,8 @@ const char *screenname, const char *alias); GList *gevo_get_groups(void); -EContactField gevo_prpl_get_field(PurpleAccount *account, PurpleBuddy *buddy); -gboolean gevo_prpl_is_supported(PurpleAccount *account, PurpleBuddy *buddy); +EContactField gevo_protocol_get_field(PurpleAccount *account, PurpleBuddy *buddy); +gboolean gevo_protocol_is_supported(PurpleAccount *account, PurpleBuddy *buddy); gboolean gevo_load_addressbook(const gchar *uri, EBook **book, GError **error); char *gevo_get_email_for_buddy(PurpleBuddy *buddy);
--- a/pidgin/plugins/gevolution/new_person_dialog.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gevolution/new_person_dialog.c Fri Jan 31 18:02:20 2014 +0530 @@ -96,7 +96,7 @@ char *full_name = NULL; if (dialog->person_only) - username = dialog->buddy->name; + username = purple_buddy_get_name(dialog->buddy); else username = gtk_entry_get_text(GTK_ENTRY(dialog->username));
--- a/pidgin/plugins/gtk-signals-test.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gtk-signals-test.c Fri Jan 31 18:02:20 2014 +0530 @@ -107,8 +107,30 @@ /************************************************************************** * Plugin stuff **************************************************************************/ +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Gary Kramlich <amc_grim@users.sf.net>", + NULL + }; + + return pidgin_plugin_info_new( + "id", GTK_SIGNAL_TEST_PLUGIN_ID, + "name", N_("GTK Signals Test"), + "version", DISPLAY_VERSION, + "category", N_("Testing"), + "summary", N_("Test to see that all ui signals are working properly."), + "description", N_("Test to see that all ui signals are working properly."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { void *accounts_handle = pidgin_accounts_get_handle(); void *blist_handle = pidgin_blist_get_handle(); @@ -142,50 +164,8 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) { +plugin_unload(PurplePlugin *plugin, GError **error) { return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - GTK_SIGNAL_TEST_PLUGIN_ID, /**< id */ - N_("GTK Signals Test"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Test to see that all ui signals are working properly."), - /** description */ - N_("Test to see that all ui signals are working properly."), - "Gary Kramlich <amc_grim@users.sf.net>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(gtksignalstest, init_plugin, info) +PURPLE_PLUGIN_INIT(gtksignalstest, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/gtkbuddynote.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/gtkbuddynote.c Fri Jan 31 18:02:20 2014 +0530 @@ -42,59 +42,7 @@ } } -static gboolean -plugin_load(PurplePlugin *plugin) -{ - purple_signal_connect(pidgin_blist_get_handle(), "drawing-tooltip", - plugin, PURPLE_CALLBACK(append_to_tooltip), NULL); - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - PurplePlugin *buddynote = NULL; - - buddynote = purple_plugins_find_with_id("core-plugin_pack-buddynote"); - - purple_plugin_unload(buddynote); - - return TRUE; -} - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, /**< major version */ - PURPLE_MINOR_VERSION, /**< minor version */ - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - "gtkbuddynote", /**< id */ - N_("Buddy Notes"), /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Store notes on particular buddies."), /**< summary */ - N_("Adds the option to store notes for buddies " - "on your buddy list."), /**< description */ - "Etan Reisner <deryni@pidgin.im>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - +#if 0 static gboolean check_for_buddynote(gpointer data) { @@ -130,16 +78,55 @@ return FALSE; } +#endif -static void -init_plugin(PurplePlugin *plugin) +static PidginPluginInfo * +plugin_query(GError **error) { - /* Use g_idle_add so that the rest of the plugins can get loaded - * before we do our check. */ - g_idle_add(check_for_buddynote, plugin); + const gchar * const authors[] = { + "Etan Reisner <deryni@pidgin.im>", + NULL + }; + + const gchar * const dependencies[] = { + "core-plugin_pack-buddynote", + NULL + }; - info.dependencies = g_list_append(info.dependencies, - "core-plugin_pack-buddynote"); + return pidgin_plugin_info_new( + "id", "gtkbuddynote", + "name", N_("Buddy Note Tooltips"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Shows stored buddy notes on the buddy's tooltip."), + "description", N_("Shows stored buddy notes on the buddy's tooltip."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "dependencies", dependencies, + NULL + ); } -PURPLE_INIT_PLUGIN(gtkbuddynote, init_plugin, info) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + purple_signal_connect(pidgin_blist_get_handle(), "drawing-tooltip", + plugin, PURPLE_CALLBACK(append_to_tooltip), NULL); + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ +#if 0 + PurplePlugin *buddynote = NULL; + + buddynote = purple_plugins_find_with_id("core-plugin_pack-buddynote"); + purple_plugin_unload(buddynote); +#endif + + return TRUE; +} + +PURPLE_PLUGIN_INIT(gtkbuddynote, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/history.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/history.c Fri Jan 31 18:02:20 2014 +0530 @@ -190,8 +190,33 @@ history_prefs_check((PurplePlugin *)data); } +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Sean Egan <seanegan@gmail.com>", + NULL + }; + + return pidgin_plugin_info_new( + "id", HISTORY_PLUGIN_ID, + "name", N_("History"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Shows recently logged conversations in new " + "conversations."), + "description", N_("When a new conversation is opened this plugin will " + "insert the last conversation into the current " + "conversation."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { purple_signal_connect(purple_conversations_get_handle(), "conversation-created", @@ -208,42 +233,10 @@ return TRUE; } -static PurplePluginInfo info = +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - PIDGIN_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - HISTORY_PLUGIN_ID, - N_("History"), - DISPLAY_VERSION, - N_("Shows recently logged conversations in new conversations."), - N_("When a new conversation is opened this plugin will insert " - "the last conversation into the current conversation."), - "Sean Egan <seanegan@gmail.com>", - PURPLE_WEBSITE, - plugin_load, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ + return TRUE; } -PURPLE_INIT_PLUGIN(history, init_plugin, info) +PURPLE_PLUGIN_INIT(history, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/iconaway.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/iconaway.c Fri Jan 31 18:02:20 2014 +0530 @@ -58,8 +58,32 @@ * EXPORTED FUNCTIONS */ +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Eric Warmenhoven <eric@warmenhoven.org>", + NULL + }; + + return pidgin_plugin_info_new( + "id", ICONAWAY_PLUGIN_ID, + "name", N_("Iconify on Away"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Iconifies the buddy list and your conversations " + "when you go away."), + "description", N_("Iconifies the buddy list and your conversations " + "when you go away."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", plugin, PURPLE_CALLBACK(iconify_windows), NULL); @@ -67,46 +91,10 @@ return TRUE; } -static PurplePluginInfo info = +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - ICONAWAY_PLUGIN_ID, /**< id */ - N_("Iconify on Away"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Iconifies the buddy list and your conversations when you go away."), - /** description */ - N_("Iconifies the buddy list and your conversations when you go away."), - "Eric Warmenhoven <eric@warmenhoven.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ + return TRUE; } -PURPLE_INIT_PLUGIN(iconaway, init_plugin, info) +PURPLE_PLUGIN_INIT(iconaway, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/mailchk.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/mailchk.c Fri Jan 31 18:02:20 2014 +0530 @@ -7,7 +7,8 @@ #include "gtkblist.h" #include "gtkplugin.h" -#define MAILCHK_PLUGIN_ID "gtk-mailchk" +#define MAILCHK_PLUGIN_ID "gtk-mailchk" +#define MAILCHK_PLUGIN_DOMAIN (g_quark_from_static_string(MAILCHK_PLUGIN_ID)) #define ANY_MAIL 0x01 #define UNREAD_MAIL 0x02 @@ -60,7 +61,7 @@ if (count == -1) return FALSE; - if (!list || !PURPLE_IS_GTK_BLIST(list) || !(PIDGIN_BLIST(list)->vbox)) + if (!list || !(PIDGIN_BLIST(list)->vbox)) return TRUE; if (!mail) { @@ -91,7 +92,7 @@ signon_cb(PurpleConnection *gc) { PurpleBuddyList *list = purple_blist_get_buddy_list(); - if (list && PURPLE_IS_GTK_BLIST(list) && !timer) { + if (list && !timer) { check_timeout(NULL); /* we want the box to be drawn immediately */ timer = purple_timeout_add_seconds(2, check_timeout, NULL); } @@ -101,7 +102,7 @@ signoff_cb(PurpleConnection *gc) { PurpleBuddyList *list = purple_blist_get_buddy_list(); - if ((!list || !PURPLE_IS_GTK_BLIST(list) || !PIDGIN_BLIST(list)->vbox) && timer) { + if ((!list || !PIDGIN_BLIST(list)->vbox) && timer) { purple_timeout_remove(timer); timer = 0; } @@ -111,18 +112,42 @@ * EXPORTED FUNCTIONS */ +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Eric Warmenhoven <eric@warmenhoven.org>", + NULL + }; + + return pidgin_plugin_info_new( + "id", MAILCHK_PLUGIN_ID, + "name", N_("Mail Checker"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Checks for new local mail."), + "description", N_("Adds a small box to the buddy list that shows if " + "you have new mail."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { PurpleBuddyList *list = purple_blist_get_buddy_list(); void *conn_handle = purple_connections_get_handle(); if (!check_timeout(NULL)) { - purple_debug_warning("mailchk", "Could not read $MAIL or /var/spool/mail/$USER\n"); + g_set_error(error, MAILCHK_PLUGIN_DOMAIN, 0, _("Could not read $MAIL " + "or /var/spool/mail/$USER\n")); return FALSE; } - if (list && PURPLE_IS_GTK_BLIST(list) && PIDGIN_BLIST(list)->vbox) + if (list && PIDGIN_BLIST(list)->vbox) timer = purple_timeout_add_seconds(2, check_timeout, NULL); purple_signal_connect(conn_handle, "signed-on", @@ -134,7 +159,7 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { if (timer) purple_timeout_remove(timer); @@ -146,36 +171,4 @@ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - PIDGIN_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - MAILCHK_PLUGIN_ID, - N_("Mail Checker"), - DISPLAY_VERSION, - N_("Checks for new local mail."), - N_("Adds a small box to the buddy list that" - " shows if you have new mail."), - "Eric Warmenhoven <eric@warmenhoven.org>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(mailchk, init_plugin, info) +PURPLE_PLUGIN_INIT(mailchk, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/markerline.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/markerline.c Fri Jan 31 18:02:20 2014 +0530 @@ -21,10 +21,11 @@ #define PLUGIN_ID "gtk-plugin_pack-markerline" #define PLUGIN_NAME N_("Markerline") +#define PLUGIN_CATEGORY N_("User interface") #define PLUGIN_STATIC_NAME Markerline #define PLUGIN_SUMMARY N_("Draw a line to indicate new messages in a conversation.") #define PLUGIN_DESCRIPTION N_("Draw a line to indicate new messages in a conversation.") -#define PLUGIN_AUTHOR "Sadrul H Chowdhury <sadrul@users.sourceforge.net>" +#define PLUGIN_AUTHORS {"Sadrul H Chowdhury <sadrul@users.sourceforge.net>", NULL} /* System headers */ #include <gdk/gdk.h> @@ -171,27 +172,6 @@ *list = g_list_append(*list, action); } -static gboolean -plugin_load(PurplePlugin *plugin) -{ - attach_to_all_windows(); - - purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed", - plugin, PURPLE_CALLBACK(conv_created), NULL); - - purple_signal_connect(purple_conversations_get_handle(), "conversation-extended-menu", - plugin, PURPLE_CALLBACK(conv_menu_cb), NULL); - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - detach_from_all_windows(); - - return TRUE; -} - static PurplePluginPrefFrame * get_plugin_pref_frame(PurplePlugin *plugin) { @@ -214,57 +194,49 @@ return frame; } -static PurplePluginUiInfo prefs_info = { - get_plugin_pref_frame, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, /* Magic */ - PURPLE_MAJOR_VERSION, /* Purple Major Version */ - PURPLE_MINOR_VERSION, /* Purple Minor Version */ - PURPLE_PLUGIN_STANDARD, /* plugin type */ - PIDGIN_PLUGIN_TYPE, /* ui requirement */ - 0, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = PLUGIN_AUTHORS; - PLUGIN_ID, /* plugin id */ - PLUGIN_NAME, /* name */ - DISPLAY_VERSION, /* version */ - PLUGIN_SUMMARY, /* summary */ - PLUGIN_DESCRIPTION, /* description */ - PLUGIN_AUTHOR, /* author */ - PURPLE_WEBSITE, /* website */ - - plugin_load, /* load */ - plugin_unload, /* unload */ - NULL, /* destroy */ + return pidgin_plugin_info_new( + "id", PLUGIN_ID, + "name", PLUGIN_NAME, + "version", DISPLAY_VERSION, + "category", PLUGIN_CATEGORY, + "summary", PLUGIN_SUMMARY, + "description", PLUGIN_DESCRIPTION, + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "pref-frame-cb", get_plugin_pref_frame, + NULL + ); +} - NULL, /* ui_info */ - NULL, /* extra_info */ - &prefs_info, /* prefs_info */ - NULL, /* actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) { purple_prefs_add_none(PREF_PREFIX); purple_prefs_add_bool(PREF_IMS, FALSE); purple_prefs_add_bool(PREF_CHATS, TRUE); + + attach_to_all_windows(); + + purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed", + plugin, PURPLE_CALLBACK(conv_created), NULL); + + purple_signal_connect(purple_conversations_get_handle(), "conversation-extended-menu", + plugin, PURPLE_CALLBACK(conv_menu_cb), NULL); + return TRUE; } -PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + detach_from_all_windows(); + + return TRUE; +} + +PURPLE_PLUGIN_INIT(PLUGIN_STATIC_NAME, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/musicmessaging/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/musicmessaging/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -41,4 +41,5 @@ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ $(GTK_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(DBUS_CFLAGS)
--- a/pidgin/plugins/musicmessaging/musicmessaging.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/musicmessaging/musicmessaging.c Fri Jan 31 18:02:20 2014 +0530 @@ -259,58 +259,6 @@ } static gboolean -plugin_load(PurplePlugin *plugin) { - void *conv_list_handle; - GList *l; - - PURPLE_DBUS_RETURN_FALSE_IF_DISABLED(plugin); - - /* First, we have to register our four exported functions with the - main purple dbus loop. Without this statement, the purple dbus - code wouldn't know about our functions. */ - PURPLE_DBUS_REGISTER_BINDINGS(plugin); - - /* Keep the plugin for reference (needed for notify's) */ - plugin_pointer = plugin; - - /* Add the button to all the current conversations */ - for (l = purple_conversations_get_all(); l != NULL; l = l->next) - init_conversation((PurpleConversation *)l->data); - - /* Listen for any new conversations */ - conv_list_handle = purple_conversations_get_handle(); - - purple_signal_connect(conv_list_handle, "conversation-created", - plugin, PURPLE_CALLBACK(init_conversation), NULL); - - /* Listen for conversations that are ending */ - purple_signal_connect(conv_list_handle, "deleting-conversation", - plugin, PURPLE_CALLBACK(conv_destroyed), NULL); - - /* Listen for sending/receiving messages to replace tags */ - purple_signal_connect(conv_list_handle, "sending-im-msg", - plugin, PURPLE_CALLBACK(intercept_sent), NULL); - purple_signal_connect(conv_list_handle, "receiving-im-msg", - plugin, PURPLE_CALLBACK(intercept_received), NULL); - - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) { - MMConversation *mmconv = NULL; - - while (conversations != NULL) - { - mmconv = conversations->data; - conv_destroyed(mmconv->conv); - } - return TRUE; -} - - - -static gboolean intercept_sent(PurpleAccount *account, const char *who, char **message, void* pData) { if (message == NULL || *message == NULL || **message == '\0') @@ -664,56 +612,83 @@ return ret; } -static PidginPluginUiInfo ui_info = -{ - get_config_frame, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ +static PidginPluginInfo * +plugin_query(GError **error) { + const gchar * const authors[] = { + "Christian Muise <christian.muise@gmail.com>", + NULL + }; - MUSICMESSAGING_PLUGIN_ID, /**< id */ - "Music Messaging", /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Music Messaging Plugin for collaborative composition."), - /** summary */ - N_("The Music Messaging Plugin allows a number of users to simultaneously " - "work on a piece of music by editing a common score in real-time."), - /** description */ - "Christian Muise <christian.muise@gmail.com>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, + return pidgin_plugin_info_new( + "id", MUSICMESSAGING_PLUGIN_ID, + "name", N_("Music Messaging"), + "version", DISPLAY_VERSION, + "category", N_("Music"), + "summary", N_("Music Messaging Plugin for " + "collaborative composition."), + "description", N_("The Music Messaging Plugin allows a " + "number of users to simultaneously work " + "on a piece of music by editing a common " + "score in real-time."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); +} - /* padding */ - NULL, - NULL, - NULL, - NULL -}; +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) { + void *conv_list_handle; + GList *l; -static void -init_plugin(PurplePlugin *plugin) { + PURPLE_DBUS_RETURN_FALSE_IF_DISABLED(plugin); + purple_prefs_add_none("/plugins/gtk/musicmessaging"); purple_prefs_add_string("/plugins/gtk/musicmessaging/editor_path", "/usr/bin/gscore"); + + /* First, we have to register our four exported functions with the + main purple dbus loop. Without this statement, the purple dbus + code wouldn't know about our functions. */ + PURPLE_DBUS_REGISTER_BINDINGS(plugin); + + /* Keep the plugin for reference (needed for notify's) */ + plugin_pointer = plugin; + + /* Add the button to all the current conversations */ + for (l = purple_conversations_get_all(); l != NULL; l = l->next) + init_conversation((PurpleConversation *)l->data); + + /* Listen for any new conversations */ + conv_list_handle = purple_conversations_get_handle(); + + purple_signal_connect(conv_list_handle, "conversation-created", + plugin, PURPLE_CALLBACK(init_conversation), NULL); + + /* Listen for conversations that are ending */ + purple_signal_connect(conv_list_handle, "deleting-conversation", + plugin, PURPLE_CALLBACK(conv_destroyed), NULL); + + /* Listen for sending/receiving messages to replace tags */ + purple_signal_connect(conv_list_handle, "sending-im-msg", + plugin, PURPLE_CALLBACK(intercept_sent), NULL); + purple_signal_connect(conv_list_handle, "receiving-im-msg", + plugin, PURPLE_CALLBACK(intercept_received), NULL); + + return TRUE; } -PURPLE_INIT_PLUGIN(musicmessaging, init_plugin, info); +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { + MMConversation *mmconv = NULL; + + while (conversations != NULL) + { + mmconv = conversations->data; + conv_destroyed(mmconv->conv); + } + return TRUE; +} + +PURPLE_PLUGIN_INIT(musicmessaging, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/notify.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/notify.c Fri Jan 31 18:02:20 2014 +0530 @@ -833,8 +833,34 @@ return ret; } +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Etan Reisner <deryni@eden.rutgers.edu>", + "Brian Tarricone <bjt23@cornell.edu>", + NULL + }; + + return pidgin_plugin_info_new( + "id", NOTIFY_PLUGIN_ID, + "name", N_("Message Notification"), + "version", DISPLAY_VERSION, + "category", N_("Notification"), + "summary", N_("Provides a variety of ways of notifying " + "you of unread messages."), + "description", N_("Provides a variety of ways of notifying " + "you of unread messages."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { GList *convs = purple_conversations_get_all(); void *conv_handle = purple_conversations_get_handle(); @@ -842,6 +868,27 @@ my_plugin = plugin; + purple_prefs_add_none("/plugins/gtk"); + purple_prefs_add_none("/plugins/gtk/X11"); + purple_prefs_add_none("/plugins/gtk/X11/notify"); + + purple_prefs_add_bool("/plugins/gtk/X11/notify/type_im", TRUE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat", FALSE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat_nick", FALSE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/type_focused", FALSE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/method_string", FALSE); + purple_prefs_add_string("/plugins/gtk/X11/notify/title_string", "(*)"); + purple_prefs_add_bool("/plugins/gtk/X11/notify/method_urgent", FALSE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count", FALSE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count_xprop", FALSE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/method_raise", FALSE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/method_present", FALSE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_focus", TRUE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_click", FALSE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_type", TRUE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_send", TRUE); + purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_switch", TRUE); + purple_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin, PURPLE_CALLBACK(message_displayed_cb), NULL); purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin, @@ -874,7 +921,7 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { GList *convs = purple_conversations_get_all(); @@ -890,78 +937,4 @@ return TRUE; } -static PidginPluginUiInfo ui_info = -{ - get_config_frame, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - NOTIFY_PLUGIN_ID, /**< id */ - N_("Message Notification"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Provides a variety of ways of notifying you of unread messages."), - /** description */ - N_("Provides a variety of ways of notifying you of unread messages."), - /**< author */ - "Etan Reisner <deryni@eden.rutgers.edu>,\nBrian Tarricone <bjt23@cornell.edu>", - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - purple_prefs_add_none("/plugins/gtk"); - purple_prefs_add_none("/plugins/gtk/X11"); - purple_prefs_add_none("/plugins/gtk/X11/notify"); - - purple_prefs_add_bool("/plugins/gtk/X11/notify/type_im", TRUE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat", FALSE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat_nick", FALSE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/type_focused", FALSE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/method_string", FALSE); - purple_prefs_add_string("/plugins/gtk/X11/notify/title_string", "(*)"); - purple_prefs_add_bool("/plugins/gtk/X11/notify/method_urgent", FALSE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count", FALSE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count_xprop", FALSE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/method_raise", FALSE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/method_present", FALSE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_focus", TRUE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_click", FALSE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_type", TRUE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_send", TRUE); - purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_switch", TRUE); -} - -PURPLE_INIT_PLUGIN(notify, init_plugin, info) +PURPLE_PLUGIN_INIT(notify, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/perl/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/perl/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -103,5 +103,6 @@ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ $(GTK_CFLAGS) \ + $(GPLUGIN_CFLAGS) \ $(PLUGIN_CFLAGS) \ $(PERL_CFLAGS)
--- a/pidgin/plugins/pidgininc.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/pidgininc.c Fri Jan 31 18:02:20 2014 +0530 @@ -48,18 +48,45 @@ static void bud(PurpleBuddy *who) { - PurpleAccount *acct = who->account; - PurpleConversation *conv = purple_im_conversation_new(acct, who->name); + PurpleAccount *acct = purple_buddy_get_account(who); + PurpleIMConversation *im = purple_im_conversation_new(acct, + purple_buddy_get_name(who)); - purple_im_conversation_send(PURPLE_CONV_IM(conv), "Hello!"); + purple_conversation_send(PURPLE_CONVERSATION(im), "Hello!"); } /* * EXPORTED FUNCTIONS */ +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Eric Warmenhoven <eric@warmenhoven.org>", + NULL + }; + + return pidgin_plugin_info_new( + "id", PURPLEINC_PLUGIN_ID, + "name", N_("Pidgin Demonstration Plugin"), + "version", DISPLAY_VERSION, + "category", N_("Example"), + "summary", N_("An example plugin that does stuff - see the description."), + "description", N_("This is a really cool plugin that does a lot of stuff:\n" + "- It tells you who wrote the program when you log in\n" + "- It reverses all incoming text\n" + "- It sends a message to people on your list immediately" + " when they sign on"), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { /* this is for doing something fun when we sign on */ purple_signal_connect(purple_connections_get_handle(), "signed-on", @@ -76,49 +103,10 @@ return TRUE; } -static PurplePluginInfo info = +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - PURPLEINC_PLUGIN_ID, /**< id */ - N_("Pidgin Demonstration Plugin"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("An example plugin that does stuff - see the description."), - /** description */ - N_("This is a really cool plugin that does a lot of stuff:\n" - "- It tells you who wrote the program when you log in\n" - "- It reverses all incoming text\n" - "- It sends a message to people on your list immediately" - " when they sign on"), - "Eric Warmenhoven <eric@warmenhoven.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ + return TRUE; } -PURPLE_INIT_PLUGIN(purpleinc, init_plugin, info) +PURPLE_PLUGIN_INIT(purpleinc, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/pidginrc.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/pidginrc.c Fri Jan 31 18:02:20 2014 +0530 @@ -340,27 +340,6 @@ gtk_window_present(GTK_WINDOW(font_dialog)); } -static gboolean -purplerc_plugin_load(PurplePlugin *plugin) -{ - purplerc_make_changes(); - - pref_callback = purple_prefs_connect_callback(plugin, - "/plugins/gtk/purplerc", - purplerc_pref_changed_cb, - NULL); - - return TRUE; -} - -static gboolean -purplerc_plugin_unload(PurplePlugin *plugin) -{ - purple_prefs_disconnect_callback(pref_callback); - - return TRUE; -} - static GtkWidget * purplerc_make_interface_vbox(void) { @@ -597,51 +576,33 @@ return ret; } -static PidginPluginUiInfo purplerc_ui_info = -{ - purplerc_get_config_frame, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo purplerc_info = +static PidginPluginInfo * +purplerc_plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - PIDGIN_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - "purplerc", - N_("Pidgin GTK+ Theme Control"), - DISPLAY_VERSION, - N_("Provides access to commonly used gtkrc settings."), - N_("Provides access to commonly used gtkrc settings."), - "Etan Reisner <deryni@pidgin.im>", - PURPLE_WEBSITE, - purplerc_plugin_load, - purplerc_plugin_unload, - NULL, - &purplerc_ui_info, - NULL, - NULL, - NULL, + const gchar * const authors[] = { + "Etan Reisner <deryni@pidgin.im>", + NULL + }; - /* padding */ - NULL, - NULL, - NULL, - NULL -}; + return pidgin_plugin_info_new( + "id", "purplerc", + "name", N_("Pidgin GTK+ Theme Control"), + "version", DISPLAY_VERSION, + "category", N_("Theming"), + "summary", N_("Provides access to commonly used gtkrc " + "settings."), + "description", N_("Provides access to commonly used gtkrc " + "settings."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", purplerc_get_config_frame, + NULL + ); +} -static void -purplerc_init(PurplePlugin *plugin) +static gboolean +purplerc_plugin_load(PurplePlugin *plugin, GError **error) { gsize i; @@ -694,6 +655,23 @@ purple_prefs_remove("/plugins/gtk/purplerc/color/GtkWidget::secondary-cursor-color"); purple_prefs_remove("/plugins/gtk/purplerc/set/color/GtkWidget::cursor-color"); purple_prefs_remove("/plugins/gtk/purplerc/set/color/GtkWidget::secondary-cursor-color"); + + purplerc_make_changes(); + + pref_callback = purple_prefs_connect_callback(plugin, + "/plugins/gtk/purplerc", + purplerc_pref_changed_cb, + NULL); + + return TRUE; } -PURPLE_INIT_PLUGIN(purplerc, purplerc_init, purplerc_info) +static gboolean +purplerc_plugin_unload(PurplePlugin *plugin, GError **error) +{ + purple_prefs_disconnect_callback(pref_callback); + + return TRUE; +} + +PURPLE_PLUGIN_INIT(purplerc, purplerc_plugin_query, purplerc_plugin_load, purplerc_plugin_unload);
--- a/pidgin/plugins/raw.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/raw.c Fri Jan 31 18:02:20 2014 +0530 @@ -26,7 +26,7 @@ #include "conversation.h" #include "debug.h" -#include "prpl.h" +#include "protocol.h" #include "version.h" #include "gtkplugin.h" @@ -49,7 +49,7 @@ static int window_closed_cb() { - purple_plugin_unload(my_plugin); + purple_plugin_unload(my_plugin, NULL); return FALSE; } @@ -59,7 +59,7 @@ { const char *txt; PurpleConnection *gc; - const char *prpl_id; + const char *protocol_id; if (account == NULL) return; @@ -68,12 +68,12 @@ txt = gtk_entry_get_text(entry); - prpl_id = purple_account_get_protocol_id(account); + protocol_id = purple_account_get_protocol_id(account); - purple_debug_misc("raw", "prpl_id = %s\n", prpl_id); + purple_debug_misc("raw", "protocol_id = %s\n", protocol_id); - if (strcmp(prpl_id, "prpl-toc") == 0) { - int *a = (int *)gc->proto_data; + if (strcmp(protocol_id, "prpl-toc") == 0) { + int *a = (int *)purple_connection_get_protocol_data(gc); unsigned short seqno = htons(a[1]++ & 0xffff); unsigned short len = htons(strlen(txt) + 1); write(*a, "*\002", 2); @@ -82,23 +82,23 @@ write(*a, txt, ntohs(len)); purple_debug(PURPLE_DEBUG_MISC, "raw", "TOC C: %s\n", txt); - } else if (strcmp(prpl_id, "prpl-msn") == 0) { - MsnSession *session = gc->proto_data; + } else if (strcmp(protocol_id, "prpl-msn") == 0) { + MsnSession *session = purple_connection_get_protocol_data(gc); char buf[strlen(txt) + 3]; g_snprintf(buf, sizeof(buf), "%s\r\n", txt); msn_servconn_write(session->notification->servconn, buf, strlen(buf)); - } else if (strcmp(prpl_id, "prpl-irc") == 0) { - write(*(int *)gc->proto_data, txt, strlen(txt)); - write(*(int *)gc->proto_data, "\r\n", 2); + } else if (strcmp(protocol_id, "prpl-irc") == 0) { + write(*(int *)purple_connection_get_protocol_data(gc), txt, strlen(txt)); + write(*(int *)purple_connection_get_protocol_data(gc), "\r\n", 2); purple_debug(PURPLE_DEBUG_MISC, "raw", "IRC C: %s\n", txt); - } else if (strcmp(prpl_id, "prpl-jabber") == 0) { - jabber_send_raw((JabberStream *)gc->proto_data, txt, -1); + } else if (strcmp(protocol_id, "prpl-jabber") == 0) { + jabber_send_raw((JabberStream *)purple_connection_get_protocol_data(gc), txt, -1); } else { - purple_debug_error("raw", "Unknown protocol ID %s\n", prpl_id); + purple_debug_error("raw", "Unknown protocol ID %s\n", protocol_id); } gtk_entry_set_text(entry, ""); @@ -111,13 +111,39 @@ account = new_account; } +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Eric Warmenhoven <eric@warmenhoven.org>", + NULL + }; + + return pidgin_plugin_info_new( + "id", RAW_PLUGIN_ID, + "name", N_("Raw"), + "version", DISPLAY_VERSION, + "category", N_("Protocol utility"), + "summary", N_("Lets you send raw input to text-based protocols."), + "description", N_("Lets you send raw input to text-based protocols " + "(XMPP, MSN, IRC, TOC). Hit 'Enter' in the entry " + "box to send. Watch the debug window."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { GtkWidget *hbox; GtkWidget *entry; GtkWidget *dropdown; + my_plugin = plugin; + /* Setup the window. */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_container_set_border_width(GTK_CONTAINER(window), 6); @@ -151,47 +177,15 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { if (window) gtk_widget_destroy(window); window = NULL; + my_plugin = NULL; return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - PIDGIN_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - RAW_PLUGIN_ID, - N_("Raw"), - DISPLAY_VERSION, - N_("Lets you send raw input to text-based protocols."), - N_("Lets you send raw input to text-based protocols (XMPP, MSN, IRC, " - "TOC). Hit 'Enter' in the entry box to send. Watch the debug window."), - "Eric Warmenhoven <eric@warmenhoven.org>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - my_plugin = plugin; -} - -PURPLE_INIT_PLUGIN(raw, init_plugin, info) +PURPLE_PLUGIN_INIT(raw, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/relnot.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/relnot.c Fri Jan 31 18:02:20 2014 +0530 @@ -35,6 +35,7 @@ #include "core.h" #include "debug.h" #include "gtkblist.h" +#include "gtkplugin.h" #include "gtkutils.h" #include "http.h" #include "notify.h" @@ -140,9 +141,35 @@ /************************************************************************** * Plugin stuff **************************************************************************/ +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Nathan Walp <faceprint@faceprint.com>", + NULL + }; + + return pidgin_plugin_info_new( + "id", "gtk-relnot", + "name", N_("Release Notification"), + "version", DISPLAY_VERSION, + "category", N_("Notification"), + "summary", N_("Checks periodically for new releases."), + "description", N_("Checks periodically for new releases and notifies " + "the user with the ChangeLog."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { + purple_prefs_add_none("/plugins/gtk/relnot"); + purple_prefs_add_int("/plugins/gtk/relnot/last_check", 0); + purple_signal_connect(purple_connections_get_handle(), "signed-on", plugin, PURPLE_CALLBACK(signed_on_cb), NULL); @@ -153,49 +180,10 @@ return TRUE; } -static PurplePluginInfo info = +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "gtk-relnot", /**< id */ - N_("Release Notification"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Checks periodically for new releases."), - /** description */ - N_("Checks periodically for new releases and notifies the user " - "with the ChangeLog."), - "Nathan Walp <faceprint@faceprint.com>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - purple_prefs_add_none("/plugins/gtk/relnot"); - purple_prefs_add_int("/plugins/gtk/relnot/last_check", 0); + return TRUE; } -PURPLE_INIT_PLUGIN(relnot, init_plugin, info) +PURPLE_PLUGIN_INIT(relnot, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/sendbutton.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/sendbutton.c Fri Jan 31 18:02:20 2014 +0530 @@ -114,8 +114,32 @@ } } +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Etan Reisner <deryni@pidgin.im>", + NULL + }; + + return pidgin_plugin_info_new( + "id", "gtksendbutton", + "name", N_("Send Button"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Conversation Window Send Button."), + "description", N_("Adds a Send button to the entry area of the " + "conversation window. Intended for use when no " + "physical keyboard is present."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { GList *convs = purple_conversations_get_all(); void *gtk_conv_handle = pidgin_conversations_get_handle(); @@ -143,7 +167,7 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { GList *convs = purple_conversations_get_all(); @@ -161,44 +185,4 @@ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, /**< major version */ - PURPLE_MINOR_VERSION, /**< minor version */ - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "gtksendbutton", /**< id */ - N_("Send Button"), /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Conversation Window Send Button."), /**< summary */ - N_("Adds a Send button to the entry area of " - "the conversation window. Intended for use " - "when no physical keyboard is present."), /**< description */ - "Etan Reisner <deryni@pidgin.im>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(sendbutton, init_plugin, info) +PURPLE_PLUGIN_INIT(sendbutton, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/spellchk.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/spellchk.c Fri Jan 31 18:02:20 2014 +0530 @@ -2107,48 +2107,6 @@ non_empty(gtk_entry_get_text(GTK_ENTRY(good_entry)))); } -/* - * EXPORTED FUNCTIONS - */ - -static gboolean -plugin_load(PurplePlugin *plugin) -{ - void *conv_handle = purple_conversations_get_handle(); - GList *convs; - - load_conf(); - - /* Attach to existing conversations */ - for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next) - { - spellchk_new_attach((PurpleConversation *)convs->data); - } - - purple_signal_connect(conv_handle, "conversation-created", - plugin, PURPLE_CALLBACK(spellchk_new_attach), NULL); - - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - GList *convs; - - /* Detach from existing conversations */ - for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next) - { - PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)convs->data); - spellchk *spell = g_object_get_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY); - - g_signal_handlers_disconnect_by_func(gtkconv->entry, message_send_cb, spell); - g_object_set_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY, NULL); - } - - return TRUE; -} - static void whole_words_button_toggled(GtkToggleButton *complete_toggle, GtkToggleButton *case_toggle) { gboolean enabled = gtk_toggle_button_get_active(complete_toggle); @@ -2316,58 +2274,76 @@ return ret; } -static PidginPluginUiInfo ui_info = -{ - get_config_frame, +/* + * EXPORTED FUNCTIONS + */ - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = +static PidginPluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - PIDGIN_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - SPELLCHECK_PLUGIN_ID, - N_("Text replacement"), - DISPLAY_VERSION, - N_("Replaces text in outgoing messages according to user-defined rules."), - N_("Replaces text in outgoing messages according to user-defined rules."), - "Eric Warmenhoven <eric@warmenhoven.org>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, - &ui_info, - NULL, - NULL, - NULL, + const gchar * const authors[] = { + "Eric Warmenhoven <eric@warmenhoven.org>", + NULL + }; - /* padding */ - NULL, - NULL, - NULL, - NULL -}; + return pidgin_plugin_info_new( + "id", SPELLCHECK_PLUGIN_ID, + "name", N_("Text replacement"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Replaces text in outgoing messages according to user-defined rules."), + "description", N_("Replaces text in outgoing messages according to user-defined rules."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); +} -static void -init_plugin(PurplePlugin *plugin) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) { + void *conv_handle = purple_conversations_get_handle(); + GList *convs; + #if 0 purple_prefs_add_none("/plugins"); purple_prefs_add_none("/plugins/gtk"); purple_prefs_add_none("/plugins/gtk/spellchk"); purple_prefs_add_bool("/plugins/gtk/spellchk/last_word_replace", TRUE); #endif + + load_conf(); + + /* Attach to existing conversations */ + for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next) + { + spellchk_new_attach((PurpleConversation *)convs->data); + } + + purple_signal_connect(conv_handle, "conversation-created", + plugin, PURPLE_CALLBACK(spellchk_new_attach), NULL); + + return TRUE; } -PURPLE_INIT_PLUGIN(spellcheck, init_plugin, info) +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + GList *convs; + + /* Detach from existing conversations */ + for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next) + { + PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)convs->data); + spellchk *spell = g_object_get_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY); + + g_signal_handlers_disconnect_by_func(gtkconv->entry, message_send_cb, spell); + g_object_set_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY, NULL); + } + + return TRUE; +} + +PURPLE_PLUGIN_INIT(spellcheck, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/themeedit.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/themeedit.c Fri Jan 31 18:02:20 2014 +0530 @@ -305,14 +305,8 @@ g_object_unref(group); } -static gboolean -plugin_load(PurplePlugin *plugin) -{ - return TRUE; -} - static GList * -actions(PurplePlugin *plugin, gpointer context) +actions(PurplePlugin *plugin) { GList *l = NULL; PurplePluginAction *act = NULL; @@ -325,46 +319,39 @@ return l; } -static PurplePluginInfo info = +static PidginPluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ + const gchar * const authors[] = { + "Sadrul Habib Chowdhury <imadil@gmail.com>", + NULL + }; - PLUGIN_ID, /**< id */ - N_("Pidgin Theme Editor"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Pidgin Theme Editor."), - /** description */ - N_("Pidgin Theme Editor"), - "Sadrul Habib Chowdhury <imadil@gmail.com>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ + return pidgin_plugin_info_new( + "id", PLUGIN_ID, + "name", N_("Pidgin Theme Editor"), + "version", DISPLAY_VERSION, + "category", N_("Theming"), + "summary", N_("Pidgin Theme Editor"), + "description", N_("Pidgin Theme Editor."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "actions-cb", actions, + NULL + ); } -PURPLE_INIT_PLUGIN(themeeditor, init_plugin, info) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + return TRUE; +} + +PURPLE_PLUGIN_INIT(themeeditor, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/ticker/Makefile.am Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/ticker/Makefile.am Fri Jan 31 18:02:20 2014 +0530 @@ -24,4 +24,5 @@ -I$(top_builddir)/libpurple \ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ - $(GTK_CFLAGS) + $(GTK_CFLAGS) \ + $(GPLUGIN_CFLAGS)
--- a/pidgin/plugins/ticker/gtkticker.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/ticker/gtkticker.c Fri Jan 31 18:02:20 2014 +0530 @@ -58,39 +58,7 @@ static GtkContainerClass *parent_class = NULL; -GType gtk_ticker_get_type (void) -{ - static GType ticker_type = 0; - - ticker_type = g_type_from_name("GtkTicker"); - - if (!ticker_type) - { - static const GTypeInfo ticker_info = - { - sizeof(GtkTickerClass), - NULL, - NULL, - (GClassInitFunc) gtk_ticker_class_init, - NULL, - NULL, - sizeof(GtkTicker), - 0, - (GInstanceInitFunc) gtk_ticker_init, - NULL - }; - - ticker_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkTicker", - &ticker_info, 0); - } - - /* kludge to re-initialise the class if it's already registered */ - else if (parent_class == NULL) { - gtk_ticker_class_init((GtkTickerClass *)g_type_class_peek(ticker_type)); - } - - return ticker_type; -} +PURPLE_DEFINE_TYPE(GtkTicker, gtk_ticker, GTK_TYPE_CONTAINER); static void gtk_ticker_finalize(GObject *object) { gtk_ticker_stop_scroll(GTK_TICKER(object));
--- a/pidgin/plugins/ticker/gtkticker.h Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/ticker/gtkticker.h Fri Jan 31 18:02:20 2014 +0530 @@ -28,6 +28,7 @@ #include <gdk/gdk.h> #include <gtk/gtk.h> +#include "plugins.h" #ifdef __cplusplus extern "C" { @@ -72,7 +73,10 @@ }; +G_MODULE_EXPORT GType gtk_ticker_get_type (void); +void gtk_ticker_register_type (PurplePlugin *plugin); + GtkWidget* gtk_ticker_new (void); void gtk_ticker_add (GtkTicker *ticker, GtkWidget *widget);
--- a/pidgin/plugins/ticker/ticker.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/ticker/ticker.c Fri Jan 31 18:02:20 2014 +0530 @@ -30,7 +30,7 @@ #include "buddylist.h" #include "conversation.h" #include "debug.h" -#include "prpl.h" +#include "protocol.h" #include "signals.h" #include "version.h" @@ -315,11 +315,35 @@ * EXPORTED FUNCTIONS */ +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Syd Logan", + NULL + }; + + return pidgin_plugin_info_new( + "id", TICKER_PLUGIN_ID, + "name", N_("Buddy Ticker"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("A horizontal scrolling version of the buddy list."), + "description", N_("A horizontal scrolling version of the buddy list."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { void *blist_handle = purple_blist_get_handle(); + gtk_ticker_register_type(plugin); + purple_signal_connect(purple_connections_get_handle(), "signed-off", plugin, PURPLE_CALLBACK(signoff_cb), NULL); purple_signal_connect(blist_handle, "buddy-signed-on", @@ -336,7 +360,7 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { TickerData *td; @@ -356,46 +380,4 @@ return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - TICKER_PLUGIN_ID, /**< id */ - N_("Buddy Ticker"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("A horizontal scrolling version of the buddy list."), - /** description */ - N_("A horizontal scrolling version of the buddy list."), - "Syd Logan", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(ticker, init_plugin, info) +PURPLE_PLUGIN_INIT(ticker, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/unity.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/unity.c Fri Jan 31 18:02:20 2014 +0530 @@ -502,8 +502,32 @@ return ret; } +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Ankit Vani <a@nevitus.org>", + NULL + }; + + return pidgin_plugin_info_new( + "id", UNITY_PLUGIN_ID, + "name", N_("Unity Integration"), + "version", DISPLAY_VERSION, + "category", N_("Notification"), + "summary", N_("Provides integration with Unity."), + "description", N_("Provides integration with Unity's " + "messaging menu and launcher."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { GList *convs = purple_conversations_get_all(); PurpleSavedStatus *saved_status; @@ -511,6 +535,12 @@ void *gtk_conv_handle = pidgin_conversations_get_handle(); void *savedstat_handle = purple_savedstatuses_get_handle(); + purple_prefs_add_none("/plugins/gtk"); + purple_prefs_add_none("/plugins/gtk/unity"); + purple_prefs_add_int("/plugins/gtk/unity/launcher_count", LAUNCHER_COUNT_SOURCES); + purple_prefs_add_int("/plugins/gtk/unity/messaging_menu_text", MESSAGING_MENU_COUNT); + purple_prefs_add_bool("/plugins/gtk/unity/alert_chat_nick", TRUE); + alert_chat_nick = purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick"); mmapp = messaging_menu_app_new("pidgin.desktop"); @@ -556,7 +586,7 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { GList *convs = purple_conversations_get_all(); while (convs) { @@ -574,64 +604,4 @@ return TRUE; } -static PidginPluginUiInfo ui_info = -{ - get_config_frame, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - UNITY_PLUGIN_ID, /**< id */ - N_("Unity Integration"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Provides integration with Unity."), - /** description */ - N_("Provides integration with Unity's messaging " - "menu and launcher."), - /**< author */ - "Ankit Vani <a@nevitus.org>", - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - purple_prefs_add_none("/plugins/gtk"); - purple_prefs_add_none("/plugins/gtk/unity"); - purple_prefs_add_int("/plugins/gtk/unity/launcher_count", LAUNCHER_COUNT_SOURCES); - purple_prefs_add_int("/plugins/gtk/unity/messaging_menu_text", MESSAGING_MENU_COUNT); - purple_prefs_add_bool("/plugins/gtk/unity/alert_chat_nick", TRUE); -} - -PURPLE_INIT_PLUGIN(unity, init_plugin, info) +PURPLE_PLUGIN_INIT(unity, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/webkit.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/webkit.c Fri Jan 31 18:02:20 2014 +0530 @@ -23,8 +23,32 @@ #include "gtkplugin.h" +static PidginPluginInfo * +plugin_query(GError **error) +{ + const gchar * const authors[] = { + "Elliott Sales de Andrade <qulogic@pidgin.im>", + NULL + }; + + return pidgin_plugin_info_new( + "id", "gtkwebkit-inspect", + "name", N_("WebKit Development"), + "version", DISPLAY_VERSION, + "category", N_("Testing"), + "summary", N_("Enables WebKit Inspector."), + "description", N_("Enables WebKit's built-in inspector. This may be " + "viewed by right-clicking a WebKit widget and " + "selecting 'Inspect Element'."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + NULL + ); +} + static gboolean -plugin_load(PurplePlugin *plugin) +plugin_load(PurplePlugin *plugin, GError **error) { purple_prefs_add_none(PIDGIN_PREFS_ROOT "/webview"); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/webview/inspector_enabled", FALSE); @@ -35,51 +59,11 @@ } static gboolean -plugin_unload(PurplePlugin *plugin) +plugin_unload(PurplePlugin *plugin, GError **error) { purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/webview/inspector_enabled", FALSE); return TRUE; } -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, /**< major version */ - PURPLE_MINOR_VERSION, /**< minor version */ - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "gtkwebkit-inspect", /**< id */ - N_("WebKit Development"), /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Enables WebKit Inspector."), /**< summary */ - N_("Enables WebKit's built-in inspector. This " - "may be viewed by right-clicking a WebKit " - "widget and selecting 'Inspect Element'."), /**< description */ - "Elliott Sales de Andrade <qulogic@pidgin.im>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ -} - -PURPLE_INIT_PLUGIN(webkit-devel, init_plugin, info) +PURPLE_PLUGIN_INIT(webkit-devel, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/win32/transparency/win2ktrans.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/win32/transparency/win2ktrans.c Fri Jan 31 18:02:20 2014 +0530 @@ -487,55 +487,6 @@ /* * EXPORTED FUNCTIONS */ -static gboolean plugin_load(PurplePlugin *plugin) { - - purple_signal_connect(purple_conversations_get_handle(), - "conversation-created", plugin, - PURPLE_CALLBACK(new_conversation_cb), NULL); - - /* Set callback to remove window from the list, if the window is destroyed */ - purple_signal_connect(purple_conversations_get_handle(), - "deleting-conversation", plugin, - PURPLE_CALLBACK(conversation_delete_cb), NULL); - - purple_signal_connect(pidgin_conversations_get_handle(), - "conversation-dragging", plugin, - PURPLE_CALLBACK(set_conv_window_trans), NULL); - - purple_signal_connect(purple_conversations_get_handle(), - "conversation-updated", plugin, - PURPLE_CALLBACK(conv_updated_cb), NULL); - - update_existing_convs(); - - if (blist) - blist_created_cb(NULL, NULL); - else - purple_signal_connect(pidgin_blist_get_handle(), - "gtkblist-created", plugin, - PURPLE_CALLBACK(blist_created_cb), NULL); - - - return TRUE; -} - -static gboolean plugin_unload(PurplePlugin *plugin) { - purple_debug_info(WINTRANS_PLUGIN_ID, "Unloading win2ktrans plugin\n"); - - remove_convs_wintrans(TRUE); - - if (blist) { - if (purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) - set_wintrans(blist, 0, FALSE, FALSE); - - /* Remove the focus cbs */ - g_signal_handlers_disconnect_by_func(G_OBJECT(blist), - G_CALLBACK(focus_blist_win_cb), blist); - } - - return TRUE; -} - static GtkWidget *get_config_frame(PurplePlugin *plugin) { GtkWidget *ret; GtkWidget *imtransbox, *bltransbox; @@ -656,55 +607,36 @@ return ret; } -static PidginPluginUiInfo ui_info = -{ - get_config_frame, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = +static PidginPluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - WINTRANS_PLUGIN_ID, /**< id */ - N_("Transparency"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Variable Transparency for the buddy list and conversations."), - /** description */ - N_("This plugin enables variable alpha transparency on conversation windows and the buddy list.\n\n" - "* Note: This plugin requires Win2000 or greater."), - "Herman Bloggs <hermanator12002@yahoo.com>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ + const gchar * const authors[] = { + "Herman Bloggs <hermanator12002@yahoo.com>", + NULL + }; - /* padding */ - NULL, - NULL, - NULL, - NULL -}; + return pidgin_plugin_info_new( + "id", WINTRANS_PLUGIN_ID, + "name", N_("Transparency"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Variable Transparency for the buddy " + "list and conversations."), + "description", N_("This plugin enables variable alpha " + "transparency on conversation windows " + "and the buddy list.\n\n" + "* Note: This plugin requires Win2000 or " + "greater."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); +} -static void -init_plugin(PurplePlugin *plugin) -{ +static gboolean plugin_load(PurplePlugin *plugin, GError **error) { + purple_prefs_add_none("/plugins/gtk/win32"); purple_prefs_add_none("/plugins/gtk/win32/wintrans"); purple_prefs_add_bool(OPT_WINTRANS_IM_ENABLED, FALSE); @@ -716,6 +648,52 @@ purple_prefs_add_int(OPT_WINTRANS_BL_ALPHA, 255); purple_prefs_add_bool(OPT_WINTRANS_BL_ONFOCUS, FALSE); purple_prefs_add_bool(OPT_WINTRANS_BL_ONTOP, FALSE); + + purple_signal_connect(purple_conversations_get_handle(), + "conversation-created", plugin, + PURPLE_CALLBACK(new_conversation_cb), NULL); + + /* Set callback to remove window from the list, if the window is destroyed */ + purple_signal_connect(purple_conversations_get_handle(), + "deleting-conversation", plugin, + PURPLE_CALLBACK(conversation_delete_cb), NULL); + + purple_signal_connect(pidgin_conversations_get_handle(), + "conversation-dragging", plugin, + PURPLE_CALLBACK(set_conv_window_trans), NULL); + + purple_signal_connect(purple_conversations_get_handle(), + "conversation-updated", plugin, + PURPLE_CALLBACK(conv_updated_cb), NULL); + + update_existing_convs(); + + if (blist) + blist_created_cb(NULL, NULL); + else + purple_signal_connect(pidgin_blist_get_handle(), + "gtkblist-created", plugin, + PURPLE_CALLBACK(blist_created_cb), NULL); + + + return TRUE; } -PURPLE_INIT_PLUGIN(wintrans, init_plugin, info) +static gboolean plugin_unload(PurplePlugin *plugin, GError **error) { + purple_debug_info(WINTRANS_PLUGIN_ID, "Unloading win2ktrans plugin\n"); + + remove_convs_wintrans(TRUE); + + if (blist) { + if (purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) + set_wintrans(blist, 0, FALSE, FALSE); + + /* Remove the focus cbs */ + g_signal_handlers_disconnect_by_func(G_OBJECT(blist), + G_CALLBACK(focus_blist_win_cb), blist); + } + + return TRUE; +} + +PURPLE_PLUGIN_INIT(wintrans, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/win32/winprefs/winprefs.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/win32/winprefs/winprefs.c Fri Jan 31 18:02:20 2014 +0530 @@ -229,40 +229,6 @@ * EXPORTED FUNCTIONS */ -static gboolean plugin_load(PurplePlugin *plugin) { - handle = plugin; - - /* blist docking init */ - if(purple_blist_get_buddy_list() && PIDGIN_BLIST(purple_blist_get_buddy_list()) - && PIDGIN_BLIST(purple_blist_get_buddy_list())->window) { - blist_create_cb(purple_blist_get_buddy_list(), NULL); - } - - /* This really shouldn't happen anymore generally, but if for some strange - reason, the blist is recreated, we need to set it up again. */ - purple_signal_connect(pidgin_blist_get_handle(), "gtkblist-created", - plugin, PURPLE_CALLBACK(blist_create_cb), NULL); - - purple_signal_connect((void*)purple_get_core(), "quitting", plugin, - PURPLE_CALLBACK(purple_quit_cb), NULL); - - purple_prefs_connect_callback(handle, PREF_BLIST_ON_TOP, - winprefs_set_blist_ontop, NULL); - purple_prefs_connect_callback(handle, PREF_DBLIST_DOCKABLE, - winprefs_set_blist_dockable, NULL); - - return TRUE; -} - -static gboolean plugin_unload(PurplePlugin *plugin) { - blist_set_dockable(FALSE); - blist_set_ontop(FALSE); - - handle = NULL; - - return TRUE; -} - static GtkWidget* get_config_frame(PurplePlugin *plugin) { GtkWidget *ret; GtkWidget *vbox; @@ -314,52 +280,31 @@ return ret; } -static PidginPluginUiInfo ui_info = -{ - get_config_frame, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginInfo info = +static PidginPluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, - PIDGIN_PLUGIN_TYPE, - 0, - NULL, - PURPLE_PRIORITY_DEFAULT, - WINPREFS_PLUGIN_ID, - N_("Windows Pidgin Options"), - DISPLAY_VERSION, - N_("Options specific to Pidgin for Windows."), - N_("Provides options specific to Pidgin for Windows, such as buddy list docking."), - "Herman Bloggs <hermanator12002@yahoo.com>", - PURPLE_WEBSITE, - plugin_load, - plugin_unload, - NULL, - &ui_info, - NULL, - NULL, - NULL, + const gchar * const authors[] = { + "Herman Bloggs <hermanator12002@yahoo.com>", + NULL + }; - /* padding */ - NULL, - NULL, - NULL, - NULL -}; + return pidgin_plugin_info_new( + "id", WINPREFS_PLUGIN_ID, + "name", N_("Windows Pidgin Options"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Options specific to Pidgin for Windows."), + "description", N_("Provides options specific to Pidgin for " + "Windows, such as buddy list docking."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, + NULL + ); +} -static void -init_plugin(PurplePlugin *plugin) -{ +static gboolean plugin_load(PurplePlugin *plugin, GError **error) { purple_prefs_add_none("/plugins/gtk"); purple_prefs_add_none("/plugins/gtk/win32"); purple_prefs_add_none("/plugins/gtk/win32/winprefs"); @@ -381,7 +326,38 @@ } else purple_prefs_add_int(PREF_BLIST_ON_TOP, BLIST_TOP_NEVER); purple_prefs_remove(PREF_CHAT_BLINK); + + handle = plugin; + + /* blist docking init */ + if(purple_blist_get_buddy_list() && PIDGIN_BLIST(purple_blist_get_buddy_list()) + && PIDGIN_BLIST(purple_blist_get_buddy_list())->window) { + blist_create_cb(purple_blist_get_buddy_list(), NULL); + } + + /* This really shouldn't happen anymore generally, but if for some strange + reason, the blist is recreated, we need to set it up again. */ + purple_signal_connect(pidgin_blist_get_handle(), "gtkblist-created", + plugin, PURPLE_CALLBACK(blist_create_cb), NULL); + + purple_signal_connect((void*)purple_get_core(), "quitting", plugin, + PURPLE_CALLBACK(purple_quit_cb), NULL); + + purple_prefs_connect_callback(handle, PREF_BLIST_ON_TOP, + winprefs_set_blist_ontop, NULL); + purple_prefs_connect_callback(handle, PREF_DBLIST_DOCKABLE, + winprefs_set_blist_dockable, NULL); + + return TRUE; } -PURPLE_INIT_PLUGIN(winprefs, init_plugin, info) +static gboolean plugin_unload(PurplePlugin *plugin, GError **error) { + blist_set_dockable(FALSE); + blist_set_ontop(FALSE); + handle = NULL; + + return TRUE; +} + +PURPLE_PLUGIN_INIT(winprefs, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/plugins/xmppconsole.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/plugins/xmppconsole.c Fri Jan 31 18:02:20 2014 +0530 @@ -22,7 +22,7 @@ #include "internal.h" #include "gtkplugin.h" #include "version.h" -#include "prpl.h" +#include "protocol.h" #include "xmlnode.h" #include "gtkimhtml.h" @@ -33,6 +33,9 @@ #include "gtk3compat.h" +#define PLUGIN_ID "gtk-xmpp" +#define PLUGIN_DOMAIN (g_quark_from_static_string(PLUGIN_ID)) + typedef struct { PurpleConnection *gc; GtkWidget *window; @@ -194,7 +197,7 @@ static gboolean message_send_cb(GtkWidget *widget, GdkEventKey *event, gpointer p) { - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleProtocol *protocol = NULL; PurpleConnection *gc; gchar *text; @@ -204,12 +207,12 @@ gc = console->gc; if (gc) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + protocol = purple_connection_get_protocol(gc); text = gtk_webview_get_body_text(GTK_WEBVIEW(widget)); - if (prpl_info && prpl_info->send_raw != NULL) - prpl_info->send_raw(gc, text, strlen(text)); + if (protocol) + purple_protocol_server_iface_send_raw(protocol, gc, text, strlen(text)); g_free(text); gtk_webview_load_html_string(GTK_WEBVIEW(console->entry), ""); @@ -706,36 +709,6 @@ } } -static gboolean -plugin_load(PurplePlugin *plugin) -{ - PurplePlugin *jabber; - - jabber = purple_find_prpl("prpl-jabber"); - if (!jabber) - return FALSE; - - xmpp_console_handle = plugin; - purple_signal_connect(jabber, "jabber-receiving-xmlnode", xmpp_console_handle, - PURPLE_CALLBACK(purple_xmlnode_received_cb), NULL); - purple_signal_connect(jabber, "jabber-sending-text", xmpp_console_handle, - PURPLE_CALLBACK(purple_xmlnode_sent_cb), NULL); - purple_signal_connect(purple_connections_get_handle(), "signing-on", - plugin, PURPLE_CALLBACK(signing_on_cb), NULL); - purple_signal_connect(purple_connections_get_handle(), "signed-off", - plugin, PURPLE_CALLBACK(signed_off_cb), NULL); - - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin) -{ - if (console) - gtk_widget_destroy(console->window); - return TRUE; -} - static void console_destroy(GtkWidget *window, gpointer nul) { @@ -843,7 +816,7 @@ } static GList * -actions(PurplePlugin *plugin, gpointer context) +actions(PurplePlugin *plugin) { GList *l = NULL; PurplePluginAction *act = NULL; @@ -854,47 +827,60 @@ return l; } - -static PurplePluginInfo info = +static PidginPluginInfo * +plugin_query(GError **error) { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ + const gchar * const authors[] = { + "Sean Egan <seanegan@gmail.com>", + NULL + }; - "gtk-xmpp", /**< id */ - N_("XMPP Console"), /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Send and receive raw XMPP stanzas."), - /** description */ - N_("This plugin is useful for debugging XMPP servers or clients."), - "Sean Egan <seanegan@gmail.com>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ + return pidgin_plugin_info_new( + "id", PLUGIN_ID, + "name", N_("XMPP Console"), + "version", DISPLAY_VERSION, + "category", N_("Protocol utility"), + "summary", N_("Send and receive raw XMPP stanzas."), + "description", N_("This plugin is useful for debugging XMPP servers " + "or clients."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "actions-cb", actions, + NULL + ); } -PURPLE_INIT_PLUGIN(xmppconsole, init_plugin, info) +static gboolean +plugin_load(PurplePlugin *plugin, GError **error) +{ + PurpleProtocol *jabber; + + jabber = purple_protocols_find("prpl-jabber"); + if (!jabber) { + g_set_error(error, PLUGIN_DOMAIN, 0, _("XMPP protocol is not loaded.")); + return FALSE; + } + + xmpp_console_handle = plugin; + purple_signal_connect(jabber, "jabber-receiving-xmlnode", xmpp_console_handle, + PURPLE_CALLBACK(purple_xmlnode_received_cb), NULL); + purple_signal_connect(jabber, "jabber-sending-text", xmpp_console_handle, + PURPLE_CALLBACK(purple_xmlnode_sent_cb), NULL); + purple_signal_connect(purple_connections_get_handle(), "signing-on", + plugin, PURPLE_CALLBACK(signing_on_cb), NULL); + purple_signal_connect(purple_connections_get_handle(), "signed-off", + plugin, PURPLE_CALLBACK(signed_off_cb), NULL); + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin, GError **error) +{ + if (console) + gtk_widget_destroy(console->window); + return TRUE; +} + +PURPLE_PLUGIN_INIT(xmppconsole, plugin_query, plugin_load, plugin_unload);
--- a/pidgin/smileyparser.c Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/smileyparser.c Fri Jan 31 18:02:20 2014 +0530 @@ -138,9 +138,9 @@ const char *proto_name = "default"; if (proto_id != NULL) { - PurplePlugin *proto; - proto = purple_find_prpl(proto_id); - proto_name = proto->info->name; + PurpleProtocol *protocol; + protocol = purple_protocols_find(proto_id); + proto_name = purple_protocol_get_name(protocol); } /* unnecessarily slow, but lets manage for now. */
--- a/pidgin/win32/nsis/pidgin-installer.nsi Fri Jan 31 17:56:27 2014 +0530 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Fri Jan 31 18:02:20 2014 +0530 @@ -335,10 +335,6 @@ ; Pidgin files SetOverwrite on - ;Delete old liboscar and libjabber since they tend to be problematic - Delete "$INSTDIR\plugins\liboscar.dll" - Delete "$INSTDIR\plugins\libjabber.dll" - File /r /x locale /x Gtk ..\..\..\${PIDGIN_INSTALL_DIR}\*.* ; Check if Perl is installed, if so add it to the AppPaths @@ -570,31 +566,27 @@ Delete "$INSTDIR\plugins\gtkbuddynote.dll" Delete "$INSTDIR\plugins\history.dll" Delete "$INSTDIR\plugins\internalkeyring.dll" - Delete "$INSTDIR\plugins\libfacebook.dll" - Delete "$INSTDIR\plugins\libgtalk.dll" Delete "$INSTDIR\plugins\ssl-gnutls.dll" Delete "$INSTDIR\plugins\webkit.dll" Delete "$INSTDIR\plugins\wincred.dll" Delete "$INSTDIR\plugins\iconaway.dll" Delete "$INSTDIR\plugins\idle.dll" Delete "$INSTDIR\plugins\joinpart.dll" - Delete "$INSTDIR\plugins\libaim.dll" Delete "$INSTDIR\plugins\libbonjour.dll" Delete "$INSTDIR\plugins\libgg.dll" - Delete "$INSTDIR\plugins\libicq.dll" Delete "$INSTDIR\plugins\libirc.dll" + Delete "$INSTDIR\plugins\libjabber.dll" Delete "$INSTDIR\plugins\libmsn.dll" Delete "$INSTDIR\plugins\libmxit.dll" Delete "$INSTDIR\plugins\libmyspace.dll" Delete "$INSTDIR\plugins\libnapster.dll" Delete "$INSTDIR\plugins\libnovell.dll" + Delete "$INSTDIR\plugins\liboscar.dll" Delete "$INSTDIR\plugins\libsametime.dll" Delete "$INSTDIR\plugins\libsilc.dll" Delete "$INSTDIR\plugins\libsimple.dll" Delete "$INSTDIR\plugins\libtoc.dll" Delete "$INSTDIR\plugins\libyahoo.dll" - Delete "$INSTDIR\plugins\libyahoojp.dll" - Delete "$INSTDIR\plugins\libxmpp.dll" Delete "$INSTDIR\plugins\log_reader.dll" Delete "$INSTDIR\plugins\markerline.dll" Delete "$INSTDIR\plugins\newline.dll" @@ -637,10 +629,8 @@ RMDir "$INSTDIR\spellcheck\lib" RMDir "$INSTDIR\spellcheck" Delete "$INSTDIR\freebl3.dll" - Delete "$INSTDIR\libjabber.dll" Delete "$INSTDIR\libnspr4.dll" Delete "$INSTDIR\libmeanwhile-1.dll" - Delete "$INSTDIR\liboscar.dll" Delete "$INSTDIR\libplc4.dll" Delete "$INSTDIR\libplds4.dll" Delete "$INSTDIR\libpurple.dll" @@ -649,7 +639,6 @@ Delete "$INSTDIR\libsilcclient-1-1-3.dll" Delete "$INSTDIR\libssp-0.dll" Delete "$INSTDIR\libxml2-2.dll" - Delete "$INSTDIR\libymsg.dll" Delete "$INSTDIR\nss3.dll" Delete "$INSTDIR\nssutil3.dll" Delete "$INSTDIR\pidgin.dll"