From 87b298a83b8bca7be01ddc853ea4cd15e455b44e Mon Sep 17 00:00:00 2001 From: Sean Egan Date: Sat, 20 Jan 2007 02:32:10 +0000 Subject: Rename gtk/ and libgaim/ to pidgin/ and libpurple/ --- libpurple/plugins/Makefile.am | 129 ++ libpurple/plugins/Makefile.mingw | 82 + libpurple/plugins/autoaccept.c | 273 +++ libpurple/plugins/autoreply.c | 435 ++++ libpurple/plugins/buddynote.c | 110 + libpurple/plugins/ciphertest.c | 287 +++ libpurple/plugins/codeinline.c | 93 + libpurple/plugins/dbus-buddyicons-example.py | 36 + libpurple/plugins/dbus-example.c | 178 ++ libpurple/plugins/filectl.c | 270 +++ libpurple/plugins/fortuneprofile.pl | 125 ++ libpurple/plugins/gaim.pl | 39 + libpurple/plugins/idle.c | 337 +++ libpurple/plugins/ipc-test-client.c | 114 ++ libpurple/plugins/ipc-test-server.c | 99 + libpurple/plugins/log_reader.c | 2151 ++++++++++++++++++++ libpurple/plugins/mono/BooPlugin.boo | 22 + libpurple/plugins/mono/GetBuddyBack.cs | 32 + libpurple/plugins/mono/MPlugin.cs | 36 + libpurple/plugins/mono/Makefile.am | 19 + libpurple/plugins/mono/api/BlistNode.cs | 4 + libpurple/plugins/mono/api/Buddy.cs | 9 + libpurple/plugins/mono/api/BuddyList.cs | 19 + libpurple/plugins/mono/api/Contact.cs | 4 + libpurple/plugins/mono/api/Debug.cs | 28 + libpurple/plugins/mono/api/Event.cs | 21 + libpurple/plugins/mono/api/GaimPlugin.cs | 61 + libpurple/plugins/mono/api/Group.cs | 4 + libpurple/plugins/mono/api/Makefile.am | 26 + libpurple/plugins/mono/api/Signal.cs | 18 + libpurple/plugins/mono/api/Status.cs | 9 + libpurple/plugins/mono/loader/Makefile.am | 25 + libpurple/plugins/mono/loader/blist-glue.c | 26 + libpurple/plugins/mono/loader/debug-glue.c | 16 + libpurple/plugins/mono/loader/mono-glue.h | 19 + libpurple/plugins/mono/loader/mono-helper.c | 251 +++ libpurple/plugins/mono/loader/mono-helper.h | 73 + libpurple/plugins/mono/loader/mono.c | 235 +++ libpurple/plugins/mono/loader/signal-glue.c | 139 ++ libpurple/plugins/mono/loader/status-glue.c | 16 + libpurple/plugins/newline.c | 91 + libpurple/plugins/offlinemsg.c | 239 +++ libpurple/plugins/perl/Makefile.am | 168 ++ libpurple/plugins/perl/Makefile.mingw | 82 + libpurple/plugins/perl/common/Account.xs | 326 +++ libpurple/plugins/perl/common/AccountOpts.xs | 168 ++ libpurple/plugins/perl/common/BuddyIcon.xs | 90 + libpurple/plugins/perl/common/BuddyList.xs | 383 ++++ libpurple/plugins/perl/common/Cipher.xs | 157 ++ libpurple/plugins/perl/common/Cmds.xs | 49 + libpurple/plugins/perl/common/Connection.xs | 89 + libpurple/plugins/perl/common/Conversation.xs | 402 ++++ libpurple/plugins/perl/common/Core.xs | 28 + libpurple/plugins/perl/common/Debug.xs | 54 + libpurple/plugins/perl/common/FT.xs | 168 ++ libpurple/plugins/perl/common/Gaim.pm | 131 ++ libpurple/plugins/perl/common/Gaim.xs | 99 + libpurple/plugins/perl/common/ImgStore.xs | 35 + libpurple/plugins/perl/common/Log.xs | 94 + libpurple/plugins/perl/common/MANIFEST | 37 + libpurple/plugins/perl/common/Makefile.PL.in | 39 + libpurple/plugins/perl/common/Makefile.mingw | 117 ++ libpurple/plugins/perl/common/Network.xs | 45 + libpurple/plugins/perl/common/Notify.xs | 138 ++ libpurple/plugins/perl/common/Plugin.xs | 156 ++ libpurple/plugins/perl/common/PluginPref.xs | 144 ++ libpurple/plugins/perl/common/Pounce.xs | 90 + libpurple/plugins/perl/common/Prefs.xs | 151 ++ libpurple/plugins/perl/common/Privacy.xs | 43 + libpurple/plugins/perl/common/Proxy.xs | 65 + libpurple/plugins/perl/common/Prpl.xs | 54 + libpurple/plugins/perl/common/Request.xs | 583 ++++++ libpurple/plugins/perl/common/Roomlist.xs | 84 + libpurple/plugins/perl/common/SSLConn.xs | 61 + libpurple/plugins/perl/common/SavedStatuses.xs | 58 + libpurple/plugins/perl/common/Server.xs | 216 ++ libpurple/plugins/perl/common/Signal.xs | 34 + libpurple/plugins/perl/common/Sound.xs | 27 + libpurple/plugins/perl/common/Status.xs | 439 ++++ libpurple/plugins/perl/common/Stringref.xs | 37 + libpurple/plugins/perl/common/Util.xs | 267 +++ libpurple/plugins/perl/common/XMLNode.xs | 88 + libpurple/plugins/perl/common/fallback/const-c.inc | 115 ++ .../plugins/perl/common/fallback/const-xs.inc | 88 + libpurple/plugins/perl/common/module.h | 278 +++ libpurple/plugins/perl/common/typemap | 209 ++ libpurple/plugins/perl/libgaimperl.c | 9 + libpurple/plugins/perl/perl-common.c | 618 ++++++ libpurple/plugins/perl/perl-common.h | 61 + libpurple/plugins/perl/perl-handlers.c | 636 ++++++ libpurple/plugins/perl/perl-handlers.h | 71 + libpurple/plugins/perl/perl.c | 611 ++++++ libpurple/plugins/perl/scripts/account.pl | 122 ++ libpurple/plugins/perl/scripts/buddy_list.pl | 107 + libpurple/plugins/perl/scripts/conversation.pl | 119 ++ libpurple/plugins/perl/scripts/count_down.pl | 89 + libpurple/plugins/perl/scripts/gtk_frame_test.pl | 66 + libpurple/plugins/perl/scripts/plugin_action.pl | 58 + libpurple/plugins/perl/scripts/plugin_pref.pl | 94 + libpurple/plugins/perl/scripts/request.pl | 109 + libpurple/plugins/pluginpref_example.c | 162 ++ libpurple/plugins/psychic.c | 164 ++ libpurple/plugins/signals-test.c | 708 +++++++ libpurple/plugins/simple.c | 61 + libpurple/plugins/ssl/Makefile.am | 35 + libpurple/plugins/ssl/Makefile.mingw | 95 + libpurple/plugins/ssl/ssl-gnutls.c | 264 +++ libpurple/plugins/ssl/ssl-nss.c | 433 ++++ libpurple/plugins/ssl/ssl.c | 118 ++ libpurple/plugins/statenotify.c | 172 ++ libpurple/plugins/tcl/Makefile.am | 22 + libpurple/plugins/tcl/Makefile.mingw | 77 + libpurple/plugins/tcl/signal-test.tcl | 110 + libpurple/plugins/tcl/tcl.c | 527 +++++ libpurple/plugins/tcl/tcl_cmd.c | 190 ++ libpurple/plugins/tcl/tcl_cmds.c | 1607 +++++++++++++++ libpurple/plugins/tcl/tcl_gaim.h | 116 ++ libpurple/plugins/tcl/tcl_glib.c | 254 +++ libpurple/plugins/tcl/tcl_glib.h | 29 + libpurple/plugins/tcl/tcl_ref.c | 152 ++ libpurple/plugins/tcl/tcl_signals.c | 396 ++++ libpurple/plugins/test.pl | 58 + 122 files changed, 20906 insertions(+) create mode 100644 libpurple/plugins/Makefile.am create mode 100644 libpurple/plugins/Makefile.mingw create mode 100644 libpurple/plugins/autoaccept.c create mode 100644 libpurple/plugins/autoreply.c create mode 100644 libpurple/plugins/buddynote.c create mode 100644 libpurple/plugins/ciphertest.c create mode 100755 libpurple/plugins/codeinline.c create mode 100644 libpurple/plugins/dbus-buddyicons-example.py create mode 100644 libpurple/plugins/dbus-example.c create mode 100644 libpurple/plugins/filectl.c create mode 100644 libpurple/plugins/fortuneprofile.pl create mode 100644 libpurple/plugins/gaim.pl create mode 100644 libpurple/plugins/idle.c create mode 100644 libpurple/plugins/ipc-test-client.c create mode 100644 libpurple/plugins/ipc-test-server.c create mode 100644 libpurple/plugins/log_reader.c create mode 100644 libpurple/plugins/mono/BooPlugin.boo create mode 100644 libpurple/plugins/mono/GetBuddyBack.cs create mode 100644 libpurple/plugins/mono/MPlugin.cs create mode 100644 libpurple/plugins/mono/Makefile.am create mode 100644 libpurple/plugins/mono/api/BlistNode.cs create mode 100644 libpurple/plugins/mono/api/Buddy.cs create mode 100644 libpurple/plugins/mono/api/BuddyList.cs create mode 100644 libpurple/plugins/mono/api/Contact.cs create mode 100644 libpurple/plugins/mono/api/Debug.cs create mode 100644 libpurple/plugins/mono/api/Event.cs create mode 100644 libpurple/plugins/mono/api/GaimPlugin.cs create mode 100644 libpurple/plugins/mono/api/Group.cs create mode 100644 libpurple/plugins/mono/api/Makefile.am create mode 100644 libpurple/plugins/mono/api/Signal.cs create mode 100644 libpurple/plugins/mono/api/Status.cs create mode 100644 libpurple/plugins/mono/loader/Makefile.am create mode 100644 libpurple/plugins/mono/loader/blist-glue.c create mode 100644 libpurple/plugins/mono/loader/debug-glue.c create mode 100644 libpurple/plugins/mono/loader/mono-glue.h create mode 100644 libpurple/plugins/mono/loader/mono-helper.c create mode 100644 libpurple/plugins/mono/loader/mono-helper.h create mode 100644 libpurple/plugins/mono/loader/mono.c create mode 100644 libpurple/plugins/mono/loader/signal-glue.c create mode 100644 libpurple/plugins/mono/loader/status-glue.c create mode 100644 libpurple/plugins/newline.c create mode 100644 libpurple/plugins/offlinemsg.c create mode 100644 libpurple/plugins/perl/Makefile.am create mode 100755 libpurple/plugins/perl/Makefile.mingw create mode 100644 libpurple/plugins/perl/common/Account.xs create mode 100644 libpurple/plugins/perl/common/AccountOpts.xs create mode 100644 libpurple/plugins/perl/common/BuddyIcon.xs create mode 100644 libpurple/plugins/perl/common/BuddyList.xs create mode 100644 libpurple/plugins/perl/common/Cipher.xs create mode 100644 libpurple/plugins/perl/common/Cmds.xs create mode 100644 libpurple/plugins/perl/common/Connection.xs create mode 100644 libpurple/plugins/perl/common/Conversation.xs create mode 100644 libpurple/plugins/perl/common/Core.xs create mode 100644 libpurple/plugins/perl/common/Debug.xs create mode 100644 libpurple/plugins/perl/common/FT.xs create mode 100644 libpurple/plugins/perl/common/Gaim.pm create mode 100644 libpurple/plugins/perl/common/Gaim.xs create mode 100644 libpurple/plugins/perl/common/ImgStore.xs create mode 100644 libpurple/plugins/perl/common/Log.xs create mode 100644 libpurple/plugins/perl/common/MANIFEST create mode 100644 libpurple/plugins/perl/common/Makefile.PL.in create mode 100644 libpurple/plugins/perl/common/Makefile.mingw create mode 100644 libpurple/plugins/perl/common/Network.xs create mode 100644 libpurple/plugins/perl/common/Notify.xs create mode 100644 libpurple/plugins/perl/common/Plugin.xs create mode 100644 libpurple/plugins/perl/common/PluginPref.xs create mode 100644 libpurple/plugins/perl/common/Pounce.xs create mode 100644 libpurple/plugins/perl/common/Prefs.xs create mode 100644 libpurple/plugins/perl/common/Privacy.xs create mode 100644 libpurple/plugins/perl/common/Proxy.xs create mode 100644 libpurple/plugins/perl/common/Prpl.xs create mode 100644 libpurple/plugins/perl/common/Request.xs create mode 100644 libpurple/plugins/perl/common/Roomlist.xs create mode 100644 libpurple/plugins/perl/common/SSLConn.xs create mode 100644 libpurple/plugins/perl/common/SavedStatuses.xs create mode 100644 libpurple/plugins/perl/common/Server.xs create mode 100644 libpurple/plugins/perl/common/Signal.xs create mode 100644 libpurple/plugins/perl/common/Sound.xs create mode 100644 libpurple/plugins/perl/common/Status.xs create mode 100644 libpurple/plugins/perl/common/Stringref.xs create mode 100644 libpurple/plugins/perl/common/Util.xs create mode 100644 libpurple/plugins/perl/common/XMLNode.xs create mode 100644 libpurple/plugins/perl/common/fallback/const-c.inc create mode 100644 libpurple/plugins/perl/common/fallback/const-xs.inc create mode 100644 libpurple/plugins/perl/common/module.h create mode 100644 libpurple/plugins/perl/common/typemap create mode 100644 libpurple/plugins/perl/libgaimperl.c create mode 100644 libpurple/plugins/perl/perl-common.c create mode 100644 libpurple/plugins/perl/perl-common.h create mode 100644 libpurple/plugins/perl/perl-handlers.c create mode 100644 libpurple/plugins/perl/perl-handlers.h create mode 100644 libpurple/plugins/perl/perl.c create mode 100644 libpurple/plugins/perl/scripts/account.pl create mode 100644 libpurple/plugins/perl/scripts/buddy_list.pl create mode 100644 libpurple/plugins/perl/scripts/conversation.pl create mode 100644 libpurple/plugins/perl/scripts/count_down.pl create mode 100644 libpurple/plugins/perl/scripts/gtk_frame_test.pl create mode 100644 libpurple/plugins/perl/scripts/plugin_action.pl create mode 100644 libpurple/plugins/perl/scripts/plugin_pref.pl create mode 100644 libpurple/plugins/perl/scripts/request.pl create mode 100644 libpurple/plugins/pluginpref_example.c create mode 100644 libpurple/plugins/psychic.c create mode 100644 libpurple/plugins/signals-test.c create mode 100644 libpurple/plugins/simple.c create mode 100644 libpurple/plugins/ssl/Makefile.am create mode 100644 libpurple/plugins/ssl/Makefile.mingw create mode 100644 libpurple/plugins/ssl/ssl-gnutls.c create mode 100644 libpurple/plugins/ssl/ssl-nss.c create mode 100644 libpurple/plugins/ssl/ssl.c create mode 100644 libpurple/plugins/statenotify.c create mode 100644 libpurple/plugins/tcl/Makefile.am create mode 100644 libpurple/plugins/tcl/Makefile.mingw create mode 100644 libpurple/plugins/tcl/signal-test.tcl create mode 100644 libpurple/plugins/tcl/tcl.c create mode 100644 libpurple/plugins/tcl/tcl_cmd.c create mode 100644 libpurple/plugins/tcl/tcl_cmds.c create mode 100644 libpurple/plugins/tcl/tcl_gaim.h create mode 100644 libpurple/plugins/tcl/tcl_glib.c create mode 100644 libpurple/plugins/tcl/tcl_glib.h create mode 100644 libpurple/plugins/tcl/tcl_ref.c create mode 100644 libpurple/plugins/tcl/tcl_signals.c create mode 100644 libpurple/plugins/test.pl (limited to 'libpurple/plugins') diff --git a/libpurple/plugins/Makefile.am b/libpurple/plugins/Makefile.am new file mode 100644 index 0000000000..26324007af --- /dev/null +++ b/libpurple/plugins/Makefile.am @@ -0,0 +1,129 @@ +DIST_SUBDIRS = mono perl ssl tcl + +if USE_PERL +PERL_DIR = perl +endif + +if USE_TCL +TCL_DIR = tcl +endif + +if ENABLE_DBUS +DBUS_LTLIB = dbus-example.la +endif + +if USE_MONO +MONO_DIR = mono +endif + +SUBDIRS = \ + $(MONO_DIR) \ + $(PERL_DIR) \ + ssl \ + $(TCL_DIR) + +plugindir = $(libdir)/gaim + +autoaccept_la_LDFLAGS = -module -avoid-version +autoreply_la_LDFLAGS = -module -avoid-version +buddynote_la_LDFLAGS = -module -avoid-version +idle_la_LDFLAGS = -module -avoid-version +log_reader_la_LDFLAGS = -module -avoid-version +newline_la_LDFLAGS = -module -avoid-version +offlinemsg_la_LDFLAGS = -module -avoid-version +psychic_la_LDFLAGS = -module -avoid-version +statenotify_la_LDFLAGS = -module -avoid-version + +# this can't be in a conditional otherwise automake 1.4 yells +dbus_example_la_LDFLAGS = -module -avoid-version + +if PLUGINS + +plugin_LTLIBRARIES = \ + autoaccept.la \ + autoreply.la \ + buddynote.la \ + idle.la \ + log_reader.la \ + newline.la \ + offlinemsg.la \ + psychic.la \ + statenotify.la \ + $(DBUS_LTLIB) + +autoaccept_la_SOURCES = autoaccept.c +autoreply_la_SOURCES = autoreply.c +buddynote_la_SOURCES = buddynote.c +idle_la_SOURCES = idle.c +log_reader_la_SOURCES = log_reader.c +newline_la_SOURCES = newline.c +offlinemsg_la_SOURCES = offlinemsg.c +psychic_la_SOURCES = psychic.c +statenotify_la_SOURCES = statenotify.c + +autoaccept_la_LIBADD = $(GLIB_LIBS) +autoreply_la_LIBADD = $(GLIB_LIBS) +buddynote_la_LIBADD = $(GLIB_LIBS) +idle_la_LIBADD = $(GLIB_LIBS) +log_reader_la_LIBADD = $(GLIB_LIBS) +newline_la_LIBADD = $(GLIB_LIBS) +offlinemsg_la_LIBADD = $(GLIB_LIBS) +psychic_la_LIBADD = $(GLIB_LIBS) +statenotify_la_LIBADD = $(GLIB_LIBS) + +if ENABLE_DBUS + +CLEANFILES = dbus-example-bindings.c +dbus_example_la_SOURCES = dbus-example.c + +dbus_example_la_LIBADD = $(GLIB_LIBS) $(DBUS_LIBS) + +.PHONY: always + +$(top_builddir)/libpurple/dbus-types.h: always + cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +dbus-example-bindings.c: $(top_srcdir)/libpurple/dbus-analyze-functions.py $(dbus_example_la_SOURCES) + cat $(srcdir)/$(dbus_example_la_SOURCES) | \ + $(PYTHON) $(top_srcdir)/libpurple/dbus-analyze-functions.py --export-only > $@ + +$(dbus_example_la_OBJECTS) dbus-example.so: dbus-example-bindings.c $(top_builddir)/libpurple/dbus-types.h + + +endif # ENABLE_DBUS + +endif # PLUGINS + +EXTRA_DIST = \ + Makefile.mingw \ + dbus-buddyicons-example.py \ + filectl.c \ + fortuneprofile.pl \ + gaim.pl \ + ipc-test-client.c \ + ipc-test-server.c \ + pluginpref_example.c \ + signals-test.c \ + simple.c + +AM_CPPFLAGS = \ + -DDATADIR=\"$(datadir)\" \ + -DVERSION=\"$(VERSION)\" \ + -I$(top_builddir)/libpurple \ + -I$(top_srcdir)/libpurple \ + $(DEBUG_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(PLUGIN_CFLAGS) \ + $(DBUS_CFLAGS) + +# +# This part allows people to build their own plugins in here. +# Yes, it's a mess. +# +SUFFIXES = .c .so +.c.so: + $(LIBTOOL) --mode=compile $(CC) -DHAVE_CONFIG_H -I$(top_srcdir) $(AM_CPPFLAGS) $(CFLAGS) -c $< -o tmp$@.lo $(PLUGIN_CFLAGS) + $(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o libtmp$@.la -rpath $(plugindir) tmp$@.lo $(LIBS) $(LDFLAGS) -module -avoid-version $(PLUGIN_LIBS) + @rm -f tmp$@.lo tmp$@.o libtmp$@.la + @cp .libs/libtmp$@.so* $@ + @rm -f .libs/libtmp$@.* diff --git a/libpurple/plugins/Makefile.mingw b/libpurple/plugins/Makefile.mingw new file mode 100644 index 0000000000..4076c06c54 --- /dev/null +++ b/libpurple/plugins/Makefile.mingw @@ -0,0 +1,82 @@ +# +# Makefile.mingw +# +# Description: Makefile for win32 (mingw) version of Gaim Plugins +# + +GAIM_TOP := ../.. +include $(GAIM_TOP)/libgaim/win32/global.mak + +PERL_PLUGIN := ./perl +TCL_PLUGIN := ./tcl +SSL_PLUGIN := ./ssl + +.SUFFIXES: +.SUFFIXES: .c .dll + +## +## INCLUDE PATHS +## +INCLUDE_PATHS += \ + -I$(GTK_TOP)/include \ + -I$(GTK_TOP)/include/glib-2.0 \ + -I$(GTK_TOP)/lib/glib-2.0/include \ + -I$(GAIM_TOP) \ + -I$(GAIM_LIB_TOP) \ + -I$(GAIM_LIB_TOP)/win32 + +LIB_PATHS = -L$(GTK_TOP)/lib \ + -L$(GAIM_LIB_TOP) + +## +## LIBRARIES +## +LIBS = \ + -lglib-2.0 \ + -lgobject-2.0 \ + -lgmodule-2.0 \ + -lintl \ + -lws2_32 \ + -lgaim + +## +## TARGET DEFINITIONS +## +.PHONY: all clean plugins install + +all: $(GAIM_LIBGAIM_DLL).a plugins + $(MAKE) -C $(PERL_PLUGIN) -f $(GAIM_WIN32_MAKEFILE) + $(MAKE) -C $(TCL_PLUGIN) -f $(GAIM_WIN32_MAKEFILE) + $(MAKE) -C $(SSL_PLUGIN) -f $(GAIM_WIN32_MAKEFILE) + +install: all $(GAIM_INSTALL_PLUGINS_DIR) + $(MAKE) -C $(PERL_PLUGIN) -f $(GAIM_WIN32_MAKEFILE) install + $(MAKE) -C $(TCL_PLUGIN) -f $(GAIM_WIN32_MAKEFILE) install + $(MAKE) -C $(SSL_PLUGIN) -f $(GAIM_WIN32_MAKEFILE) install + cp *.dll $(GAIM_INSTALL_PLUGINS_DIR) + +.c.dll: + $(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@.o -c $< + $(CC) -shared $@.o $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $@ + +plugins: \ + autoaccept.dll \ + autoreply.dll \ + buddynote.dll \ + idle.dll \ + log_reader.dll \ + newline.dll \ + offlinemsg.dll \ + psychic.dll \ + statenotify.dll + +## +## CLEAN RULES +## +clean: + rm -f *.o *.dll + $(MAKE) -C $(PERL_PLUGIN) -f $(GAIM_WIN32_MAKEFILE) clean + $(MAKE) -C $(TCL_PLUGIN) -f $(GAIM_WIN32_MAKEFILE) clean + $(MAKE) -C $(SSL_PLUGIN) -f $(GAIM_WIN32_MAKEFILE) clean + +include $(GAIM_COMMON_TARGETS) diff --git a/libpurple/plugins/autoaccept.c b/libpurple/plugins/autoaccept.c new file mode 100644 index 0000000000..cd4185f126 --- /dev/null +++ b/libpurple/plugins/autoaccept.c @@ -0,0 +1,273 @@ +/* + * Autoaccept - Auto-accept file transfers from selected users + * Copyright (C) 2006 + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include "internal.h" + +#define PLUGIN_ID "core-plugin_pack-autoaccept" +#define PLUGIN_NAME N_("Autoaccept") +#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 " + +/* System headers */ +#include +#if GLIB_CHECK_VERSION(2,6,0) +# include +#else +# include +# include +# define g_mkdir mkdir +#endif + +/* Gaim headers */ +#include +#include + +#include +#include +#include +#include +#include +#include + +#define PREF_PREFIX "/plugins/core/" PLUGIN_ID +#define PREF_PATH PREF_PREFIX "/path" +#define PREF_STRANGER PREF_PREFIX "/reject_stranger" +#define PREF_NOTIFY PREF_PREFIX "/notify" + +typedef enum +{ + FT_ASK, + FT_ACCEPT, + FT_REJECT +} AutoAcceptSetting; + +static gboolean +ensure_path_exists(const char *dir) +{ + if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) + { + if (gaim_build_dir(dir, S_IRUSR | S_IWUSR | S_IXUSR)) + return FALSE; + } + + return TRUE; +} + +static void +auto_accept_complete_cb(GaimXfer *xfer, GaimXfer *my) +{ + if (xfer == my && gaim_prefs_get_bool(PREF_NOTIFY) && + !gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, xfer->who, xfer->account)) + { + char *message = g_strdup_printf(_("Autoaccepted file transfer of \"%s\" from \"%s\" completed."), + xfer->filename, xfer->who); + gaim_notify_info(NULL, _("Autoaccept complete"), message, NULL); + g_free(message); + } +} + +static void +file_recv_request_cb(GaimXfer *xfer, gpointer handle) +{ + GaimAccount *account; + GaimBlistNode *node; + const char *pref; + char *filename; + char *dirname; + + account = xfer->account; + node = (GaimBlistNode *)gaim_find_buddy(account, xfer->who); + + if (!node) + { + if (gaim_prefs_get_bool(PREF_STRANGER)) + xfer->status = GAIM_XFER_STATUS_CANCEL_LOCAL; + return; + } + + node = node->parent; + g_return_if_fail(GAIM_BLIST_NODE_IS_CONTACT(node)); + + pref = gaim_prefs_get_string(PREF_PATH); + switch (gaim_blist_node_get_int(node, "autoaccept")) + { + case FT_ASK: + break; + case FT_ACCEPT: + if (ensure_path_exists(pref)) + { + dirname = g_build_filename(pref, xfer->who, NULL); + + if (!ensure_path_exists(dirname)) + { + g_free(dirname); + break; + } + + filename = g_build_filename(dirname, xfer->filename, NULL); + + gaim_xfer_request_accepted(xfer, filename); + + g_free(dirname); + g_free(filename); + } + + gaim_signal_connect(gaim_xfers_get_handle(), "file-recv-complete", handle, + GAIM_CALLBACK(auto_accept_complete_cb), xfer); + break; + case FT_REJECT: + xfer->status = GAIM_XFER_STATUS_CANCEL_LOCAL; + break; + } +} + +static void +save_cb(GaimBlistNode *node, int choice) +{ + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + node = node->parent; + g_return_if_fail(GAIM_BLIST_NODE_IS_CONTACT(node)); + gaim_blist_node_set_int(node, "autoaccept", choice); +} + +static void +set_auto_accept_settings(GaimBlistNode *node, gpointer plugin) +{ + char *message; + + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + node = node->parent; + g_return_if_fail(GAIM_BLIST_NODE_IS_CONTACT(node)); + + message = g_strdup_printf(_("When a file-transfer request arrives from %s"), + gaim_contact_get_alias((GaimContact *)node)); + gaim_request_choice(plugin, _("Set Autoaccept Setting"), message, + NULL, gaim_blist_node_get_int(node, "autoaccept"), + _("_Save"), G_CALLBACK(save_cb), + _("_Cancel"), NULL, node, + _("Ask"), FT_ASK, + _("Auto Accept"), FT_ACCEPT, + _("Auto Reject"), FT_REJECT, + NULL); + g_free(message); +} + +static void +context_menu(GaimBlistNode *node, GList **menu, gpointer plugin) +{ + GaimMenuAction *action; + + if (!GAIM_BLIST_NODE_IS_BUDDY(node) && !GAIM_BLIST_NODE_IS_CONTACT(node)) + return; + + action = gaim_menu_action_new(_("Autoaccept File Transfers..."), + GAIM_CALLBACK(set_auto_accept_settings), plugin, NULL); + (*menu) = g_list_prepend(*menu, action); +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + gaim_signal_connect(gaim_xfers_get_handle(), "file-recv-request", plugin, + GAIM_CALLBACK(file_recv_request_cb), plugin); + gaim_signal_connect(gaim_blist_get_handle(), "blist-node-extended-menu", plugin, + GAIM_CALLBACK(context_menu), plugin); + return TRUE; +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + return TRUE; +} + +static GaimPluginPrefFrame * +get_plugin_pref_frame(GaimPlugin *plugin) +{ + GaimPluginPrefFrame *frame; + GaimPluginPref *pref; + + frame = gaim_plugin_pref_frame_new(); + + /* XXX: Is there a better way than this? There really should be. */ + pref = gaim_plugin_pref_new_with_name_and_label(PREF_PATH, _("Path to save the files in\n" + "(Please provide the full path)")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name_and_label(PREF_STRANGER, + _("Automatically reject from users not in buddy list")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name_and_label(PREF_NOTIFY, + _("Notify with a popup when an autoaccepted file transfer is complete\n" + "(only when there's no conversation with the sender)")); + gaim_plugin_pref_frame_add(frame, pref); + + return frame; +} + +static GaimPluginUiInfo prefs_info = { + get_plugin_pref_frame, + 0, + NULL, +}; + +static GaimPluginInfo info = { + GAIM_PLUGIN_MAGIC, /* Magic */ + GAIM_MAJOR_VERSION, /* Gaim Major Version */ + GAIM_MINOR_VERSION, /* Gaim Minor Version */ + GAIM_PLUGIN_STANDARD, /* plugin type */ + NULL, /* ui requirement */ + 0, /* flags */ + NULL, /* dependencies */ + GAIM_PRIORITY_DEFAULT, /* priority */ + + PLUGIN_ID, /* plugin id */ + PLUGIN_NAME, /* name */ + VERSION, /* version */ + PLUGIN_SUMMARY, /* summary */ + PLUGIN_DESCRIPTION, /* description */ + PLUGIN_AUTHOR, /* author */ + GAIM_WEBSITE, /* website */ + + plugin_load, /* load */ + plugin_unload, /* unload */ + NULL, /* destroy */ + + NULL, /* ui_info */ + NULL, /* extra_info */ + &prefs_info, /* prefs_info */ + NULL /* actions */ +}; + +static void +init_plugin(GaimPlugin *plugin) { + char *dirname; + + dirname = g_build_filename(gaim_user_dir(), "autoaccept", NULL); + gaim_prefs_add_none(PREF_PREFIX); + gaim_prefs_add_string(PREF_PATH, dirname); + gaim_prefs_add_bool(PREF_STRANGER, TRUE); + gaim_prefs_add_bool(PREF_NOTIFY, TRUE); + g_free(dirname); +} + +GAIM_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) diff --git a/libpurple/plugins/autoreply.c b/libpurple/plugins/autoreply.c new file mode 100644 index 0000000000..df4602247d --- /dev/null +++ b/libpurple/plugins/autoreply.c @@ -0,0 +1,435 @@ +/* + * Autoreply - Autoreply feature for all the protocols + * Copyright (C) 2005 + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include "internal.h" + +#define PLUGIN_ID "core-plugin_pack-autoreply" +#define PLUGIN_NAME N_("Autoreply") +#define PLUGIN_STATIC_NAME "Autoreply" +#define PLUGIN_SUMMARY N_("Autoreply for all the protocols") +#define PLUGIN_DESCRIPTION N_("This plugin lets you set autoreply message for any protocol. "\ + "You can set the global autoreply message from the Plugin-options dialog. " \ + "To set some specific autoreply message for a particular buddy, right click " \ + "on the buddy in the buddy-list window. To set autoreply messages for some " \ + "account, go to the `Advanced' tab of the Account-edit dialog.") +#define PLUGIN_AUTHOR "Sadrul Habib Chowdhury " + +/* System headers */ +#include + +/* Gaim headers */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREFS_PREFIX "/core/" PLUGIN_ID +#define PREFS_IDLE PREFS_PREFIX "/idle" +#define PREFS_AWAY PREFS_PREFIX "/away" +#define PREFS_GLOBAL PREFS_PREFIX "/global" +#define PREFS_MINTIME PREFS_PREFIX "/mintime" +#define PREFS_MAXSEND PREFS_PREFIX "/maxsend" +#define PREFS_USESTATUS PREFS_PREFIX "/usestatus" + +typedef struct _GaimAutoReply GaimAutoReply; + +struct _GaimAutoReply +{ + GaimBuddy *buddy; + char *reply; +}; + +typedef enum +{ + STATUS_NEVER, + STATUS_ALWAYS, + STATUS_FALLBACK +} UseStatusMessage; + +static GHashTable *options = NULL; + +/** + * Returns the auto-reply message for buddy + */ +static const char * +get_autoreply_message(GaimBuddy *buddy, GaimAccount *account) +{ + const char *reply = NULL; + UseStatusMessage use_status; + + use_status = gaim_prefs_get_int(PREFS_USESTATUS); + if (use_status == STATUS_ALWAYS) + { + GaimStatus *status = gaim_account_get_active_status(account); + GaimStatusType *type = gaim_status_get_type(status); + if (gaim_status_type_get_attr(type, "message") != NULL) + reply = gaim_status_get_attr_string(status, "message"); + else + reply = gaim_savedstatus_get_message(gaim_savedstatus_get_current()); + } + + if (!reply && buddy) + { + /* Is there any special auto-reply for this buddy? */ + reply = gaim_blist_node_get_string((GaimBlistNode*)buddy, "autoreply"); + + if (!reply && GAIM_BLIST_NODE_IS_BUDDY((GaimBlistNode*)buddy)) + { + /* Anything for the contact, then? */ + reply = gaim_blist_node_get_string(((GaimBlistNode*)buddy)->parent, "autoreply"); + } + } + + if (!reply) + { + /* Is there any specific auto-reply for this account? */ + reply = gaim_account_get_string(account, "autoreply", NULL); + } + + if (!reply) + { + /* Get the global auto-reply message */ + reply = gaim_prefs_get_string(PREFS_GLOBAL); + } + + if (*reply == ' ') + reply = NULL; + + if (!reply && use_status == STATUS_FALLBACK) + reply = gaim_status_get_attr_string(gaim_account_get_active_status(account), "message"); + + return reply; +} + +static void +written_msg(GaimAccount *account, const char *who, const char *message, + GaimConversation *conv, GaimMessageFlags flags, gpointer null) +{ + GaimBuddy *buddy; + GaimPresence *presence; + const char *reply = NULL; + gboolean trigger = FALSE; + + if (!(flags & GAIM_MESSAGE_RECV)) + return; + + if (!message || !*message) + return; + + /* Do not send an autoreply for an autoreply */ + if (flags & GAIM_MESSAGE_AUTO_RESP) + return; + + g_return_if_fail(gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM); + + presence = gaim_account_get_presence(account); + + if (gaim_prefs_get_bool(PREFS_AWAY) && !gaim_presence_is_available(presence)) + trigger = TRUE; + if (gaim_prefs_get_bool(PREFS_IDLE) && gaim_presence_is_idle(presence)) + trigger = TRUE; + + if (!trigger) + return; + + buddy = gaim_find_buddy(account, who); + reply = get_autoreply_message(buddy, account); + + if (reply) + { + GaimConnection *gc; + GaimMessageFlags flag = GAIM_MESSAGE_SEND; + time_t last_sent, now; + int count_sent, maxsend; + + last_sent = GPOINTER_TO_INT(gaim_conversation_get_data(conv, "autoreply_lastsent")); + now = time(NULL); + + /* Have we spent enough time after our last autoreply? */ + if (now - last_sent >= (gaim_prefs_get_int(PREFS_MINTIME)*60)) + { + count_sent = GPOINTER_TO_INT(gaim_conversation_get_data(conv, "autoreply_count")); + maxsend = gaim_prefs_get_int(PREFS_MAXSEND); + + /* Have we sent the autoreply enough times? */ + if (count_sent < maxsend || maxsend == -1) + { + gaim_conversation_set_data(conv, "autoreply_count", GINT_TO_POINTER(++count_sent)); + gaim_conversation_set_data(conv, "autoreply_lastsent", GINT_TO_POINTER(now)); + gc = gaim_account_get_connection(account); + if (gc->flags & GAIM_CONNECTION_AUTO_RESP) + flag |= GAIM_MESSAGE_AUTO_RESP; + serv_send_im(gc, who, reply, flag); + gaim_conv_im_write(GAIM_CONV_IM(conv), NULL, reply, flag, time(NULL)); + } + } + } +} + +static void +set_auto_reply_cb(GaimBlistNode *node, char *message) +{ + if (!message || !*message) + message = " "; + gaim_blist_node_set_string(node, "autoreply", message); +} + +static void +set_auto_reply(GaimBlistNode *node, gpointer plugin) +{ + char *message; + GaimBuddy *buddy; + GaimAccount *account; + GaimConnection *gc; + + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + buddy = (GaimBuddy *)node; + else + buddy = gaim_contact_get_priority_buddy((GaimContact*)node); + + account = gaim_buddy_get_account(buddy); + gc = gaim_account_get_connection(account); + + /* XXX: There should be a way to reset to the default/account-default autoreply */ + + message = g_strdup_printf(_("Set autoreply message for %s"), + gaim_buddy_get_contact_alias(buddy)); + gaim_request_input(plugin, _("Set Autoreply Message"), message, + _("The following message will be sent to the buddy when " + "the buddy sends you a message and autoreply is enabled."), + get_autoreply_message(buddy, account), TRUE, FALSE, + (gc->flags & GAIM_CONNECTION_HTML) ? "html" : NULL, + _("_Save"), G_CALLBACK(set_auto_reply_cb), + _("_Cancel"), NULL, node); + g_free(message); +} + +static void +context_menu(GaimBlistNode *node, GList **menu, gpointer plugin) +{ + GaimMenuAction *action; + + if (!GAIM_BLIST_NODE_IS_BUDDY(node) && !GAIM_BLIST_NODE_IS_CONTACT(node)) + return; + + action = gaim_menu_action_new(_("Set _Autoreply Message"), + GAIM_CALLBACK(set_auto_reply), plugin, NULL); + (*menu) = g_list_prepend(*menu, action); +} + +static void +add_option_for_protocol(GaimPlugin *plg) +{ + GaimPluginProtocolInfo *info = GAIM_PLUGIN_PROTOCOL_INFO(plg); + GaimAccountOption *option; + + option = gaim_account_option_string_new(_("Autoreply message"), "autoreply", NULL); + info->protocol_options = g_list_append(info->protocol_options, option); + + if (!g_hash_table_lookup(options, plg)) + g_hash_table_insert(options, plg, option); +} + +static void +remove_option_for_protocol(GaimPlugin *plg) +{ + GaimPluginProtocolInfo *info = GAIM_PLUGIN_PROTOCOL_INFO(plg); + GaimAccountOption *option = g_hash_table_lookup(options, plg); + + if (g_list_find(info->protocol_options, option)) + { + info->protocol_options = g_list_remove(info->protocol_options, option); + gaim_account_option_destroy(option); + g_hash_table_remove(options, plg); + } +} + +static void +plugin_load_cb(GaimPlugin *plugin, gboolean load) +{ + if (plugin->info && plugin->info->type == GAIM_PLUGIN_PROTOCOL) + { + if (load) + add_option_for_protocol(plugin); + else + remove_option_for_protocol(plugin); + } +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + GList *list; + + gaim_signal_connect(gaim_conversations_get_handle(), "wrote-im-msg", plugin, + GAIM_CALLBACK(written_msg), NULL); + gaim_signal_connect(gaim_blist_get_handle(), "blist-node-extended-menu", plugin, + GAIM_CALLBACK(context_menu), plugin); + gaim_signal_connect(gaim_plugins_get_handle(), "plugin-load", plugin, + GAIM_CALLBACK(plugin_load_cb), GINT_TO_POINTER(TRUE)); + gaim_signal_connect(gaim_plugins_get_handle(), "plugin-unload", plugin, + GAIM_CALLBACK(plugin_load_cb), GINT_TO_POINTER(FALSE)); + + /* Perhaps it's necessary to do this after making sure the prpl-s have been loaded? */ + options = g_hash_table_new(g_direct_hash, g_direct_equal); + list = gaim_plugins_get_protocols(); + while (list) + { + add_option_for_protocol(list->data); + list = list->next; + } + + return TRUE; +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + GList *list; + + if (options == NULL) + return TRUE; + + list = gaim_plugins_get_protocols(); + while (list) + { + remove_option_for_protocol(list->data); + list = list->next; + } + g_hash_table_destroy(options); + options = NULL; + + return TRUE; +} + +static GaimPluginPrefFrame * +get_plugin_pref_frame(GaimPlugin *plugin) +{ + GaimPluginPrefFrame *frame; + GaimPluginPref *pref; + + frame = gaim_plugin_pref_frame_new(); + + pref = gaim_plugin_pref_new_with_label(_("Send autoreply messages when")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name_and_label(PREFS_AWAY, + _("When my account is _away")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name_and_label(PREFS_IDLE, + _("When my account is _idle")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name_and_label(PREFS_GLOBAL, + _("_Default reply")); + gaim_plugin_pref_set_type(pref, GAIM_PLUGIN_PREF_STRING_FORMAT); + gaim_plugin_pref_set_format_type(pref, + GAIM_STRING_FORMAT_TYPE_MULTILINE | GAIM_STRING_FORMAT_TYPE_HTML); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_label(_("Status message")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name_and_label(PREFS_USESTATUS, + _("Autoreply with status message")); + gaim_plugin_pref_set_type(pref, GAIM_PLUGIN_PREF_CHOICE); + gaim_plugin_pref_add_choice(pref, _("Never"), + GINT_TO_POINTER(STATUS_NEVER)); + gaim_plugin_pref_add_choice(pref, _("Always when there is a status message"), + GINT_TO_POINTER(STATUS_ALWAYS)); + gaim_plugin_pref_add_choice(pref, _("Only when there's no autoreply message"), + GINT_TO_POINTER(STATUS_FALLBACK)); + + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_label(_("Delay between autoreplies")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name_and_label(PREFS_MINTIME, + _("_Minimum delay (mins)")); + gaim_plugin_pref_set_bounds(pref, 0, 9999); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_label(_("Times to send autoreplies")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name_and_label(PREFS_MAXSEND, + _("Ma_ximum count")); + gaim_plugin_pref_set_bounds(pref, 0, 9999); + gaim_plugin_pref_frame_add(frame, pref); + + return frame; +} + +static GaimPluginUiInfo prefs_info = { + get_plugin_pref_frame, + 0, + NULL +}; + +static GaimPluginInfo info = { + GAIM_PLUGIN_MAGIC, /* Magic */ + GAIM_MAJOR_VERSION, /* Gaim Major Version */ + GAIM_MINOR_VERSION, /* Gaim Minor Version */ + GAIM_PLUGIN_STANDARD, /* plugin type */ + NULL, /* ui requirement */ + 0, /* flags */ + NULL, /* dependencies */ + GAIM_PRIORITY_DEFAULT, /* priority */ + + PLUGIN_ID, /* plugin id */ + PLUGIN_NAME, /* name */ + VERSION, /* version */ + PLUGIN_SUMMARY, /* summary */ + PLUGIN_DESCRIPTION, /* description */ + PLUGIN_AUTHOR, /* author */ + GAIM_WEBSITE, /* website */ + + plugin_load, /* load */ + plugin_unload, /* unload */ + NULL, /* destroy */ + + NULL, /* ui_info */ + NULL, /* extra_info */ + &prefs_info, /* prefs_info */ + NULL /* actions */ +}; + +static void +init_plugin(GaimPlugin *plugin) +{ + gaim_prefs_add_none(PREFS_PREFIX); + gaim_prefs_add_bool(PREFS_IDLE, TRUE); + gaim_prefs_add_bool(PREFS_AWAY, TRUE); + gaim_prefs_add_string(PREFS_GLOBAL, _("I am currently not available. Please leave your message, " + "and I will get back to you as soon as possible.")); + gaim_prefs_add_int(PREFS_MINTIME, 10); + gaim_prefs_add_int(PREFS_MAXSEND, 10); + gaim_prefs_add_int(PREFS_USESTATUS, STATUS_NEVER); +} + +GAIM_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) diff --git a/libpurple/plugins/buddynote.c b/libpurple/plugins/buddynote.c new file mode 100644 index 0000000000..9bfe3f922a --- /dev/null +++ b/libpurple/plugins/buddynote.c @@ -0,0 +1,110 @@ +/* + * BuddyNote - Store notes on particular buddies + * Copyright (C) 2004 Stu Tomlinson + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +static void +dont_do_it_cb(GaimBlistNode *node, const char *note) +{ +} + +static void +do_it_cb(GaimBlistNode *node, const char *note) +{ + gaim_blist_node_set_string(node, "notes", note); +} + +static void +buddynote_edit_cb(GaimBlistNode *node, gpointer data) +{ + const char *note; + + note = gaim_blist_node_get_string(node, "notes"); + + gaim_request_input(node, _("Notes"), + _("Enter your notes below..."), + NULL, + note, TRUE, FALSE, "html", + _("Save"), G_CALLBACK(do_it_cb), + _("Cancel"), G_CALLBACK(dont_do_it_cb), + node); +} + +static void +buddynote_extended_menu_cb(GaimBlistNode *node, GList **m) +{ + GaimMenuAction *bna = NULL; + + *m = g_list_append(*m, bna); + bna = gaim_menu_action_new(_("Edit Notes..."), GAIM_CALLBACK(buddynote_edit_cb), NULL, NULL); + *m = g_list_append(*m, bna); +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + + gaim_signal_connect(gaim_blist_get_handle(), "blist-node-extended-menu", + plugin, GAIM_CALLBACK(buddynote_extended_menu_cb), NULL); + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, /**< major version */ + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + "core-plugin_pack-buddynote", /**< id */ + N_("Buddy Notes"), /**< name */ + 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 ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, /**< prefs_info */ + NULL /**< actions */ +}; + + +static void +init_plugin(GaimPlugin *plugin) { +} + +GAIM_INIT_PLUGIN(buddynote, init_plugin, info) diff --git a/libpurple/plugins/ciphertest.c b/libpurple/plugins/ciphertest.c new file mode 100644 index 0000000000..d7416dbc71 --- /dev/null +++ b/libpurple/plugins/ciphertest.c @@ -0,0 +1,287 @@ +/* + * A plugin to test the ciphers that ship with gaim + * + * Copyright (C) 2004, Gary Kramlich + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef GAIM_PLUGINS +#define GAIM_PLUGINS +#endif + +#include "internal.h" + +#include +#include + +#include "cipher.h" +#include "debug.h" +#include "plugin.h" +#include "version.h" + +struct test { + const gchar *question; + const gchar *answer; +}; + +/************************************************************************** + * MD5 Stuff + **************************************************************************/ +struct test md5_tests[8] = { + { "", "d41d8cd98f00b204e9800998ecf8427e"}, + { "a", "0cc175b9c0f1b6a831c399e269772661"}, + { "abc", "900150983cd24fb0d6963f7d28e17f72"}, + { "message digest", "f96b697d7cb7938d525a2f31aaf161d0"}, + { "abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"}, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789", "d174ab98d277d9f5a5611c2c9f419d9f"}, + {"123456789012345678901234567" + "890123456789012345678901234" + "56789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a"}, + { NULL, NULL } +}; + +static void +cipher_test_md5() { + GaimCipher *cipher; + GaimCipherContext *context; + gchar digest[33]; + gboolean ret; + gint i = 0; + + cipher = gaim_ciphers_find_cipher("md5"); + if(!cipher) { + gaim_debug_info("cipher-test", + "could not find md5 cipher, not testing\n"); + return; + } + + gaim_debug_info("cipher-test", "Running md5 tests\n"); + + context = gaim_cipher_context_new(cipher, NULL); + + while(md5_tests[i].answer) { + gaim_debug_info("cipher-test", "Test %02d:\n", i); + gaim_debug_info("cipher-test", "Testing '%s'\n", md5_tests[i].question); + + gaim_cipher_context_append(context, (guchar *)md5_tests[i].question, + strlen(md5_tests[i].question)); + + ret = gaim_cipher_context_digest_to_str(context, sizeof(digest), + digest, NULL); + + if(!ret) { + gaim_debug_info("cipher-test", "failed\n"); + } else { + gaim_debug_info("cipher-test", "\tGot: %s\n", digest); + gaim_debug_info("cipher-test", "\tWanted: %s\n", + md5_tests[i].answer); + } + + gaim_cipher_context_reset(context, NULL); + i++; + } + + gaim_cipher_context_destroy(context); + + gaim_debug_info("cipher-test", "md5 tests completed\n\n"); +} + +/************************************************************************** + * SHA-1 stuff + **************************************************************************/ +struct test sha1_tests[5] = { + {"a", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"}, + {"abc", "a9993e364706816aba3e25717850c26c9cd0d89d"} , + {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "84983e441c3bd26ebaae4aa1f95129e5e54670f1"} , + {NULL, "34aa973cd4c4daa4f61eeb2bdbad27316534016f"}, + {NULL, NULL} +}; + +static void +cipher_test_sha1() { + GaimCipher *cipher; + GaimCipherContext *context; + gchar digest[41]; + gint i = 0; + gboolean ret; + + cipher = gaim_ciphers_find_cipher("sha1"); + if(!cipher) { + gaim_debug_info("cipher-test", + "could not find sha1 cipher, not testing\n"); + return; + } + + gaim_debug_info("cipher-test", "Running sha1 tests\n"); + + context = gaim_cipher_context_new(cipher, NULL); + + while(sha1_tests[i].answer) { + gaim_debug_info("cipher-test", "Test %02d:\n", i); + gaim_debug_info("cipher-test", "Testing '%s'\n", + (sha1_tests[i].question != NULL) ? + sha1_tests[i].question : "'a'x1000, 1000 times"); + + if(sha1_tests[i].question) { + gaim_cipher_context_append(context, (guchar *)sha1_tests[i].question, + strlen(sha1_tests[i].question)); + } else { + gint j; + guchar buff[1000]; + + memset(buff, 'a', 1000); + + for(j = 0; j < 1000; j++) + gaim_cipher_context_append(context, buff, 1000); + } + + ret = gaim_cipher_context_digest_to_str(context, sizeof(digest), + digest, NULL); + + if(!ret) { + gaim_debug_info("cipher-test", "failed\n"); + } else { + gaim_debug_info("cipher-test", "\tGot: %s\n", digest); + gaim_debug_info("cipher-test", "\tWanted: %s\n", + sha1_tests[i].answer); + } + + gaim_cipher_context_reset(context, NULL); + i++; + } + + gaim_cipher_context_destroy(context); + + gaim_debug_info("cipher-test", "sha1 tests completed\n\n"); +} + +static void +cipher_test_digest() +{ + const gchar *nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093"; + const gchar *client_nonce = "0a4f113b"; + const gchar *username = "Mufasa"; + const gchar *realm = "testrealm@host.com"; + const gchar *password = "Circle Of Life"; + const gchar *algorithm = "md5"; + const gchar *nonce_count = "00000001"; + const gchar *method = "GET"; + const gchar *qop = "auth"; + const gchar *digest_uri = "/dir/index.html"; + const gchar *entity = NULL; + + gchar *session_key; + + gaim_debug_info("cipher-test", "Running HTTP Digest tests\n"); + + session_key = gaim_cipher_http_digest_calculate_session_key( + algorithm, username, realm, password, + nonce, client_nonce); + + if (session_key == NULL) + { + gaim_debug_info("cipher-test", + "gaim_cipher_http_digest_calculate_session_key failed.\n"); + } + else + { + gchar *response; + + gaim_debug_info("cipher-test", "\tsession_key: Got: %s\n", session_key); + gaim_debug_info("cipher-test", "\tsession_key: Wanted: %s\n", "939e7578ed9e3c518a452acee763bce9"); + + response = gaim_cipher_http_digest_calculate_response( + algorithm, method, digest_uri, qop, entity, + nonce, nonce_count, client_nonce, session_key); + + g_free(session_key); + + if (response == NULL) + { + gaim_debug_info("cipher-test", + "gaim_cipher_http_digest_calculate_session_key failed.\n"); + } + else + { + gaim_debug_info("cipher-test", "\tresponse: Got: %s\n", response); + gaim_debug_info("cipher-test", "\tresponse: Wanted: %s\n", "6629fae49393a05397450978507c4ef1"); + g_free(response); + } + } + + gaim_debug_info("cipher-test", "HTTP Digest tests completed\n\n"); +} + +/************************************************************************** + * Plugin stuff + **************************************************************************/ +static gboolean +plugin_load(GaimPlugin *plugin) { + cipher_test_md5(); + cipher_test_sha1(); + cipher_test_digest(); + + return TRUE; +} + +static gboolean +plugin_unload(GaimPlugin *plugin) { + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + "core-cipher-test", /**< id */ + N_("Cipher Test"), /**< name */ + VERSION, /**< version */ + /** summary */ + N_("Tests the ciphers that ship with gaim."), + /** description */ + N_("Tests the ciphers that ship with gaim."), + "Gary Kramlich ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, + NULL +}; + +static void +init_plugin(GaimPlugin *plugin) { +} + +GAIM_INIT_PLUGIN(cipher_test, init_plugin, info) diff --git a/libpurple/plugins/codeinline.c b/libpurple/plugins/codeinline.c new file mode 100755 index 0000000000..734a918000 --- /dev/null +++ b/libpurple/plugins/codeinline.c @@ -0,0 +1,93 @@ +/* + * gaim + * + * Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal.h" +#include "plugin.h" +#include "notify.h" +#include "util.h" +#include "version.h" + +GaimPlugin *plugin_handle = NULL; + +static gboolean outgoing_msg_cb(GaimAccount *account, const char *who, char **message, + GaimConversation *conv, GaimMessageFlags flags, gpointer null) +{ + char *m; + char **ms = g_strsplit(*message, "", -1); + m = g_strjoinv("", ms); + g_strfreev(ms); + + ms = g_strsplit(m, "", -1); + g_free(m); + m = g_strjoinv("", ms); + g_free(*message); + *message = m; + return FALSE; +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + void *handle = gaim_conversations_get_handle(); + plugin_handle = plugin; + gaim_signal_connect(handle, "writing-im-msg", plugin, + GAIM_CALLBACK(outgoing_msg_cb), NULL); + gaim_signal_connect(handle, "sending-im-msg", plugin, + GAIM_CALLBACK(outgoing_msg_cb), NULL); + + return TRUE; +} + + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, + NULL, + 0, + NULL, + GAIM_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 ", + "http://gaim.sourceforge.net", + plugin_load, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + static void + init_plugin(GaimPlugin *plugin) + { + } + +GAIM_INIT_PLUGIN(urlcatcher, init_plugin, info) diff --git a/libpurple/plugins/dbus-buddyicons-example.py b/libpurple/plugins/dbus-buddyicons-example.py new file mode 100644 index 0000000000..d501370568 --- /dev/null +++ b/libpurple/plugins/dbus-buddyicons-example.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# +# Print the aliases of buddies who have a buddy-icon set. +# +# Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import dbus + +bus = dbus.SessionBus() +obj = bus.get_object("net.sf.gaim.GaimService", "/net/sf/gaim/GaimObject") +gaim = dbus.Interface(obj, "net.sf.gaim.GaimInterface") + +node = gaim.GaimBlistGetRoot() +while node != 0: + if gaim.GaimBlistNodeIsBuddy(node): + icon = gaim.GaimBuddyGetIcon(node) + if icon != 0: + print gaim.GaimBuddyGetAlias(node) + node = gaim.GaimBlistNodeNext(node, 0) diff --git a/libpurple/plugins/dbus-example.c b/libpurple/plugins/dbus-example.c new file mode 100644 index 0000000000..ceba0d5e2e --- /dev/null +++ b/libpurple/plugins/dbus-example.c @@ -0,0 +1,178 @@ +/* + * This is an example of a gaim dbus plugin. After enabling this + * plugin, the following commands should work from the command line: + * + * prompt$ gaim-send DbusExampleGetHelloObject + * + * returns, say: int32 74 + * + * prompt$ gaim-send DbusExampleGetText int32:74 + * + * returns: string "Hello." + * + * prompt$ gaim-send DbusExampleSetText int32:74 string:Bye! + * + * prompt$ gaim-send DbusExampleGetText int32:74 + * + * returns: string "Bye!" + * + * Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal.h" + +#include "blist.h" +#include "notify.h" +#include "plugin.h" +#include "version.h" + +#include +#include +#include + +#define DBUS_API_SUBJECT_TO_CHANGE +#include "dbus-maybe.h" +#include "dbus-bindings.h" + +typedef struct { + char *text; +} GaimText; + +/* This makes the structure GaimText visible to the gaim-dbus type + system. It defines GaimText as a type with no parent. From now + on, we will be able to register pointers to structures of this + type. You to dbus-define types you want to be directly accessible + by external applications. */ +GAIM_DBUS_DEFINE_TYPE(GaimText) + +/* Here we make four functions accessible to other applications by + DBus. These functions can access types defined in gaim proper + (GaimBuddy) as well as the types defined in the plugin (GaimText). */ +DBUS_EXPORT GaimText* dbus_example_get_hello_object(void); +DBUS_EXPORT void dbus_example_set_text(GaimText *obj, const char *text); +DBUS_EXPORT const char *dbus_example_get_text(GaimText *obj); +DBUS_EXPORT const char *dbus_example_get_buddy_name(GaimBuddy *buddy); + +/* This file has been generated by the #dbus-analize-functions.py + script. It contains dbus wrappers for the four functions declared + above. */ +#include "dbus-example-bindings.c" + +/* This is the GaimText object we want to make publicly visible. */ +static GaimText hello; + +/* Here come the definitions of the four exported functions. */ +GaimText* dbus_example_get_hello_object(void) +{ + return &hello; +} + +void dbus_example_set_text(GaimText *obj, const char *text) +{ + if (obj != NULL) { + g_free(obj->text); + obj->text = g_strdup(text); + } +} + +const char *dbus_example_get_text(GaimText *obj) +{ + if (obj != NULL) + return obj->text; + else + return NULL; +} + +const char *dbus_example_get_buddy_name(GaimBuddy *buddy) +{ + return gaim_buddy_get_name(buddy); +} + +/* And now standard plugin stuff */ + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + GAIM_DBUS_RETURN_FALSE_IF_DISABLED(plugin); + + /* First, we have to register our four exported functions with the + main gaim dbus loop. Without this statement, the gaim dbus + code wouldn't know about our functions. */ + GAIM_DBUS_REGISTER_BINDINGS(plugin); + + /* Then, we register the hello object of type GaimText. Note that + pointer registrations / unregistrations are completely dynamic; + they don't have to be made when the plugin is loaded / + unloaded. Without this statement the dbus gaim code wouldn't + know about the hello object. */ + GAIM_DBUS_REGISTER_POINTER(&hello, GaimText); + + hello.text = g_strdup("Hello."); + + return TRUE; +} + + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + g_free(hello.text); + + /* It is necessary to unregister all pointers registered by the module. */ + GAIM_DBUS_UNREGISTER_POINTER(&hello); + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + "dbus-example", /**< id */ + N_("DBus Example"), /**< name */ + VERSION, /**< version */ + /** summary */ + N_("DBus Plugin Example"), + /** description */ + N_("DBus Plugin Example"), + "Piotr Zielinski (http://cl.cam.ac.uk/~pz215)", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, /**< prefs_info */ + NULL +}; + +static void init_plugin(GaimPlugin *plugin) +{ +} + +GAIM_INIT_PLUGIN(dbus_example, init_plugin, info) diff --git a/libpurple/plugins/filectl.c b/libpurple/plugins/filectl.c new file mode 100644 index 0000000000..cdcb13adfb --- /dev/null +++ b/libpurple/plugins/filectl.c @@ -0,0 +1,270 @@ +/** + * Send commands to Gaim via ~/.gaim/control + * + * Originally by Eric Warmenhoven + * Compile fixes/mini hacks Alex Bennee + * and Brian Tarricone + */ + +/* system includes */ +#include +#include +#include +#include +#include +#include +#include + +#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" + +#define FILECTL_PLUGIN_ID "core-filectl" +static int check; +static time_t mtime; + +static void init_file(void); +static gboolean check_file(void); + +/* parse char * as if were word array */ +char *getarg(char *, int, int); + +/* go through file and run any commands */ +void +run_commands() +{ + struct stat finfo; + char filename[256]; + char buffer[1024]; + char *command, *arg1, *arg2; + FILE *file; + + sprintf(filename, "%s" G_DIR_SEPARATOR_S "control", gaim_user_dir()); + + file = g_fopen(filename, "r+"); + while (fgets(buffer, sizeof(buffer), file)) { + + /* Read the next command */ + if (buffer[strlen(buffer) - 1] == '\n') + buffer[strlen(buffer) - 1] = 0; + gaim_debug_misc("filectl", "read: %s\n", buffer); + command = getarg(buffer, 0, 0); + + if (!strncasecmp(command, "login", 6)) { + GaimAccount *account; + + arg1 = getarg(buffer, 1, 0); + arg2 = getarg(buffer, 2, 1); + + account = gaim_accounts_find(arg1, arg2); + if (account != NULL) /* username found */ + gaim_account_connect(account); + + free(arg1); + free(arg2); + + } else if (!strncasecmp(command, "logout", 7)) { + GaimAccount *account; + + arg1 = getarg(buffer, 1, 1); + arg2 = getarg(buffer, 2, 1); + + account = gaim_accounts_find(arg1, arg2); + if (account != NULL) + { + gaim_account_disconnect(account); + } + else if (arg1 == NULL) + gaim_connections_disconnect_all(); + + free(arg1); + free(arg2); + +/* gaim_find_conversation() is gone in 2.0.0. */ +#if 0 + } else if (!strncasecmp(command, "send", 4)) { + GaimConversation *conv; + + arg1 = getarg(buffer, 1, 0); + arg2 = getarg(buffer, 2, 1); + + conv = gaim_find_conversation(GAIM_CONV_TYPE_ANY, arg1); + if (conv != NULL) + { + /* + gaim_conversation_write(conv, arg2, WFLAG_SEND, NULL, time(NULL), -1); + serv_send_im(conv->gc, arg1, arg2, 0); + */ + } + + free(arg1); + free(arg2); +#endif + + } else if (!strncasecmp(command, "away", 4)) { + arg1 = getarg(buffer, 1, 1); + /* serv_set_away_all(arg1); */ + free(arg1); + + } else if (!strncasecmp(command, "hide", 4)) { + gaim_blist_set_visible(FALSE); + + } else if (!strncasecmp(command, "unhide", 6)) { + gaim_blist_set_visible(TRUE); + + } else if (!strncasecmp(command, "back", 4)) { + /* do_im_back(); */ + + } else if (!strncasecmp(command, "quit", 4)) { + gaim_core_quit(); + + } + + free(command); + } + + fclose(file); + + if (g_stat(filename, &finfo) != 0) + return; + mtime = finfo.st_mtime; +} + +/** + * Check to see if the size of the file is > 0. if so, run commands. + */ +void +init_file() +{ + /* most of this was taken from Bash v2.04 by the FSF */ + struct stat finfo; + char filename[256]; + + sprintf(filename, "%s" G_DIR_SEPARATOR_S "control", gaim_user_dir()); + + if ((g_stat(filename, &finfo) == 0) && (finfo.st_size > 0)) + run_commands(); +} + +/** + * Check to see if we need to run commands from the file. + */ +gboolean +check_file() +{ + /* most of this was taken from Bash v2.04 by the FSF */ + struct stat finfo; + char filename[256]; + + sprintf(filename, "%s" G_DIR_SEPARATOR_S "control", gaim_user_dir()); + + if ((g_stat(filename, &finfo) == 0) && (finfo.st_size > 0)) + { + if (mtime != finfo.st_mtime) { + gaim_debug_info("filectl", "control changed, checking\n"); + run_commands(); + } + } + + return TRUE; +} + +char * +getarg(char *line, int which, int remain) +{ + char *arr; + char *val; + int count = -1; + int i; + int state = 0; + + for (i = 0; i < strlen(line) && count < which; i++) { + switch (state) { + case 0: /* in whitespace, expecting word */ + if (isalnum(line[i])) { + count++; + state = 1; + } + break; + case 1: /* inside word, waiting for whitespace */ + if (isspace(line[i])) { + state = 0; + } + break; + } + } + + arr = strdup(&line[i - 1]); + if (remain) + return arr; + + for (i = 0; i < strlen(arr) && isalnum(arr[i]); i++); + arr[i] = 0; + val = strdup(arr); + arr[i] = ' '; + free(arr); + return val; +} + +/* + * EXPORTED FUNCTIONS + */ + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + init_file(); + check = gaim_timeout_add(5000, (GSourceFunc)check_file, NULL); + + return TRUE; +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + gaim_timeout_remove(check); + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + FILECTL_PLUGIN_ID, /**< id */ + N_("Gaim File Control"), /**< name */ + VERSION, /**< version */ + /** summary */ + N_("Allows you to control Gaim by entering commands in a file."), + /** description */ + N_("Allows you to control Gaim by entering commands in a file."), + "Eric Warmenhoven ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL /**< extra_info */ +}; + +static void +init_plugin(GaimPlugin *plugin) +{ +} + +GAIM_INIT_PLUGIN(filectl, init_plugin, info) diff --git a/libpurple/plugins/fortuneprofile.pl b/libpurple/plugins/fortuneprofile.pl new file mode 100644 index 0000000000..4a0570efaa --- /dev/null +++ b/libpurple/plugins/fortuneprofile.pl @@ -0,0 +1,125 @@ +# FORTUNE PROFILE +# +# Sets your AIM profile to a fortune (with a header and footer of your +# choice). +# + +# By Sean Egan +# seanegan@gmail.com +# AIM: SeanEgn +# +# Updated by Nathan Conrad, 31 January 2002 +# Changes: +# * Fortunes have HTML tabs and newlines +# AIM: t98502 +# ICQ: 16106363 +# +# Updated by Mark Doliner, 15 October 2002 +# Changes: +# * Modified to work with the changed perl interface of gaim 0.60 +# * Fixed a bug where your info would be set to nothing if you had +# no pre and no post message +# AIM: lbdash +# +# Updated by Christian Hammond, 20 August 2003 +# Changes: +# * Modified to work with the changed perl interface of gaim 0.68 +# AIM: ChipX86 + +# Copyright (C) 2001 Sean Egan + +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +use Gaim; + +%PLUGIN_INFO = ( + perl_api_version => 2, + name => "Fortune Profile", + version => "3.4", + summary => "Sets your AIM profile to a fortune (with a header and footer of your choice).", + description => "Sets your AIM profile to a fortune (with a header and footer of your choice).", + author => "Sean Egan ", + url => "http://gaim.sf.net/", + + load => "plugin_load" +); + +sub plugin_init { + return %PLUGIN_INFO; +} + +sub plugin_load { + $plugin = shift; + + $tab = " "; + $tab = $tab . $tab . $tab . $tab; + $nl = "
"; + + $seconds = 30; # Delay before updating away messages. + $max = 1020; # Max length of an profile. It should be + # 1024, but I am being safe + $pre_message = ""; # This gets added before the fortune + + $post_message =""; + + $len = 0; + if ($pre_message ne "") { + $len += length( $pre_message . "---$nl" ); + } + if ($post_message ne "") { + $len += length("---$nl" . $post_message); + } + + # Command to get dynamic message from + $command = "fortune -sn " . ($max - $len); + + # output the first message and start the timers... + # This is done as a timeout to prevent attempts to set the + # profile before logging in. + Gaim::timeout_add($plugin, $seconds, \&update_away, 0); +} + +sub update_away { + # The fortunes are expanded into HTML (the tabs and newlines) which + # causes the -s option of fortune to be a little bit meaningless. This + # will loop until it gets a fortune of a good size (after expansion). + + do { + do { #It's a while loop because it doesn't always work for some reason + $fortune = `$command`; + if ($? == -1) { + return; + } + } while ($fortune eq ""); + $fortune =~ s/\n/$nl/g; + $fortune =~ s/\t/$tab/g; + } while ((length($fortune) + $len ) > $max); + + $message = $fortune; + if ($pre_message ne "") { + $message = $pre_message . "---$nl" . $message; + } + if ($post_message ne "") { + $message = $message . "---$nl" . $post_message ; + } + + foreach $account (Gaim::accounts()) { + if ($account->is_connected()) { + $account->set_user_info($message); + } + } + + Gaim::timeout_add($plugin, $seconds, \&update_away, 0); +} diff --git a/libpurple/plugins/gaim.pl b/libpurple/plugins/gaim.pl new file mode 100644 index 0000000000..c21c6c4f29 --- /dev/null +++ b/libpurple/plugins/gaim.pl @@ -0,0 +1,39 @@ +sub description { + my($a, $b, $c, $d, $e, $f) = @_; + ("Example", "1.0", "An example Gaim perl script that does nothing particularly useful:\n\t-Show a dialog on load\n\t-Set user idle for 6,000 seconds\n\t-Greets people signing on with \"Hello\"\n\t-Informs you when script has been loaded for one minute.", "Eric Warmenhoven <eric\@warmenhoven.org>", "http://gaim.sf.net", "/dev/null"); +} + +$handle = GAIM::register("Example", "1.0", "goodbye", ""); + +GAIM::print("Perl Says", "Handle $handle"); + +$ver = GAIM::get_info(0); +@ids = GAIM::get_info(1); + +$msg = "Gaim $ver:"; +foreach $id (@ids) { + $pro = GAIM::get_info(7, $id); + $nam = GAIM::get_info(3, $id); + $msg .= "\n$nam using $pro"; +} + + +GAIM::command("idle", 6000); + +GAIM::add_event_handler($handle, "event_buddy_signon", "echo_reply"); +GAIM::add_timeout_handler($handle, 60, "notify"); + +sub echo_reply { + $index = $_[0]; + $who = $_[1]; + GAIM::print_to_conv($index, $who, "Hello", 0); +} + +sub notify { + GAIM::print("1 minute", "gaim test has been loaded for 1 minute"); +} + +sub goodbye { + GAIM::print("You Bastard!", "You killed Kenny!"); +} + diff --git a/libpurple/plugins/idle.c b/libpurple/plugins/idle.c new file mode 100644 index 0000000000..06fff27458 --- /dev/null +++ b/libpurple/plugins/idle.c @@ -0,0 +1,337 @@ +/* + * idle.c - I'dle Mak'er plugin for Gaim + * + * This file is part of Gaim. + * + * Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal.h" + +#include "connection.h" +#include "debug.h" +#include "notify.h" +#include "plugin.h" +#include "request.h" +#include "server.h" +#include "status.h" +#include "version.h" + +/* This plugin no longer depends on gtk */ +#define IDLE_PLUGIN_ID "core-idle" + +static GList *idled_accts = NULL; + +static gboolean +unidle_filter(GaimAccount *acct) +{ + if (g_list_find(idled_accts, acct)) + return TRUE; + + return FALSE; +} + +static gboolean +idleable_filter(GaimAccount *account) +{ + GaimPlugin *prpl; + + prpl = gaim_find_prpl(gaim_account_get_protocol_id(account)); + g_return_val_if_fail(prpl != NULL, FALSE); + + return (GAIM_PLUGIN_PROTOCOL_INFO(prpl)->set_idle != NULL); +} + +static void +set_idle_time(GaimAccount *acct, int mins_idle) +{ + time_t t; + GaimConnection *gc = gaim_account_get_connection(acct); + GaimPresence *presence = gaim_account_get_presence(acct); + + if (!gc) + return; + + gaim_debug_info("idle", + "setting idle time for %s to %d\n", + gaim_account_get_username(acct), mins_idle); + + if (mins_idle) + t = time(NULL) - (60 * mins_idle); /* subtract seconds idle from current time */ + else + t = 0; /* time idle is irrelevant */ + + gaim_presence_set_idle(presence, mins_idle ? TRUE : FALSE, t); +} + +static void +idle_action_ok(void *ignored, GaimRequestFields *fields) +{ + int tm = gaim_request_fields_get_integer(fields, "mins"); + GaimAccount *acct = gaim_request_fields_get_account(fields, "acct"); + + /* only add the account to the GList if it's not already been idled */ + if (!unidle_filter(acct)) + { + gaim_debug_misc("idle", + "%s hasn't been idled yet; adding to list.\n", + gaim_account_get_username(acct)); + idled_accts = g_list_append(idled_accts, acct); + } + + set_idle_time(acct, tm); +} + +static void +idle_all_action_ok(void *ignored, GaimRequestFields *fields) +{ + GaimAccount *acct = NULL; + GList *list, *iter; + int tm = gaim_request_fields_get_integer(fields, "mins"); + const char *prpl_id = NULL; + + list = gaim_accounts_get_all_active(); + for(iter = list; iter; iter = iter->next) { + acct = (GaimAccount *)(iter->data); + + if(acct) + prpl_id = gaim_account_get_protocol_id(acct); + + if(acct && idleable_filter(acct)) { + gaim_debug_misc("idle", "Idling %s.\n", + gaim_account_get_username(acct)); + + set_idle_time(acct, tm); + + if(!g_list_find(idled_accts, acct)) + idled_accts = g_list_append(idled_accts, acct); + } + } + + g_list_free(list); +} + +static void +unidle_action_ok(void *ignored, GaimRequestFields *fields) +{ + GaimAccount *acct = gaim_request_fields_get_account(fields, "acct"); + + set_idle_time(acct, 0); /* unidle the account */ + + /* once the account has been unidled it shouldn't be in the list */ + idled_accts = g_list_remove(idled_accts, acct); +} + + +static void +idle_action(GaimPluginAction *action) +{ + /* Use the super fancy request API */ + + GaimRequestFields *request; + GaimRequestFieldGroup *group; + GaimRequestField *field; + + group = gaim_request_field_group_new(NULL); + + field = gaim_request_field_account_new("acct", _("Account"), NULL); + gaim_request_field_account_set_filter(field, idleable_filter); + gaim_request_field_account_set_show_all(field, FALSE); + gaim_request_field_group_add_field(group, field); + + field = gaim_request_field_int_new("mins", _("Minutes"), 10); + gaim_request_field_group_add_field(group, field); + + request = gaim_request_fields_new(); + gaim_request_fields_add_group(request, group); + + gaim_request_fields(action->plugin, + N_("I'dle Mak'er"), + _("Set Account Idle Time"), + NULL, + request, + _("_Set"), G_CALLBACK(idle_action_ok), + _("_Cancel"), NULL, + NULL); +} + +static void +unidle_action(GaimPluginAction *action) +{ + GaimRequestFields *request; + GaimRequestFieldGroup *group; + GaimRequestField *field; + + if (idled_accts == NULL) + { + gaim_notify_info(NULL, NULL, _("None of your accounts are idle."), NULL); + return; + } + + group = gaim_request_field_group_new(NULL); + + field = gaim_request_field_account_new("acct", _("Account"), NULL); + gaim_request_field_account_set_filter(field, unidle_filter); + gaim_request_field_account_set_show_all(field, FALSE); + gaim_request_field_group_add_field(group, field); + + request = gaim_request_fields_new(); + gaim_request_fields_add_group(request, group); + + gaim_request_fields(action->plugin, + N_("I'dle Mak'er"), + _("Unset Account Idle Time"), + NULL, + request, + _("_Unset"), G_CALLBACK(unidle_action_ok), + _("_Cancel"), NULL, + NULL); +} + +static void +idle_all_action(GaimPluginAction *action) +{ + GaimRequestFields *request; + GaimRequestFieldGroup *group; + GaimRequestField *field; + + group = gaim_request_field_group_new(NULL); + + field = gaim_request_field_int_new("mins", _("Minutes"), 10); + gaim_request_field_group_add_field(group, field); + + request = gaim_request_fields_new(); + gaim_request_fields_add_group(request, group); + + gaim_request_fields(action->plugin, + N_("I'dle Mak'er"), + _("Set Idle Time for All Accounts"), + NULL, + request, + _("_Set"), G_CALLBACK(idle_all_action_ok), + _("_Cancel"), NULL, + NULL); +} + +static void +unidle_all_action(GaimPluginAction *action) +{ + GList *l; + + /* freeing the list here will cause segfaults if the user idles an account + * after the list is freed */ + for (l = idled_accts; l; l = l->next) + { + GaimAccount *account = l->data; + set_idle_time(account, 0); + } + + g_list_free(idled_accts); + idled_accts = NULL; +} + +static GList * +actions(GaimPlugin *plugin, gpointer context) +{ + GList *l = NULL; + GaimPluginAction *act = NULL; + + act = gaim_plugin_action_new(_("Set Account Idle Time"), + idle_action); + l = g_list_append(l, act); + + act = gaim_plugin_action_new(_("Unset Account Idle Time"), + unidle_action); + l = g_list_append(l, act); + + act = gaim_plugin_action_new(_("Set Idle Time for All Accounts"), + idle_all_action); + l = g_list_append(l, act); + + act = gaim_plugin_action_new( + _("Unset Idle Time for All Idled Accounts"), unidle_all_action); + l = g_list_append(l, act); + + return l; +} + +static void +signing_off_cb(GaimConnection *gc, void *data) +{ + GaimAccount *account; + + account = gaim_connection_get_account(gc); + idled_accts = g_list_remove(idled_accts, account); +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + gaim_signal_connect(gaim_connections_get_handle(), "signing-off", + plugin, + GAIM_CALLBACK(signing_off_cb), NULL); + + return TRUE; +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + unidle_all_action(NULL); + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, + NULL, + 0, + NULL, + GAIM_PRIORITY_DEFAULT, + 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. */ + N_("I'dle Mak'er"), + 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 ", + GAIM_WEBSITE, + plugin_load, + plugin_unload, + NULL, + NULL, + NULL, + NULL, + actions +}; + +static void +init_plugin(GaimPlugin *plugin) +{ +} + + +GAIM_INIT_PLUGIN(idle, init_plugin, info) + diff --git a/libpurple/plugins/ipc-test-client.c b/libpurple/plugins/ipc-test-client.c new file mode 100644 index 0000000000..c31de71f42 --- /dev/null +++ b/libpurple/plugins/ipc-test-client.c @@ -0,0 +1,114 @@ +/* + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, 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(GaimPlugin *plugin) +{ + GaimPlugin *server_plugin; + gboolean ok; + int result; + + server_plugin = gaim_plugins_find_with_id("core-ipc-test-server"); + + if (server_plugin == NULL) + { + gaim_debug_error("ipc-test-client", + "Unable to locate plugin core-ipc-test-server, " + "needed for IPC.\n"); + + return TRUE; + } + + result = (int)gaim_plugin_ipc_call(server_plugin, "add", &ok, 36, 6); + + if (!ok) + { + gaim_debug_error("ipc-test-client", + "Unable to call IPC function 'add' in " + "core-ipc-test-server plugin."); + + return TRUE; + } + + gaim_debug_info("ipc-test-client", "36 + 6 = %d\n", result); + + result = (int)gaim_plugin_ipc_call(server_plugin, "sub", &ok, 50, 8); + + if (!ok) + { + gaim_debug_error("ipc-test-client", + "Unable to call IPC function 'sub' in " + "core-ipc-test-server plugin."); + + return TRUE; + } + + gaim_debug_info("ipc-test-client", "50 - 8 = %d\n", result); + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + IPC_TEST_CLIENT_PLUGIN_ID, /**< id */ + N_("IPC Test Client"), /**< name */ + 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 ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, + NULL +}; + +static void +init_plugin(GaimPlugin *plugin) +{ + info.dependencies = g_list_append(info.dependencies, + "core-ipc-test-server"); +} + +GAIM_INIT_PLUGIN(ipctestclient, init_plugin, info) diff --git a/libpurple/plugins/ipc-test-server.c b/libpurple/plugins/ipc-test-server.c new file mode 100644 index 0000000000..f082661e9c --- /dev/null +++ b/libpurple/plugins/ipc-test-server.c @@ -0,0 +1,99 @@ +/* + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, 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) +{ + gaim_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) +{ + gaim_debug_misc("ipc-test-server", "Got %d, %d, returning %d\n", + i1, i2, i1 - i2); + return i1 - i2; +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + gaim_plugin_ipc_register(plugin, "add", GAIM_CALLBACK(add_func), + gaim_marshal_INT__INT_INT, + gaim_value_new(GAIM_TYPE_INT), 2, + gaim_value_new(GAIM_TYPE_INT), + gaim_value_new(GAIM_TYPE_INT)); + + gaim_plugin_ipc_register(plugin, "sub", GAIM_CALLBACK(sub_func), + gaim_marshal_INT__INT_INT, + gaim_value_new(GAIM_TYPE_INT), 2, + gaim_value_new(GAIM_TYPE_INT), + gaim_value_new(GAIM_TYPE_INT)); + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + IPC_TEST_SERVER_PLUGIN_ID, /**< id */ + N_("IPC Test Server"), /**< name */ + 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 ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, + NULL +}; + +static void +init_plugin(GaimPlugin *plugin) +{ +} + +GAIM_INIT_PLUGIN(ipctestserver, init_plugin, info) diff --git a/libpurple/plugins/log_reader.c b/libpurple/plugins/log_reader.c new file mode 100644 index 0000000000..826aeed664 --- /dev/null +++ b/libpurple/plugins/log_reader.c @@ -0,0 +1,2151 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifndef GAIM_PLUGINS +# define GAIM_PLUGINS +#endif + +#include "internal.h" + +#include "debug.h" +#include "log.h" +#include "plugin.h" +#include "pluginpref.h" +#include "prefs.h" +#include "stringref.h" +#include "util.h" +#include "version.h" +#include "xmlnode.h" + +/* This must be the last Gaim header included. */ +#ifdef _WIN32 +#include "win32dep.h" +#endif + +/* Where is the Windows partition mounted? */ +#ifndef GAIM_LOG_READER_WINDOWS_MOUNT_POINT +#define GAIM_LOG_READER_WINDOWS_MOUNT_POINT "/mnt/windows" +#endif + +enum name_guesses { + NAME_GUESS_UNKNOWN, + NAME_GUESS_ME, + NAME_GUESS_THEM +}; + + +/***************************************************************************** + * Adium Logger * + *****************************************************************************/ + +/* The adium logger doesn't write logs, only reads them. This is to include + * Adium logs in the log viewer transparently. + */ + +static GaimLogLogger *adium_logger; + +enum adium_log_type { + ADIUM_HTML, + ADIUM_TEXT, +}; + +struct adium_logger_data { + char *path; + enum adium_log_type type; +}; + +static GList *adium_logger_list(GaimLogType type, const char *sn, GaimAccount *account) +{ + GList *list = NULL; + const char *logdir; + GaimPlugin *plugin; + GaimPluginProtocolInfo *prpl_info; + char *prpl_name; + char *temp; + char *path; + GDir *dir; + + g_return_val_if_fail(sn != NULL, list); + g_return_val_if_fail(account != NULL, list); + + logdir = gaim_prefs_get_string("/plugins/core/log_reader/adium/log_directory"); + + /* By clearing the log directory path, this logger can be (effectively) disabled. */ + if (!*logdir) + return list; + + plugin = gaim_find_prpl(gaim_account_get_protocol_id(account)); + if (!plugin) + return NULL; + + prpl_info = GAIM_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, account->username); + path = g_build_filename(logdir, temp, sn, NULL); + g_free(temp); + + dir = g_dir_open(path, 0, NULL); + if (dir) { + const gchar *file; + + while ((file = g_dir_read_name(dir))) { + if (!gaim_str_has_prefix(file, sn)) + continue; + if (gaim_str_has_suffix(file, ".html") || gaim_str_has_suffix(file, ".AdiumHTMLLog")) { + struct tm tm; + const char *date = file; + + date += strlen(sn) + 2; + if (sscanf(date, "%u|%u|%u", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3) { + + gaim_debug(GAIM_DEBUG_ERROR, "Adium log parse", + "Filename timestamp parsing error\n"); + } else { + char *filename = g_build_filename(path, file, NULL); + FILE *handle = g_fopen(filename, "rb"); + char *contents; + char *contents2; + struct adium_logger_data *data; + GaimLog *log; + + if (!handle) { + g_free(filename); + continue; + } + + /* XXX: This is really inflexible. */ + contents = g_malloc(57); + fread(contents, 56, 1, handle); + fclose(handle); + contents[56] = '\0'; + + /* XXX: This is fairly inflexible. */ + contents2 = contents; + while (*contents2 && *contents2 != '>') + contents2++; + if (*contents2) + contents2++; + while (*contents2 && *contents2 != '>') + contents2++; + if (*contents2) + contents2++; + + if (sscanf(contents2, "%u.%u.%u", + &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) { + + gaim_debug(GAIM_DEBUG_ERROR, "Adium log parse", + "Contents timestamp parsing error\n"); + g_free(contents); + g_free(filename); + continue; + } + g_free(contents); + + data = g_new0(struct adium_logger_data, 1); + data->path = filename; + data->type = ADIUM_HTML; + + tm.tm_year -= 1900; + tm.tm_mon -= 1; + + /* XXX: Look into this later... Should we pass in a struct tm? */ + log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, mktime(&tm), NULL); + log->logger = adium_logger; + log->logger_data = data; + + list = g_list_append(list, log); + } + } else if (gaim_str_has_suffix(file, ".adiumLog")) { + struct tm tm; + const char *date = file; + + date += strlen(sn) + 2; + if (sscanf(date, "%u|%u|%u", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3) { + + gaim_debug(GAIM_DEBUG_ERROR, "Adium log parse", + "Filename timestamp parsing error\n"); + } else { + char *filename = g_build_filename(path, file, NULL); + FILE *handle = g_fopen(filename, "rb"); + char *contents; + char *contents2; + struct adium_logger_data *data; + GaimLog *log; + + if (!handle) { + g_free(filename); + continue; + } + + /* XXX: This is really inflexible. */ + contents = g_malloc(14); + fread(contents, 13, 1, handle); + fclose(handle); + contents[13] = '\0'; + + contents2 = contents; + while (*contents2 && *contents2 != '(') + contents2++; + if (*contents2) + contents2++; + + if (sscanf(contents2, "%u.%u.%u", + &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) { + + gaim_debug(GAIM_DEBUG_ERROR, "Adium log parse", + "Contents timestamp parsing error\n"); + g_free(contents); + g_free(filename); + continue; + } + + g_free(contents); + + tm.tm_year -= 1900; + tm.tm_mon -= 1; + + data = g_new0(struct adium_logger_data, 1); + data->path = filename; + data->type = ADIUM_TEXT; + + /* XXX: Look into this later... Should we pass in a struct tm? */ + log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, mktime(&tm), NULL); + log->logger = adium_logger; + log->logger_data = data; + + list = g_list_append(list, log); + } + } + } + g_dir_close(dir); + } + + g_free(prpl_name); + g_free(path); + + return list; +} + +static char *adium_logger_read (GaimLog *log, GaimLogReadFlags *flags) +{ + struct adium_logger_data *data; + GError *error = NULL; + gchar *read = NULL; + gsize length; + + g_return_val_if_fail(log != NULL, g_strdup("")); + + data = log->logger_data; + + g_return_val_if_fail(data->path != NULL, g_strdup("")); + + gaim_debug(GAIM_DEBUG_INFO, "Adium log read", + "Reading %s\n", data->path); + if (!g_file_get_contents(data->path, &read, &length, &error)) { + gaim_debug(GAIM_DEBUG_ERROR, "Adium log read", + "Error reading log\n"); + if (error) + g_error_free(error); + return g_strdup(""); + } + + if (data->type != ADIUM_HTML) { + char *escaped = g_markup_escape_text(read, -1); + g_free(read); + read = escaped; + } + +#ifdef WIN32 + /* This problem only seems to show up on Windows. + * The BOM is displaying as a space at the beginning of the log. + */ + if (gaim_str_has_prefix(read, "\xef\xbb\xbf")) + { + /* FIXME: This feels so wrong... */ + char *temp = g_strdup(&(read[3])); + g_free(read); + read = temp; + } +#endif + + /* TODO: Apply formatting. + * Replace the above hack with something better, since we'll + * be looping over the entire log file contents anyway. + */ + + return read; +} + +static int adium_logger_size (GaimLog *log) +{ + struct adium_logger_data *data; + char *text; + size_t size; + + g_return_val_if_fail(log != NULL, 0); + + data = log->logger_data; + + if (gaim_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) { + struct stat st; + + if (!data->path || stat(data->path, &st)) + st.st_size = 0; + + return st.st_size; + } + + text = adium_logger_read(log, NULL); + size = strlen(text); + g_free(text); + + return size; +} + +static void adium_logger_finalize(GaimLog *log) +{ + struct adium_logger_data *data; + + g_return_if_fail(log != NULL); + + data = log->logger_data; + + g_free(data->path); +} + + +/***************************************************************************** + * Fire Logger * + *****************************************************************************/ + +#if 0 +/* The fire logger doesn't write logs, only reads them. This is to include + * Fire logs in the log viewer transparently. + */ + +static GaimLogLogger *fire_logger; + +struct fire_logger_data { +}; + +static GList *fire_logger_list(GaimLogType type, const char *sn, GaimAccount *account) +{ + /* TODO: Do something here. */ + return NULL; +} + +static char * fire_logger_read (GaimLog *log, GaimLogReadFlags *flags) +{ + struct fire_logger_data *data; + + g_return_val_if_fail(log != NULL, g_strdup("")); + + data = log->logger_data; + + /* TODO: Do something here. */ + return g_strdup(""); +} + +static int fire_logger_size (GaimLog *log) +{ + g_return_val_if_fail(log != NULL, 0); + + if (gaim_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) + return 0; + + /* TODO: Do something here. */ + return 0; +} + +static void fire_logger_finalize(GaimLog *log) +{ + g_return_if_fail(log != NULL); + + /* TODO: Do something here. */ +} +#endif + + +/***************************************************************************** + * Messenger Plus! Logger * + *****************************************************************************/ + +#if 0 +/* The messenger_plus logger doesn't write logs, only reads them. This is to include + * Messenger Plus! logs in the log viewer transparently. + */ + +static GaimLogLogger *messenger_plus_logger; + +struct messenger_plus_logger_data { +}; + +static GList *messenger_plus_logger_list(GaimLogType type, const char *sn, GaimAccount *account) +{ + /* TODO: Do something here. */ + return NULL; +} + +static char * messenger_plus_logger_read (GaimLog *log, GaimLogReadFlags *flags) +{ + struct messenger_plus_logger_data *data = log->logger_data; + + g_return_val_if_fail(log != NULL, g_strdup("")); + + data = log->logger_data; + + /* TODO: Do something here. */ + return g_strdup(""); +} + +static int messenger_plus_logger_size (GaimLog *log) +{ + g_return_val_if_fail(log != NULL, 0); + + if (gaim_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) + return 0; + + /* TODO: Do something here. */ + return 0; +} + +static void messenger_plus_logger_finalize(GaimLog *log) +{ + g_return_if_fail(log != NULL); + + /* TODO: Do something here. */ +} +#endif + + +/***************************************************************************** + * MSN Messenger Logger * + *****************************************************************************/ + +/* The msn logger doesn't write logs, only reads them. This is to include + * MSN Messenger message histories in the log viewer transparently. + */ + +static GaimLogLogger *msn_logger; + +struct msn_logger_data { + xmlnode *root; + xmlnode *message; + const char *session_id; + int last_log; + GString *text; +}; + +/* This function is really confusing. It makes baby rlaager cry... + In other news: "You lost a lot of blood but we found most of it." + */ +static time_t msn_logger_parse_timestamp(xmlnode *message, struct tm **tm_out) +{ + const char *datetime; + static struct tm tm2; + time_t stamp; + const char *date; + const char *time; + int month; + int day; + int year; + int hour; + int min; + int sec; + char am_pm; + char *str; + static struct tm tm; + time_t t; + time_t diff; + +#ifndef G_DISABLE_CHECKS + if (message != NULL) + { + *tm_out = NULL; + + /* Trigger the usual warning. */ + g_return_val_if_fail(message != NULL, (time_t)0); + } +#endif + + datetime = xmlnode_get_attrib(message, "DateTime"); + if (!(datetime && *datetime)) + { + gaim_debug_error("MSN log timestamp parse", + "Attribute missing: %s\n", "DateTime"); + return (time_t)0; + } + + stamp = gaim_str_to_time(datetime, TRUE, &tm2, NULL, NULL); +#ifdef HAVE_TM_GMTOFF + tm2.tm_gmtoff = 0; +#endif +#ifdef HAVE_STRUCT_TM_TM_ZONE + /* This is used in the place of a timezone abbreviation if the + * offset is way off. The user should never really see it, but + * it's here just in case. The parens are to make it clear it's + * not a real timezone. */ + tm2.tm_zone = _("(UTC)"); +#endif + + + date = xmlnode_get_attrib(message, "Date"); + if (!(date && *date)) + { + gaim_debug_error("MSN log timestamp parse", + "Attribute missing: %s\n", "Date"); + *tm_out = &tm2; + return stamp; + } + + time = xmlnode_get_attrib(message, "Time"); + if (!(time && *time)) + { + gaim_debug_error("MSN log timestamp parse", + "Attribute missing: %s\n", "Time"); + *tm_out = &tm2; + return stamp; + } + + if (sscanf(date, "%u/%u/%u", &month, &day, &year) != 3) + { + gaim_debug_error("MSN log timestamp parse", + "%s parsing error\n", "Date"); + *tm_out = &tm2; + return stamp; + } + else + { + if (month > 12) + { + int tmp = day; + day = month; + month = tmp; + } + } + + if (sscanf(time, "%u:%u:%u %c", &hour, &min, &sec, &am_pm) != 4) + { + gaim_debug_error("MSN log timestamp parse", + "%s parsing error\n", "Time"); + *tm_out = &tm2; + return stamp; + } + + if (am_pm == 'P') { + hour += 12; + } else if (hour == 12) { + /* 12 AM = 00 hr */ + hour = 0; + } + + str = g_strdup_printf("%04i-%02i-%02iT%02i:%02i:%02i", year, month, day, hour, min, sec); + t = gaim_str_to_time(str, TRUE, &tm, NULL, NULL); + + + if (stamp > t) + diff = stamp - t; + else + diff = t - stamp; + + if (diff > (14 * 60 * 60)) + { + if (day <= 12) + { + /* Swap day & month variables, to see if it's a non-US date. */ + g_free(str); + str = g_strdup_printf("%04i-%02i-%02iT%02i:%02i:%02i", year, month, day, hour, min, sec); + t = gaim_str_to_time(str, TRUE, &tm, NULL, NULL); + + if (stamp > t) + diff = stamp - t; + else + diff = t - stamp; + + if (diff > (14 * 60 * 60)) + { + /* We got a time, it's not impossible, but + * the diff is too large. Display the UTC time. */ + g_free(str); + *tm_out = &tm2; + return stamp; + } + else + { + /* Legal time */ + /* Fall out */ + } + } + else + { + /* We got a time, it's not impossible, but + * the diff is too large. Display the UTC time. */ + g_free(str); + *tm_out = &tm2; + return stamp; + } + } + + /* If we got here, the time is legal with a reasonable offset. + * Let's find out if it's in our TZ. */ + if (gaim_str_to_time(str, FALSE, &tm, NULL, NULL) == stamp) + { + g_free(str); + *tm_out = &tm; + return stamp; + } + g_free(str); + + /* The time isn't in our TZ, but it's reasonable. */ +#ifdef HAVE_STRUCT_TM_TM_ZONE + tm.tm_zone = " "; +#endif + *tm_out = &tm; + return stamp; +} + +static GList *msn_logger_list(GaimLogType type, const char *sn, GaimAccount *account) +{ + GList *list = NULL; + char *username; + GaimBuddy *buddy; + const char *logdir; + const char *savedfilename = NULL; + char *logfile; + char *path; + GError *error = NULL; + gchar *contents = NULL; + gsize length; + xmlnode *root; + xmlnode *message; + const char *old_session_id = ""; + struct msn_logger_data *data = NULL; + + g_return_val_if_fail(sn != NULL, list); + g_return_val_if_fail(account != NULL, list); + + if (strcmp(account->protocol_id, "prpl-msn")) + return list; + + logdir = gaim_prefs_get_string("/plugins/core/log_reader/msn/log_directory"); + + /* By clearing the log directory path, this logger can be (effectively) disabled. */ + if (!*logdir) + return list; + + buddy = gaim_find_buddy(account, sn); + + if ((username = g_strdup(gaim_account_get_string( + account, "log_reader_msn_log_folder", NULL)))) { + /* As a special case, we allow the null string to kill the parsing + * straight away. This would allow the user to deal with the case + * when two account have the same username at different domains and + * only one has logs stored. + */ + if (!*username) { + g_free(username); + return list; + } + } else { + username = g_strdup(gaim_normalize(account, account->username)); + } + + if (buddy) + savedfilename = gaim_blist_node_get_string(&buddy->node, "log_reader_msn_log_filename"); + + if (savedfilename) { + /* As a special case, we allow the null string to kill the parsing + * straight away. This would allow the user to deal with the case + * when two buddies have the same username at different domains and + * only one has logs stored. + */ + if (!*savedfilename) { + g_free(username); + return list; + } + + logfile = g_strdup(savedfilename); + } else { + logfile = g_strdup_printf("%s.xml", gaim_normalize(account, sn)); + } + + path = g_build_filename(logdir, username, "History", logfile, NULL); + + if (!g_file_test(path, G_FILE_TEST_EXISTS)) { + gboolean found = FALSE; + char *at_sign; + GDir *dir; + + g_free(path); + + if (savedfilename) { + /* We had a saved filename, but it doesn't exist. + * Returning now is the right course of action because we don't + * want to detect another file incorrectly. + */ + g_free(username); + g_free(logfile); + return list; + } + + /* Perhaps we're using a new version of MSN with the weird numbered folders. + * I don't know how the numbers are calculated, so I'm going to attempt to + * find logs by pattern matching... + */ + + at_sign = g_strrstr(username, "@"); + if (at_sign) + *at_sign = '\0'; + + dir = g_dir_open(logdir, 0, NULL); + if (dir) { + const gchar *name; + + while ((name = g_dir_read_name(dir))) { + const char *c = name; + + if (!gaim_str_has_prefix(c, username)) + continue; + + c += strlen(username); + while (*c) { + if (!g_ascii_isdigit(*c)) + break; + + c++; + } + + path = g_build_filename(logdir, name, NULL); + /* The !c makes sure we got to the end of the while loop above. */ + if (!*c && g_file_test(path, G_FILE_TEST_IS_DIR)) { + char *history_path = g_build_filename( + path, "History", NULL); + if (g_file_test(history_path, G_FILE_TEST_IS_DIR)) { + gaim_account_set_string(account, + "log_reader_msn_log_folder", name); + g_free(path); + path = history_path; + found = TRUE; + break; + } + g_free(path); + g_free(history_path); + } + else + g_free(path); + } + g_dir_close(dir); + } + g_free(username); + + if (!found) { + g_free(logfile); + return list; + } + + /* If we've reached this point, we've found a History folder. */ + + username = g_strdup(gaim_normalize(account, sn)); + at_sign = g_strrstr(username, "@"); + if (at_sign) + *at_sign = '\0'; + + found = FALSE; + dir = g_dir_open(path, 0, NULL); + if (dir) { + const gchar *name; + + while ((name = g_dir_read_name(dir))) { + const char *c = name; + + if (!gaim_str_has_prefix(c, username)) + continue; + + c += strlen(username); + while (*c) { + if (!g_ascii_isdigit(*c)) + break; + + c++; + } + + path = g_build_filename(path, name, NULL); + if (!strcmp(c, ".xml") && + g_file_test(path, G_FILE_TEST_EXISTS)) { + found = TRUE; + g_free(logfile); + logfile = g_strdup(name); + break; + } + else + g_free(path); + } + g_dir_close(dir); + } + g_free(username); + + if (!found) { + g_free(logfile); + return list; + } + } else { + g_free(username); + g_free(logfile); + logfile = NULL; /* No sense saving the obvious buddy@domain.com. */ + } + + gaim_debug(GAIM_DEBUG_INFO, "MSN log read", + "Reading %s\n", path); + if (!g_file_get_contents(path, &contents, &length, &error)) { + g_free(path); + gaim_debug(GAIM_DEBUG_ERROR, "MSN log read", + "Error reading log\n"); + if (error) + g_error_free(error); + return list; + } + g_free(path); + + /* Reading the file was successful... + * Save its name if it involves the crazy numbers. The idea here is that you could + * then tweak the blist.xml file by hand if need be. This would be the case if two + * buddies have the same username at different domains. One set of logs would get + * detected for both buddies. + */ + if (buddy && logfile) { + gaim_blist_node_set_string(&buddy->node, "log_reader_msn_log_filename", logfile); + g_free(logfile); + } + + root = xmlnode_from_str(contents, length); + g_free(contents); + if (!root) + return list; + + for (message = xmlnode_get_child(root, "Message"); message; + message = xmlnode_get_next_twin(message)) { + const char *session_id; + + session_id = xmlnode_get_attrib(message, "SessionID"); + if (!session_id) { + gaim_debug(GAIM_DEBUG_ERROR, "MSN log parse", + "Error parsing message: %s\n", "SessionID missing"); + continue; + } + + if (strcmp(session_id, old_session_id)) { + /* + * The session ID differs from the last message. + * Thus, this is the start of a new conversation. + */ + struct tm *tm; + time_t stamp; + GaimLog *log; + + data = g_new0(struct msn_logger_data, 1); + data->root = root; + data->message = message; + data->session_id = session_id; + data->text = NULL; + data->last_log = FALSE; + + stamp = msn_logger_parse_timestamp(message, &tm); + + log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, stamp, tm); + log->logger = msn_logger; + log->logger_data = data; + + list = g_list_append(list, log); + } + old_session_id = session_id; + } + + if (data) + data->last_log = TRUE; + + return list; +} + +static char * msn_logger_read (GaimLog *log, GaimLogReadFlags *flags) +{ + struct msn_logger_data *data; + GString *text = NULL; + xmlnode *message; + + g_return_val_if_fail(log != NULL, g_strdup("")); + + data = log->logger_data; + + if (data->text) { + /* The GTK code which displays the logs g_free()s whatever is + * returned from this function. Thus, we can't reuse the str + * part of the GString. The only solution is to free it and + * start over. + */ + g_string_free(data->text, FALSE); + } + + text = g_string_new(""); + + if (!data->root || !data->message || !data->session_id) { + /* Something isn't allocated correctly. */ + gaim_debug(GAIM_DEBUG_ERROR, "MSN log parse", + "Error parsing message: %s\n", "Internal variables inconsistent"); + data->text = text; + + return text->str; + } + + for (message = data->message; message; + message = xmlnode_get_next_twin(message)) { + + const char *new_session_id; + xmlnode *text_node; + const char *from_name = NULL; + const char *to_name = NULL; + xmlnode *from; + xmlnode *to; + enum name_guesses name_guessed = NAME_GUESS_UNKNOWN; + const char *their_name; + time_t time_unix; + struct tm *tm; + char *timestamp; + char *tmp; + const char *style; + + new_session_id = xmlnode_get_attrib(message, "SessionID"); + + /* If this triggers, something is wrong with the XML. */ + if (!new_session_id) { + gaim_debug(GAIM_DEBUG_ERROR, "MSN log parse", + "Error parsing message: %s\n", "New SessionID missing"); + break; + } + + if (strcmp(new_session_id, data->session_id)) { + /* The session ID differs from the first message. + * Thus, this is the start of a new conversation. + */ + break; + } + + text_node = xmlnode_get_child(message, "Text"); + if (!text_node) + continue; + + from = xmlnode_get_child(message, "From"); + if (from) { + xmlnode *user = xmlnode_get_child(from, "User"); + + if (user) { + from_name = xmlnode_get_attrib(user, "FriendlyName"); + + /* This saves a check later. */ + if (!*from_name) + from_name = NULL; + } + } + + to = xmlnode_get_child(message, "To"); + if (to) { + xmlnode *user = xmlnode_get_child(to, "User"); + if (user) { + to_name = xmlnode_get_attrib(user, "FriendlyName"); + + /* This saves a check later. */ + if (!*to_name) + to_name = NULL; + } + } + + their_name = from_name; + if (from_name && gaim_prefs_get_bool("/plugins/core/log_reader/use_name_heuristics")) { + const char *friendly_name = gaim_connection_get_display_name(log->account->gc); + + if (friendly_name != NULL) { + int friendly_name_length = strlen(friendly_name); + const char *alias; + int alias_length; + GaimBuddy *buddy = gaim_find_buddy(log->account, log->name); + gboolean from_name_matches; + gboolean to_name_matches; + + if (buddy && buddy->alias) + their_name = buddy->alias; + + if (log->account->alias) + { + alias = log->account->alias; + alias_length = strlen(alias); + } + else + { + alias = ""; + alias_length = 0; + } + + /* Try to guess which user is me. + * The first step is to determine if either of the names matches either my + * friendly name or alias. For this test, "match" is defined as: + * ^(friendly_name|alias)([^a-zA-Z0-9].*)?$ + */ + from_name_matches = (gaim_str_has_prefix(from_name, friendly_name) && + !isalnum(*(from_name + friendly_name_length))) || + (gaim_str_has_prefix(from_name, alias) && + !isalnum(*(from_name + alias_length))); + + to_name_matches = to_name != NULL && ( + (gaim_str_has_prefix(to_name, friendly_name) && + !isalnum(*(to_name + friendly_name_length))) || + (gaim_str_has_prefix(to_name, alias) && + !isalnum(*(to_name + alias_length)))); + + if (from_name_matches) { + if (!to_name_matches) { + name_guessed = NAME_GUESS_ME; + } + } else if (to_name_matches) { + name_guessed = NAME_GUESS_THEM; + } else { + if (buddy && buddy->alias) { + char *alias = g_strdup(buddy->alias); + + /* "Truncate" the string at the first non-alphanumeric + * character. The idea is to relax the comparison. + */ + char *temp; + for (temp = alias; *temp ; temp++) { + if (!isalnum(*temp)) { + *temp = '\0'; + break; + } + } + alias_length = strlen(alias); + + /* Try to guess which user is them. + * The first step is to determine if either of the names + * matches their alias. For this test, "match" is + * defined as: ^alias([^a-zA-Z0-9].*)?$ + */ + from_name_matches = (gaim_str_has_prefix( + from_name, alias) && + !isalnum(*(from_name + + alias_length))); + + to_name_matches = to_name && (gaim_str_has_prefix( + to_name, alias) && + !isalnum(*(to_name + + alias_length))); + + g_free(alias); + + if (from_name_matches) { + if (!to_name_matches) { + name_guessed = NAME_GUESS_THEM; + } + } else if (to_name_matches) { + name_guessed = NAME_GUESS_ME; + } else if (buddy->server_alias) { + friendly_name_length = + strlen(buddy->server_alias); + + /* Try to guess which user is them. + * The first step is to determine if either of + * the names matches their friendly name. For + * this test, "match" is defined as: + * ^friendly_name([^a-zA-Z0-9].*)?$ + */ + from_name_matches = (gaim_str_has_prefix( + from_name, + buddy->server_alias) && + !isalnum(*(from_name + + friendly_name_length))); + + to_name_matches = to_name && ( + (gaim_str_has_prefix( + to_name, buddy->server_alias) && + !isalnum(*(to_name + + friendly_name_length)))); + + if (from_name_matches) { + if (!to_name_matches) { + name_guessed = NAME_GUESS_THEM; + } + } else if (to_name_matches) { + name_guessed = NAME_GUESS_ME; + } + } + } + } + } + } + + if (name_guessed != NAME_GUESS_UNKNOWN) { + text = g_string_append(text, ""); + } + + time_unix = msn_logger_parse_timestamp(message, &tm); + + timestamp = g_strdup_printf("(%02u:%02u:%02u) ", + tm->tm_hour, tm->tm_min, tm->tm_sec); + text = g_string_append(text, timestamp); + g_free(timestamp); + + if (from_name) { + text = g_string_append(text, ""); + + if (name_guessed == NAME_GUESS_ME) { + if (log->account->alias) + text = g_string_append(text, log->account->alias); + else + text = g_string_append(text, log->account->username); + } + else if (name_guessed == NAME_GUESS_THEM) + text = g_string_append(text, their_name); + else + text = g_string_append(text, from_name); + + text = g_string_append(text, ": "); + } + + if (name_guessed != NAME_GUESS_UNKNOWN) + text = g_string_append(text, ""); + + style = xmlnode_get_attrib(text_node, "Style"); + + tmp = xmlnode_get_data(text_node); + if (style && *style) { + text = g_string_append(text, ""); + text = g_string_append(text, tmp); + text = g_string_append(text, "\n"); + } else { + text = g_string_append(text, tmp); + text = g_string_append(text, "\n"); + } + g_free(tmp); + } + + data->text = text; + + return text->str; +} + +static int msn_logger_size (GaimLog *log) +{ + char *text; + size_t size; + + g_return_val_if_fail(log != NULL, 0); + + if (gaim_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) + return 0; + + text = msn_logger_read(log, NULL); + size = strlen(text); + g_free(text); + + return size; +} + +static void msn_logger_finalize(GaimLog *log) +{ + struct msn_logger_data *data; + + g_return_if_fail(log != NULL); + + data = log->logger_data; + + if (data->last_log) + xmlnode_free(data->root); + + if (data->text) + g_string_free(data->text, FALSE); +} + + +/***************************************************************************** + * Trillian Logger * + *****************************************************************************/ + +/* The trillian logger doesn't write logs, only reads them. This is to include + * Trillian logs in the log viewer transparently. + */ + +static GaimLogLogger *trillian_logger; +static void trillian_logger_finalize(GaimLog *log); + +struct trillian_logger_data { + char *path; /* FIXME: Change this to use GaimStringref like log.c:old_logger_list */ + int offset; + int length; + char *their_nickname; +}; + +static GList *trillian_logger_list(GaimLogType type, const char *sn, GaimAccount *account) +{ + GList *list = NULL; + const char *logdir; + GaimPlugin *plugin; + GaimPluginProtocolInfo *prpl_info; + char *prpl_name; + const char *buddy_name; + char *filename; + char *path; + GError *error = NULL; + gchar *contents = NULL; + gsize length; + gchar *line; + gchar *c; + + g_return_val_if_fail(sn != NULL, list); + g_return_val_if_fail(account != NULL, list); + + logdir = gaim_prefs_get_string("/plugins/core/log_reader/trillian/log_directory"); + + /* By clearing the log directory path, this logger can be (effectively) disabled. */ + if (!*logdir) + return list; + + plugin = gaim_find_prpl(gaim_account_get_protocol_id(account)); + if (!plugin) + return NULL; + + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); + if (!prpl_info->list_icon) + return NULL; + + prpl_name = g_ascii_strup(prpl_info->list_icon(account, NULL), -1); + + buddy_name = gaim_normalize(account, sn); + + filename = g_strdup_printf("%s.log", buddy_name); + path = g_build_filename( + logdir, prpl_name, filename, NULL); + + gaim_debug(GAIM_DEBUG_INFO, "Trillian log list", + "Reading %s\n", path); + /* FIXME: There's really no need to read the entire file at once. + * See src/log.c:old_logger_list for a better approach. + */ + if (!g_file_get_contents(path, &contents, &length, &error)) { + if (error) { + g_error_free(error); + error = NULL; + } + g_free(path); + + path = g_build_filename( + logdir, prpl_name, "Query", filename, NULL); + gaim_debug(GAIM_DEBUG_INFO, "Trillian log list", + "Reading %s\n", path); + if (!g_file_get_contents(path, &contents, &length, &error)) { + if (error) + g_error_free(error); + } + } + g_free(filename); + + if (contents) { + struct trillian_logger_data *data = NULL; + int offset = 0; + int last_line_offset = 0; + + line = contents; + c = contents; + while (*c) { + offset++; + + if (*c != '\n') { + c++; + continue; + } + + *c = '\0'; + if (gaim_str_has_prefix(line, "Session Close ")) { + if (data && !data->length) { + if (!(data->length = last_line_offset - data->offset)) { + /* This log had no data, so we remove it. */ + GList *last = g_list_last(list); + + gaim_debug(GAIM_DEBUG_INFO, "Trillian log list", + "Empty log. Offset %i\n", data->offset); + + trillian_logger_finalize((GaimLog *)last->data); + list = g_list_delete_link(list, last); + } + } + } else if (line[0] && line[1] && line [3] && + gaim_str_has_prefix(&line[3], "sion Start ")) { + + char *their_nickname = line; + char *timestamp; + + if (data && !data->length) + data->length = last_line_offset - data->offset; + + while (*their_nickname && (*their_nickname != ':')) + their_nickname++; + their_nickname++; + + /* This code actually has nothing to do with + * the timestamp YET. I'm simply using this + * variable for now to NUL-terminate the + * their_nickname string. + */ + timestamp = their_nickname; + while (*timestamp && *timestamp != ')') + timestamp++; + + if (*timestamp == ')') { + char *month; + struct tm tm; + + *timestamp = '\0'; + if (line[0] && line[1] && line[2]) + timestamp += 3; + + /* Now we start dealing with the timestamp. */ + + /* Skip over the day name. */ + while (*timestamp && (*timestamp != ' ')) + timestamp++; + *timestamp = '\0'; + timestamp++; + + /* Parse out the month. */ + month = timestamp; + while (*timestamp && (*timestamp != ' ')) + timestamp++; + *timestamp = '\0'; + timestamp++; + + /* Parse the day, time, and year. */ + if (sscanf(timestamp, "%u %u:%u:%u %u", + &tm.tm_mday, &tm.tm_hour, + &tm.tm_min, &tm.tm_sec, + &tm.tm_year) != 5) { + + gaim_debug(GAIM_DEBUG_ERROR, + "Trillian log timestamp parse", + "Session Start parsing error\n"); + } else { + GaimLog *log; + + tm.tm_year -= 1900; + + /* Let the C library deal with + * daylight savings time. + */ + tm.tm_isdst = -1; + + /* Ugly hack, in case current locale + * is not English. This code is taken + * from log.c. + */ + if (strcmp(month, "Jan") == 0) { + tm.tm_mon= 0; + } else if (strcmp(month, "Feb") == 0) { + tm.tm_mon = 1; + } else if (strcmp(month, "Mar") == 0) { + tm.tm_mon = 2; + } else if (strcmp(month, "Apr") == 0) { + tm.tm_mon = 3; + } else if (strcmp(month, "May") == 0) { + tm.tm_mon = 4; + } else if (strcmp(month, "Jun") == 0) { + tm.tm_mon = 5; + } else if (strcmp(month, "Jul") == 0) { + tm.tm_mon = 6; + } else if (strcmp(month, "Aug") == 0) { + tm.tm_mon = 7; + } else if (strcmp(month, "Sep") == 0) { + tm.tm_mon = 8; + } else if (strcmp(month, "Oct") == 0) { + tm.tm_mon = 9; + } else if (strcmp(month, "Nov") == 0) { + tm.tm_mon = 10; + } else if (strcmp(month, "Dec") == 0) { + tm.tm_mon = 11; + } + + data = g_new0( + struct trillian_logger_data, 1); + data->path = g_strdup(path); + data->offset = offset; + data->length = 0; + data->their_nickname = + g_strdup(their_nickname); + + /* XXX: Look into this later... Should we pass in a struct tm? */ + log = gaim_log_new(GAIM_LOG_IM, + sn, account, NULL, mktime(&tm), NULL); + log->logger = trillian_logger; + log->logger_data = data; + + list = g_list_append(list, log); + } + } + } + c++; + line = c; + last_line_offset = offset; + } + + g_free(contents); + } + g_free(path); + + g_free(prpl_name); + + return list; +} + +static char * trillian_logger_read (GaimLog *log, GaimLogReadFlags *flags) +{ + struct trillian_logger_data *data; + char *read; + FILE *file; + GaimBuddy *buddy; + char *escaped; + GString *formatted; + char *c; + char *line; + + g_return_val_if_fail(log != NULL, g_strdup("")); + + data = log->logger_data; + + g_return_val_if_fail(data->path != NULL, g_strdup("")); + g_return_val_if_fail(data->length > 0, g_strdup("")); + g_return_val_if_fail(data->their_nickname != NULL, g_strdup("")); + + gaim_debug(GAIM_DEBUG_INFO, "Trillian log read", + "Reading %s\n", data->path); + + read = g_malloc(data->length + 2); + + file = g_fopen(data->path, "rb"); + fseek(file, data->offset, SEEK_SET); + fread(read, data->length, 1, file); + fclose(file); + + if (read[data->length-1] == '\n') { + read[data->length] = '\0'; + } else { + read[data->length] = '\n'; + read[data->length+1] = '\0'; + } + + /* Load miscellaneous data. */ + buddy = gaim_find_buddy(log->account, log->name); + + escaped = g_markup_escape_text(read, -1); + g_free(read); + read = escaped; + + /* Apply formatting... */ + formatted = g_string_new(""); + c = read; + line = read; + while (*c) + { + if (*c == '\n') + { + char *link_temp_line; + char *link; + char *timestamp; + char *footer = NULL; + *c = '\0'; + + /* Convert links. + * + * The format is (Link: URL)URL + * So, I want to find each occurance of "(Link: " and replace that chunk with: + * + * Then, replace the next " " (or add this if the end-of-line is reached) with: + * + */ + link_temp_line = NULL; + while ((link = g_strstr_len(line, strlen(line), "(Link: "))) { + GString *temp; + + if (!*link) + continue; + + *link = '\0'; + link++; + + temp = g_string_new(line); + g_string_append(temp, "= 6) { + link += (sizeof("(Link: ") - 1); + + while (*link && *link != ')') { + g_string_append_c(temp, *link); + link++; + } + if (link) { + link++; + + g_string_append(temp, "\">"); + while (*link && *link != ' ') { + g_string_append_c(temp, *link); + link++; + } + g_string_append(temp, ""); + } + + g_string_append(temp, link); + + /* Free the last round's line. */ + if (link_temp_line) + g_free(line); + + line = temp->str; + g_string_free(temp, FALSE); + + /* Save this memory location so we can free it later. */ + link_temp_line = line; + } + } + + timestamp = ""; + if (*line == '[') { + timestamp = line; + while (*timestamp && *timestamp != ']') + timestamp++; + if (*timestamp == ']') { + *timestamp = '\0'; + line++; + /* TODO: Parse the timestamp and convert it to Gaim's format. */ + g_string_append_printf(formatted, + "(%s) ", line); + line = timestamp; + if (line[1] && line[2]) + line += 2; + } + + if (gaim_str_has_prefix(line, "*** ")) { + line += (sizeof("*** ") - 1); + g_string_append(formatted, ""); + footer = ""; + if (gaim_str_has_prefix(line, "NOTE: This user is offline.")) { + line = _("User is offline."); + } else if (gaim_str_has_prefix(line, + "NOTE: Your status is currently set to ")) { + + line += (sizeof("NOTE: ") - 1); + } else if (gaim_str_has_prefix(line, "Auto-response sent to ")) { + g_string_append(formatted, _("Auto-response sent:")); + while (*line && *line != ':') + line++; + if (*line) + line++; + g_string_append(formatted, ""); + footer = NULL; + } else if (strstr(line, " signed off ")) { + if (buddy != NULL && buddy->alias) + g_string_append_printf(formatted, + _("%s has signed off."), buddy->alias); + else + g_string_append_printf(formatted, + _("%s has signed off."), log->name); + line = ""; + } else if (strstr(line, " signed on ")) { + if (buddy != NULL && buddy->alias) + g_string_append(formatted, buddy->alias); + else + g_string_append(formatted, log->name); + line = " logged in."; + } else if (gaim_str_has_prefix(line, + "One or more messages may have been undeliverable.")) { + + g_string_append(formatted, + ""); + g_string_append(formatted, + _("One or more messages may have been " + "undeliverable.")); + line = ""; + footer = ""; + } else if (gaim_str_has_prefix(line, + "You have been disconnected.")) { + + g_string_append(formatted, + ""); + g_string_append(formatted, + _("You were disconnected from the server.")); + line = ""; + footer = ""; + } else if (gaim_str_has_prefix(line, + "You are currently disconnected.")) { + + g_string_append(formatted, + ""); + line = _("You are currently disconnected. Messages " + "will not be received unless you are " + "logged in."); + footer = ""; + } else if (gaim_str_has_prefix(line, + "Your previous message has not been sent.")) { + + g_string_append(formatted, + ""); + + if (gaim_str_has_prefix(line, + "Your previous message has not been sent. " + "Reason: Maximum length exceeded.")) { + + g_string_append(formatted, + _("Message could not be sent because " + "the maximum length was exceeded.")); + line = ""; + } else { + g_string_append(formatted, + _("Message could not be sent.")); + line += (sizeof( + "Your previous message " + "has not been sent. ") - 1); + } + + footer = ""; + } + } else if (gaim_str_has_prefix(line, data->their_nickname)) { + if (buddy != NULL && buddy->alias) { + line += strlen(data->their_nickname) + 2; + g_string_append_printf(formatted, + "" + "%s: ", buddy->alias); + } + } else { + char *line2 = line; + while (*line2 && *line2 != ':') + line2++; + if (*line2 == ':') { + const char *acct_name; + line2++; + line = line2; + acct_name = gaim_account_get_alias(log->account); + if (!acct_name) + acct_name = gaim_account_get_username(log->account); + + g_string_append_printf(formatted, + "" + "%s:", acct_name); + } + } + } + + g_string_append(formatted, line); + + if (footer) + g_string_append(formatted, footer); + + g_string_append_c(formatted, '\n'); + + if (link_temp_line) + g_free(link_temp_line); + + c++; + line = c; + } else + c++; + } + + g_free(read); + read = formatted->str; + g_string_free(formatted, FALSE); + + return read; +} + +static int trillian_logger_size (GaimLog *log) +{ + struct trillian_logger_data *data; + char *text; + size_t size; + + g_return_val_if_fail(log != NULL, 0); + + data = log->logger_data; + + if (gaim_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) { + return data ? data->length : 0; + } + + text = trillian_logger_read(log, NULL); + size = strlen(text); + g_free(text); + + return size; +} + +static void trillian_logger_finalize(GaimLog *log) +{ + struct trillian_logger_data *data; + + g_return_if_fail(log != NULL); + + data = log->logger_data; + + g_free(data->path); + g_free(data->their_nickname); + +} + + +/***************************************************************************** + * Plugin Code * + *****************************************************************************/ + +static void +init_plugin(GaimPlugin *plugin) +{ + char *path; +#ifdef _WIN32 + char *folder; + gboolean found = FALSE; +#endif + + g_return_if_fail(plugin != NULL); + + gaim_prefs_add_none("/plugins/core/log_reader"); + + + /* Add general preferences. */ + + gaim_prefs_add_bool("/plugins/core/log_reader/fast_sizes", FALSE); + gaim_prefs_add_bool("/plugins/core/log_reader/use_name_heuristics", TRUE); + + + /* Add Adium log directory preference. */ + gaim_prefs_add_none("/plugins/core/log_reader/adium"); + + /* Calculate default Adium log directory. */ +#ifdef _WIN32 + path = ""; +#else + path = g_build_filename(gaim_home_dir(), "Library", "Application Support", + "Adium 2.0", "Users", "Default", "Logs", NULL); +#endif + + gaim_prefs_add_string("/plugins/core/log_reader/adium/log_directory", path); + +#ifndef _WIN32 + g_free(path); +#endif + + + /* Add Fire log directory preference. */ + gaim_prefs_add_none("/plugins/core/log_reader/fire"); + + /* Calculate default Fire log directory. */ +#ifdef _WIN32 + path = ""; +#else + path = g_build_filename(gaim_home_dir(), "Library", "Application Support", + "Fire", "Sessions", NULL); +#endif + + gaim_prefs_add_string("/plugins/core/log_reader/fire/log_directory", path); + +#ifndef _WIN32 + g_free(path); +#endif + + + /* Add Messenger Plus! log directory preference. */ + gaim_prefs_add_none("/plugins/core/log_reader/messenger_plus"); + + /* Calculate default Messenger Plus! log directory. */ +#ifdef _WIN32 + folder = wgaim_get_special_folder(CSIDL_PERSONAL); + if (folder) { +#endif + path = g_build_filename( +#ifdef _WIN32 + folder, +#else + GAIM_LOG_READER_WINDOWS_MOUNT_POINT, "Documents and Settings", + g_get_user_name(), "My Documents", +#endif + "My Chat Logs", NULL); +#ifdef _WIN32 + g_free(folder); + } else /* !folder */ + path = g_strdup(""); +#endif + + gaim_prefs_add_string("/plugins/core/log_reader/messenger_plus/log_directory", path); + g_free(path); + + + /* Add MSN Messenger log directory preference. */ + gaim_prefs_add_none("/plugins/core/log_reader/msn"); + + /* Calculate default MSN message history directory. */ +#ifdef _WIN32 + folder = wgaim_get_special_folder(CSIDL_PERSONAL); + if (folder) { +#endif + path = g_build_filename( +#ifdef _WIN32 + folder, +#else + GAIM_LOG_READER_WINDOWS_MOUNT_POINT, "Documents and Settings", + g_get_user_name(), "My Documents", +#endif + "My Received Files", NULL); +#ifdef _WIN32 + g_free(folder); + } else /* !folder */ + path = g_strdup(""); +#endif + + gaim_prefs_add_string("/plugins/core/log_reader/msn/log_directory", path); + g_free(path); + + + /* Add Trillian log directory preference. */ + gaim_prefs_add_none("/plugins/core/log_reader/trillian"); + +#ifdef _WIN32 + /* XXX: While a major hack, this is the most reliable way I could + * think of to determine the Trillian installation directory. + */ + + path = NULL; + if ((folder = wgaim_read_reg_string(HKEY_CLASSES_ROOT, "Trillian.SkinZip\\shell\\Add\\command\\", NULL))) { + char *value = folder; + char *temp; + + /* Break apart buffer. */ + if (*value == '"') { + value++; + temp = value; + while (*temp && *temp != '"') + temp++; + } else { + temp = value; + while (*temp && *temp != ' ') + temp++; + } + *temp = '\0'; + + /* Set path. */ + if (gaim_str_has_suffix(value, "trillian.exe")) { + value[strlen(value) - (sizeof("trillian.exe") - 1)] = '\0'; + path = g_build_filename(value, "users", "default", "talk.ini", NULL); + } + g_free(folder); + } + + if (!path) { + char *folder = wgaim_get_special_folder(CSIDL_PROGRAM_FILES); + if (folder) { + path = g_build_filename(folder, "Trillian", + "users", "default", "talk.ini", NULL); + g_free(folder); + } + } + + if (path) { + /* Read talk.ini file to find the log directory. */ + GError *error = NULL; + +#if 0 && GLIB_CHECK_VERSION(2,6,0) /* FIXME: Not tested yet. */ + GKeyFile *key_file; + + gaim_debug(GAIM_DEBUG_INFO, "Trillian talk.ini read", + "Reading %s\n", path); + if (!g_key_file_load_from_file(key_file, path, G_KEY_FILE_NONE, GError &error)) { + gaim_debug(GAIM_DEBUG_ERROR, "Trillian talk.ini read", + "Error reading talk.ini\n"); + if (error) + g_error_free(error); + } else { + char *logdir = g_key_file_get_string(key_file, "Logging", "Directory", &error); + if (error) { + gaim_debug(GAIM_DEBUG_ERROR, "Trillian talk.ini read", + "Error reading Directory value from Logging section\n"); + g_error_free(error); + } + + if (logdir) { + g_strchomp(logdir); + gaim_prefs_add_string( + "/plugins/core/log_reader/trillian/log_directory", logdir); + found = TRUE; + } + + g_key_file_free(key_file); + } +#else /* !GLIB_CHECK_VERSION(2,6,0) */ + gsize length; + gchar *contents = NULL; + + gaim_debug(GAIM_DEBUG_INFO, "Trillian talk.ini read", + "Reading %s\n", path); + if (!g_file_get_contents(path, &contents, &length, &error)) { + gaim_debug(GAIM_DEBUG_ERROR, "Trillian talk.ini read", + "Error reading talk.ini\n"); + if (error) + g_error_free(error); + } else { + char *line = contents; + while (*contents) { + if (*contents == '\n') { + *contents = '\0'; + + /* XXX: This assumes the first Directory key is under [Logging]. */ + if (gaim_str_has_prefix(line, "Directory=")) { + line += (sizeof("Directory=") - 1); + g_strchomp(line); + gaim_prefs_add_string( + "/plugins/core/log_reader/trillian/log_directory", + line); + found = TRUE; + } + + contents++; + line = contents; + } else + contents++; + } + g_free(path); + g_free(contents); + } +#endif /* !GTK_CHECK_VERSION(2,6,0) */ + } /* path */ + + if (!found) { +#endif /* defined(_WIN32) */ + + /* Calculate default Trillian log directory. */ +#ifdef _WIN32 + folder = wgaim_get_special_folder(CSIDL_PROGRAM_FILES); + if (folder) { +#endif + path = g_build_filename( +#ifdef _WIN32 + folder, +#else + GAIM_LOG_READER_WINDOWS_MOUNT_POINT, "Program Files", +#endif + "Trillian", "users", "default", "logs", NULL); +#ifdef _WIN32 + g_free(folder); + } else /* !folder */ + path = g_strdup(""); +#endif + + gaim_prefs_add_string("/plugins/core/log_reader/trillian/log_directory", path); + g_free(path); + +#ifdef _WIN32 + } /* !found */ +#endif +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + g_return_val_if_fail(plugin != NULL, FALSE); + + /* The names of IM clients are marked for translation at the request of + translators who wanted to transliterate them. Many translators + choose to leave them alone. Choose what's best for your language. */ + adium_logger = gaim_log_logger_new("adium", _("Adium"), 6, + NULL, + NULL, + adium_logger_finalize, + adium_logger_list, + adium_logger_read, + adium_logger_size); + gaim_log_logger_add(adium_logger); + +#if 0 + /* The names of IM clients are marked for translation at the request of + translators who wanted to transliterate them. Many translators + choose to leave them alone. Choose what's best for your language. */ + fire_logger = gaim_log_logger_new("fire", _("Fire"), 6, + NULL, + NULL, + fire_logger_finalize, + fire_logger_list, + fire_logger_read, + fire_logger_size); + gaim_log_logger_add(fire_logger); + + /* The names of IM clients are marked for translation at the request of + translators who wanted to transliterate them. Many translators + choose to leave them alone. Choose what's best for your language. */ + messenger_plus_logger = gaim_log_logger_new("messenger_plus", _("Messenger Plus!"), 6, + NULL, + NULL, + messenger_plus_logger_finalize, + messenger_plus_logger_list, + messenger_plus_logger_read, + messenger_plus_logger_size); + gaim_log_logger_add(messenger_plus_logger); +#endif + + /* The names of IM clients are marked for translation at the request of + translators who wanted to transliterate them. Many translators + choose to leave them alone. Choose what's best for your language. */ + msn_logger = gaim_log_logger_new("msn", _("MSN Messenger"), 6, + NULL, + NULL, + msn_logger_finalize, + msn_logger_list, + msn_logger_read, + msn_logger_size); + gaim_log_logger_add(msn_logger); + + /* The names of IM clients are marked for translation at the request of + translators who wanted to transliterate them. Many translators + choose to leave them alone. Choose what's best for your language. */ + trillian_logger = gaim_log_logger_new("trillian", _("Trillian"), 6, + NULL, + NULL, + trillian_logger_finalize, + trillian_logger_list, + trillian_logger_read, + trillian_logger_size); + gaim_log_logger_add(trillian_logger); + + return TRUE; +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + g_return_val_if_fail(plugin != NULL, FALSE); + + gaim_log_logger_remove(adium_logger); +#if 0 + gaim_log_logger_remove(fire_logger); + gaim_log_logger_remove(messenger_plus_logger); +#endif + gaim_log_logger_remove(msn_logger); + gaim_log_logger_remove(trillian_logger); + + return TRUE; +} + +static GaimPluginPrefFrame * +get_plugin_pref_frame(GaimPlugin *plugin) +{ + GaimPluginPrefFrame *frame; + GaimPluginPref *ppref; + + g_return_val_if_fail(plugin != NULL, FALSE); + + frame = gaim_plugin_pref_frame_new(); + + + /* Add general preferences. */ + + ppref = gaim_plugin_pref_new_with_label(_("General Log Reading Configuration")); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/fast_sizes", _("Fast size calculations")); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/use_name_heuristics", _("Use name heuristics")); + gaim_plugin_pref_frame_add(frame, ppref); + + + /* Add Log Directory preferences. */ + + ppref = gaim_plugin_pref_new_with_label(_("Log Directory")); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/adium/log_directory", _("Adium")); + gaim_plugin_pref_frame_add(frame, ppref); + +#if 0 + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/fire/log_directory", _("Fire")); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/messenger_plus/log_directory", _("Messenger Plus!")); + gaim_plugin_pref_frame_add(frame, ppref); +#endif + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/msn/log_directory", _("MSN Messenger")); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/trillian/log_directory", _("Trillian")); + gaim_plugin_pref_frame_add(frame, ppref); + + return frame; +} + +static GaimPluginUiInfo prefs_info = { + get_plugin_pref_frame, + 0, /* page_num (reserved) */ + NULL /* frame (reserved) */ +}; + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + "core-log_reader", /**< id */ + N_("Log Reader"), /**< name */ + 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, and Trillian.\n\n" + "WARNING: This plugin is still alpha code and " + "may crash frequently. Use it at your own risk!"), + + "Richard Laager ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + NULL, /**< ui_info */ + NULL, /**< extra_info */ + &prefs_info, /**< prefs_info */ + NULL /**< actions */ +}; + +GAIM_INIT_PLUGIN(log_reader, init_plugin, info) diff --git a/libpurple/plugins/mono/BooPlugin.boo b/libpurple/plugins/mono/BooPlugin.boo new file mode 100644 index 0000000000..bae8579f74 --- /dev/null +++ b/libpurple/plugins/mono/BooPlugin.boo @@ -0,0 +1,22 @@ +import Gaim + +class BooPlugin(GaimPlugin): + + def handle(*args as (object)): + b as Buddy + b = args[0] + Debug.debug(Debug.INFO, "booplugin", "Boo Plugin knows that " + b.Alias + " is away\n") + + override def Load(): + Debug.debug(Debug.INFO, "booplugin", "loading...\n") + BuddyList.OnBuddyAway.connect(self, handle) + + override def Unload(): + Debug.debug(Debug.INFO, "booplugin", "unloading...\n") + + override def Destroy(): + Debug.debug(Debug.INFO, "booplugin", "destroying...\n") + + override def Info(): + return GaimPluginInfo("Boo Plugin", "0.1", "Test Boo Plugin", "Longer Description", "Eoin Coffey", "urled") + diff --git a/libpurple/plugins/mono/GetBuddyBack.cs b/libpurple/plugins/mono/GetBuddyBack.cs new file mode 100644 index 0000000000..09e667dd40 --- /dev/null +++ b/libpurple/plugins/mono/GetBuddyBack.cs @@ -0,0 +1,32 @@ +using Gaim; + +public class GetBuddyBack : GaimPlugin +{ + public void HandleSig(object[] args) + { + Buddy buddy = (Buddy)args[0]; + + Debug.debug(Debug.INFO, "buddyback", "buddy " + buddy.Name + " is back!\n"); + } + + public override void Load() + { + Debug.debug(Debug.INFO, "buddyback", "loading...\n"); + + /*Signal.connect(BuddyList.GetHandle(), this, "buddy-back", new Signal.Handler(HandleSig));*/ + /*BuddyList.OnBuddyBack.connect(this, new Signal.Handler(HandleSig));*/ + } + + public override void Unload() + { + } + + public override void Destroy() + { + } + + public override GaimPluginInfo Info() + { + return new GaimPluginInfo("C# Get Buddy Back", "0.1", "Prints when a Buddy returns", "Longer Description", "Eoin Coffey", "urled"); + } +} diff --git a/libpurple/plugins/mono/MPlugin.cs b/libpurple/plugins/mono/MPlugin.cs new file mode 100644 index 0000000000..0a6762d2e4 --- /dev/null +++ b/libpurple/plugins/mono/MPlugin.cs @@ -0,0 +1,36 @@ +using Gaim; + +public class MPlugin : GaimPlugin +{ + public void HandleSig(object[] args) + { + Buddy buddy = (Buddy)args[0]; + Status old_status = (Status)args[1]; + Status status = (Status)args[2]; + + Debug.debug(Debug.INFO, "mplug", "buddy " + buddy.Name + " went from " + old_status.Id + " to " + status.Id + "\n"); + } + + public override void Load() + { + Debug.debug(Debug.INFO, "mplug", "loading...\n"); + + /*Signal.connect(BuddyList.GetHandle(), this, "buddy-away", new Signal.Handler(HandleSig));*/ + BuddyList.OnBuddyStatusChanged.connect(this, new Signal.Handler(HandleSig)); + } + + public override void Unload() + { + Debug.debug(Debug.INFO, "mplug", "unloading...\n"); + } + + public override void Destroy() + { + Debug.debug(Debug.INFO, "mplug", "destroying...\n"); + } + + public override GaimPluginInfo Info() + { + return new GaimPluginInfo("C# Plugin", "0.1", "Test C# Plugin", "Longer Description", "Eoin Coffey", "urled"); + } +} diff --git a/libpurple/plugins/mono/Makefile.am b/libpurple/plugins/mono/Makefile.am new file mode 100644 index 0000000000..8fb0c75fcd --- /dev/null +++ b/libpurple/plugins/mono/Makefile.am @@ -0,0 +1,19 @@ +SUBDIRS = api loader + +mono_sources = GetBuddyBack.cs \ + MPlugin.cs + +EXTRA_DIST = $(mono_sources) + +monodir = $(libdir)/gaim +mono_SCRIPTS = MPlugin.dll GetBuddyBack.dll +mono_build_sources = $(addprefix $(srcdir)/, $(mono_sources)) + +all: $(mono_SCRIPTS) + +SUFFIXES = .cs .dll +.cs.dll: api/GaimAPI.dll $(mono_build_sources) + mcs -t:library -lib:./api -out:$@ -r:GaimAPI.dll $< + +clean-local: + rm -f $(mono_SCRIPTS) diff --git a/libpurple/plugins/mono/api/BlistNode.cs b/libpurple/plugins/mono/api/BlistNode.cs new file mode 100644 index 0000000000..a6108171a9 --- /dev/null +++ b/libpurple/plugins/mono/api/BlistNode.cs @@ -0,0 +1,4 @@ +namespace Gaim { + public abstract class BlistNode { + } +} diff --git a/libpurple/plugins/mono/api/Buddy.cs b/libpurple/plugins/mono/api/Buddy.cs new file mode 100644 index 0000000000..2e1287b337 --- /dev/null +++ b/libpurple/plugins/mono/api/Buddy.cs @@ -0,0 +1,9 @@ +namespace Gaim { + public class Buddy : BlistNode { + private string name; + private string alias; + + public string Name { get { return name; } set { name = value; } } + public string Alias { get { return alias; } set { alias = value; } } + } +} diff --git a/libpurple/plugins/mono/api/BuddyList.cs b/libpurple/plugins/mono/api/BuddyList.cs new file mode 100644 index 0000000000..384df53d58 --- /dev/null +++ b/libpurple/plugins/mono/api/BuddyList.cs @@ -0,0 +1,19 @@ +namespace Gaim { + using System; + using System.Runtime.CompilerServices; + + public class BuddyList { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern private static IntPtr _get_handle(); + + private static IntPtr handle = _get_handle(); + + public static Event OnBuddyStatusChanged = + new Event(handle, "buddy-status-changed"); + + public static IntPtr GetHandle() + { + return _get_handle(); + } + } +} diff --git a/libpurple/plugins/mono/api/Contact.cs b/libpurple/plugins/mono/api/Contact.cs new file mode 100644 index 0000000000..627eea2319 --- /dev/null +++ b/libpurple/plugins/mono/api/Contact.cs @@ -0,0 +1,4 @@ +namespace Gaim { + public class Contact : BlistNode { + } +} diff --git a/libpurple/plugins/mono/api/Debug.cs b/libpurple/plugins/mono/api/Debug.cs new file mode 100644 index 0000000000..fde8bb3f11 --- /dev/null +++ b/libpurple/plugins/mono/api/Debug.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Gaim +{ + public class Debug + { + public static int ALL = 0; + public static int MISC = 1; + public static int INFO = 2; + public static int WARNING = 3; + public static int ERROR = 4; + public static int FATAL = 5; + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern private static void _debug(int type, string cat, string str); + + public static void debug(int type, string cat, string format) + { + _debug(type, cat, format); + } + + public static void debug(int type, string cat, string format, params object[] args) + { + _debug(type, cat, String.Format(format, args)); + } + } +} diff --git a/libpurple/plugins/mono/api/Event.cs b/libpurple/plugins/mono/api/Event.cs new file mode 100644 index 0000000000..266ea12f37 --- /dev/null +++ b/libpurple/plugins/mono/api/Event.cs @@ -0,0 +1,21 @@ +using System; + +namespace Gaim +{ + public class Event + { + private IntPtr handle; + private string signal; + + public Event(IntPtr h, string s) + { + handle = h; + signal = s; + } + + public void connect(object plugin, Signal.Handler handler) + { + Signal.connect(handle, plugin, signal, handler); + } + } +} diff --git a/libpurple/plugins/mono/api/GaimPlugin.cs b/libpurple/plugins/mono/api/GaimPlugin.cs new file mode 100644 index 0000000000..89739ca418 --- /dev/null +++ b/libpurple/plugins/mono/api/GaimPlugin.cs @@ -0,0 +1,61 @@ +namespace Gaim { + public class PluginInfo { + private string name; + private string version; + private string summary; + private string description; + private string author; + private string homepage; + + public PluginInfo(string name, string version, string summary, + string description, string author, string homepage) + { + this.name = name; + this.version = version; + this.summary = summary; + this.description = description; + this.author = author; + this.homepage = homepage; + } + + public string Name { + get { return name; } + } + + public string Version { + get { return version; } + } + + public string Summary { + get { return summary; } + } + + public string Description { + get { return description; } + } + + public string Author { + get { return author; } + } + + public string Homepage { + get { return homepage; } + } + } + + abstract public class Plugin { + private PluginInfo info; + + public Plugin(PluginInfo info) { + this.info = info; + } + + public abstract void Load(); + public abstract void Unload(); + public abstract void Destroy(); + + public PluginInfo Info { + get { return info; } + } + } +} diff --git a/libpurple/plugins/mono/api/Group.cs b/libpurple/plugins/mono/api/Group.cs new file mode 100644 index 0000000000..87ce3b8230 --- /dev/null +++ b/libpurple/plugins/mono/api/Group.cs @@ -0,0 +1,4 @@ +namespace Gaim { + public class Group : BlistNode { + } +} diff --git a/libpurple/plugins/mono/api/Makefile.am b/libpurple/plugins/mono/api/Makefile.am new file mode 100644 index 0000000000..43bc5476cd --- /dev/null +++ b/libpurple/plugins/mono/api/Makefile.am @@ -0,0 +1,26 @@ +monodir=$(libdir)/gaim +mono_sources = \ + BlistNode.cs \ + BuddyList.cs \ + Buddy.cs \ + Contact.cs \ + Debug.cs \ + Event.cs \ + GaimPlugin.cs \ + Group.cs \ + Signal.cs \ + Status.cs + +EXTRA_DIST = $(mono_sources) + +mono_SCRIPTS = GaimAPI.dll + +mono_build_sources = $(addprefix $(srcdir)/, $(mono_sources)) + +all: $(mono_SCRIPTS) + +$(mono_SCRIPTS): $(mono_build_sources) + mcs -t:library -out:$(mono_SCRIPTS) $(mono_build_sources) + +clean-local: + rm -rf $(mono_SCRIPTS) diff --git a/libpurple/plugins/mono/api/Signal.cs b/libpurple/plugins/mono/api/Signal.cs new file mode 100644 index 0000000000..eeb1fab2d5 --- /dev/null +++ b/libpurple/plugins/mono/api/Signal.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Gaim +{ + public class Signal + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern private static int _connect(IntPtr handle, object plugin, string signal, object evnt); + + public delegate void Handler(object[] args); + + public static int connect(IntPtr handle, object plugin, string signal, object evnt) + { + return _connect(handle, plugin, signal, evnt); + } + } +} diff --git a/libpurple/plugins/mono/api/Status.cs b/libpurple/plugins/mono/api/Status.cs new file mode 100644 index 0000000000..a000fabf95 --- /dev/null +++ b/libpurple/plugins/mono/api/Status.cs @@ -0,0 +1,9 @@ +namespace Gaim +{ + public class Status + { + private string id; + + public string Id { get { return id; } set { id = value; } } + } +} diff --git a/libpurple/plugins/mono/loader/Makefile.am b/libpurple/plugins/mono/loader/Makefile.am new file mode 100644 index 0000000000..58a57f9362 --- /dev/null +++ b/libpurple/plugins/mono/loader/Makefile.am @@ -0,0 +1,25 @@ +plugindir = $(libdir)/gaim + +plugin_LTLIBRARIES = mono.la + +mono_la_SOURCES = \ + mono.c \ + mono-glue.h \ + mono-helper.c \ + mono-helper.h \ + debug-glue.c \ + signal-glue.c \ + blist-glue.c \ + status-glue.c + +mono_la_LDFLAGS = -module -avoid-version + +mono_la_LIBADD = $(MONO_LIBS) + +AM_CPPFLAGS = \ + -DVERSION=\"$(VERSION)\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libgaim \ + $(DEBUG_CFLAGS) \ + $(PLUGIN_CFLAGS) \ + $(MONO_CFLAGS) diff --git a/libpurple/plugins/mono/loader/blist-glue.c b/libpurple/plugins/mono/loader/blist-glue.c new file mode 100644 index 0000000000..a6673a2e95 --- /dev/null +++ b/libpurple/plugins/mono/loader/blist-glue.c @@ -0,0 +1,26 @@ +#include +#include "blist.h" +#include "mono-helper.h" +#include "mono-glue.h" + +MonoObject* gaim_blist_get_handle_glue(void) +{ + void *handle = gaim_blist_get_handle(); + + return mono_value_box(ml_get_domain(), mono_get_intptr_class(), &handle); +} + +MonoObject* gaim_blist_build_buddy_object(void* data) +{ + MonoObject *obj = NULL; + + GaimBuddy *buddy = (GaimBuddy*)data; + + obj = ml_create_api_object("Buddy"); + g_return_val_if_fail(obj != NULL, NULL); + + ml_set_prop_string(obj, "Name", (char*)gaim_buddy_get_name(buddy)); + ml_set_prop_string(obj, "Alias", (char*)gaim_buddy_get_alias(buddy)); + + return obj; +} diff --git a/libpurple/plugins/mono/loader/debug-glue.c b/libpurple/plugins/mono/loader/debug-glue.c new file mode 100644 index 0000000000..9d5107bad3 --- /dev/null +++ b/libpurple/plugins/mono/loader/debug-glue.c @@ -0,0 +1,16 @@ +#include "mono-glue.h" +#include "debug.h" + +void gaim_debug_glue(int type, MonoString *cat, MonoString *str) +{ + char *ccat; + char *cstr; + + ccat = mono_string_to_utf8(cat); + cstr = mono_string_to_utf8(str); + + gaim_debug(type, ccat, cstr); + + g_free(ccat); + g_free(cstr); +} diff --git a/libpurple/plugins/mono/loader/mono-glue.h b/libpurple/plugins/mono/loader/mono-glue.h new file mode 100644 index 0000000000..da0d37e988 --- /dev/null +++ b/libpurple/plugins/mono/loader/mono-glue.h @@ -0,0 +1,19 @@ +#ifndef _GAIM_MONO_LOADER_GLUE_H_ +#define _GAIM_MONO_LOADER_GLUE_H_ + +#include +#include +#include +#include + +void gaim_debug_glue(int type, MonoString *cat, MonoString *str); + +int gaim_signal_connect_glue(MonoObject *h, MonoObject *plugin, MonoString *signal, MonoObject *func); + +MonoObject* gaim_blist_get_handle_glue(void); + +MonoObject* gaim_blist_build_buddy_object(void* buddy); + +MonoObject* gaim_status_build_status_object(void* data); + +#endif diff --git a/libpurple/plugins/mono/loader/mono-helper.c b/libpurple/plugins/mono/loader/mono-helper.c new file mode 100644 index 0000000000..84f6200332 --- /dev/null +++ b/libpurple/plugins/mono/loader/mono-helper.c @@ -0,0 +1,251 @@ +/* + * Mono Plugin Loader + * + * -- Thanks to the perl plugin loader for all the great tips ;-) + * + * Eoin Coffey + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "mono-helper.h" +#include "mono-glue.h" +#include "value.h" +#include "debug.h" + +static gboolean _runtime_active = FALSE; + +gboolean ml_init() +{ + MonoDomain *d; + + g_return_val_if_fail(_runtime_active == FALSE, TRUE); + + d = mono_jit_init("gaim"); + + if (!d) { + ml_set_domain(NULL); + return FALSE; + } + + ml_set_domain(d); + + ml_init_internal_calls(); + + _runtime_active = TRUE; + + return TRUE; +} + +void ml_uninit() +{ + g_return_if_fail(_runtime_active == TRUE); + + mono_jit_cleanup(ml_get_domain()); + + ml_set_domain(NULL); + + _runtime_active = FALSE; +} + +MonoObject* ml_delegate_invoke(MonoObject *method, void **params) +{ + MonoObject *ret, *exception; + + ret = mono_runtime_delegate_invoke(method, params, &exception); + if (exception) { + gaim_debug(GAIM_DEBUG_ERROR, "mono", "caught exception: %s\n", mono_class_get_name(mono_object_get_class(exception))); + } + + return ret; +} + +MonoObject* ml_invoke(MonoMethod *method, void *obj, void **params) +{ + MonoObject *ret, *exception; + + ret = mono_runtime_invoke(method, obj, params, &exception); + if (exception) { + gaim_debug(GAIM_DEBUG_ERROR, "mono", "caught exception: %s\n", mono_class_get_name(mono_object_get_class(exception))); + } + + return ret; +} + +MonoClass* ml_find_plugin_class(MonoImage *image) +{ + MonoClass *klass, *pklass = NULL; + int i, total; + + total = mono_image_get_table_rows (image, MONO_TABLE_TYPEDEF); + for (i = 1; i <= total; ++i) { + klass = mono_class_get (image, MONO_TOKEN_TYPE_DEF | i); + pklass = mono_class_get_parent(klass); + if (pklass) + if (strcmp("GaimPlugin", mono_class_get_name(pklass)) == 0) + return klass; + } + + return NULL; +} + +void ml_set_prop_string(MonoObject *obj, char *field, char *data) +{ + MonoClass *klass; + MonoProperty *prop; + MonoString *str; + gpointer args[1]; + + klass = mono_object_get_class(obj); + + prop = mono_class_get_property_from_name(klass, field); + + str = mono_string_new(ml_get_domain(), data); + + args[0] = str; + + mono_property_set_value(prop, obj, args, NULL); +} + +gchar* ml_get_prop_string(MonoObject *obj, char *field) +{ + MonoClass *klass; + MonoProperty *prop; + MonoString *str; + + klass = mono_object_get_class(obj); + + prop = mono_class_get_property_from_name(klass, field); + + str = (MonoString*)mono_property_get_value(prop, obj, NULL, NULL); + + return mono_string_to_utf8(str); +} + +gboolean ml_is_api_dll(MonoImage *image) +{ + MonoClass *klass; + int i, total; + + total = mono_image_get_table_rows (image, MONO_TABLE_TYPEDEF); + for (i = 1; i <= total; ++i) { + klass = mono_class_get (image, MONO_TOKEN_TYPE_DEF | i); + if (strcmp(mono_class_get_name(klass), "Debug") == 0) + if (strcmp(mono_class_get_namespace(klass), "Gaim") == 0) { + ml_set_api_image(image); + return TRUE; + } + } + + return FALSE; +} + +MonoObject* ml_object_from_gaim_type(GaimType type, gpointer data) +{ + return NULL; +} + +MonoObject* ml_object_from_gaim_subtype(GaimSubType type, gpointer data) +{ + MonoObject *obj = NULL; + + switch (type) { + case GAIM_SUBTYPE_BLIST_BUDDY: + obj = gaim_blist_build_buddy_object(data); + break; + case GAIM_SUBTYPE_STATUS: + obj = gaim_status_build_status_object(data); + break; + default: + break; + } + + return obj; +} + +MonoObject* ml_create_api_object(char *class_name) +{ + MonoObject *obj = NULL; + MonoClass *klass = NULL; + + klass = mono_class_from_name(ml_get_api_image(), "Gaim", class_name); + if (!klass) { + gaim_debug(GAIM_DEBUG_FATAL, "mono", "couldn't find the '%s' class\n", class_name); + return NULL; + } + + obj = mono_object_new(ml_get_domain(), klass); + if (!obj) { + gaim_debug(GAIM_DEBUG_FATAL, "mono", "couldn't create the object from class '%s'\n", class_name); + return NULL; + } + + mono_runtime_object_init(obj); + + return obj; +} + +static MonoDomain *_domain = NULL; + +MonoDomain* ml_get_domain(void) +{ + return _domain; +} + +void ml_set_domain(MonoDomain *d) +{ + _domain = d; +} + +static MonoImage *_api_image = NULL; + +void ml_set_api_image(MonoImage *image) +{ + _api_image = image; +} + +MonoImage* ml_get_api_image() +{ + return _api_image; +} + +void ml_init_internal_calls(void) +{ + mono_add_internal_call("Gaim.Debug::_debug", gaim_debug_glue); + mono_add_internal_call("Gaim.Signal::_connect", gaim_signal_connect_glue); + mono_add_internal_call("Gaim.BuddyList::_get_handle", gaim_blist_get_handle_glue); +} + +static GHashTable *plugins_hash = NULL; + +void ml_add_plugin(GaimMonoPlugin *plugin) +{ + if (!plugins_hash) + plugins_hash = g_hash_table_new(NULL, NULL); + + g_hash_table_insert(plugins_hash, plugin->klass, plugin); +} + +gboolean ml_remove_plugin(GaimMonoPlugin *plugin) +{ + return g_hash_table_remove(plugins_hash, plugin->klass); +} + +gpointer ml_find_plugin(GaimMonoPlugin *plugin) +{ + return g_hash_table_lookup(plugins_hash, plugin->klass); +} + +gpointer ml_find_plugin_by_class(MonoClass *klass) +{ + return g_hash_table_lookup(plugins_hash, klass); +} + +GHashTable* ml_get_plugin_hash() +{ + return plugins_hash; +} diff --git a/libpurple/plugins/mono/loader/mono-helper.h b/libpurple/plugins/mono/loader/mono-helper.h new file mode 100644 index 0000000000..d2198d6123 --- /dev/null +++ b/libpurple/plugins/mono/loader/mono-helper.h @@ -0,0 +1,73 @@ +#ifndef _GAIM_MONO_LOADER_MONO_HELPER_H_ +#define _GAIM_MONO_LOADER_MONO_HELPER_H_ + +#include +#include +#include +#include +#include +#include +#include "plugin.h" +#include "value.h" +#include "debug.h" + +typedef struct { + GaimPlugin *plugin; + + MonoAssembly *assm; + MonoClass *klass; + MonoObject *obj; + + MonoMethod *init; + MonoMethod *load; + MonoMethod *unload; + MonoMethod *destroy; + + GList *signal_data; +} GaimMonoPlugin; + +gboolean ml_init(void); + +void ml_uninit(void); + +MonoObject* ml_invoke(MonoMethod *method, void *obj, void **params); + +MonoObject* ml_delegate_invoke(MonoObject *method, void **params); + +MonoClass* ml_find_plugin_class(MonoImage *image); + +gchar* ml_get_prop_string(MonoObject *obj, char *field); + +void ml_set_prop_string(MonoObject *obj, char *field, char *data); + +gboolean ml_is_api_dll(MonoImage *image); + +MonoDomain* ml_get_domain(void); + +void ml_set_domain(MonoDomain *d); + +void ml_init_internal_calls(void); + +MonoObject* ml_object_from_gaim_type(GaimType type, gpointer data); + +MonoObject* ml_object_from_gaim_subtype(GaimSubType type, gpointer data); + +MonoObject* ml_create_api_object(char *class_name); + +void ml_set_api_image(MonoImage *image); + +MonoImage* ml_get_api_image(void); + +/* hash table stuff; probably don't need it anymore */ + +void ml_add_plugin(GaimMonoPlugin *plugin); + +gboolean ml_remove_plugin(GaimMonoPlugin *plugin); + +gpointer ml_find_plugin(GaimMonoPlugin *plugin); + +gpointer ml_find_plugin_by_class(MonoClass *klass); + +GHashTable* ml_get_plugin_hash(void); + +#endif diff --git a/libpurple/plugins/mono/loader/mono.c b/libpurple/plugins/mono/loader/mono.c new file mode 100644 index 0000000000..4539065d3b --- /dev/null +++ b/libpurple/plugins/mono/loader/mono.c @@ -0,0 +1,235 @@ +/* + * Mono Plugin Loader + * + * -- Thanks to the perl plugin loader for all the great tips ;-) + * + * Eoin Coffey + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "internal.h" +#include "debug.h" +#include "plugin.h" +#include "version.h" +#include "mono-helper.h" + +#define MONO_PLUGIN_ID "core-mono" + +/****************************************************************************** + * Loader Stuff + *****************************************************************************/ +/* probes the given plugin to determine if its a plugin */ +static gboolean probe_mono_plugin(GaimPlugin *plugin) +{ + MonoAssembly *assm; + MonoMethod *m = NULL; + MonoMethod *info_method = NULL; + MonoObject *plugin_info; + gboolean found_load = FALSE, found_unload = FALSE, found_destroy = FALSE, found_info = FALSE; + gpointer iter = NULL; + + GaimPluginInfo *info; + GaimMonoPlugin *mplug; + + char *file = plugin->path; + + assm = mono_domain_assembly_open(ml_get_domain(), file); + + if (!assm) { + return FALSE; + } + + gaim_debug(GAIM_DEBUG_INFO, "mono", "Probing plugin\n"); + + if (ml_is_api_dll(mono_assembly_get_image(assm))) { + gaim_debug(GAIM_DEBUG_INFO, "mono", "Found our GaimAPI.dll\n"); + return FALSE; + } + + info = g_new0(GaimPluginInfo, 1); + mplug = g_new0(GaimMonoPlugin, 1); + + mplug->signal_data = NULL; + + mplug->assm = assm; + + mplug->klass = ml_find_plugin_class(mono_assembly_get_image(mplug->assm)); + if (!mplug->klass) { + gaim_debug(GAIM_DEBUG_ERROR, "mono", "no plugin class in \'%s\'\n", file); + return FALSE; + } + + mplug->obj = mono_object_new(ml_get_domain(), mplug->klass); + if (!mplug->obj) { + gaim_debug(GAIM_DEBUG_ERROR, "mono", "obj not valid\n"); + return FALSE; + } + + mono_runtime_object_init(mplug->obj); + + while ((m = mono_class_get_methods(mplug->klass, &iter))) { + if (strcmp(mono_method_get_name(m), "Load") == 0) { + mplug->load = m; + found_load = TRUE; + } else if (strcmp(mono_method_get_name(m), "Unload") == 0) { + mplug->unload = m; + found_unload = TRUE; + } else if (strcmp(mono_method_get_name(m), "Destroy") == 0) { + mplug->destroy = m; + found_destroy = TRUE; + } else if (strcmp(mono_method_get_name(m), "Info") == 0) { + info_method = m; + found_info = TRUE; + } + } + + if (!(found_load && found_unload && found_destroy && found_info)) { + gaim_debug(GAIM_DEBUG_ERROR, "mono", "did not find the required methods\n"); + return FALSE; + } + + plugin_info = ml_invoke(info_method, mplug->obj, NULL); + + /* now that the methods are filled out we can populate + the info struct with all the needed info */ + + info->name = ml_get_prop_string(plugin_info, "Name"); + info->version = ml_get_prop_string(plugin_info, "Version"); + info->summary = ml_get_prop_string(plugin_info, "Summary"); + info->description = ml_get_prop_string(plugin_info, "Description"); + info->author = ml_get_prop_string(plugin_info, "Author"); + info->homepage = ml_get_prop_string(plugin_info, "Homepage"); + + info->magic = GAIM_PLUGIN_MAGIC; + info->major_version = GAIM_MAJOR_VERSION; + info->minor_version = GAIM_MINOR_VERSION; + info->type = GAIM_PLUGIN_STANDARD; + + /* this plugin depends on us; duh */ + info->dependencies = g_list_append(info->dependencies, MONO_PLUGIN_ID); + mplug->plugin = plugin; + + plugin->info = info; + info->extra_info = mplug; + + ml_add_plugin(mplug); + + return gaim_plugin_register(plugin); +} + +/* Loads a Mono Plugin by calling 'load' in the class */ +static gboolean load_mono_plugin(GaimPlugin *plugin) +{ + GaimMonoPlugin *mplug; + + gaim_debug(GAIM_DEBUG_INFO, "mono", "Loading plugin\n"); + + mplug = (GaimMonoPlugin*)plugin->info->extra_info; + + ml_invoke(mplug->load, mplug->obj, NULL); + + return TRUE; +} + +/* Unloads a Mono Plugin by calling 'unload' in the class */ +static gboolean unload_mono_plugin(GaimPlugin *plugin) +{ + GaimMonoPlugin *mplug; + + gaim_debug(GAIM_DEBUG_INFO, "mono", "Unloading plugin\n"); + + mplug = (GaimMonoPlugin*)plugin->info->extra_info; + + gaim_signals_disconnect_by_handle((gpointer)mplug->klass); + g_list_foreach(mplug->signal_data, (GFunc)g_free, NULL); + g_list_free(mplug->signal_data); + mplug->signal_data = NULL; + + ml_invoke(mplug->unload, mplug->obj, NULL); + + return TRUE; +} + +static void destroy_mono_plugin(GaimPlugin *plugin) +{ + GaimMonoPlugin *mplug; + + gaim_debug(GAIM_DEBUG_INFO, "mono", "Destroying plugin\n"); + + mplug = (GaimMonoPlugin*)plugin->info->extra_info; + + ml_invoke(mplug->destroy, mplug->obj, NULL); + + if (plugin->info) { + g_free(plugin->info->name); + g_free(plugin->info->version); + g_free(plugin->info->summary); + g_free(plugin->info->description); + g_free(plugin->info->author); + g_free(plugin->info->homepage); + } + + if (mplug) { + if (mplug->assm) { + mono_assembly_close(mplug->assm); + } + + g_free(mplug); + mplug = NULL; + } +} + +/****************************************************************************** + * Plugin Stuff + *****************************************************************************/ +static void plugin_destroy(GaimPlugin *plugin) +{ + ml_uninit(); +} + +static GaimPluginLoaderInfo loader_info = +{ + NULL, + probe_mono_plugin, + load_mono_plugin, + unload_mono_plugin, + destroy_mono_plugin +}; + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_LOADER, + NULL, + 0, + NULL, + GAIM_PRIORITY_DEFAULT, + MONO_PLUGIN_ID, + N_("Mono Plugin Loader"), + VERSION, + N_("Loads .NET plugins with Mono."), + N_("Loads .NET plugins with Mono."), + "Eoin Coffey ", + GAIM_WEBSITE, + NULL, + NULL, + plugin_destroy, + NULL, + &loader_info, + NULL, + NULL +}; + +static void init_plugin(GaimPlugin *plugin) +{ + ml_init(); + + loader_info.exts = g_list_append(loader_info.exts, "dll"); +} + +GAIM_INIT_PLUGIN(mono, init_plugin, info) diff --git a/libpurple/plugins/mono/loader/signal-glue.c b/libpurple/plugins/mono/loader/signal-glue.c new file mode 100644 index 0000000000..49d13822a1 --- /dev/null +++ b/libpurple/plugins/mono/loader/signal-glue.c @@ -0,0 +1,139 @@ +#include "mono-glue.h" +#include "mono-helper.h" +#include "debug.h" +#include "blist.h" +#include "signals.h" +#include "value.h" + +typedef struct { + MonoObject *func; + char *signal; + GaimValue **values; + GaimValue *ret_value; + int num_vals; +} SignalData; + +static GaimCallback get_callback(SignalData *sig_data); + +static gpointer dispatch_callback(SignalData *sig_data, int num_vals, ...) +{ + MonoArray *array; + MonoObject *obj; + int i; + gpointer meth_args[1]; + gpointer gaim_obj; + + va_list args; + + va_start(args, num_vals); + + array = mono_array_new(ml_get_domain(), mono_get_object_class(), num_vals); + + for (i = 0; i < num_vals; i++) { + if (gaim_value_get_type(sig_data->values[i]) == GAIM_TYPE_SUBTYPE) { + gaim_obj = va_arg(args, gpointer); + obj = ml_object_from_gaim_subtype(gaim_value_get_subtype(sig_data->values[i]), gaim_obj); + mono_array_set(array, MonoObject*, i, obj); + } else { + gaim_obj = va_arg(args, gpointer); + obj = ml_object_from_gaim_type(gaim_value_get_type(sig_data->values[i]), gaim_obj); + mono_array_set(array, MonoObject*, i, obj); + } + } + + va_end(args); + + meth_args[0] = array; + + return ml_delegate_invoke(sig_data->func, meth_args); +} + +static void cb_void__pointer(void *arg1, void *data) +{ + dispatch_callback((SignalData*)data, ((SignalData*)data)->num_vals, arg1); +} + +static void cb_void__pointer_pointer_pointer(void *arg1, void *arg2, void *arg3, void *data) +{ + dispatch_callback((SignalData*)data, ((SignalData*)data)->num_vals, arg1, arg2, arg3); +} + + +int gaim_signal_connect_glue(MonoObject* h, MonoObject *plugin, MonoString *signal, MonoObject *func) +{ + char *sig; + void **instance = NULL; + SignalData *sig_data; + GaimMonoPlugin *mplug; + MonoClass *klass; + + sig = mono_string_to_utf8(signal); + gaim_debug(GAIM_DEBUG_INFO, "mono", "connecting signal: %s\n", sig); + + instance = (void*)mono_object_unbox(h); + + sig_data = g_new0(SignalData, 1); + + sig_data->func = func; + sig_data->signal = sig; + + gaim_signal_get_values(*instance, sig, &sig_data->ret_value, &sig_data->num_vals, &sig_data->values); + + klass = mono_object_get_class(plugin); + + mplug = ml_find_plugin_by_class(klass); + + mplug->signal_data = g_list_append(mplug->signal_data, (gpointer)sig_data); + + return gaim_signal_connect(*instance, sig, (gpointer)klass, get_callback(sig_data), (gpointer)sig_data); +} + +static int determine_index(GaimType type) +{ + switch (type) { + case GAIM_TYPE_SUBTYPE: + case GAIM_TYPE_STRING: + case GAIM_TYPE_OBJECT: + case GAIM_TYPE_POINTER: + case GAIM_TYPE_BOXED: + return 1; + break; + default: + return type; + break; + } +} + +static gpointer callbacks[]= { + NULL, + cb_void__pointer, + NULL, + cb_void__pointer_pointer_pointer + }; + +static int callbacks_array_size = sizeof(callbacks) / sizeof(GaimCallback); + + +static GaimCallback get_callback(SignalData *sig_data) +{ + int i, index = 0; + + if (sig_data->ret_value == NULL) + index = 0; + else + index = determine_index(gaim_value_get_type(sig_data->ret_value)); + + for (i = 0; i < sig_data->num_vals; i++) { + index += determine_index(gaim_value_get_type(sig_data->values[i])); + } + + gaim_debug(GAIM_DEBUG_INFO, "mono", "get_callback index = %d\n", index); + + if (index >= callbacks_array_size || callbacks[index] == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "mono", "couldn't find a callback function for signal: %s\n", sig_data->signal); + return NULL; + } + + gaim_debug(GAIM_DEBUG_MISC, "mono", "using callback at index: %d\n", index); + return GAIM_CALLBACK(callbacks[index]); +} diff --git a/libpurple/plugins/mono/loader/status-glue.c b/libpurple/plugins/mono/loader/status-glue.c new file mode 100644 index 0000000000..342f6ecc0b --- /dev/null +++ b/libpurple/plugins/mono/loader/status-glue.c @@ -0,0 +1,16 @@ +#include "status.h" +#include "mono-helper.h" +#include "mono-glue.h" + +MonoObject* gaim_status_build_status_object(void* data) +{ + MonoObject *obj = NULL; + GaimStatus *status = (GaimStatus*)data; + + obj = ml_create_api_object("Status"); + g_return_val_if_fail(obj != NULL, NULL); + + ml_set_prop_string(obj, "Id", (char*)gaim_status_get_id(status)); + + return obj; +} diff --git a/libpurple/plugins/newline.c b/libpurple/plugins/newline.c new file mode 100644 index 0000000000..a3dbf3ef41 --- /dev/null +++ b/libpurple/plugins/newline.c @@ -0,0 +1,91 @@ +/* + * Displays messages on a new line, below the nick + * Copyright (C) 2004 Stu Tomlinson + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include "internal.h" + +#include + +#include +#include +#include +#include +#include +#include + +static gboolean +addnewline_msg_cb(GaimAccount *account, char *sender, char **message, + GaimConversation *conv, int *flags, void *data) +{ + if (g_ascii_strncasecmp(*message, "/me ", strlen("/me "))) { + char *tmp = g_strdup_printf("\n%s", *message); + g_free(*message); + *message = tmp; + } + + return FALSE; +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + void *conversation = gaim_conversations_get_handle(); + + gaim_signal_connect(conversation, "writing-im-msg", + plugin, GAIM_CALLBACK(addnewline_msg_cb), NULL); + gaim_signal_connect(conversation, "writing-chat-msg", + plugin, GAIM_CALLBACK(addnewline_msg_cb), NULL); + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, /**< magic */ + GAIM_MAJOR_VERSION, /**< major version */ + GAIM_MINOR_VERSION, /**< minor version */ + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + "core-plugin_pack-newline", /**< id */ + N_("New Line"), /**< name */ + 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 " + "screen name in the conversation window."), /** description */ + "Stu Tomlinson ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, /**< prefs_info */ + NULL /**< actions */ +}; + +static void +init_plugin(GaimPlugin *plugin) { +} + +GAIM_INIT_PLUGIN(lastseen, init_plugin, info) diff --git a/libpurple/plugins/offlinemsg.c b/libpurple/plugins/offlinemsg.c new file mode 100644 index 0000000000..976f3799e2 --- /dev/null +++ b/libpurple/plugins/offlinemsg.c @@ -0,0 +1,239 @@ +/* + * Offline Message Emulation - Save messages sent to an offline user as pounce + * Copyright (C) 2004 + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include "internal.h" + +#define PLUGIN_ID "core-plugin_pack-offlinemsg" +#define PLUGIN_NAME N_("Offline Message Emulation") +#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 " + +/* Gaim headers */ +#include + +#include +#include +#include +#include +#include +#include + +#define PREF_PREFIX "/plugins/core/" PLUGIN_ID +#define PREF_ALWAYS PREF_PREFIX "/always" + +typedef struct _OfflineMsg OfflineMsg; + +typedef enum +{ + OFFLINE_MSG_NONE, + OFFLINE_MSG_YES, + OFFLINE_MSG_NO +} OfflineMessageSetting; + +struct _OfflineMsg +{ + GaimAccount *account; + GaimConversation *conv; + char *who; + char *message; +}; + +static void +discard_data(OfflineMsg *offline) +{ + g_free(offline->who); + g_free(offline->message); + g_free(offline); +} + +static void +cancel_poune(OfflineMsg *offline) +{ + gaim_conversation_set_data(offline->conv, "plugin_pack:offlinemsg", + GINT_TO_POINTER(OFFLINE_MSG_NO)); + gaim_conv_im_send_with_flags(GAIM_CONV_IM(offline->conv), offline->message, 0); + discard_data(offline); +} + +static void +record_pounce(OfflineMsg *offline) +{ + GaimPounce *pounce; + GaimPounceEvent event; + GaimPounceOption option; + GaimConversation *conv; + + event = GAIM_POUNCE_SIGNON; + option = GAIM_POUNCE_OPTION_NONE; + + pounce = gaim_pounce_new(gaim_core_get_ui(), offline->account, offline->who, + event, option); + + gaim_pounce_action_set_enabled(pounce, "send-message", TRUE); + gaim_pounce_action_set_attribute(pounce, "send-message", "message", offline->message); + + conv = offline->conv; + if (!gaim_conversation_get_data(conv, "plugin_pack:offlinemsg")) + gaim_conversation_write(conv, NULL, _("The rest of the messages will be saved " + "as pounce. You can edit/delete the pounce from the `Buddy " + "Pounce' dialog."), + GAIM_MESSAGE_SYSTEM, time(NULL)); + gaim_conversation_set_data(conv, "plugin_pack:offlinemsg", + GINT_TO_POINTER(OFFLINE_MSG_YES)); + + gaim_conv_im_write(GAIM_CONV_IM(conv), offline->who, offline->message, + GAIM_MESSAGE_SEND, time(NULL)); + + discard_data(offline); +} + +static void +sending_msg_cb(GaimAccount *account, const char *who, char **message, gpointer handle) +{ + GaimBuddy *buddy; + OfflineMsg *offline; + GaimConversation *conv; + OfflineMessageSetting setting; + + buddy = gaim_find_buddy(account, who); + if (!buddy) + return; + + if (gaim_presence_is_online(gaim_buddy_get_presence(buddy))) + return; + + if (gaim_account_supports_offline_message(account, buddy)) + { + gaim_debug_info("offlinemsg", "Account \"%s\" supports offline message.", + gaim_account_get_username(account)); + return; + } + + conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, + who, account); + + if (!conv) + return; + + setting = GPOINTER_TO_INT(gaim_conversation_get_data(conv, "plugin_pack:offlinemsg")); + if (setting == OFFLINE_MSG_NO) + return; + + offline = g_new0(OfflineMsg, 1); + offline->conv = conv; + offline->account = account; + offline->who = g_strdup(who); + offline->message = *message; + *message = NULL; + + if (gaim_prefs_get_bool(PREF_ALWAYS) || setting == OFFLINE_MSG_YES) + record_pounce(offline); + else if (setting == OFFLINE_MSG_NONE) + { + char *ask; + ask = g_strdup_printf(_("\"%s\" is currently offline. Do you want to save the " + "rest of the messages in a pounce and automatically send them " + "when \"%s\" logs back in?"), who, who); + + gaim_request_action(handle, _("Offline Message"), ask, + _("You can edit/delete the pounce from the `Buddy Pounces' dialog"), + 1, offline, 2, + _("Yes"), record_pounce, + _("No"), cancel_poune); + g_free(ask); + } +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + gaim_signal_connect(gaim_conversations_get_handle(), "sending-im-msg", + plugin, GAIM_CALLBACK(sending_msg_cb), plugin); + return TRUE; +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + return TRUE; +} + +static GaimPluginPrefFrame * +get_plugin_pref_frame(GaimPlugin *plugin) +{ + GaimPluginPrefFrame *frame; + GaimPluginPref *pref; + + frame = gaim_plugin_pref_frame_new(); + + pref = gaim_plugin_pref_new_with_label(_("Save offline messages in pounce")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name_and_label(PREF_ALWAYS, + _("Do not ask. Always save in pounce.")); + gaim_plugin_pref_frame_add(frame, pref); + + return frame; +} + +static GaimPluginUiInfo prefs_info = { + get_plugin_pref_frame, + 0, + NULL +}; + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, /* Magic */ + GAIM_MAJOR_VERSION, /* Gaim Major Version */ + GAIM_MINOR_VERSION, /* Gaim Minor Version */ + GAIM_PLUGIN_STANDARD, /* plugin type */ + NULL, /* ui requirement */ + 0, /* flags */ + NULL, /* dependencies */ + GAIM_PRIORITY_DEFAULT, /* priority */ + + PLUGIN_ID, /* plugin id */ + PLUGIN_NAME, /* name */ + VERSION, /* version */ + PLUGIN_SUMMARY, /* summary */ + PLUGIN_DESCRIPTION, /* description */ + PLUGIN_AUTHOR, /* author */ + GAIM_WEBSITE, /* website */ + + plugin_load, /* load */ + plugin_unload, /* unload */ + NULL, /* destroy */ + + NULL, /* ui_info */ + NULL, /* extra_info */ + &prefs_info, /* prefs_info */ + NULL /* actions */ +}; + +static void +init_plugin(GaimPlugin *plugin) +{ + gaim_prefs_add_none(PREF_PREFIX); + gaim_prefs_add_bool(PREF_ALWAYS, FALSE); +} + +GAIM_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) diff --git a/libpurple/plugins/perl/Makefile.am b/libpurple/plugins/perl/Makefile.am new file mode 100644 index 0000000000..e2d3e3e399 --- /dev/null +++ b/libpurple/plugins/perl/Makefile.am @@ -0,0 +1,168 @@ +plugindir = $(libdir)/gaim +hackdir = $(plugindir)/private + +perl_dirs = common + +plugin_LTLIBRARIES = perl.la +hack_LTLIBRARIES = libgaimperl.la + +perl_la_LDFLAGS = -module -avoid-version +perl_la_LIBADD = $(GLIB_LIBS) $(PERL_LIBS) libgaimperl.la +perl_la_SOURCES = \ + perl.c \ + perl-common.c \ + perl-common.h \ + perl-handlers.c \ + perl-handlers.h + +perl_la_DEPENDENCIES = \ + .libs/libperl_orig.a \ + .libs/DynaLoader.a \ + libgaimperl.la + +libgaimperl_la_LDFLAGS = -module -avoid-version +libgaimperl_la_LIBADD = $(GLIB_LIBS) +libgaimperl_la_SOURCES = libgaimperl.c + +.libs/libperl_orig.a: + @mkdir -p .libs + @rm -f .libs/libperl_orig.a + @if [ x$(LIBPERL_A) = x ]; then \ + touch .libs/libperl_orig.a; \ + else \ + $(LN_S) $(LIBPERL_A) .libs/libperl_orig.a; \ + fi + +.libs/DynaLoader.a: + @mkdir -p .libs + @rm -f .libs/DynaLoader.a + @if [ x$(DYNALOADER_A) = x ]; then \ + touch .libs/DynaLoader.a; \ + else \ + $(LN_S) $(DYNALOADER_A) .libs/DynaLoader.a; \ + fi + + +common_sources = \ + common/Account.xs \ + common/AccountOpts.xs \ + common/BuddyIcon.xs \ + common/BuddyList.xs \ + common/Cipher.xs \ + common/Cmds.xs \ + common/Core.xs \ + common/Connection.xs \ + common/Conversation.xs \ + common/Debug.xs \ + common/FT.xs \ + common/Gaim.pm \ + common/Gaim.xs \ + common/ImgStore.xs \ + common/Log.xs \ + common/Makefile.PL.in \ + common/Network.xs \ + common/Notify.xs \ + common/Plugin.xs \ + common/PluginPref.xs \ + common/Pounce.xs \ + common/Prefs.xs \ + common/Privacy.xs \ + common/Proxy.xs \ + common/Prpl.xs \ + common/Request.xs \ + common/Roomlist.xs \ + common/SSLConn.xs \ + common/SavedStatuses.xs \ + common/Server.xs \ + common/Signal.xs \ + common/Sound.xs \ + common/Status.xs \ + common/Stringref.xs \ + common/Util.xs \ + common/XMLNode.xs \ + common/fallback/const-c.inc \ + common/fallback/const-xs.inc \ + common/module.h \ + common/typemap + + +EXTRA_DIST = \ + Makefile.mingw \ + common/Makefile.mingw \ + $(common_sources) \ + libgaimperl.c + +common/Makefile: common/Makefile.PL + @if test "x${top_srcdir}" != "x${top_builddir}"; then \ + for f in ${common_sources}; do \ + ${LN_S} -f ../${srcdir}/$$f $$f; \ + done; \ + fi + @cd common && $(perlpath) Makefile.PL $(PERL_MM_PARAMS) + +common/Makefile.PL: common/Makefile.PL.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) + +all-local: common/Makefile + @for dir in $(perl_dirs); do \ + cd $$dir && \ + if [ ! -f Makefile ]; then \ + $(perlpath) Makefile.PL $(PERL_MM_PARAMS); \ + fi && \ + ($(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS) || \ + $(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS)) && \ + cd ..; \ + done + +install-exec-local: + @for dir in $(perl_dirs); do \ + cd $$dir; \ + $(MAKE) install; \ + cd ..; \ + done + +# Evil Hack (TM) +# ... which doesn't work with DESTDIR installs. FIXME? +uninstall-local: + @for dir in $(perl_dirs); do \ + cd $$dir && \ + `$(MAKE) uninstall | grep unlink | sed -e 's#/usr#${prefix}#' -e 's#unlink#rm -f#'` && \ + cd ..; \ + done + +clean-generic: + @for dir in $(perl_dirs); do \ + cd $$dir; \ + $(MAKE) clean; \ + cd ..; \ + done + rm -f *.so + +distclean-generic: + @for dir in $(perl_dirs); do \ + cd $$dir; \ + $(MAKE) realclean; \ + rm -f Makefile.PL; \ + rm -f Makefile.old; \ + rm -f Makefile; \ + cd ..; \ + done + + @rm -f Makefile + @rm -f common/const-c.inc common/const-xs.inc + + @if test "x${top_srcdir}" != "x${top_builddir}"; then \ + for f in ${common_sources}; do \ + ${LN_S} -f ../${srcdir}/$$f $$f; \ + done; \ + fi + + +AM_CPPFLAGS = \ + -DVERSION=\"$(VERSION)\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libpurple \ + $(DEBUG_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(PLUGIN_CFLAGS) \ + $(PERL_CFLAGS) diff --git a/libpurple/plugins/perl/Makefile.mingw b/libpurple/plugins/perl/Makefile.mingw new file mode 100755 index 0000000000..91d9f330dc --- /dev/null +++ b/libpurple/plugins/perl/Makefile.mingw @@ -0,0 +1,82 @@ +# +# Makefile.mingw +# +# Description: Makefile for perl plugin loader plugin. +# + +GAIM_TOP := ../../.. +include $(GAIM_TOP)/libgaim/win32/global.mak + +TARGET = perl + +# Perl headers with /* /* */ type comments.. Turn off warnings. +CFLAGS += -Wno-comment + +## +## INCLUDE PATHS +## +INCLUDE_PATHS += -I. \ + -I$(GAIM_TOP) \ + -I$(GAIM_LIB_TOP) \ + -I$(GAIM_LIB_TOP)/win32 \ + -I$(GTK_TOP)/include \ + -I$(GTK_TOP)/include/glib-2.0 \ + -I$(GTK_TOP)/lib/glib-2.0/include \ + -I$(PERL_LIB_TOP)/CORE + +LIB_PATHS = -L$(GTK_TOP)/lib \ + -L$(GAIM_LIB_TOP) \ + -L$(PERL_LIB_TOP) + +## +## SOURCES, OBJECTS +## +C_SRC = perl.c \ + perl-common.c \ + perl-handlers.c + +OBJECTS = $(C_SRC:%.c=%.o) + +## +## LIBRARIES +## +LIBS = \ + -lglib-2.0 \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lws2_32 \ + -lintl \ + -lgaim \ + -lperl58 + +include $(GAIM_COMMON_RULES) + +## +## TARGET DEFINITIONS +## +.PHONY: all install clean + +all: $(TARGET).dll + $(MAKE) -C ./common -f $(GAIM_WIN32_MAKEFILE) + +install: all $(GAIM_INSTALL_PLUGINS_DIR) + cp $(TARGET).dll $(GAIM_INSTALL_PLUGINS_DIR) + $(MAKE) -C ./common -f $(GAIM_WIN32_MAKEFILE) install + +$(OBJECTS): $(GAIM_CONFIG_H) + +## +## BUILD DLL +## +$(TARGET).dll $(TARGET).dll.a: $(GAIM_LIBGAIM_DLL).a $(OBJECTS) + $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--export-all-symbols -Wl,--out-implib,$(TARGET).dll.a -o $(TARGET).dll + +## +## CLEAN RULES +## +clean: + rm -rf $(OBJECTS) + rm -rf $(TARGET).dll $(TARGET).dll.a + $(MAKE) -C ./common -f $(GAIM_WIN32_MAKEFILE) clean + +include $(GAIM_COMMON_TARGETS) diff --git a/libpurple/plugins/perl/common/Account.xs b/libpurple/plugins/perl/common/Account.xs new file mode 100644 index 0000000000..c419fc5a82 --- /dev/null +++ b/libpurple/plugins/perl/common/Account.xs @@ -0,0 +1,326 @@ +#include "module.h" + +MODULE = Gaim::Account PACKAGE = Gaim::Account PREFIX = gaim_account_ +PROTOTYPES: ENABLE + +Gaim::Presence +gaim_account_get_presence(account) + Gaim::Account account + +Gaim::Account +gaim_account_new(class, username, protocol_id) + const char * username + const char * protocol_id + C_ARGS: + username, protocol_id + +void +gaim_account_destroy(account) + Gaim::Account account + +void +gaim_account_connect(account) + Gaim::Account account + +void +gaim_account_register(account) + Gaim::Account account + +void +gaim_account_disconnect(account) + Gaim::Account account + +void +gaim_account_request_change_password(account) + Gaim::Account account + +void +gaim_account_request_change_user_info(account) + Gaim::Account account + +void +gaim_account_set_username(account, username) + Gaim::Account account + const char * username + +void +gaim_account_set_password(account, password) + Gaim::Account account + const char * password + +void +gaim_account_set_alias(account, alias) + Gaim::Account account + const char * alias + +void +gaim_account_set_user_info(account, user_info) + Gaim::Account account + const char *user_info + +void +gaim_account_set_buddy_icon(account, icon) + Gaim::Account account + const char *icon + +void +gaim_account_set_connection(account, gc) + Gaim::Account account + Gaim::Connection gc + +void +gaim_account_set_remember_password(account, value) + Gaim::Account account + gboolean value + +void +gaim_account_set_check_mail(account, value) + Gaim::Account account + gboolean value + +void gaim_account_set_enabled(account, ui, value) + Gaim::Account account + const char *ui + gboolean value + +void +gaim_account_set_proxy_info(account, info) + Gaim::Account account + Gaim::ProxyInfo info + +void +gaim_account_set_status(account, status_id, active) + Gaim::Account account + const char *status_id + gboolean active +CODE: + gaim_account_set_status(account, status_id, active, NULL); + +void +gaim_account_set_status_types(account, status_types) + Gaim::Account account + SV * status_types +PREINIT: + GList *t_GL; + int i, t_len; +PPCODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(status_types)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(status_types), i, 0), t_sl)); + } + gaim_account_set_status_types(account, t_GL); + +void +gaim_account_clear_settings(account) + Gaim::Account account + +void +gaim_account_set_int(account, name, value) + Gaim::Account account + const char *name + int value + +gboolean +gaim_account_is_connected(account) + Gaim::Account account + +const char * +gaim_account_get_username(account) + Gaim::Account account + +const char * +gaim_account_get_password(account) + Gaim::Account account + +const char * +gaim_account_get_alias(account) + Gaim::Account account + +const char * +gaim_account_get_user_info(account) + Gaim::Account account + +const char * +gaim_account_get_buddy_icon(account) + Gaim::Account account + +const char * +gaim_account_get_protocol_id(account) + Gaim::Account account + +const char * +gaim_account_get_protocol_name(account) + Gaim::Account account + +Gaim::Connection +gaim_account_get_connection(account) + Gaim::Account account + +gboolean +gaim_account_get_remember_password(account) + Gaim::Account account + +gboolean +gaim_account_get_check_mail(account) + Gaim::Account account + +gboolean +gaim_account_get_enabled(account, ui) + Gaim::Account account + const char *ui + +Gaim::ProxyInfo +gaim_account_get_proxy_info(account) + Gaim::Account account + +Gaim::Status +gaim_account_get_active_status(account) + Gaim::Account account + +void +gaim_account_get_status_types(account) + Gaim::Account account +PREINIT: + const GList *l; +PPCODE: + for (l = gaim_account_get_status_types(account); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::StatusType"))); + } + +Gaim::Log +gaim_account_get_log(account, create) + Gaim::Account account + gboolean create + +void +gaim_account_destroy_log(account) + Gaim::Account account + +void +gaim_account_add_buddies(account, list) + Gaim::Account account + SV * list +PREINIT: + GList *t_GL; + int i, t_len; +PPCODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(list)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(list), i, 0), t_sl)); + } + gaim_account_add_buddies(account, t_GL); + +void +gaim_account_add_buddy(account, buddy) + Gaim::Account account + Gaim::BuddyList::Buddy buddy + +void +gaim_account_change_password(account, a, b) + Gaim::Account account + const char * a + const char * b + +void +gaim_account_remove_buddies(account, A, B) + Gaim::Account account + SV * A + SV * B +PREINIT: + GList *t_GL1, *t_GL2; + int i, t_len; +PPCODE: + t_GL1 = NULL; + t_len = av_len((AV *)SvRV(A)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL1 = g_list_append(t_GL1, SvPV(*av_fetch((AV *)SvRV(A), i, 0), t_sl)); + } + + t_GL2 = NULL; + t_len = av_len((AV *)SvRV(B)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL2 = g_list_append(t_GL2, SvPV(*av_fetch((AV *)SvRV(B), i, 0), t_sl)); + } + gaim_account_remove_buddies(account, t_GL1, t_GL2); + +void +gaim_account_remove_buddy(account, buddy, group) + Gaim::Account account + Gaim::BuddyList::Buddy buddy + Gaim::BuddyList::Group group + +void +gaim_account_remove_group(account, group) + Gaim::Account account + Gaim::BuddyList::Group group + +MODULE = Gaim::Account PACKAGE = Gaim::Accounts PREFIX = gaim_accounts_ +PROTOTYPES: ENABLE + +void +gaim_accounts_add(account) + Gaim::Account account + +void +gaim_accounts_remove(account) + Gaim::Account account + +void +gaim_accounts_delete(account) + Gaim::Account account + +void +gaim_accounts_reorder(account, new_index) + Gaim::Account account + size_t new_index + +void +gaim_accounts_get_all() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_accounts_get_all(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Account"))); + } + +void +gaim_accounts_get_all_active() +PREINIT: + GList *list, *iter; +PPCODE: + list = gaim_accounts_get_all_active(); + for (iter = list; iter != NULL; iter = iter->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(iter->data, "Gaim::Account"))); + } + g_list_free(list); + +Gaim::Account +gaim_accounts_find(name, protocol) + const char * name + const char * protocol + +void +gaim_accounts_set_ui_ops(ops) + Gaim::Account::UiOps ops + +Gaim::Account::UiOps +gaim_accounts_get_ui_ops() + +Gaim::Handle +gaim_accounts_get_handle() + +void +gaim_accounts_init() + +void +gaim_accounts_uninit() diff --git a/libpurple/plugins/perl/common/AccountOpts.xs b/libpurple/plugins/perl/common/AccountOpts.xs new file mode 100644 index 0000000000..a9a4eee9e0 --- /dev/null +++ b/libpurple/plugins/perl/common/AccountOpts.xs @@ -0,0 +1,168 @@ +#include "module.h" + +MODULE = Gaim::Account::Option PACKAGE = Gaim::Account::Option PREFIX = gaim_account_option_ +PROTOTYPES: ENABLE + +void +gaim_account_option_destroy(option) + Gaim::Account::Option option + +const char * +gaim_account_option_get_default_string(option) + Gaim::Account::Option option + +void +gaim_account_option_add_list_item(option, key, value) + Gaim::Account::Option option + const char * key + const char * value + +void +gaim_account_option_set_default_string(option, value); + Gaim::Account::Option option + const char * value + +void +gaim_account_option_set_default_int(option, value); + Gaim::Account::Option option + int value + +void +gaim_account_option_set_default_bool(option, value); + Gaim::Account::Option option + gboolean value + +Gaim::Account::Option +gaim_account_option_list_new(class, text, pref_name, values) + const char * text + const char * pref_name + SV * values +PREINIT: + GList *t_GL; + int i, t_len; +CODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(values)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(values), i, 0), t_sl)); + } + RETVAL = gaim_account_option_list_new(text, pref_name, t_GL); +OUTPUT: + RETVAL + +Gaim::Account::Option +gaim_account_option_string_new(class, text, pref_name, default_value) + const char * text + const char * pref_name + const char * default_value + C_ARGS: + text, pref_name, default_value + +Gaim::Account::Option +gaim_account_option_int_new(class, text, pref_name, default_value) + const char * text + const char * pref_name + gboolean default_value + C_ARGS: + text, pref_name, default_value + +Gaim::Account::Option +gaim_account_option_bool_new(class, text, pref_name, default_value) + const char * text + const char * pref_name + gboolean default_value + C_ARGS: + text, pref_name, default_value + +Gaim::Account::Option +gaim_account_option_new(class, type, text, pref_name) + Gaim::PrefType type + const char * text + const char * pref_name + C_ARGS: + type, text, pref_name + +void +gaim_account_option_get_list(option) + Gaim::Account::Option option +PREINIT: + const GList *l; +PPCODE: + for (l = gaim_account_option_get_list(option); l != NULL; l = l->next) { + /* XXX These are actually GaimKeyValuePairs but we don't have a + * type for that and even if we did I don't think there's + * anything perl could do with them, so I'm just going to + * leave this as a Gaim::ListEntry for now. */ + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::ListEntry"))); + } + +Gaim::PrefType +gaim_account_option_get_type(option) + Gaim::Account::Option option + +gboolean +gaim_account_option_get_masked(option) + Gaim::Account::Option option + +int +gaim_account_option_get_default_int(option) + Gaim::Account::Option option; + +gboolean +gaim_account_option_get_default_bool(option) + Gaim::Account::Option option; + +const char * +gaim_account_option_get_setting(option) + Gaim::Account::Option option + +const char * +gaim_account_option_get_text(option) + Gaim::Account::Option option + +void +gaim_account_option_set_list(option, values) + Gaim::Account::Option option + SV * values +PREINIT: + GList *t_GL; + int i, t_len; +PPCODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(values)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(values), i, 0), t_sl)); + } + gaim_account_option_set_list(option, t_GL); + +void +gaim_account_option_set_masked(option, masked) + Gaim::Account::Option option + gboolean masked + +MODULE = Gaim::Account::Option PACKAGE = Gaim::Account::UserSplit PREFIX = gaim_account_user_split_ +PROTOTYPES: ENABLE + +Gaim::Account::UserSplit +gaim_account_user_split_new(class, text, default_value, sep) + const char * text + const char * default_value + char sep + C_ARGS: + text, default_value, sep + +char +gaim_account_user_split_get_separator(split) + Gaim::Account::UserSplit split + +const char * +gaim_account_user_split_get_text(split) + Gaim::Account::UserSplit split + +void +gaim_account_user_split_destroy(split) + Gaim::Account::UserSplit split diff --git a/libpurple/plugins/perl/common/BuddyIcon.xs b/libpurple/plugins/perl/common/BuddyIcon.xs new file mode 100644 index 0000000000..e109631b73 --- /dev/null +++ b/libpurple/plugins/perl/common/BuddyIcon.xs @@ -0,0 +1,90 @@ +#include "module.h" + +MODULE = Gaim::Buddy::Icon PACKAGE = Gaim::Buddy::Icon PREFIX = gaim_buddy_icon_ +PROTOTYPES: ENABLE + +void +gaim_buddy_icon_destroy(icon) + Gaim::Buddy::Icon icon + +Gaim::Buddy::Icon +gaim_buddy_icon_ref(icon) + Gaim::Buddy::Icon icon + +Gaim::Buddy::Icon +gaim_buddy_icon_unref(icon) + Gaim::Buddy::Icon icon + +void +gaim_buddy_icon_update(icon) + Gaim::Buddy::Icon icon + +void +gaim_buddy_icon_cache(icon, buddy) + Gaim::Buddy::Icon icon + Gaim::BuddyList::Buddy buddy + +void +gaim_buddy_icon_set_account(icon, account) + Gaim::Buddy::Icon icon + Gaim::Account account + +void +gaim_buddy_icon_set_username(icon, username) + Gaim::Buddy::Icon icon + const char * username + +void +gaim_buddy_icon_set_data(icon, data, len) + Gaim::Buddy::Icon icon + void * data + size_t len + +Gaim::Account +gaim_buddy_icon_get_account(icon) + Gaim::Buddy::Icon icon + +const char * +gaim_buddy_icon_get_username(icon) + Gaim::Buddy::Icon icon + +const void * +gaim_buddy_icon_get_data(icon, len) + Gaim::Buddy::Icon icon + size_t &len + +const char * +gaim_buddy_icon_get_type(icon) + Gaim::Buddy::Icon icon + +void +gaim_buddy_icon_get_scale_size(spec, width, height) + Gaim::Buddy::Icon::Spec spec + int *width + int *height + +MODULE = Gaim::Buddy::Icon PACKAGE = Gaim::Buddy::Icons PREFIX = gaim_buddy_icons_ +PROTOTYPES: ENABLE + +void +gaim_buddy_icons_set_caching(caching) + gboolean caching + +gboolean +gaim_buddy_icons_is_caching() + +void +gaim_buddy_icons_set_cache_dir(cache_dir) + const char *cache_dir + +const char * +gaim_buddy_icons_get_cache_dir(); + +Gaim::Handle +gaim_buddy_icons_get_handle(); + +void +gaim_buddy_icons_init(); + +void +gaim_buddy_icons_uninit() diff --git a/libpurple/plugins/perl/common/BuddyList.xs b/libpurple/plugins/perl/common/BuddyList.xs new file mode 100644 index 0000000000..c4091743c3 --- /dev/null +++ b/libpurple/plugins/perl/common/BuddyList.xs @@ -0,0 +1,383 @@ +#include "module.h" +#include "../perl-handlers.h" + +MODULE = Gaim::BuddyList PACKAGE = Gaim PREFIX = gaim_ +PROTOTYPES: ENABLE + +Gaim::BuddyList +gaim_get_blist() + +void +gaim_set_blist(blist) + Gaim::BuddyList blist + +MODULE = Gaim::BuddyList PACKAGE = Gaim::Find PREFIX = gaim_find_ +PROTOTYPES: ENABLE + +Gaim::BuddyList::Buddy +gaim_find_buddy(account, name) + Gaim::Account account + const char * name + +void +gaim_find_buddies(account, name) + Gaim::Account account + const char * name +PREINIT: + GSList *l; +PPCODE: + for (l = gaim_find_buddies(account, name); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::BuddyList::Buddy"))); + } + +gboolean +gaim_group_on_account(group, account) + Gaim::BuddyList::Group group + Gaim::Account account + +Gaim::BuddyList::Group +gaim_find_group(name) + const char *name + +MODULE = Gaim::BuddyList PACKAGE = Gaim::BuddyList::Contact PREFIX = gaim_contact_ +PROTOTYPES: ENABLE + +Gaim::BuddyList::Contact +gaim_contact_new(); + +Gaim::BuddyList::Buddy +gaim_contact_get_priority_buddy(contact) + Gaim::BuddyList::Contact contact + +void +gaim_contact_set_alias(contact, alias) + Gaim::BuddyList::Contact contact + const char * alias + +const char * +gaim_contact_get_alias(contact) + Gaim::BuddyList::Contact contact + +gboolean +gaim_contact_on_account(contact, account) + Gaim::BuddyList::Contact contact + Gaim::Account account + +void +gaim_contact_invalidate_priority_buddy(contact) + Gaim::BuddyList::Contact contact + +MODULE = Gaim::BuddyList PACKAGE = Gaim::BuddyList::Group PREFIX = gaim_group_ +PROTOTYPES: ENABLE + +Gaim::BuddyList::Group +gaim_group_new(name) + const char *name + +void +gaim_group_get_accounts(group) + Gaim::BuddyList::Group group +PREINIT: + GSList *l; +PPCODE: + for (l = gaim_group_get_accounts(group); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Account"))); + } + +gboolean +gaim_group_on_account(group, account) + Gaim::BuddyList::Group group + Gaim::Account account + +MODULE = Gaim::BuddyList PACKAGE = Gaim::BuddyList PREFIX = gaim_blist_ +PROTOTYPES: ENABLE + +void +gaim_blist_add_contact(contact, group, node) + Gaim::BuddyList::Contact contact + Gaim::BuddyList::Group group + Gaim::BuddyList::Node node + +void +gaim_blist_merge_contact(source, node) + Gaim::BuddyList::Contact source + Gaim::BuddyList::Node node + +void +gaim_blist_add_group(group, node) + Gaim::BuddyList::Group group + Gaim::BuddyList::Node node + +void +gaim_blist_add_buddy(buddy, contact, group, node) + Gaim::BuddyList::Buddy buddy + Gaim::BuddyList::Contact contact + Gaim::BuddyList::Group group + Gaim::BuddyList::Node node + +void +gaim_blist_remove_buddy(buddy) + Gaim::BuddyList::Buddy buddy + +void +gaim_blist_remove_contact(contact) + Gaim::BuddyList::Contact contact + +void +gaim_blist_remove_chat(chat) + Gaim::BuddyList::Chat chat + +void +gaim_blist_remove_group(group) + Gaim::BuddyList::Group group + +Gaim::BuddyList::Chat +gaim_blist_find_chat(account, name) + Gaim::Account account + const char *name + +void +gaim_blist_add_chat(chat, group, node) + Gaim::BuddyList::Chat chat + Gaim::BuddyList::Group group + Gaim::BuddyList::Node node + +Gaim::BuddyList +gaim_blist_new() + +void +gaim_blist_show() + +void +gaim_blist_destroy(); + +void +gaim_blist_set_visible(show) + gboolean show + +void +gaim_blist_update_buddy_status(buddy, old_status) + Gaim::BuddyList::Buddy buddy + Gaim::Status old_status + +void +gaim_blist_update_buddy_icon(buddy) + Gaim::BuddyList::Buddy buddy + +void +gaim_blist_rename_buddy(buddy, name) + Gaim::BuddyList::Buddy buddy + const char * name + +void +gaim_blist_alias_buddy(buddy, alias) + Gaim::BuddyList::Buddy buddy + const char * alias + +void +gaim_blist_server_alias_buddy(buddy, alias) + Gaim::BuddyList::Buddy buddy + const char * alias + +void +gaim_blist_alias_chat(chat, alias) + Gaim::BuddyList::Chat chat + const char * alias + +void +gaim_blist_rename_group(group, name) + Gaim::BuddyList::Group group + const char * name + +void +gaim_blist_add_account(account) + Gaim::Account account + +void +gaim_blist_remove_account(account) + Gaim::Account account + +int +gaim_blist_get_group_size(group, offline) + Gaim::BuddyList::Group group + gboolean offline + +int +gaim_blist_get_group_online_count(group) + Gaim::BuddyList::Group group + +void +gaim_blist_load() + +void +gaim_blist_schedule_save() + +void +gaim_blist_request_add_group() + +void +gaim_blist_set_ui_ops(ops) + Gaim::BuddyList::UiOps ops + +Gaim::BuddyList::UiOps +gaim_blist_get_ui_ops() + +Gaim::Handle +gaim_blist_get_handle() + +void +gaim_blist_init() + +void +gaim_blist_uninit() + +MODULE = Gaim::BuddyList PACKAGE = Gaim::BuddyList::Node PREFIX = gaim_blist_node_ +PROTOTYPES: ENABLE + +void +gaim_blist_node_get_extended_menu(node) + Gaim::BuddyList::Node node +PREINIT: + GList *l; +PPCODE: + for (l = gaim_blist_node_get_extended_menu(node); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Menu::Action"))); + } + +void +gaim_blist_node_set_bool(node, key, value) + Gaim::BuddyList::Node node + const char * key + gboolean value + +gboolean +gaim_blist_node_get_bool(node, key) + Gaim::BuddyList::Node node + const char * key + +void +gaim_blist_node_set_int(node, key, value) + Gaim::BuddyList::Node node + const char * key + int value + +int +gaim_blist_node_get_int(node, key) + Gaim::BuddyList::Node node + const char * key + +const char * +gaim_blist_node_get_string(node, key) + Gaim::BuddyList::Node node + const char * key + +void +gaim_blist_node_remove_setting(node, key) + Gaim::BuddyList::Node node + const char * key + +void +gaim_blist_node_set_flags(node, flags) + Gaim::BuddyList::Node node + Gaim::BuddyList::NodeFlags flags + +Gaim::BuddyList::NodeFlags +gaim_blist_node_get_flags(node) + Gaim::BuddyList::Node node + +MODULE = Gaim::BuddyList PACKAGE = Gaim::BuddyList::Chat PREFIX = gaim_chat_ +PROTOTYPES: ENABLE + +Gaim::BuddyList::Group +gaim_chat_get_group(chat) + Gaim::BuddyList::Chat chat + +const char * +gaim_chat_get_name(chat) + Gaim::BuddyList::Chat chat + +Gaim::BuddyList::Chat +gaim_chat_new(account, alias, components) + Gaim::Account account + const char * alias + SV * components +INIT: + HV * t_HV; + HE * t_HE; + SV * t_SV; + GHashTable * t_GHash; + I32 len; + char *t_key, *t_value; +CODE: + t_HV = (HV *)SvRV(components); + t_GHash = g_hash_table_new(g_str_hash, g_str_equal); + + for (t_HE = hv_iternext(t_HV); t_HE != NULL; t_HE = hv_iternext(t_HV) ) { + t_key = hv_iterkey(t_HE, &len); + t_SV = *hv_fetch(t_HV, t_key, len, 0); + t_value = SvPV(t_SV, PL_na); + + g_hash_table_insert(t_GHash, t_key, t_value); + } + + RETVAL = gaim_chat_new(account, alias, t_GHash); +OUTPUT: + RETVAL + +MODULE = Gaim::BuddyList PACKAGE = Gaim::BuddyList::Buddy PREFIX = gaim_buddy_ +PROTOTYPES: ENABLE + +Gaim::BuddyList::Buddy +gaim_buddy_new(account, screenname, alias) + Gaim::Account account + const char *screenname + const char *alias + +const char * +gaim_buddy_get_server_alias(buddy) + Gaim::BuddyList::Buddy buddy + +void +gaim_buddy_set_icon(buddy, icon) + Gaim::BuddyList::Buddy buddy + Gaim::Buddy::Icon icon + +Gaim::Account +gaim_buddy_get_account(buddy) + Gaim::BuddyList::Buddy buddy + +Gaim::BuddyList::Group +gaim_buddy_get_group(buddy) + Gaim::BuddyList::Buddy buddy + +const char * +gaim_buddy_get_name(buddy) + Gaim::BuddyList::Buddy buddy + +Gaim::Buddy::Icon +gaim_buddy_get_icon(buddy) + Gaim::BuddyList::Buddy buddy + +Gaim::BuddyList::Contact +gaim_buddy_get_contact(buddy) + Gaim::BuddyList::Buddy buddy + +Gaim::Presence +gaim_buddy_get_presence(buddy) + Gaim::BuddyList::Buddy buddy + +const char * +gaim_buddy_get_alias_only(buddy) + Gaim::BuddyList::Buddy buddy + +const char * +gaim_buddy_get_contact_alias(buddy) + Gaim::BuddyList::Buddy buddy + +const char * +gaim_buddy_get_local_alias(buddy) + Gaim::BuddyList::Buddy buddy + +const char * +gaim_buddy_get_alias(buddy) + Gaim::BuddyList::Buddy buddy diff --git a/libpurple/plugins/perl/common/Cipher.xs b/libpurple/plugins/perl/common/Cipher.xs new file mode 100644 index 0000000000..64f0a1e59d --- /dev/null +++ b/libpurple/plugins/perl/common/Cipher.xs @@ -0,0 +1,157 @@ +#include "module.h" + +MODULE = Gaim::Cipher PACKAGE = Gaim::Cipher PREFIX = gaim_cipher_ +PROTOTYPES: ENABLE + +const gchar * +gaim_cipher_get_name(cipher) + Gaim::Cipher cipher + +guint +gaim_cipher_get_capabilities(cipher) + Gaim::Cipher cipher + +gboolean +gaim_cipher_digest_region(name, data, data_len, in_len, digest, out_len) + const gchar * name + const guchar * data + size_t data_len + size_t in_len + guchar &digest + size_t * out_len + +MODULE = Gaim::Cipher PACKAGE = Gaim::Ciphers PREFIX = gaim_ciphers_ +PROTOTYPES: ENABLE + +Gaim::Cipher +gaim_ciphers_find_cipher(name) + gchar * name + +Gaim::Cipher +gaim_ciphers_register_cipher(name, ops) + gchar * name + Gaim::Cipher::Ops ops + +gboolean +gaim_ciphers_unregister_cipher(cipher) + Gaim::Cipher cipher + +void +gaim_ciphers_get_ciphers() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_ciphers_get_ciphers(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Cipher"))); + } + +Gaim::Handle +gaim_ciphers_get_handle() + +void +gaim_ciphers_init() + +void +gaim_ciphers_uninit() + +MODULE = Gaim::Cipher PACKAGE = Gaim::Cipher::Context PREFIX = gaim_cipher_context_ +PROTOTYPES: ENABLE + +void +gaim_cipher_context_set_option(context, name, value) + Gaim::Cipher::Context context + gchar *name + gpointer value + +gpointer +gaim_cipher_context_get_option(context, name) + Gaim::Cipher::Context context + gchar *name + +Gaim::Cipher::Context +gaim_cipher_context_new(cipher, extra) + Gaim::Cipher cipher + void *extra + +Gaim::Cipher::Context +gaim_cipher_context_new_by_name(name, extra) + gchar *name + void *extra + +void +gaim_cipher_context_reset(context, extra) + Gaim::Cipher::Context context + gpointer extra + +void +gaim_cipher_context_destroy(context) + Gaim::Cipher::Context context + +void +gaim_cipher_context_set_iv(context, iv, len) + Gaim::Cipher::Context context + guchar * iv + size_t len + +void +gaim_cipher_context_append(context, data, len) + Gaim::Cipher::Context context + guchar * data + size_t len + +gboolean +gaim_cipher_context_digest(context, in_len, digest, out_len) + Gaim::Cipher::Context context + size_t in_len + guchar &digest + size_t &out_len + +gboolean +gaim_cipher_context_digest_to_str(context, in_len, digest_s, out_len) + Gaim::Cipher::Context context + size_t in_len + gchar &digest_s + size_t &out_len + +gint +gaim_cipher_context_encrypt(context, data, len, output, outlen) + Gaim::Cipher::Context context + guchar &data + size_t len + guchar &output + size_t &outlen + +gint +gaim_cipher_context_decrypt(context, data, len, output, outlen) + Gaim::Cipher::Context context + guchar &data + size_t len + guchar &output + size_t &outlen + +void +gaim_cipher_context_set_salt(context, salt) + Gaim::Cipher::Context context + guchar *salt + +size_t +gaim_cipher_context_get_salt_size(context) + Gaim::Cipher::Context context + +void +gaim_cipher_context_set_key(context, key) + Gaim::Cipher::Context context + guchar *key + +size_t +gaim_cipher_context_get_key_size(context) + Gaim::Cipher::Context context + +void +gaim_cipher_context_set_data(context, data) + Gaim::Cipher::Context context + gpointer data + +gpointer +gaim_cipher_context_get_data(context) + Gaim::Cipher::Context context diff --git a/libpurple/plugins/perl/common/Cmds.xs b/libpurple/plugins/perl/common/Cmds.xs new file mode 100644 index 0000000000..0aa87f6060 --- /dev/null +++ b/libpurple/plugins/perl/common/Cmds.xs @@ -0,0 +1,49 @@ +#include "module.h" +#include "../perl-handlers.h" + +MODULE = Gaim::Cmd PACKAGE = Gaim::Cmd PREFIX = gaim_cmd_ +PROTOTYPES: ENABLE + +void +gaim_cmd_help(conv, command) + Gaim::Conversation conv + const gchar *command +PREINIT: + GList *l; +PPCODE: + for (l = gaim_cmd_help(conv, command); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(newSVpv(l->data, 0))); + } + +void +gaim_cmd_list(conv) + Gaim::Conversation conv +PREINIT: + GList *l; +PPCODE: + for (l = gaim_cmd_list(conv); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(newSVpv(l->data, 0))); + } + +Gaim::Cmd::Id +gaim_cmd_register(plugin, command, args, priority, flag, prpl_id, func, helpstr, data = 0) + Gaim::Plugin plugin + const gchar *command + const gchar *args + Gaim::Cmd::Priority priority + Gaim::Cmd::Flag flag + const gchar *prpl_id + SV *func + const gchar *helpstr + SV *data +CODE: + RETVAL = gaim_perl_cmd_register(plugin, command, args, priority, flag, + prpl_id, func, helpstr, data); +OUTPUT: + RETVAL + +void +gaim_cmd_unregister(id) + Gaim::Cmd::Id id +CODE: + gaim_perl_cmd_unregister(id); diff --git a/libpurple/plugins/perl/common/Connection.xs b/libpurple/plugins/perl/common/Connection.xs new file mode 100644 index 0000000000..bae0d030dc --- /dev/null +++ b/libpurple/plugins/perl/common/Connection.xs @@ -0,0 +1,89 @@ +#include "module.h" + +MODULE = Gaim::Connection PACKAGE = Gaim::Connection PREFIX = gaim_connection_ +PROTOTYPES: ENABLE + +Gaim::Account +gaim_connection_get_account(gc) + Gaim::Connection gc + +const char * +gaim_connection_get_password(gc) + Gaim::Connection gc + +const char * +gaim_connection_get_display_name(gc) + Gaim::Connection gc + +void +gaim_connection_notice(gc, text) + Gaim::Connection gc + const char *text + +void +gaim_connection_error(gc, reason) + Gaim::Connection gc + const char *reason + +void +gaim_connection_destroy(gc) + Gaim::Connection gc + +void +gaim_connection_set_state(gc, state) + Gaim::Connection gc + Gaim::ConnectionState state + +void +gaim_connection_set_account(gc, account) + Gaim::Connection gc + Gaim::Account account + +void +gaim_connection_set_display_name(gc, name) + Gaim::Connection gc + const char *name + +Gaim::ConnectionState +gaim_connection_get_state(gc) + Gaim::Connection gc + +MODULE = Gaim::Connection PACKAGE = Gaim::Connections PREFIX = gaim_connections_ +PROTOTYPES: ENABLE + +void +gaim_connections_disconnect_all() + +void +gaim_connections_get_all() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_connections_get_all(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Connection"))); + } + +void +gaim_connections_get_connecting() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_connections_get_connecting(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Connection"))); + } + +void +gaim_connections_set_ui_ops(ops) + Gaim::Connection::UiOps ops + +Gaim::Connection::UiOps +gaim_connections_get_ui_ops() + +void +gaim_connections_init() + +void +gaim_connections_uninit() + +Gaim::Handle +gaim_connections_get_handle() diff --git a/libpurple/plugins/perl/common/Conversation.xs b/libpurple/plugins/perl/common/Conversation.xs new file mode 100644 index 0000000000..7c2ef3187c --- /dev/null +++ b/libpurple/plugins/perl/common/Conversation.xs @@ -0,0 +1,402 @@ +#include "module.h" + +MODULE = Gaim::Conversation PACKAGE = Gaim PREFIX = gaim_ +PROTOTYPES: ENABLE + +void +gaim_get_ims() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_get_ims(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Conversation"))); + } + +void +gaim_get_conversations() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_get_conversations(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Conversation"))); + } + +void +gaim_get_chats() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_get_chats(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Conversation"))); + } + +MODULE = Gaim::Conversation PACKAGE = Gaim::Conversations PREFIX = gaim_conversations_ +PROTOTYPES: ENABLE + +Gaim::Handle +gaim_conversations_get_handle() + +void +gaim_conversations_init() + +void +gaim_conversations_uninit() + +MODULE = Gaim::Conversation PACKAGE = Gaim::Conversation PREFIX = gaim_conversation_ +PROTOTYPES: ENABLE + +void +gaim_conversation_destroy(conv) + Gaim::Conversation conv + +Gaim::ConversationType +gaim_conversation_get_type(conv) + Gaim::Conversation conv + +Gaim::Account +gaim_conversation_get_account(conv) + Gaim::Conversation conv + +Gaim::Connection +gaim_conversation_get_gc(conv) + Gaim::Conversation conv + +void +gaim_conversation_set_title(conv, title); + Gaim::Conversation conv + const char * title + +const char * +gaim_conversation_get_title(conv) + Gaim::Conversation conv + +void +gaim_conversation_autoset_title(conv) + Gaim::Conversation conv + +void +gaim_conversation_set_name(conv, name) + Gaim::Conversation conv + const char *name + +const char * +gaim_conversation_get_name(conv) + Gaim::Conversation conv + +void +gaim_conversation_set_logging(conv, log) + Gaim::Conversation conv + gboolean log + +gboolean +gaim_conversation_is_logging(conv) + Gaim::Conversation conv + +Gaim::Conversation::IM +gaim_conversation_get_im_data(conv) + Gaim::Conversation conv + +Gaim::Conversation::Chat +gaim_conversation_get_chat_data(conv) + Gaim::Conversation conv + +gpointer +gaim_conversation_get_data(conv, key) + Gaim::Conversation conv + const char * key + +Gaim::ConnectionFlags +gaim_conversation_get_features(conv) + Gaim::Conversation conv + +gboolean +gaim_conversation_has_focus(conv) + Gaim::Conversation conv + +void +gaim_conversation_update(conv, type) + Gaim::Conversation conv + Gaim::ConvUpdateType type + +Gaim::Conversation +gaim_conversation_new(class, type, account, name) + Gaim::ConversationType type + Gaim::Account account + const char *name + C_ARGS: + type, account, name + +void +gaim_conversation_set_account(conv, account); + Gaim::Conversation conv + Gaim::Account account + +MODULE = Gaim::Conversation PACKAGE = Gaim::Conversation::IM PREFIX = gaim_conv_im_ +PROTOTYPES: ENABLE + +Gaim::Conversation +gaim_conv_im_get_conversation(im) + Gaim::Conversation::IM im + +void +gaim_conv_im_set_icon(im, icon) + Gaim::Conversation::IM im + Gaim::Buddy::Icon icon + +Gaim::Buddy::Icon +gaim_conv_im_get_icon(im) + Gaim::Conversation::IM im + +void +gaim_conv_im_set_typing_state(im, state) + Gaim::Conversation::IM im + Gaim::TypingState state + +Gaim::TypingState +gaim_conv_im_get_typing_state(im) + Gaim::Conversation::IM im + +void +gaim_conv_im_start_typing_timeout(im, timeout) + Gaim::Conversation::IM im + int timeout + +void +gaim_conv_im_stop_typing_timeout(im) + Gaim::Conversation::IM im + +guint +gaim_conv_im_get_typing_timeout(im) + Gaim::Conversation::IM im + +void +gaim_conv_im_set_type_again(im, val) + Gaim::Conversation::IM im + time_t val + +time_t +gaim_conv_im_get_type_again(im) + Gaim::Conversation::IM im + +void +gaim_conv_im_start_send_typed_timeout(im) + Gaim::Conversation::IM im + +void +gaim_conv_im_stop_send_typed_timeout(im) + Gaim::Conversation::IM im + +guint +gaim_conv_im_get_send_typed_timeout(im) + Gaim::Conversation::IM im + +void +gaim_conv_im_update_typing(im) + Gaim::Conversation::IM im + +void +gaim_conv_im_send(im, message) + Gaim::Conversation::IM im + const char *message + +void +gaim_conv_im_write(im, who, message, flags, mtime) + Gaim::Conversation::IM im + const char *who + const char *message + Gaim::MessageFlags flags + time_t mtime + +MODULE = Gaim::Conversation PACKAGE = Gaim::Conversation PREFIX = gaim_conv_ +PROTOTYPES: ENABLE + +gboolean +gaim_conv_present_error(who, account, what) + const char *who + Gaim::Account account + const char *what + +void +gaim_conv_custom_smiley_close(conv, smile) + Gaim::Conversation conv + const char *smile + +MODULE = Gaim::Conversation PACKAGE = Gaim::Conversation::Chat PREFIX = gaim_conv_chat_ +PROTOTYPES: ENABLE + +Gaim::Conversation +gaim_conv_chat_get_conversation(chat) + Gaim::Conversation::Chat chat + +void +gaim_conv_chat_set_users(chat, users) + Gaim::Conversation::Chat chat + SV * users +PREINIT: + GList *l, *t_GL; + int i, t_len; +PPCODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(users)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(users), i, 0), t_sl)); + } + + for (l = gaim_conv_chat_set_users(chat, t_GL); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::ListEntry"))); + } + +void +gaim_conv_chat_get_users(chat) + Gaim::Conversation::Chat chat +PREINIT: + GList *l; +PPCODE: + for (l = gaim_conv_chat_get_users(chat); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::ListEntry"))); + } + +void +gaim_conv_chat_ignore(chat, name) + Gaim::Conversation::Chat chat + const char *name + +void +gaim_conv_chat_unignore(chat, name) + Gaim::Conversation::Chat chat + const char *name + +void +gaim_conv_chat_set_ignored(chat, ignored) + Gaim::Conversation::Chat chat + SV * ignored +PREINIT: + GList *l, *t_GL; + int i, t_len; +PPCODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(ignored)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(ignored), i, 0), t_sl)); + } + + for (l = gaim_conv_chat_set_ignored(chat, t_GL); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::ListEntry"))); + } + +void +gaim_conv_chat_get_ignored(chat) + Gaim::Conversation::Chat chat +PREINIT: + GList *l; +PPCODE: + for (l = gaim_conv_chat_get_ignored(chat); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::ListEntry"))); + } + +const char * +gaim_conv_chat_get_topic(chat) + Gaim::Conversation::Chat chat + +void +gaim_conv_chat_set_id(chat, id) + Gaim::Conversation::Chat chat + int id + +int +gaim_conv_chat_get_id(chat) + Gaim::Conversation::Chat chat + +void +gaim_conv_chat_send(chat, message) + Gaim::Conversation::Chat chat + const char * message + +void +gaim_conv_chat_write(chat, who, message, flags, mtime) + Gaim::Conversation::Chat chat + const char *who + const char *message + Gaim::MessageFlags flags + time_t mtime + +void +gaim_conv_chat_add_users(chat, users, extra_msgs, flags, new_arrivals) + Gaim::Conversation::Chat chat + SV * users + SV * extra_msgs + SV * flags + gboolean new_arrivals +PREINIT: + GList *t_GL_users, *t_GL_extra_msgs, *t_GL_flags; + int i, t_len; +PPCODE: + t_GL_users = NULL; + t_len = av_len((AV *)SvRV(users)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL_users = g_list_append(t_GL_users, SvPV(*av_fetch((AV *)SvRV(users), i, 0), t_sl)); + } + + t_GL_flags = NULL; + t_len = av_len((AV *)SvRV(flags)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL_flags = g_list_append(t_GL_flags, SvPV(*av_fetch((AV *)SvRV(flags), i, 0), t_sl)); + } + + t_GL_extra_msgs = NULL; + t_len = av_len((AV *)SvRV(extra_msgs)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL_extra_msgs = g_list_append(t_GL_extra_msgs, SvPV(*av_fetch((AV *)SvRV(extra_msgs), i, 0), t_sl)); + } + + gaim_conv_chat_add_users(chat, t_GL_users, t_GL_extra_msgs, t_GL_flags, new_arrivals); + +gboolean +gaim_conv_chat_find_user(chat, user) + Gaim::Conversation::Chat chat + const char * user + +void gaim_conv_chat_clear_users(chat) + Gaim::Conversation::Chat chat + +void gaim_conv_chat_set_nick(chat, nick) + Gaim::Conversation::Chat chat + const char * nick + +const char * +gaim_conv_chat_get_nick(chat) + Gaim::Conversation::Chat chat + +Gaim::Conversation +gaim_find_chat(gc, id) + Gaim::Connection gc + int id + +void gaim_conv_chat_left(chat) + Gaim::Conversation::Chat chat + +gboolean gaim_conv_chat_has_left(chat) + Gaim::Conversation::Chat chat + +Gaim::Conversation::ChatBuddy +gaim_conv_chat_cb_find(chat, name) + Gaim::Conversation::Chat chat + const char *name + +const char * +gaim_conv_chat_cb_get_name(cb) + Gaim::Conversation::ChatBuddy cb + +void +gaim_conv_chat_cb_destroy(cb); + Gaim::Conversation::ChatBuddy cb diff --git a/libpurple/plugins/perl/common/Core.xs b/libpurple/plugins/perl/common/Core.xs new file mode 100644 index 0000000000..15db1632a2 --- /dev/null +++ b/libpurple/plugins/perl/common/Core.xs @@ -0,0 +1,28 @@ +#include "module.h" + +MODULE = Gaim::Core PACKAGE = Gaim::Core PREFIX = gaim_core_ +PROTOTYPES: ENABLE + +gboolean +gaim_core_quit_cb() +PPCODE: + /* The argument to gaim_core_quit_cb is not used, + * so there's little point in requiring it on the + * Perl side. */ + RETVAL = gaim_core_quit_cb(NULL); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + +const char * +gaim_core_get_version() + +const char * +gaim_core_get_ui() + +void +gaim_core_set_ui_ops(ops) + Gaim::Core::UiOps ops + +Gaim::Core::UiOps +gaim_core_get_ui_ops() + diff --git a/libpurple/plugins/perl/common/Debug.xs b/libpurple/plugins/perl/common/Debug.xs new file mode 100644 index 0000000000..d73e55f9e3 --- /dev/null +++ b/libpurple/plugins/perl/common/Debug.xs @@ -0,0 +1,54 @@ +#include "module.h" + +MODULE = Gaim::Debug PACKAGE = Gaim::Debug PREFIX = gaim_debug_ +PROTOTYPES: ENABLE + +void +gaim_debug(level, category, string) + Gaim::DebugLevel level + const char *category + const char *string +CODE: + gaim_debug(level, category, "%s", string); + +void +gaim_debug_misc(category, string) + const char *category + const char *string +CODE: + gaim_debug_misc(category, "%s", string); + +void +gaim_debug_info(category, string) + const char *category + const char *string +CODE: + gaim_debug_info(category, "%s", string); + +void +gaim_debug_warning(category, string) + const char *category + const char *string +CODE: + gaim_debug_warning(category, "%s", string); + +void +gaim_debug_error(category, string) + const char *category + const char *string +CODE: + gaim_debug_error(category, "%s", string); + +void +gaim_debug_fatal(category, string) + const char *category + const char *string +CODE: + gaim_debug_fatal(category, "%s", string); + +void +gaim_debug_set_enabled(enabled) + gboolean enabled + +gboolean +gaim_debug_is_enabled() diff --git a/libpurple/plugins/perl/common/FT.xs b/libpurple/plugins/perl/common/FT.xs new file mode 100644 index 0000000000..c4232fbcc8 --- /dev/null +++ b/libpurple/plugins/perl/common/FT.xs @@ -0,0 +1,168 @@ +#include "module.h" + +MODULE = Gaim::Xfer PACKAGE = Gaim::Xfer PREFIX = gaim_xfer_ +PROTOTYPES: ENABLE + +Gaim::Xfer +gaim_xfer_new(class, account, type, who) + Gaim::Account account + Gaim::XferType type + const char *who + C_ARGS: + account, type, who + +void +gaim_xfer_add(xfer) + Gaim::Xfer xfer + +void +gaim_xfer_cancel_local(xfer) + Gaim::Xfer xfer + +void +gaim_xfer_cancel_remote(xfer) + Gaim::Xfer xfer + +void +gaim_xfer_end(xfer) + Gaim::Xfer xfer + +void +gaim_xfer_error(type, account, who, msg) + Gaim::XferType type + Gaim::Account account + const char *who + const char *msg + +Gaim::Account +gaim_xfer_get_account(xfer) + Gaim::Xfer xfer + +size_t +gaim_xfer_get_bytes_remaining(xfer) + Gaim::Xfer xfer + +size_t +gaim_xfer_get_bytes_sent(xfer) + Gaim::Xfer xfer + +const char * +gaim_xfer_get_filename(xfer) + Gaim::Xfer xfer + +const char * +gaim_xfer_get_local_filename(xfer) + Gaim::Xfer xfer + +unsigned int +gaim_xfer_get_local_port(xfer) + Gaim::Xfer xfer + +double +gaim_xfer_get_progress(xfer) + Gaim::Xfer xfer + +const char * +gaim_xfer_get_remote_ip(xfer) + Gaim::Xfer xfer + +unsigned int +gaim_xfer_get_remote_port(xfer) + Gaim::Xfer xfer + +size_t +gaim_xfer_get_size(xfer) + Gaim::Xfer xfer + +Gaim::XferStatusType +gaim_xfer_get_status(xfer) + Gaim::Xfer xfer + +Gaim::XferType +gaim_xfer_get_type(xfer) + Gaim::Xfer xfer + +Gaim::XferUiOps +gaim_xfer_get_ui_ops(xfer) + Gaim::Xfer xfer + +gboolean +gaim_xfer_is_canceled(xfer) + Gaim::Xfer xfer + +gboolean +gaim_xfer_is_completed(xfer) + Gaim::Xfer xfer + +ssize_t +gaim_xfer_read(xfer, buffer) + Gaim::Xfer xfer + guchar **buffer + +void +gaim_xfer_ref(xfer) + Gaim::Xfer xfer + +void +gaim_xfer_request(xfer) + Gaim::Xfer xfer + +void +gaim_xfer_request_accepted(xfer, filename) + Gaim::Xfer xfer + const char *filename + +void +gaim_xfer_request_denied(xfer) + Gaim::Xfer xfer + +void +gaim_xfer_set_completed(xfer, completed) + Gaim::Xfer xfer + gboolean completed + +void +gaim_xfer_set_filename(xfer, filename) + Gaim::Xfer xfer + const char *filename + +void +gaim_xfer_set_local_filename(xfer, filename) + Gaim::Xfer xfer + const char *filename + +void +gaim_xfer_set_message(xfer, message) + Gaim::Xfer xfer + const char *message + +void +gaim_xfer_set_size(xfer, size) + Gaim::Xfer xfer + size_t size + +void +gaim_xfer_unref(xfer) + Gaim::Xfer xfer + +void +gaim_xfer_update_progress(xfer) + Gaim::Xfer xfer + +ssize_t +gaim_xfer_write(xfer, buffer, size) + Gaim::Xfer xfer + const guchar *buffer + size_t size + +MODULE = Gaim::Xfer PACKAGE = Gaim::Xfers PREFIX = gaim_xfers_ +PROTOTYPES: ENABLE + +Gaim::XferUiOps +gaim_xfers_get_ui_ops() + + +void +gaim_xfers_set_ui_ops(ops) + Gaim::XferUiOps ops + diff --git a/libpurple/plugins/perl/common/Gaim.pm b/libpurple/plugins/perl/common/Gaim.pm new file mode 100644 index 0000000000..c2ca5b85fa --- /dev/null +++ b/libpurple/plugins/perl/common/Gaim.pm @@ -0,0 +1,131 @@ +package Gaim; + +use 5.008; +use strict; +use warnings; +use Carp; + +require Exporter; +use AutoLoader; + +our @ISA = qw(Exporter); + +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. + +# This allows declaration use Gaim ':all'; +# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK +# will save memory. +our %EXPORT_TAGS = ( 'all' => [ qw( + +) ] ); + +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); + +our @EXPORT = qw( + +); + +our $VERSION = '0.01'; + +sub AUTOLOAD { + # This AUTOLOAD is used to 'autoload' constants from the constant() + # XS function. + + my $constname; + our $AUTOLOAD; + ($constname = $AUTOLOAD) =~ s/.*:://; + croak "&Gaim::constant not defined" if $constname eq 'constant'; + my ($error, $val) = constant($constname); + if ($error) { croak $error; } + { + no strict 'refs'; + + *$AUTOLOAD = sub { $val }; + } + + goto &$AUTOLOAD; +} + +require XSLoader; +XSLoader::load('Gaim', $VERSION); + +# Preloaded methods go here. + +1; +__END__ + +=head1 NAME + +Gaim - Perl extension the Gaim instant messenger. + +=head1 SYNOPSIS + + use Gaim; + +=head1 ABSTRACT + + This module provides the interface for using perl scripts as plugins + in Gaim. + +=head1 DESCRIPTION + +This module provides the interface for using perl scripts as plugins +in Gaim. With this, developers can write perl scripts that can be +loaded in Gaim as plugins. The scripts can interact with IMs, chats, +accounts, the buddy list, gaim signals, and more. + +The API for the perl interface is very similar to that of the Gaim C +API, which can be viewed at http://gaim.sourceforge.net/api/ or in +the header files in the Gaim source tree. + +=head1 FUNCTIONS + +=over + +=item @accounts = Gaim::accounts + +Returns a list of all accounts, online or offline. + +=item @chats = Gaim::chats + +Returns a list of all chats currently open. + +=item @connections = Gaim::connections + +Returns a list of all active connections. + +=item @conversations = Gaim::conversations + +Returns a list of all conversations, both IM and chat, currently open. + +=item @conv_windows = Gaim::conv_windows + +Returns a list of all conversation windows currently open. + +=item @ims = Gaim::ims + +Returns a list of all instant messages currently open. + +=back + +=head1 SEE ALSO + +Gaim C API documentation - http//gaim.sourceforge.net/api/ + +Gaim website - http://gaim.sourceforge.net/ + +=head1 AUTHOR + +Christian Hammond, Echipx86@gnupdate.orgE + +=head1 COPYRIGHT AND LICENSE + +Copyright 2003 by Christian Hammond + +This library is free software; you can redistribute it and/or modify +it under the terms of the General Public License (GPL). For +more information, see http://www.fsf.org/licenses/gpl.txt + +=cut diff --git a/libpurple/plugins/perl/common/Gaim.xs b/libpurple/plugins/perl/common/Gaim.xs new file mode 100644 index 0000000000..49a0f6b9a1 --- /dev/null +++ b/libpurple/plugins/perl/common/Gaim.xs @@ -0,0 +1,99 @@ +#include "module.h" +#include "../perl-handlers.h" +#include "const-c.inc" + +/* Prototypes for the BOOT section below. */ +GAIM_PERL_BOOT_PROTO(Account); +GAIM_PERL_BOOT_PROTO(Account__Option); +GAIM_PERL_BOOT_PROTO(Buddy__Icon); +GAIM_PERL_BOOT_PROTO(BuddyList); +GAIM_PERL_BOOT_PROTO(Cipher); +GAIM_PERL_BOOT_PROTO(Cmd); +GAIM_PERL_BOOT_PROTO(Connection); +GAIM_PERL_BOOT_PROTO(Conversation); +GAIM_PERL_BOOT_PROTO(Core); +GAIM_PERL_BOOT_PROTO(Debug); +GAIM_PERL_BOOT_PROTO(Xfer); +GAIM_PERL_BOOT_PROTO(ImgStore); +GAIM_PERL_BOOT_PROTO(Log); +GAIM_PERL_BOOT_PROTO(Network); +GAIM_PERL_BOOT_PROTO(Notify); +GAIM_PERL_BOOT_PROTO(Plugin); +GAIM_PERL_BOOT_PROTO(PluginPref); +GAIM_PERL_BOOT_PROTO(Pounce); +GAIM_PERL_BOOT_PROTO(Prefs); +GAIM_PERL_BOOT_PROTO(Privacy); +GAIM_PERL_BOOT_PROTO(Proxy); +GAIM_PERL_BOOT_PROTO(Prpl); +GAIM_PERL_BOOT_PROTO(Request); +GAIM_PERL_BOOT_PROTO(Roomlist); +GAIM_PERL_BOOT_PROTO(SSL); +GAIM_PERL_BOOT_PROTO(SavedStatus); +GAIM_PERL_BOOT_PROTO(Serv); +GAIM_PERL_BOOT_PROTO(Signal); +GAIM_PERL_BOOT_PROTO(Sound); +GAIM_PERL_BOOT_PROTO(Status); +GAIM_PERL_BOOT_PROTO(Stringref); +GAIM_PERL_BOOT_PROTO(Util); +GAIM_PERL_BOOT_PROTO(XMLNode); + +MODULE = Gaim PACKAGE = Gaim PREFIX = gaim_ +PROTOTYPES: ENABLE + +INCLUDE: const-xs.inc + +BOOT: + GAIM_PERL_BOOT(Account); + GAIM_PERL_BOOT(Account__Option); + GAIM_PERL_BOOT(Buddy__Icon); + GAIM_PERL_BOOT(BuddyList); + GAIM_PERL_BOOT(Cipher); + GAIM_PERL_BOOT(Cmd); + GAIM_PERL_BOOT(Connection); + GAIM_PERL_BOOT(Conversation); + GAIM_PERL_BOOT(Core); + GAIM_PERL_BOOT(Debug); + GAIM_PERL_BOOT(Xfer); + GAIM_PERL_BOOT(ImgStore); + GAIM_PERL_BOOT(Log); + GAIM_PERL_BOOT(Network); + GAIM_PERL_BOOT(Notify); + GAIM_PERL_BOOT(Plugin); + GAIM_PERL_BOOT(PluginPref); + GAIM_PERL_BOOT(Pounce); + GAIM_PERL_BOOT(Prefs); + GAIM_PERL_BOOT(Privacy); + GAIM_PERL_BOOT(Proxy); + GAIM_PERL_BOOT(Prpl); + GAIM_PERL_BOOT(Request); + GAIM_PERL_BOOT(Roomlist); + GAIM_PERL_BOOT(SSL); + GAIM_PERL_BOOT(SavedStatus); + GAIM_PERL_BOOT(Serv); + GAIM_PERL_BOOT(Signal); + GAIM_PERL_BOOT(Sound); + GAIM_PERL_BOOT(Status); + GAIM_PERL_BOOT(Stringref); + GAIM_PERL_BOOT(Util); + GAIM_PERL_BOOT(XMLNode); + +void +timeout_add(plugin, seconds, callback, data = 0) + Gaim::Plugin plugin + int seconds + SV *callback + SV *data +CODE: + gaim_perl_timeout_add(plugin, seconds, callback, data); + +void +deinit() +CODE: + gaim_perl_timeout_clear(); + + +MODULE = Gaim PACKAGE = Gaim PREFIX = gaim_ +PROTOTYPES: ENABLE + +Gaim::Core +gaim_get_core() diff --git a/libpurple/plugins/perl/common/ImgStore.xs b/libpurple/plugins/perl/common/ImgStore.xs new file mode 100644 index 0000000000..17fba026a7 --- /dev/null +++ b/libpurple/plugins/perl/common/ImgStore.xs @@ -0,0 +1,35 @@ +#include "module.h" + +MODULE = Gaim::ImgStore PACKAGE = Gaim::ImgStore PREFIX = gaim_imgstore_ +PROTOTYPES: ENABLE + +int +gaim_imgstore_add(data, size, filename) + void *data + size_t size + const char *filename + +Gaim::StoredImage +gaim_imgstore_get(id) + int id + +gpointer +gaim_imgstore_get_data(i) + Gaim::StoredImage i + +const char * +gaim_imgstore_get_filename(i) + Gaim::StoredImage i + +size_t +gaim_imgstore_get_size(i) + Gaim::StoredImage i + +void +gaim_imgstore_ref(id) + int id + +void +gaim_imgstore_unref(id) + int id + diff --git a/libpurple/plugins/perl/common/Log.xs b/libpurple/plugins/perl/common/Log.xs new file mode 100644 index 0000000000..decc4b085f --- /dev/null +++ b/libpurple/plugins/perl/common/Log.xs @@ -0,0 +1,94 @@ +#include "module.h" + +MODULE = Gaim::Log PACKAGE = Gaim::Log PREFIX = gaim_log_ +PROTOTYPES: ENABLE + +int +gaim_log_common_sizer(log) + Gaim::Log log + +void +gaim_log_common_writer(log, ext) + Gaim::Log log + const char *ext + +gint +gaim_log_compare(y, z) + gconstpointer y + gconstpointer z + +void +gaim_log_free(log) + Gaim::Log log + +gchar_own * +gaim_log_get_log_dir(type, name, account) + Gaim::LogType type + const char *name + Gaim::Account account + +void +gaim_log_get_log_sets() +PREINIT: + GHashTable *l; +PPCODE: + l = gaim_log_get_log_sets(); + XPUSHs(sv_2mortal(gaim_perl_bless_object(l, "GHashTable"))); + +void +gaim_log_get_logs(type, name, account) + Gaim::LogType type + const char *name + Gaim::Account account +PREINIT: + GList *l; +PPCODE: + for (l = gaim_log_get_logs(type, name, account); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::ListEntry"))); + } + +int +gaim_log_get_size(log) + Gaim::Log log + +void +gaim_log_get_system_logs(account) + Gaim::Account account +PREINIT: + GList *l; +PPCODE: + for (l = gaim_log_get_system_logs(account); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::ListEntry"))); + } + +int +gaim_log_get_total_size(type, name, account) + Gaim::LogType type + const char *name + Gaim::Account account + +void +gaim_log_init() + +void +gaim_log_logger_free(logger) + Gaim::Log::Logger logger + +void +gaim_log_logger_get_options() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_log_logger_get_options(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::ListEntry"))); + } + +gchar_own * +gaim_log_read(log, flags) + Gaim::Log log + Gaim::Log::ReadFlags flags + +gint +gaim_log_set_compare(y, z) + gconstpointer y + gconstpointer z diff --git a/libpurple/plugins/perl/common/MANIFEST b/libpurple/plugins/perl/common/MANIFEST new file mode 100644 index 0000000000..2ed457d68b --- /dev/null +++ b/libpurple/plugins/perl/common/MANIFEST @@ -0,0 +1,37 @@ +Account.xs +AccountOpts.xs +BuddyIcon.xs +BuddyList.xs +Cipher.xs +Cmds.xs +Connection.xs +Conversation.xs +Debug.xs +FT.xs +Gaim.pm +Gaim.xs +ImgStore.xs +Log.xs +Makefile.PL +Network.xs +Notify.xs +Plugin.xs +PluginPref.xs +Pounce.xs +Prefs.xs +Privacy.xs +Proxy.xs +Prpl.xs +Request.xs +Roomlist.xs +SSLConn.xs +SavedStatuses.xs +Server.xs +Signal.xs +Sound.xs +Status.xs +Stringref.xs +Util.xs +XMLNode.xs +MANIFEST +typemap diff --git a/libpurple/plugins/perl/common/Makefile.PL.in b/libpurple/plugins/perl/common/Makefile.PL.in new file mode 100644 index 0000000000..908c32172b --- /dev/null +++ b/libpurple/plugins/perl/common/Makefile.PL.in @@ -0,0 +1,39 @@ +use 5.006; +use ExtUtils::MakeMaker; +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. +WriteMakefile( + 'NAME' => 'Gaim', + 'VERSION_FROM' => '@srcdir@/Gaim.pm', # finds $VERSION + 'PREREQ_PM' => {}, # e.g., Module::Name => 1.1 + ($] >= 5.005 ? ## Add these new keywords supported since 5.005 + (ABSTRACT_FROM => '@srcdir@/Gaim.pm', # retrieve abstract from module + AUTHOR => 'Gaim ') : ()), + 'LIBS' => [''], # e.g., '-lm' + 'DEFINE' => '@DEBUG_CFLAGS@', # e.g., '-DHAVE_SOMETHING' + 'INC' => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_srcdir@/libgaim @GLIB_CFLAGS@', # e.g., '-I. -I/usr/include/other' + 'OBJECT' => '$(O_FILES)', # link all the C files too +); + +if (eval {require ExtUtils::Constant; 1}) { + foreach (qw(GAIM_DEBUG_ALL GAIM_DEBUG_MISC GAIM_DEBUG_INFO + GAIM_DEBUG_WARNING GAIM_DEBUG_ERROR GAIM_DEBUG_FATAL)) { + push @names, {name => $_, type => "IV", macro => 1}; + } + + ExtUtils::Constant::WriteConstants( + NAME => 'Gaim::DebugLevel', + NAMES => \@names, + C_FILE => 'const-c.inc', + XS_FILE => 'const-xs.inc' + ); +} +else { + use File::Copy; + use File::Spec; + + foreach my $file ('const-c.inc', 'const-xs.inc') { + my $fallback = File::Spec->catfile('fallback', $file); + copy ($fallback, $file) or die "Can't copy $fallback to $file: $!"; + } +} diff --git a/libpurple/plugins/perl/common/Makefile.mingw b/libpurple/plugins/perl/common/Makefile.mingw new file mode 100644 index 0000000000..d884bd7073 --- /dev/null +++ b/libpurple/plugins/perl/common/Makefile.mingw @@ -0,0 +1,117 @@ +# +# Makefile.mingw +# +# Description: Makefile for Gaim perl module. +# + +GAIM_TOP := ../../../.. +include $(GAIM_TOP)/libgaim/win32/global.mak + +TARGET = Gaim +AUTOSPLIT = lib/auto/Gaim/autosplit.ix +EXTUTILS := C:/perl/lib/ExtUtils +PERL_PLUGIN_TOP := .. + +CFLAGS += -Wno-comment + +## +## INCLUDE PATHS +## +INCLUDE_PATHS = -I. \ + -I$(GAIM_TOP) \ + -I$(GAIM_LIB_TOP) \ + -I$(GTK_TOP)/include \ + -I$(GTK_TOP)/include/glib-2.0 \ + -I$(GTK_TOP)/lib/glib-2.0/include \ + -I$(PERL_LIB_TOP)/CORE + +LIB_PATHS = -L$(PERL_LIB_TOP) \ + -L$(PERL_PLUGIN_TOP) \ + -L$(GAIM_LIB_TOP) \ + -L$(GTK_TOP)/lib + +## +## SOURCES, OBJECTS +## +XS_FILES = Account.xs \ + AccountOpts.xs \ + BuddyIcon.xs \ + BuddyList.xs \ + Cipher.xs \ + Cmds.xs \ + Connection.xs \ + Conversation.xs \ + Core.xs \ + Debug.xs \ + FT.xs \ + Gaim.xs \ + ImgStore.xs \ + Log.xs \ + Network.xs \ + Notify.xs \ + Plugin.xs \ + PluginPref.xs \ + Pounce.xs \ + Prefs.xs \ + Privacy.xs \ + Proxy.xs \ + Prpl.xs \ + Request.xs \ + Roomlist.xs \ + SSLConn.xs \ + SavedStatuses.xs \ + Signal.xs \ + Server.xs \ + Sound.xs \ + Status.xs \ + Stringref.xs \ + Util.xs \ + XMLNode.xs \ + +FALLBACKS = const-c.inc const-xs.inc +C_FILES = $(XS_FILES:%.xs=%.c) +OBJECTS = $(C_FILES:%.c=%.o) + +## +## LIBRARIES +## +LIBS = -lperl58 \ + -lperl \ + -lgaim \ + -lglib-2.0 + +include $(GAIM_COMMON_RULES) + +%.inc: + cp fallback/$@ ./ + +## +## TARGETS +## +.PHONY: all install clean + +all: $(TARGET).dll $(AUTOSPLIT) + +install: all + rm -rf $(GAIM_INSTALL_PERLMOD_DIR) + cp -R lib $(GAIM_INSTALL_PERLMOD_DIR) + cp $(TARGET).dll $(GAIM_INSTALL_PERLMOD_DIR) + +$(C_FILES): $(GAIM_CONFIG_H) + +$(AUTOSPLIT): + mkdir -p ./lib/auto + cp Gaim.pm ./lib + $(PERL) -MAutoSplit -e 'autosplit("lib/Gaim.pm")' + +$(TARGET).dll: $(GAIM_LIBGAIM_DLL).a $(GAIM_LIBGAIM_PERL_DLL).a $(FALLBACKS) $(OBJECTS) + $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) -o $(TARGET).dll + +## +## CLEAN +## +clean: + rm -rf $(TARGET).dll $(FALLBACKS) lib + rm -f *.o $(C_FILES) + +include $(GAIM_COMMON_TARGETS) diff --git a/libpurple/plugins/perl/common/Network.xs b/libpurple/plugins/perl/common/Network.xs new file mode 100644 index 0000000000..c4cdc3daa8 --- /dev/null +++ b/libpurple/plugins/perl/common/Network.xs @@ -0,0 +1,45 @@ +#include "module.h" + +MODULE = Gaim::Network PACKAGE = Gaim::Network PREFIX = gaim_network_ +PROTOTYPES: ENABLE + +const char * +gaim_network_get_local_system_ip(fd) + int fd + +const char * +gaim_network_get_my_ip(fd) + int fd + +unsigned short +gaim_network_get_port_from_fd(fd) + int fd + +const char * +gaim_network_get_public_ip() + +void +gaim_network_init() + +const unsigned char * +gaim_network_ip_atoi(ip) + const char *ip + +Gaim::NetworkListenData +gaim_network_listen(port, socket_type, cb, cb_data) + unsigned short port + int socket_type + Gaim::NetworkListenCallback cb + gpointer cb_data + +Gaim::NetworkListenData +gaim_network_listen_range(start, end, socket_type, cb, cb_data) + unsigned short start + unsigned short end + int socket_type + Gaim::NetworkListenCallback cb + gpointer cb_data + +void +gaim_network_set_public_ip(ip) + const char *ip diff --git a/libpurple/plugins/perl/common/Notify.xs b/libpurple/plugins/perl/common/Notify.xs new file mode 100644 index 0000000000..967ee30e84 --- /dev/null +++ b/libpurple/plugins/perl/common/Notify.xs @@ -0,0 +1,138 @@ +#include "module.h" + +MODULE = Gaim::Notify PACKAGE = Gaim::Notify PREFIX = gaim_notify_ +PROTOTYPES: ENABLE + +void +gaim_notify_close(type, ui_handle) + Gaim::NotifyType type + void * ui_handle + +void +gaim_notify_close_with_handle(handle) + void * handle + +void * +gaim_notify_email(handle, subject, from, to, url, cb, user_data) + void * handle + const char *subject + const char *from + const char *to + const char *url + Gaim::NotifyCloseCallback cb + gpointer user_data + +void * +gaim_notify_emails(handle, count, detailed, subjects, froms, tos, urls, cb, user_data) + void * handle + size_t count + gboolean detailed + const char **subjects + const char **froms + const char **tos + const char **urls + Gaim::NotifyCloseCallback cb + gpointer user_data + +void * +gaim_notify_formatted(handle, title, primary, secondary, text, cb, user_data) + void * handle + const char *title + const char *primary + const char *secondary + const char *text + Gaim::NotifyCloseCallback cb + gpointer user_data + +void * +gaim_notify_userinfo(gc, who, user_info, cb, user_data) + Gaim::Connection gc + const char *who + Gaim::NotifyUserInfo user_info + Gaim::NotifyCloseCallback cb + gpointer user_data + +Gaim::NotifyUserInfo +gaim_notify_user_info_new() + +void +gaim_notify_user_info_destroy(user_info) + Gaim::NotifyUserInfo user_info + +void +gaim_notify_user_info_get_entries(user_info) + Gaim::NotifyUserInfo user_info +PREINIT: + const GList *l; +PPCODE: + l = gaim_notify_user_info_get_entries(user_info); + for (; l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::NotifyUserInfoEntry"))); + } + +gchar_own * +gaim_notify_user_info_get_text_with_newline(user_info, newline) + Gaim::NotifyUserInfo user_info + const char *newline + +void gaim_notify_user_info_add_pair(user_info, label, value) + Gaim::NotifyUserInfo user_info + const char *label + const char *value + +void gaim_notify_user_info_prepend_pair(user_info, label, value) + Gaim::NotifyUserInfo user_info + const char *label + const char *value + +void gaim_notify_user_info_add_section_break(user_info) + Gaim::NotifyUserInfo user_info + +void gaim_notify_user_info_add_section_header(user_info, label) + Gaim::NotifyUserInfo user_info + const char *label + +void gaim_notify_user_info_remove_last_item(user_info) + Gaim::NotifyUserInfo user_info + +gchar * +gaim_notify_user_info_entry_get_label(user_info_entry) + Gaim::NotifyUserInfoEntry user_info_entry + +gchar * +gaim_notify_user_info_entry_get_value(user_info_entry) + Gaim::NotifyUserInfoEntry user_info_entry + +Gaim::NotifyUiOps +gaim_notify_get_ui_ops() + + +void * +gaim_notify_message(handle, type, title, primary, secondary, cb, user_data) + void * handle + Gaim::NotifyMsgType type + const char *title + const char *primary + const char *secondary + Gaim::NotifyCloseCallback cb + gpointer user_data + +void * +gaim_notify_searchresults(gc, title, primary, secondary, results, cb, user_data) + Gaim::Connection gc + const char *title + const char *primary + const char *secondary + Gaim::NotifySearchResults results + Gaim::NotifyCloseCallback cb + gpointer user_data + +void +gaim_notify_set_ui_ops(ops) + Gaim::NotifyUiOps ops + +void * +gaim_notify_uri(handle, uri) + void * handle + const char *uri + diff --git a/libpurple/plugins/perl/common/Plugin.xs b/libpurple/plugins/perl/common/Plugin.xs new file mode 100644 index 0000000000..35affa902d --- /dev/null +++ b/libpurple/plugins/perl/common/Plugin.xs @@ -0,0 +1,156 @@ +#include "module.h" + +MODULE = Gaim::Plugin PACKAGE = Gaim::Plugin PREFIX = gaim_plugin_ +PROTOTYPES: ENABLE + +Gaim::Plugin +gaim_plugin_new(native, path) + gboolean native + const char *path + +Gaim::Plugin +gaim_plugin_probe(filename) + const char *filename + +gboolean +gaim_plugin_register(plugin) + Gaim::Plugin plugin + +gboolean +gaim_plugin_load(plugin) + Gaim::Plugin plugin + +gboolean +gaim_plugin_unload(plugin) + Gaim::Plugin plugin + +gboolean +gaim_plugin_reload(plugin) + Gaim::Plugin plugin + +void +gaim_plugin_destroy(plugin) + Gaim::Plugin plugin + +gboolean +gaim_plugin_is_loaded(plugin) + Gaim::Plugin plugin + +gboolean +gaim_plugin_is_unloadable(plugin) + Gaim::Plugin plugin + +const gchar * +gaim_plugin_get_id(plugin) + Gaim::Plugin plugin + +const gchar * +gaim_plugin_get_name(plugin) + Gaim::Plugin plugin + +const gchar * +gaim_plugin_get_version(plugin) + Gaim::Plugin plugin + +const gchar * +gaim_plugin_get_summary(plugin) + Gaim::Plugin plugin + +const gchar * +gaim_plugin_get_description(plugin) + Gaim::Plugin plugin + +const gchar * +gaim_plugin_get_author(plugin) + Gaim::Plugin plugin + +const gchar * +gaim_plugin_get_homepage(plugin) + Gaim::Plugin plugin + +MODULE = Gaim::Plugin PACKAGE = Gaim::Plugin::IPC PREFIX = gaim_plugin_ipc_ + +void +gaim_plugin_ipc_unregister(plugin, command) + Gaim::Plugin plugin + const char *command + +void +gaim_plugin_ipc_unregister_all(plugin) + Gaim::Plugin plugin + +MODULE = Gaim::Plugin PACKAGE = Gaim::Plugins PREFIX = gaim_plugins_ +PROTOTYPES: ENABLE + +void +gaim_plugins_add_search_path(path) + const char *path + +void +gaim_plugins_unload_all() + +void +gaim_plugins_destroy_all() + +void +gaim_plugins_load_saved(key) + const char *key + +void +gaim_plugins_probe(ext) + const char *ext + +gboolean +gaim_plugins_enabled() + +Gaim::Plugin +gaim_plugins_find_with_name(name) + const char *name + +Gaim::Plugin +gaim_plugins_find_with_filename(filename) + const char *filename + +Gaim::Plugin +gaim_plugins_find_with_basename(basename) + const char *basename + +Gaim::Plugin +gaim_plugins_find_with_id(id) + const char *id + +void +gaim_plugins_get_loaded() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_plugins_get_loaded(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Plugin"))); + } + +void +gaim_plugins_get_protocols() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_plugins_get_protocols(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Plugin"))); + } + +void +gaim_plugins_get_all() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_plugins_get_all(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Plugin"))); + } + +Gaim::Handle +gaim_plugins_get_handle() + +void +gaim_plugins_init() + +void +gaim_plugins_uninit() diff --git a/libpurple/plugins/perl/common/PluginPref.xs b/libpurple/plugins/perl/common/PluginPref.xs new file mode 100644 index 0000000000..ac42b4a69d --- /dev/null +++ b/libpurple/plugins/perl/common/PluginPref.xs @@ -0,0 +1,144 @@ +#include "module.h" + +MODULE = Gaim::PluginPref PACKAGE = Gaim::PluginPref::Frame PREFIX = gaim_plugin_pref_frame_ +PROTOTYPES: ENABLE + +void +gaim_plugin_pref_frame_add(frame, pref) + Gaim::PluginPref::Frame frame + Gaim::PluginPref pref + +void +gaim_plugin_pref_frame_destroy(frame) + Gaim::PluginPref::Frame frame + +void +gaim_plugin_pref_frame_get_prefs(frame) + Gaim::PluginPref::Frame frame +PREINIT: + GList *l; +PPCODE: + for (l = gaim_plugin_pref_frame_get_prefs(frame); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::PluginPref"))); + } + +Gaim::PluginPref::Frame +gaim_plugin_pref_frame_new(class) + C_ARGS: /* void */ + +MODULE = Gaim::PluginPref PACKAGE = Gaim::PluginPref PREFIX = gaim_plugin_pref_ +PROTOTYPES: ENABLE + +void +gaim_plugin_pref_add_choice(pref, label, choice) + Gaim::PluginPref pref + const char *label +# Do the appropriate conversion based on the perl type specified. +# Currently only Strings and Ints will work. + gpointer choice = (SvPOKp($arg) ? SvPV($arg, PL_na) : (SvIOKp($arg) ? GINT_TO_POINTER(SvIV($arg)) : NULL)); + +void +gaim_plugin_pref_destroy(pref) + Gaim::PluginPref pref + + +void +gaim_plugin_pref_get_bounds(pref, min, max) + Gaim::PluginPref pref + int *min + int *max + +void +gaim_plugin_pref_get_choices(pref) + Gaim::PluginPref pref +PREINIT: + GList *l; +PPCODE: + for (l = gaim_plugin_pref_get_choices(pref); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::ListItem"))); + } + +const char * +gaim_plugin_pref_get_label(pref) + Gaim::PluginPref pref + +gboolean +gaim_plugin_pref_get_masked(pref) + Gaim::PluginPref pref + +unsigned int +gaim_plugin_pref_get_max_length(pref) + Gaim::PluginPref pref + +const char * +gaim_plugin_pref_get_name(pref) + Gaim::PluginPref pref + +Gaim::PluginPrefType +gaim_plugin_pref_get_type(pref) + Gaim::PluginPref pref + +Gaim::PluginPref +gaim_plugin_pref_new(class) + C_ARGS: /* void */ + +Gaim::PluginPref +gaim_plugin_pref_new_with_label(class, label) + const char *label + C_ARGS: + label + +Gaim::PluginPref +gaim_plugin_pref_new_with_name(class, name) + const char *name + C_ARGS: + name + +Gaim::PluginPref +gaim_plugin_pref_new_with_name_and_label(class, name, label) + const char *name + const char *label + C_ARGS: + name, label + +void +gaim_plugin_pref_set_bounds(pref, min, max) + Gaim::PluginPref pref + int min + int max + +void +gaim_plugin_pref_set_label(pref, label) + Gaim::PluginPref pref + const char *label + +void +gaim_plugin_pref_set_masked(pref, mask) + Gaim::PluginPref pref + gboolean mask + +void +gaim_plugin_pref_set_max_length(pref, max_length) + Gaim::PluginPref pref + unsigned int max_length + +void +gaim_plugin_pref_set_name(pref, name) + Gaim::PluginPref pref + const char *name + +void +gaim_plugin_pref_set_type(pref, type) + Gaim::PluginPref pref + Gaim::PluginPrefType type +PREINIT: + GaimPluginPrefType gpp_type; +CODE: + gpp_type = GAIM_PLUGIN_PREF_NONE; + + if (type == 1) { + gpp_type = GAIM_PLUGIN_PREF_CHOICE; + } else if (type == 2) { + gpp_type = GAIM_PLUGIN_PREF_INFO; + } + gaim_plugin_pref_set_type(pref, gpp_type); diff --git a/libpurple/plugins/perl/common/Pounce.xs b/libpurple/plugins/perl/common/Pounce.xs new file mode 100644 index 0000000000..d9ab8960ae --- /dev/null +++ b/libpurple/plugins/perl/common/Pounce.xs @@ -0,0 +1,90 @@ +#include "module.h" + +MODULE = Gaim::Pounce PACKAGE = Gaim::Pounce PREFIX = gaim_pounce_ +PROTOTYPES: ENABLE + +void +gaim_pounce_action_register(pounce, name) + Gaim::Pounce pounce + const char *name + +void +gaim_pounce_destroy(pounce) + Gaim::Pounce pounce + +void +gaim_pounce_destroy_all_by_account(account) + Gaim::Account account + +void * +gaim_pounce_get_data(pounce) + Gaim::Pounce pounce + +Gaim::PounceEvent +gaim_pounce_get_events(pounce) + Gaim::Pounce pounce + +const char * +gaim_pounce_get_pouncee(pounce) + Gaim::Pounce pounce + +Gaim::Account +gaim_pounce_get_pouncer(pounce) + Gaim::Pounce pounce + +gboolean +gaim_pounce_get_save(pounce) + Gaim::Pounce pounce + +void +gaim_pounce_set_data(pounce, data) + Gaim::Pounce pounce + void * data + +void +gaim_pounce_set_events(pounce, events) + Gaim::Pounce pounce + Gaim::PounceEvent events + +void +gaim_pounce_set_pouncee(pounce, pouncee) + Gaim::Pounce pounce + const char *pouncee + +void +gaim_pounce_set_pouncer(pounce, pouncer) + Gaim::Pounce pounce + Gaim::Account pouncer + +void +gaim_pounce_set_save(pounce, save) + Gaim::Pounce pounce + gboolean save + +MODULE = Gaim::Pounce PACKAGE = Gaim::Pounces PREFIX = gaim_pounces_ +PROTOTYPES: ENABLE + +void +gaim_pounces_get_all() +PREINIT: + GList *l; +PPCODE: + for (l = gaim_pounces_get_all(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Pounce"))); + } + +Gaim::Handle +gaim_pounces_get_handle() + +void +gaim_pounces_init() + +gboolean +gaim_pounces_load() + +void +gaim_pounces_uninit() + +void +gaim_pounces_unregister_handler(ui) + const char *ui diff --git a/libpurple/plugins/perl/common/Prefs.xs b/libpurple/plugins/perl/common/Prefs.xs new file mode 100644 index 0000000000..5e169b12f7 --- /dev/null +++ b/libpurple/plugins/perl/common/Prefs.xs @@ -0,0 +1,151 @@ +#include "module.h" + +MODULE = Gaim::Prefs PACKAGE = Gaim::Prefs PREFIX = gaim_prefs_ +PROTOTYPES: ENABLE + +void +gaim_prefs_add_bool(name, value) + const char *name + gboolean value + +void +gaim_prefs_add_int(name, value) + const char *name + int value + +void +gaim_prefs_add_none(name) + const char *name + +void +gaim_prefs_add_string(name, value) + const char *name + const char *value + +void +gaim_prefs_add_string_list(name, value) + const char *name + SV *value +PREINIT: + GList *t_GL; + int i, t_len; +PPCODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(value)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(value), i, 0), t_sl)); + } + gaim_prefs_add_string_list(name, t_GL); + +void +gaim_prefs_destroy() + +void +gaim_prefs_disconnect_by_handle(handle) + void * handle + +void +gaim_prefs_disconnect_callback(callback_id) + guint callback_id + +gboolean +gaim_prefs_exists(name) + const char *name + +gboolean +gaim_prefs_get_bool(name) + const char *name + +Gaim::Handle +gaim_prefs_get_handle() + +int +gaim_prefs_get_int(name) + const char *name + +const char * +gaim_prefs_get_string(name) + const char *name + +void +gaim_prefs_get_string_list(name) + const char *name +PREINIT: + GList *l; +PPCODE: + for (l = gaim_prefs_get_string_list(name); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::PrefValue"))); + } + +Gaim::PrefType +gaim_prefs_get_type(name) + const char *name + +void +gaim_prefs_init() + +gboolean +gaim_prefs_load() + +void +gaim_prefs_remove(name) + const char *name + +void +gaim_prefs_rename(oldname, newname) + const char *oldname + const char *newname + +void +gaim_prefs_rename_boolean_toggle(oldname, newname) + const char *oldname + const char *newname + +void +gaim_prefs_set_bool(name, value) + const char *name + gboolean value + +void +gaim_prefs_set_generic(name, value) + const char *name + gpointer value + +void +gaim_prefs_set_int(name, value) + const char *name + int value + +void +gaim_prefs_set_string(name, value) + const char *name + const char *value + +void +gaim_prefs_set_string_list(name, value) + const char *name + SV *value +PREINIT: + GList *t_GL; + int i, t_len; +PPCODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(value)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(value), i, 0), t_sl)); + } + gaim_prefs_set_string_list(name, t_GL); + +void +gaim_prefs_trigger_callback(name) + const char *name + +void +gaim_prefs_uninit() + +void +gaim_prefs_update_old() diff --git a/libpurple/plugins/perl/common/Privacy.xs b/libpurple/plugins/perl/common/Privacy.xs new file mode 100644 index 0000000000..601fe08d4a --- /dev/null +++ b/libpurple/plugins/perl/common/Privacy.xs @@ -0,0 +1,43 @@ +#include "module.h" + +MODULE = Gaim::Privacy PACKAGE = Gaim::Privacy PREFIX = gaim_privacy_ +PROTOTYPES: ENABLE + +Gaim::Privacy::UiOps +gaim_privacy_get_ui_ops() + +void +gaim_privacy_init() + +void +gaim_privacy_set_ui_ops(ops) + Gaim::Privacy::UiOps ops + +gboolean +gaim_privacy_permit_add(account, name, local_only) + Gaim::Account account + const char * name + gboolean local_only + +gboolean +gaim_privacy_permit_remove(account, name, local_only) + Gaim::Account account + const char * name + gboolean local_only + +gboolean +gaim_privacy_deny_add(account, name, local_only) + Gaim::Account account + const char * name + gboolean local_only + +gboolean +gaim_privacy_deny_remove(account, name, local_only) + Gaim::Account account + const char * name + gboolean local_only + +gboolean +gaim_privacy_check(account, who) + Gaim::Account account + const char * who diff --git a/libpurple/plugins/perl/common/Proxy.xs b/libpurple/plugins/perl/common/Proxy.xs new file mode 100644 index 0000000000..f21e737431 --- /dev/null +++ b/libpurple/plugins/perl/common/Proxy.xs @@ -0,0 +1,65 @@ +#include "module.h" + +MODULE = Gaim::Proxy PACKAGE = Gaim::Proxy PREFIX = gaim_proxy_ +PROTOTYPES: ENABLE + +Gaim::ProxyInfo +gaim_global_proxy_get_info() + +Gaim::Handle +gaim_proxy_get_handle() + +void +gaim_proxy_info_destroy(info) + Gaim::ProxyInfo info + +const char * +gaim_proxy_info_get_host(info) + Gaim::ProxyInfo info + +const char * +gaim_proxy_info_get_password(info) + Gaim::ProxyInfo info + +int +gaim_proxy_info_get_port(info) + Gaim::ProxyInfo info + +Gaim::ProxyType +gaim_proxy_info_get_type(info) + Gaim::ProxyInfo info + +const char * +gaim_proxy_info_get_username(info) + Gaim::ProxyInfo info + +Gaim::ProxyInfo +gaim_proxy_info_new() + +void +gaim_proxy_info_set_host(info, host) + Gaim::ProxyInfo info + const char *host + +void +gaim_proxy_info_set_password(info, password) + Gaim::ProxyInfo info + const char *password + +void +gaim_proxy_info_set_port(info, port) + Gaim::ProxyInfo info + int port + +void +gaim_proxy_info_set_type(info, type) + Gaim::ProxyInfo info + Gaim::ProxyType type + +void +gaim_proxy_info_set_username(info, username) + Gaim::ProxyInfo info + const char *username + +void +gaim_proxy_init() diff --git a/libpurple/plugins/perl/common/Prpl.xs b/libpurple/plugins/perl/common/Prpl.xs new file mode 100644 index 0000000000..757d4161cb --- /dev/null +++ b/libpurple/plugins/perl/common/Prpl.xs @@ -0,0 +1,54 @@ +#include "module.h" + +MODULE = Gaim::Prpl PACKAGE = Gaim::Find PREFIX = gaim_find_ +PROTOTYPES: ENABLE + +Gaim::Plugin +gaim_find_prpl(id) + const char *id + +MODULE = Gaim::Prpl PACKAGE = Gaim::Prpl PREFIX = gaim_prpl_ +PROTOTYPES: ENABLE + +void +gaim_prpl_change_account_status(account, old_status, new_status) + Gaim::Account account + Gaim::Status old_status + Gaim::Status new_status + +void +gaim_prpl_get_statuses(account, presence) + Gaim::Account account + Gaim::Presence presence +PREINIT: + GList *l; +PPCODE: + for (l = gaim_prpl_get_statuses(account,presence); l != NULL; l = l->next) { + /* XXX Someone please test and make sure this is the right + * type for these things. */ + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Status"))); + } + +void +gaim_prpl_got_account_idle(account, idle, idle_time) + Gaim::Account account + gboolean idle + time_t idle_time + +void +gaim_prpl_got_account_login_time(account, login_time) + Gaim::Account account + time_t login_time + +void +gaim_prpl_got_user_idle(account, name, idle, idle_time) + Gaim::Account account + const char *name + gboolean idle + time_t idle_time + +void +gaim_prpl_got_user_login_time(account, name, login_time) + Gaim::Account account + const char *name + time_t login_time diff --git a/libpurple/plugins/perl/common/Request.xs b/libpurple/plugins/perl/common/Request.xs new file mode 100644 index 0000000000..ba5369a8b4 --- /dev/null +++ b/libpurple/plugins/perl/common/Request.xs @@ -0,0 +1,583 @@ +#include "module.h" + +/* This breaks on faceprint's amd64 box +void * +gaim_request_action_varg(handle, title, primary, secondary, default_action, user_data, action_count, actions) + void * handle + const char *title + const char *primary + const char *secondary + unsigned int default_action + void *user_data + size_t action_count + va_list actions + */ + + +typedef struct { + char *cancel_cb; + char *ok_cb; +} GaimPerlRequestData; + +/********************************************************/ +/* */ +/* Callback function that calls a perl subroutine */ +/* */ +/* The void * field data is being used as a way to hide */ +/* the perl sub's name in a GaimPerlRequestData */ +/* */ +/********************************************************/ +static void +gaim_perl_request_ok_cb(void * data, GaimRequestFields *fields) +{ + GaimPerlRequestData *gpr = (GaimPerlRequestData *)data; + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(sp); + + XPUSHs(gaim_perl_bless_object(fields, "Gaim::Request::Fields")); + PUTBACK; + + call_pv(gpr->ok_cb, G_EVAL | G_SCALAR); + SPAGAIN; + + PUTBACK; + FREETMPS; + LEAVE; + + g_free(gpr->ok_cb); + g_free(gpr->cancel_cb); + g_free(gpr); +} + +static void +gaim_perl_request_cancel_cb(void * data, GaimRequestFields *fields) +{ + + GaimPerlRequestData *gpr = (GaimPerlRequestData *)data; + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(sp); + + XPUSHs(gaim_perl_bless_object(fields, "Gaim::Request::Fields")); + PUTBACK; + call_pv(gpr->cancel_cb, G_EVAL | G_SCALAR); + SPAGAIN; + + PUTBACK; + FREETMPS; + LEAVE; + + g_free(gpr->ok_cb); + g_free(gpr->cancel_cb); + g_free(gpr); +} + +MODULE = Gaim::Request PACKAGE = Gaim::Request PREFIX = gaim_request_ +PROTOTYPES: ENABLE + +void * +gaim_request_input(handle, title, primary, secondary, default_value, multiline, masked, hint, ok_text, ok_cb, cancel_text, cancel_cb) + Gaim::Plugin handle + const char * title + const char * primary + const char * secondary + const char * default_value + gboolean multiline + gboolean masked + gchar * hint + const char * ok_text + SV * ok_cb + const char * cancel_text + SV * cancel_cb +CODE: + GaimPerlRequestData *gpr; + STRLEN len; + char *basename; + + basename = g_path_get_basename(handle->path); + gaim_perl_normalize_script_name(basename); + gpr = g_new(GaimPerlRequestData, 1); + gpr->ok_cb = g_strdup_printf("Gaim::Script::%s::%s", basename, SvPV(ok_cb, len)); + gpr->cancel_cb = g_strdup_printf("Gaim::Script::%s::%s", basename, SvPV(cancel_cb, len)); + g_free(basename); + + RETVAL = gaim_request_input(handle, title, primary, secondary, default_value, multiline, masked, hint, ok_text, G_CALLBACK(gaim_perl_request_ok_cb), cancel_text, G_CALLBACK(gaim_perl_request_cancel_cb), gpr); +OUTPUT: + RETVAL + +void * +gaim_request_file(handle, title, filename, savedialog, ok_cb, cancel_cb) + Gaim::Plugin handle + const char * title + const char * filename + gboolean savedialog + SV * ok_cb + SV * cancel_cb +CODE: + GaimPerlRequestData *gpr; + STRLEN len; + char *basename; + + basename = g_path_get_basename(handle->path); + gaim_perl_normalize_script_name(basename); + gpr = g_new(GaimPerlRequestData, 1); + gpr->ok_cb = g_strdup_printf("Gaim::Script::%s::%s", basename, SvPV(ok_cb, len)); + gpr->cancel_cb = g_strdup_printf("Gaim::Script::%s::%s", basename, SvPV(cancel_cb, len)); + g_free(basename); + + RETVAL = gaim_request_file(handle, title, filename, savedialog, G_CALLBACK(gaim_perl_request_ok_cb), G_CALLBACK(gaim_perl_request_cancel_cb), gpr); +OUTPUT: + RETVAL + +void * +gaim_request_fields(handle, title, primary, secondary, fields, ok_text, ok_cb, cancel_text, cancel_cb) + Gaim::Plugin handle + const char * title + const char * primary + const char * secondary + Gaim::Request::Fields fields + const char * ok_text + SV * ok_cb + const char * cancel_text + SV * cancel_cb +CODE: + GaimPerlRequestData *gpr; + STRLEN len; + char *basename; + + basename = g_path_get_basename(handle->path); + gaim_perl_normalize_script_name(basename); + gpr = g_new(GaimPerlRequestData, 1); + gpr->ok_cb = g_strdup_printf("Gaim::Script::%s::%s", basename, SvPV(ok_cb, len)); + gpr->cancel_cb = g_strdup_printf("Gaim::Script::%s::%s", basename, SvPV(cancel_cb, len)); + g_free(basename); + + RETVAL = gaim_request_fields(handle, title, primary, secondary, fields, ok_text, G_CALLBACK(gaim_perl_request_ok_cb), cancel_text, G_CALLBACK(gaim_perl_request_cancel_cb), gpr); +OUTPUT: + RETVAL + +void +gaim_request_close(type, uihandle) + Gaim::RequestType type + void * uihandle + +void +gaim_request_close_with_handle(handle) + void * handle + +MODULE = Gaim::Request PACKAGE = Gaim::Request::Field PREFIX = gaim_request_field_ +PROTOTYPES: ENABLE + +Gaim::Account +gaim_request_field_account_get_default_value(field) + Gaim::Request::Field field + +IV +gaim_request_field_account_get_filter(field) + Gaim::Request::Field field +CODE: + RETVAL = PTR2IV(gaim_request_field_account_get_filter(field)); +OUTPUT: + RETVAL + +gboolean +gaim_request_field_account_get_show_all(field) + Gaim::Request::Field field + +Gaim::Account +gaim_request_field_account_get_value(field) + Gaim::Request::Field field + +Gaim::Request::Field +gaim_request_field_account_new(id, text, account = NULL) + const char *id + const char *text + Gaim::Account account + +void +gaim_request_field_account_set_default_value(field, default_value) + Gaim::Request::Field field + Gaim::Account default_value + +void +gaim_request_field_account_set_show_all(field, show_all) + Gaim::Request::Field field + gboolean show_all + +void +gaim_request_field_account_set_value(field, value) + Gaim::Request::Field field + Gaim::Account value + +gboolean +gaim_request_field_bool_get_default_value(field) + Gaim::Request::Field field + +gboolean +gaim_request_field_bool_get_value(field) + Gaim::Request::Field field + +Gaim::Request::Field +gaim_request_field_bool_new(id, text, default_value = TRUE) + const char *id + const char *text + gboolean default_value + +void +gaim_request_field_bool_set_default_value(field, default_value) + Gaim::Request::Field field + gboolean default_value + +void +gaim_request_field_bool_set_value(field, value) + Gaim::Request::Field field + gboolean value + +void +gaim_request_field_choice_add(field, label) + Gaim::Request::Field field + const char *label + +int +gaim_request_field_choice_get_default_value(field) + Gaim::Request::Field field + +void +gaim_request_field_choice_get_labels(field) + Gaim::Request::Field field +PREINIT: + GList *l; +PPCODE: + for (l = gaim_request_field_choice_get_labels(field); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(newSVpv(l->data, 0))); + } + +int +gaim_request_field_choice_get_value(field) + Gaim::Request::Field field + +Gaim::Request::Field +gaim_request_field_choice_new(id, text, default_value = 0) + const char *id + const char *text + int default_value + +void +gaim_request_field_choice_set_default_value(field, default_value) + Gaim::Request::Field field + int default_value + +void +gaim_request_field_choice_set_value(field, value) + Gaim::Request::Field field + int value + +void +gaim_request_field_destroy(field) + Gaim::Request::Field field + +const char * +gaim_request_field_get_id(field) + Gaim::Request::Field field + +const char * +gaim_request_field_get_label(field) + Gaim::Request::Field field + +Gaim::RequestFieldType +gaim_request_field_get_type(field) + Gaim::Request::Field field + +const char * +gaim_request_field_get_type_hint(field) + Gaim::Request::Field field + +int +gaim_request_field_int_get_default_value(field) + Gaim::Request::Field field + +int +gaim_request_field_int_get_value(field) + Gaim::Request::Field field + +Gaim::Request::Field +gaim_request_field_int_new(id, text, default_value = 0) + const char *id + const char *text + int default_value + +void +gaim_request_field_int_set_default_value(field, default_value) + Gaim::Request::Field field + int default_value + +void +gaim_request_field_int_set_value(field, value) + Gaim::Request::Field field + int value + +gboolean +gaim_request_field_is_required(field) + Gaim::Request::Field field + +gboolean +gaim_request_field_is_visible(field) + Gaim::Request::Field field + +Gaim::Request::Field +gaim_request_field_label_new(id, text) + const char *id + const char *text + +void +gaim_request_field_list_add(field, item, data) + Gaim::Request::Field field + const char *item + void * data + +void +gaim_request_field_list_add_selected(field, item) + Gaim::Request::Field field + const char *item + +void +gaim_request_field_list_clear_selected(field) + Gaim::Request::Field field + +void * +gaim_request_field_list_get_data(field, text) + Gaim::Request::Field field + const char *text + +void +gaim_request_field_list_get_items(field) + Gaim::Request::Field field +PREINIT: + const GList *l; +PPCODE: + for (l = gaim_request_field_list_get_items(field); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(newSVpv(l->data, 0))); + } + +gboolean +gaim_request_field_list_get_multi_select(field) + Gaim::Request::Field field + +void +gaim_request_field_list_get_selected(field) + Gaim::Request::Field field +PREINIT: + const GList *l; +PPCODE: + for (l = gaim_request_field_list_get_selected(field); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(newSVpv(l->data, 0))); + } + +gboolean +gaim_request_field_list_is_selected(field, item) + Gaim::Request::Field field + const char *item + +Gaim::Request::Field +gaim_request_field_list_new(id, text) + const char *id + const char *text + +void +gaim_request_field_list_set_multi_select(field, multi_select) + Gaim::Request::Field field + gboolean multi_select + +Gaim::Request::Field +gaim_request_field_new(id, text, type) + const char *id + const char *text + Gaim::RequestFieldType type + +void +gaim_request_field_set_label(field, label) + Gaim::Request::Field field + const char *label + +void +gaim_request_field_set_required(field, required) + Gaim::Request::Field field + gboolean required + +void +gaim_request_field_set_type_hint(field, type_hint) + Gaim::Request::Field field + const char *type_hint + +void +gaim_request_field_set_visible(field, visible) + Gaim::Request::Field field + gboolean visible + +const char * +gaim_request_field_string_get_default_value(field) + Gaim::Request::Field field + +const char * +gaim_request_field_string_get_value(field) + Gaim::Request::Field field + +gboolean +gaim_request_field_string_is_editable(field) + Gaim::Request::Field field + +gboolean +gaim_request_field_string_is_masked(field) + Gaim::Request::Field field + +gboolean +gaim_request_field_string_is_multiline(field) + Gaim::Request::Field field + +Gaim::Request::Field +gaim_request_field_string_new(id, text, default_value, multiline) + const char *id + const char *text + const char *default_value + gboolean multiline + +void +gaim_request_field_string_set_default_value(field, default_value) + Gaim::Request::Field field + const char *default_value + +void +gaim_request_field_string_set_editable(field, editable) + Gaim::Request::Field field + gboolean editable + +void +gaim_request_field_string_set_masked(field, masked) + Gaim::Request::Field field + gboolean masked + +void +gaim_request_field_string_set_value(field, value) + Gaim::Request::Field field + const char *value + +Gaim::Request::UiOps +gaim_request_get_ui_ops() + +void +gaim_request_set_ui_ops(ops) + Gaim::Request::UiOps ops + +MODULE = Gaim::Request PACKAGE = Gaim::Request::Field::Group PREFIX = gaim_request_field_group_ +PROTOTYPES: ENABLE + +void +gaim_request_field_group_add_field(group, field) + Gaim::Request::Field::Group group + Gaim::Request::Field field + +void +gaim_request_field_group_destroy(group) + Gaim::Request::Field::Group group + +void +gaim_request_field_group_get_fields(group) + Gaim::Request::Field::Group group +PREINIT: + GList *l; +PPCODE: + for (l = gaim_request_field_group_get_fields(group); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Request::Field"))); + } + +const char * +gaim_request_field_group_get_title(group) + Gaim::Request::Field::Group group + +Gaim::Request::Field::Group +gaim_request_field_group_new(title) + const char *title + +MODULE = Gaim::Request PACKAGE = Gaim::Request::Fields PREFIX = gaim_request_fields_ +PROTOTYPES: ENABLE + +void +gaim_request_fields_add_group(fields, group) + Gaim::Request::Fields fields + Gaim::Request::Field::Group group + +gboolean +gaim_request_fields_all_required_filled(fields) + Gaim::Request::Fields fields + +void +gaim_request_fields_destroy(fields) + Gaim::Request::Fields fields + +gboolean +gaim_request_fields_exists(fields, id) + Gaim::Request::Fields fields + const char *id + +Gaim::Account +gaim_request_fields_get_account(fields, id) + Gaim::Request::Fields fields + const char *id + +gboolean +gaim_request_fields_get_bool(fields, id) + Gaim::Request::Fields fields + const char *id + +int +gaim_request_fields_get_choice(fields, id) + Gaim::Request::Fields fields + const char *id + +Gaim::Request::Field +gaim_request_fields_get_field(fields, id) + Gaim::Request::Fields fields + const char *id + +void +gaim_request_fields_get_groups(fields) + Gaim::Request::Fields fields +PREINIT: + GList *l; +PPCODE: + for (l = gaim_request_fields_get_groups(fields); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Request::Field::Group"))); + } + +int +gaim_request_fields_get_integer(fields, id) + Gaim::Request::Fields fields + const char *id + +void +gaim_request_fields_get_required(fields) + Gaim::Request::Fields fields +PREINIT: + const GList *l; +PPCODE: + for (l = gaim_request_fields_get_required(fields); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Request::Field"))); + } + +const char * +gaim_request_fields_get_string(fields, id) + Gaim::Request::Fields fields + const char *id + +gboolean +gaim_request_fields_is_field_required(fields, id) + Gaim::Request::Fields fields + const char *id + +Gaim::Request::Fields +gaim_request_fields_new() diff --git a/libpurple/plugins/perl/common/Roomlist.xs b/libpurple/plugins/perl/common/Roomlist.xs new file mode 100644 index 0000000000..eee92aee15 --- /dev/null +++ b/libpurple/plugins/perl/common/Roomlist.xs @@ -0,0 +1,84 @@ +#include "module.h" + +MODULE = Gaim::Roomlist PACKAGE = Gaim::Roomlist PREFIX = gaim_roomlist_ +PROTOTYPES: ENABLE + +void +gaim_roomlist_cancel_get_list(list) + Gaim::Roomlist list + +void +gaim_roomlist_expand_category(list, category) + Gaim::Roomlist list + Gaim::Roomlist::Room category + +gboolean +gaim_roomlist_get_in_progress(list) + Gaim::Roomlist list + +Gaim::Roomlist +gaim_roomlist_get_list(gc) + Gaim::Connection gc + +Gaim::Roomlist::UiOps +gaim_roomlist_get_ui_ops() + + +Gaim::Roomlist +gaim_roomlist_new(account) + Gaim::Account account + +void +gaim_roomlist_ref(list) + Gaim::Roomlist list + +void +gaim_roomlist_room_add(list, room) + Gaim::Roomlist list + Gaim::Roomlist::Room room + +void +gaim_roomlist_room_add_field(list, room, field) + Gaim::Roomlist list + Gaim::Roomlist::Room room + gconstpointer field + +void +gaim_roomlist_room_join(list, room) + Gaim::Roomlist list + Gaim::Roomlist::Room room + +void +gaim_roomlist_set_fields(list, fields) + Gaim::Roomlist list + SV *fields +PREINIT: + GList *t_GL; + int i, t_len; +PPCODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(fields)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(fields), i, 0), t_sl)); + } + gaim_roomlist_set_fields(list, t_GL); + +void +gaim_roomlist_set_in_progress(list, in_progress) + Gaim::Roomlist list + gboolean in_progress + +void +gaim_roomlist_set_ui_ops(ops) + Gaim::Roomlist::UiOps ops + +void +gaim_roomlist_show_with_account(account) + Gaim::Account account + +void +gaim_roomlist_unref(list) + Gaim::Roomlist list + diff --git a/libpurple/plugins/perl/common/SSLConn.xs b/libpurple/plugins/perl/common/SSLConn.xs new file mode 100644 index 0000000000..2b6e5ac00e --- /dev/null +++ b/libpurple/plugins/perl/common/SSLConn.xs @@ -0,0 +1,61 @@ +#include "module.h" + +/* TODO + + +Gaim::Ssl::Connection +gaim_ssl_connect(account, host, port, func, error_func, data) + Gaim::Account account + const char *host + int port + GaimSslInputFunction func + GaimSslErrorFunction error_func + +void +gaim_ssl_input_add(gsc, func, data) + Gaim::Ssl::Connection gsc + Gaim::SslInputFunction func + +Gaim::Ssl::Connection +gaim_ssl_connect_fd(account, fd, func, error_func, data) + Gaim::Account account + int fd + GaimSslInputFunction func + GaimSslErrorFunction error_func + +*/ + +MODULE = Gaim::SSL PACKAGE = Gaim::SSL PREFIX = gaim_ssl_ +PROTOTYPES: ENABLE + +void +gaim_ssl_close(gsc) + Gaim::Ssl::Connection gsc + +Gaim::Ssl::Ops +gaim_ssl_get_ops() + +void +gaim_ssl_init() + +gboolean +gaim_ssl_is_supported() + +size_t +gaim_ssl_read(gsc, buffer, len) + Gaim::Ssl::Connection gsc + void * buffer + size_t len + +void +gaim_ssl_set_ops(ops) + Gaim::Ssl::Ops ops + +void +gaim_ssl_uninit() + +size_t +gaim_ssl_write(gsc, buffer, len) + Gaim::Ssl::Connection gsc + void * buffer + size_t len diff --git a/libpurple/plugins/perl/common/SavedStatuses.xs b/libpurple/plugins/perl/common/SavedStatuses.xs new file mode 100644 index 0000000000..8851ed84bb --- /dev/null +++ b/libpurple/plugins/perl/common/SavedStatuses.xs @@ -0,0 +1,58 @@ +#include "module.h" + +MODULE = Gaim::SavedStatus PACKAGE = Gaim::SavedStatus PREFIX = gaim_savedstatus_ +PROTOTYPES: ENABLE + +gboolean +gaim_savedstatus_delete(title) + const char *title + +Gaim::SavedStatus +gaim_savedstatus_find(title) + const char *title + +const char * +gaim_savedstatus_get_message(saved_status) + Gaim::SavedStatus saved_status + +const char * +gaim_savedstatus_get_title(saved_status) + Gaim::SavedStatus saved_status + +Gaim::StatusPrimitive +gaim_savedstatus_get_type(saved_status) + Gaim::SavedStatus saved_status + +Gaim::SavedStatus +gaim_savedstatus_new(title, type) + const char *title + Gaim::StatusPrimitive type + +void +gaim_savedstatus_set_message(status, message) + Gaim::SavedStatus status + const char *message + +Gaim::SavedStatus +gaim_savedstatus_get_current() + +MODULE = Gaim::SavedStatus PACKAGE = Gaim::SavedStatuses PREFIX = gaim_savedstatuses_ +PROTOTYPES: ENABLE + +void +gaim_savedstatuses_get_all() +PREINIT: + const GList *l; +PPCODE: + for (l = gaim_savedstatuses_get_all(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::SavedStatus"))); + } + +Gaim::Handle +gaim_savedstatuses_get_handle() + +void +gaim_savedstatuses_init() + +void +gaim_savedstatuses_uninit() diff --git a/libpurple/plugins/perl/common/Server.xs b/libpurple/plugins/perl/common/Server.xs new file mode 100644 index 0000000000..e5fb5027df --- /dev/null +++ b/libpurple/plugins/perl/common/Server.xs @@ -0,0 +1,216 @@ +#include "module.h" + +MODULE = Gaim::Serv PACKAGE = Gaim::Serv PREFIX = serv_ +PROTOTYPES: ENABLE + + +void +serv_add_deny(con, a) + Gaim::Connection con + const char * a + +void +serv_add_permit(a, b) + Gaim::Connection a + const char * b + +void +serv_alias_buddy(buddy) + Gaim::BuddyList::Buddy buddy + +void +serv_chat_invite(con, a, b, c) + Gaim::Connection con + int a + const char * b + const char * c + +void +serv_chat_leave(a, b) + Gaim::Connection a + int b + +int +serv_chat_send(con, a, b, flags) + Gaim::Connection con + int a + const char * b + Gaim::MessageFlags flags + +void +serv_chat_whisper(con, a, b, c) + Gaim::Connection con + int a + const char * b + const char * c + +void +serv_get_info(con, a) + Gaim::Connection con + const char * a + +void +serv_got_alias(gc, who, alias) + Gaim::Connection gc + const char *who + const char *alias + +void +serv_got_chat_in(g, id, who, chatflags, message, mtime) + Gaim::Connection g + int id + const char *who + Gaim::MessageFlags chatflags + const char *message + time_t mtime + +void +serv_got_chat_invite(gc, name, who, message, components) + Gaim::Connection gc + const char *name + const char *who + const char *message + SV * components +INIT: + HV * t_HV; + HE * t_HE; + SV * t_SV; + GHashTable * t_GHash; + I32 len; + char *t_key, *t_value; +CODE: + t_HV = (HV *)SvRV(components); + t_GHash = g_hash_table_new(g_str_hash, g_str_equal); + + for (t_HE = hv_iternext(t_HV); t_HE != NULL; t_HE = hv_iternext(t_HV) ) { + t_key = hv_iterkey(t_HE, &len); + t_SV = *hv_fetch(t_HV, t_key, len, 0); + t_value = SvPV(t_SV, PL_na); + + g_hash_table_insert(t_GHash, t_key, t_value); + } + serv_got_chat_invite(gc, name, who, message, t_GHash); + +void +serv_got_chat_left(g, id) + Gaim::Connection g + int id + +void +serv_got_im(gc, who, msg, imflags, mtime) + Gaim::Connection gc + const char *who + const char *msg + Gaim::MessageFlags imflags + time_t mtime + +Gaim::Conversation +serv_got_joined_chat(gc, id, name) + Gaim::Connection gc + int id + const char *name + +void +serv_got_typing(gc, name, timeout, state) + Gaim::Connection gc + const char *name + int timeout + Gaim::TypingState state + +void +serv_got_typing_stopped(gc, name) + Gaim::Connection gc + const char *name + +void +serv_join_chat(con, components) + Gaim::Connection con + SV * components +INIT: + HV * t_HV; + HE * t_HE; + SV * t_SV; + GHashTable * t_GHash; + I32 len; + char *t_key, *t_value; +CODE: + t_HV = (HV *)SvRV(components); + t_GHash = g_hash_table_new(g_str_hash, g_str_equal); + + for (t_HE = hv_iternext(t_HV); t_HE != NULL; t_HE = hv_iternext(t_HV) ) { + t_key = hv_iterkey(t_HE, &len); + t_SV = *hv_fetch(t_HV, t_key, len, 0); + t_value = SvPV(t_SV, PL_na); + + g_hash_table_insert(t_GHash, t_key, t_value); + } + serv_join_chat(con, t_GHash); + +void +serv_move_buddy(buddy, group1, group2) + Gaim::BuddyList::Buddy buddy + Gaim::BuddyList::Group group1 + Gaim::BuddyList::Group group2 + +void +serv_reject_chat(con, components) + Gaim::Connection con + SV * components +INIT: + HV * t_HV; + HE * t_HE; + SV * t_SV; + GHashTable * t_GHash; + I32 len; + char *t_key, *t_value; +CODE: + t_HV = (HV *)SvRV(components); + t_GHash = g_hash_table_new(g_str_hash, g_str_equal); + + for (t_HE = hv_iternext(t_HV); t_HE != NULL; t_HE = hv_iternext(t_HV) ) { + t_key = hv_iterkey(t_HE, &len); + t_SV = *hv_fetch(t_HV, t_key, len, 0); + t_value = SvPV(t_SV, PL_na); + + g_hash_table_insert(t_GHash, t_key, t_value); + } + serv_reject_chat(con, t_GHash); + +void +serv_rem_deny(con, a) + Gaim::Connection con + const char * a + +void +serv_rem_permit(con, a) + Gaim::Connection con + const char * a + +void +serv_send_file(gc, who, file) + Gaim::Connection gc + const char *who + const char *file + +int +serv_send_im(con, a, b, flags ) + Gaim::Connection con + const char * a + const char * b + Gaim::MessageFlags flags + +int +serv_send_typing(con, a, state) + Gaim::Connection con + const char * a + Gaim::TypingState state + +void +serv_set_info(con, a) + Gaim::Connection con + const char * a + +void +serv_set_permit_deny(con) + Gaim::Connection con + diff --git a/libpurple/plugins/perl/common/Signal.xs b/libpurple/plugins/perl/common/Signal.xs new file mode 100644 index 0000000000..793f1dd223 --- /dev/null +++ b/libpurple/plugins/perl/common/Signal.xs @@ -0,0 +1,34 @@ +#include "module.h" +#include "../perl-handlers.h" + +MODULE = Gaim::Signal PACKAGE = Gaim::Signal PREFIX = gaim_signal_ +PROTOTYPES: ENABLE + +void +gaim_signal_connect_priority(instance, signal, plugin, callback, priority, data = 0) + Gaim::Handle instance + const char *signal + Gaim::Plugin plugin + SV *callback + int priority + SV *data +CODE: + gaim_perl_signal_connect(plugin, instance, signal, callback, data, priority); + +void +gaim_signal_connect(instance, signal, plugin, callback, data = 0) + Gaim::Handle instance + const char *signal + Gaim::Plugin plugin + SV *callback + SV *data +CODE: + gaim_perl_signal_connect(plugin, instance, signal, callback, data, GAIM_SIGNAL_PRIORITY_DEFAULT); + +void +gaim_signal_disconnect(instance, signal, plugin) + Gaim::Handle instance + const char *signal + Gaim::Plugin plugin +CODE: + gaim_perl_signal_disconnect(plugin, instance, signal); diff --git a/libpurple/plugins/perl/common/Sound.xs b/libpurple/plugins/perl/common/Sound.xs new file mode 100644 index 0000000000..0f732797c5 --- /dev/null +++ b/libpurple/plugins/perl/common/Sound.xs @@ -0,0 +1,27 @@ +#include "module.h" + +MODULE = Gaim::Sound PACKAGE = Gaim::Sound PREFIX = gaim_sound_ +PROTOTYPES: ENABLE + +Gaim::Sound::UiOps +gaim_sound_get_ui_ops() + +void +gaim_sound_init() + +void +gaim_sound_play_event(event, account) + Gaim::SoundEventID event + Gaim::Account account + +void +gaim_sound_play_file(filename, account) + const char *filename + Gaim::Account account + +void +gaim_sound_set_ui_ops(ops) + Gaim::Sound::UiOps ops + +void +gaim_sound_uninit() diff --git a/libpurple/plugins/perl/common/Status.xs b/libpurple/plugins/perl/common/Status.xs new file mode 100644 index 0000000000..02ae4b2b4b --- /dev/null +++ b/libpurple/plugins/perl/common/Status.xs @@ -0,0 +1,439 @@ +#include "module.h" + +/* TODO + +void +gaim_status_type_add_attrs(status_type, id, name, value, gaim_status_type_add_attrs) + Gaim::StatusType status_type + const char *id + const char *name + Gaim::Value value + ... + +Gaim::StatusType +gaim_status_type_new_with_attrs(primitive, id, name, saveable, user_settable, independent, attr_id, attr_name, attr_value, gaim_status_type_new_with_attrs) + Gaim::StatusPrimitive primitive + const char *id + const char *name + gboolean saveable + gboolean user_settable + gboolean independent + const char *attr_id + const char *attr_name + Gaim::Value attr_value + ... + +*/ + +/* These break on faceprint's amd64 box +void +gaim_status_type_add_attrs_vargs(status_type, args) + Gaim::StatusType status_type + va_list args + +void +gaim_status_set_active_with_attrs(status, active, args) + Gaim::Status status + gboolean active + va_list args + + */ + +MODULE = Gaim::Status PACKAGE = Gaim::Presence PREFIX = gaim_presence_ +PROTOTYPES: ENABLE + +void +gaim_presence_add_list(presence, source_list) + Gaim::Presence presence + SV *source_list +PREINIT: + GList *t_GL; + int i, t_len; +PPCODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(source_list)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(source_list), i, 0), t_sl)); + } + gaim_presence_add_list(presence, t_GL); + +void +gaim_presence_add_status(presence, status) + Gaim::Presence presence + Gaim::Status status + +gint +gaim_presence_compare(presence1, presence2) + Gaim::Presence presence1 + Gaim::Presence presence2 + +void +gaim_presence_destroy(presence) + Gaim::Presence presence + +Gaim::Account +gaim_presence_get_account(presence) + Gaim::Presence presence + +Gaim::Status +gaim_presence_get_active_status(presence) + Gaim::Presence presence + +void +gaim_presence_get_buddies(presence) + Gaim::Presence presence +PREINIT: + const GList *l; +PPCODE: + for (l = gaim_presence_get_buddies(presence); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::BuddyList::Buddy"))); + } + +const char * +gaim_presence_get_chat_user(presence) + Gaim::Presence presence + +Gaim::PresenceContext +gaim_presence_get_context(presence) + Gaim::Presence presence + +Gaim::Conversation +gaim_presence_get_conversation(presence) + Gaim::Presence presence + +time_t +gaim_presence_get_idle_time(presence) + Gaim::Presence presence + +time_t +gaim_presence_get_login_time(presence) + Gaim::Presence presence + +Gaim::Status +gaim_presence_get_status(presence, status_id) + Gaim::Presence presence + const char *status_id + +void +gaim_presence_get_statuses(presence) + Gaim::Presence presence +PREINIT: + const GList *l; +PPCODE: + for (l = gaim_presence_get_statuses(presence); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::Status"))); + } + +gboolean +gaim_presence_is_available(presence) + Gaim::Presence presence + +gboolean +gaim_presence_is_idle(presence) + Gaim::Presence presence + +gboolean +gaim_presence_is_online(presence) + Gaim::Presence presence + +gboolean +gaim_presence_is_status_active(presence, status_id) + Gaim::Presence presence + const char *status_id + +gboolean +gaim_presence_is_status_primitive_active(presence, primitive) + Gaim::Presence presence + Gaim::StatusPrimitive primitive + +Gaim::Presence +gaim_presence_new(context) + Gaim::PresenceContext context + +Gaim::Presence +gaim_presence_new_for_account(account) + Gaim::Account account + +Gaim::Presence +gaim_presence_new_for_buddy(buddy) + Gaim::BuddyList::Buddy buddy + +Gaim::Presence +gaim_presence_new_for_conv(conv) + Gaim::Conversation conv + +void +gaim_presence_remove_buddy(presence, buddy) + Gaim::Presence presence + Gaim::BuddyList::Buddy buddy + +void +gaim_presence_set_idle(presence, idle, idle_time) + Gaim::Presence presence + gboolean idle + time_t idle_time + +void +gaim_presence_set_login_time(presence, login_time) + Gaim::Presence presence + time_t login_time + +void +gaim_presence_set_status_active(presence, status_id, active) + Gaim::Presence presence + const char *status_id + gboolean active + +void +gaim_presence_switch_status(presence, status_id) + Gaim::Presence presence + const char *status_id + +MODULE = Gaim::Status PACKAGE = Gaim::Primitive PREFIX = gaim_primitive_ +PROTOTYPES: ENABLE + +const char * +gaim_primitive_get_id_from_type(type) + Gaim::StatusPrimitive type + +const char * +gaim_primitive_get_name_from_type(type) + Gaim::StatusPrimitive type + +Gaim::StatusPrimitive +gaim_primitive_get_type_from_id(id) + const char *id + +MODULE = Gaim::Status PACKAGE = Gaim::StatusAttr PREFIX = gaim_status_attr_ +PROTOTYPES: ENABLE + +void +gaim_status_attr_destroy(attr) + Gaim::StatusAttr attr + +const char * +gaim_status_attr_get_id(attr) + Gaim::StatusAttr attr + +const char * +gaim_status_attr_get_name(attr) + Gaim::StatusAttr attr + +Gaim::Value +gaim_status_attr_get_value(attr) + Gaim::StatusAttr attr + +Gaim::StatusAttr +gaim_status_attr_new(id, name, value_type) + const char *id + const char *name + Gaim::Value value_type + +MODULE = Gaim::Status PACKAGE = Gaim::Status PREFIX = gaim_status_ +PROTOTYPES: ENABLE + +gint +gaim_status_compare(status1, status2) + Gaim::Status status1 + Gaim::Status status2 + +void +gaim_status_destroy(status) + Gaim::Status status + +gboolean +gaim_status_get_attr_boolean(status, id) + Gaim::Status status + const char *id + +int +gaim_status_get_attr_int(status, id) + Gaim::Status status + const char *id + +const char * +gaim_status_get_attr_string(status, id) + Gaim::Status status + const char *id + +Gaim::Value +gaim_status_get_attr_value(status, id) + Gaim::Status status + const char *id + +Gaim::Handle +gaim_status_get_handle() + +const char * +gaim_status_get_id(status) + Gaim::Status status + +const char * +gaim_status_get_name(status) + Gaim::Status status + +Gaim::Presence +gaim_status_get_presence(status) + Gaim::Status status + +Gaim::StatusType +gaim_status_get_type(status) + Gaim::Status status + +gboolean +gaim_status_is_active(status) + Gaim::Status status + +gboolean +gaim_status_is_available(status) + Gaim::Status status + +gboolean +gaim_status_is_exclusive(status) + Gaim::Status status + +gboolean +gaim_status_is_independent(status) + Gaim::Status status + +gboolean +gaim_status_is_online(status) + Gaim::Status status + +Gaim::Status +gaim_status_new(status_type, presence) + Gaim::StatusType status_type + Gaim::Presence presence + +void +gaim_status_set_active(status, active) + Gaim::Status status + gboolean active + +void +gaim_status_set_attr_boolean(status, id, value) + Gaim::Status status + const char *id + gboolean value + +void +gaim_status_set_attr_string(status, id, value) + Gaim::Status status + const char *id + const char *value + +void +gaim_status_init() + +void +gaim_status_uninit() + +MODULE = Gaim::Status PACKAGE = Gaim::StatusType PREFIX = gaim_status_type_ +PROTOTYPES: ENABLE + +void +gaim_status_type_add_attr(status_type, id, name, value) + Gaim::StatusType status_type + const char *id + const char *name + Gaim::Value value + +void +gaim_status_type_destroy(status_type) + Gaim::StatusType status_type + +Gaim::StatusType +gaim_status_type_find_with_id(status_types, id) + SV *status_types + const char *id +PREINIT: +/* XXX Check that this function actually works, I think it might need a */ +/* status_type as it's first argument to work as $status_type->find_with_id */ +/* properly. */ + GList *t_GL; + int i, t_len; +CODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(status_types)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(status_types), i, 0), t_sl)); + } + RETVAL = (GaimStatusType *)gaim_status_type_find_with_id(t_GL, id); +OUTPUT: + RETVAL + +Gaim::StatusAttr +gaim_status_type_get_attr(status_type, id) + Gaim::StatusType status_type + const char *id + +void +gaim_status_type_get_attrs(status_type) + Gaim::StatusType status_type +PREINIT: + const GList *l; +PPCODE: + for (l = gaim_status_type_get_attrs(status_type); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(gaim_perl_bless_object(l->data, "Gaim::StatusAttr"))); + } + +const char * +gaim_status_type_get_id(status_type) + Gaim::StatusType status_type + +const char * +gaim_status_type_get_name(status_type) + Gaim::StatusType status_type + +const char * +gaim_status_type_get_primary_attr(status_type) + Gaim::StatusType status_type + +Gaim::StatusPrimitive +gaim_status_type_get_primitive(status_type) + Gaim::StatusType status_type + +gboolean +gaim_status_type_is_available(status_type) + Gaim::StatusType status_type + +gboolean +gaim_status_type_is_exclusive(status_type) + Gaim::StatusType status_type + +gboolean +gaim_status_type_is_independent(status_type) + Gaim::StatusType status_type + +gboolean +gaim_status_type_is_saveable(status_type) + Gaim::StatusType status_type + +gboolean +gaim_status_type_is_user_settable(status_type) + Gaim::StatusType status_type + +Gaim::StatusType +gaim_status_type_new(primitive, id, name, user_settable) + Gaim::StatusPrimitive primitive + const char *id + const char *name + gboolean user_settable + +Gaim::StatusType +gaim_status_type_new_full(primitive, id, name, saveable, user_settable, independent) + Gaim::StatusPrimitive primitive + const char *id + const char *name + gboolean saveable + gboolean user_settable + gboolean independent + +void +gaim_status_type_set_primary_attr(status_type, attr_id) + Gaim::StatusType status_type + const char *attr_id diff --git a/libpurple/plugins/perl/common/Stringref.xs b/libpurple/plugins/perl/common/Stringref.xs new file mode 100644 index 0000000000..710cab42ec --- /dev/null +++ b/libpurple/plugins/perl/common/Stringref.xs @@ -0,0 +1,37 @@ +#include "module.h" + +MODULE = Gaim::Stringref PACKAGE = Gaim::Stringref PREFIX = gaim_stringref_ +PROTOTYPES: ENABLE + +int +gaim_stringref_cmp(s1, s2) + Gaim::Stringref s1 + Gaim::Stringref s2 + +size_t +gaim_stringref_len(stringref) + Gaim::Stringref stringref + +Gaim::Stringref +gaim_stringref_new(class, value) + const char *value + C_ARGS: + value + +Gaim::Stringref +gaim_stringref_new_noref(class, value) + const char *value + C_ARGS: + value + +Gaim::Stringref +gaim_stringref_ref(stringref) + Gaim::Stringref stringref + +void +gaim_stringref_unref(stringref) + Gaim::Stringref stringref + +const char * +gaim_stringref_value(stringref) + Gaim::Stringref stringref diff --git a/libpurple/plugins/perl/common/Util.xs b/libpurple/plugins/perl/common/Util.xs new file mode 100644 index 0000000000..815507d7a8 --- /dev/null +++ b/libpurple/plugins/perl/common/Util.xs @@ -0,0 +1,267 @@ +#include "module.h" + +typedef struct { + char *cb; +} GaimPerlUrlData; + +static void gaim_perl_util_url_cb(GaimUtilFetchUrlData *url_data, void *user_data, const gchar *url_text, size_t size, const gchar *error_message) { + GaimPerlUrlData *gpr = (GaimPerlUrlData *)user_data; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + + XPUSHs(sv_2mortal(newSVpvn(url_text, size))); + PUTBACK; + + call_pv(gpr->cb, G_EVAL | G_SCALAR); + SPAGAIN; + + g_free(gpr->cb); + g_free(gpr); + + PUTBACK; + FREETMPS; + LEAVE; +} + +MODULE = Gaim::Util PACKAGE = Gaim::Util PREFIX = gaim_ +PROTOTYPES: ENABLE + +void +gaim_util_fetch_url(handle, url, full, user_agent, http11, cb) + Gaim::Plugin handle + const char *url + gboolean full + const char *user_agent + gboolean http11 + SV * cb +CODE: + GaimPerlUrlData *gpr; + STRLEN len; + char *basename; + + basename = g_path_get_basename(handle->path); + gaim_perl_normalize_script_name(basename); + gpr = g_new(GaimPerlUrlData, 1); + + gpr->cb = g_strdup_printf("Gaim::Script::%s::%s", basename, SvPV(cb, len)); + g_free(basename); + gaim_util_fetch_url(url, full, user_agent, http11, gaim_perl_util_url_cb, gpr); + +int +gaim_build_dir(path, mode) + const char *path + int mode + +const char * +gaim_date_format_full(tm) + const struct tm *tm + +const char * +gaim_date_format_long(tm) + const struct tm *tm + +const char * +gaim_date_format_short(tm) + const struct tm *tm + +gboolean +gaim_email_is_valid(address) + const char *address + +const char * +gaim_escape_filename(str) + const char *str + +gchar_own * +gaim_fd_get_ip(fd) + int fd + +const gchar * +gaim_home_dir() + +gboolean +gaim_markup_extract_info_field(str, len, user_info, start_token, skip, end_token, check_value, no_value_token, display_name, is_link, link_prefix, format_cb) + const char *str + int len + Gaim::NotifyUserInfo user_info + const char *start_token + int skip + const char *end_token + char check_value + const char *no_value_token + const char *display_name + gboolean is_link + const char *link_prefix + Gaim::Util::InfoFieldFormatCallback format_cb + +gboolean +gaim_markup_find_tag(needle, haystack, start, end, attributes) + const char *needle + const char *haystack + const char **start + const char **end + GData **attributes + +gchar_own * +gaim_markup_get_tag_name(tag) + const char *tag + +void +gaim_markup_html_to_xhtml(html, dest_xhtml, dest_plain) + const char *html + char **dest_xhtml + char **dest_plain + +gchar_own * +gaim_markup_linkify(str) + const char *str + +gchar_own * +gaim_markup_slice(str, x, y) + const char *str + guint x + guint y + +gchar_own * +gaim_markup_strip_html(str) + const char *str + +gboolean +gaim_message_meify(message, len) + char *message + size_t len + +FILE * +gaim_mkstemp(path, binary) + char **path + gboolean binary + +const char * +gaim_normalize(account, str) + Gaim::Account account + const char *str + +gboolean +gaim_program_is_valid(program) + const char *program + +gchar_own * +gaim_str_add_cr(str) + const char *str + +gchar_own * +gaim_str_binary_to_ascii(binary, len) + const unsigned char *binary + guint len + +gboolean +gaim_str_has_prefix(s, p) + const char *s + const char *p + +gboolean +gaim_str_has_suffix(s, x) + const char *s + const char *x + +gchar_own * +gaim_str_seconds_to_string(sec) + guint sec + +gchar_own * +gaim_str_size_to_units(size) + size_t size + +void +gaim_str_strip_char(str, thechar) + char *str + char thechar + +time_t +gaim_str_to_time(timestamp, utc = FALSE, tm = NULL, tz_off = NULL, rest = NULL) + const char *timestamp + gboolean utc + struct tm *tm + long *tz_off + const char **rest + +gchar_own * +gaim_strcasereplace(string, delimiter, replacement) + const char *string + const char *delimiter + const char *replacement + +const char * +gaim_strcasestr(haystack, needle) + const char *haystack + const char *needle + +gchar_own * +gaim_strdup_withhtml(src) + const gchar *src + +gchar_own * +gaim_strreplace(string, delimiter, replacement) + const char *string + const char *delimiter + const char *replacement + +gchar_own * +gaim_text_strip_mnemonic(in) + const char *in + +time_t +gaim_time_build(year, month, day, hour, min, sec) + int year + int month + int day + int hour + int min + int sec + +const char * +gaim_time_format(tm) + const struct tm *tm + +const char * +gaim_unescape_filename(str) + const char *str + +gchar_own * +gaim_unescape_html(html) + const char *html + +const char * +gaim_url_decode(str) + const char *str + +const char * +gaim_url_encode(str) + const char *str + +gboolean +gaim_url_parse(url, ret_host, ret_port, ret_path, ret_user, ret_passwd) + const char *url + char **ret_host + int *ret_port + char **ret_path + char **ret_user + char **ret_passwd + +const char * +gaim_user_dir() + +const char * +gaim_utf8_strftime(const char *format, const struct tm *tm); + +void +gaim_util_set_user_dir(dir) + const char *dir + +gboolean +gaim_util_write_data_to_file(filename, data, size) + const char *filename + const char *data + size_t size diff --git a/libpurple/plugins/perl/common/XMLNode.xs b/libpurple/plugins/perl/common/XMLNode.xs new file mode 100644 index 0000000000..601b9f36a6 --- /dev/null +++ b/libpurple/plugins/perl/common/XMLNode.xs @@ -0,0 +1,88 @@ +#include "module.h" + +MODULE = Gaim::XMLNode PACKAGE = Gaim::XMLNode PREFIX = xmlnode_ +PROTOTYPES: ENABLE + +Gaim::XMLNode +xmlnode_copy(class, src) + Gaim::XMLNode src + C_ARGS: + src + +void +xmlnode_free(node) + Gaim::XMLNode node + +Gaim::XMLNode +xmlnode_from_str(class, str, size) + const char *str + gssize size + C_ARGS: + str, size + +const char * +xmlnode_get_attrib(node, attr) + Gaim::XMLNode node + const char *attr + +Gaim::XMLNode +xmlnode_get_child(parent, name) + Gaim::XMLNode parent + const char *name + +Gaim::XMLNode +xmlnode_get_child_with_namespace(parent, name, xmlns) + Gaim::XMLNode parent + const char *name + const char *xmlns + +gchar_own * +xmlnode_get_data(node) + Gaim::XMLNode node + +Gaim::XMLNode +xmlnode_get_next_twin(node) + Gaim::XMLNode node + +void +xmlnode_insert_child(parent, child) + Gaim::XMLNode parent + Gaim::XMLNode child + +void +xmlnode_insert_data(node, data, size) + Gaim::XMLNode node + const char *data + gssize size + +Gaim::XMLNode +xmlnode_new(class, name) + const char *name + C_ARGS: + name + +Gaim::XMLNode +xmlnode_new_child(parent, name) + Gaim::XMLNode parent + const char *name + +void +xmlnode_remove_attrib(node, attr) + Gaim::XMLNode node + const char *attr + +void +xmlnode_set_attrib(node, attr, value) + Gaim::XMLNode node + const char *attr + const char *value + +gchar_own * +xmlnode_to_formatted_str(node, len) + Gaim::XMLNode node + int *len + +gchar_own * +xmlnode_to_str(node, len) + Gaim::XMLNode node + int *len diff --git a/libpurple/plugins/perl/common/fallback/const-c.inc b/libpurple/plugins/perl/common/fallback/const-c.inc new file mode 100644 index 0000000000..4e1290d661 --- /dev/null +++ b/libpurple/plugins/perl/common/fallback/const-c.inc @@ -0,0 +1,115 @@ +#define PERL_constant_NOTFOUND 1 +#define PERL_constant_NOTDEF 2 +#define PERL_constant_ISIV 3 +#define PERL_constant_ISNO 4 +#define PERL_constant_ISNV 5 +#define PERL_constant_ISPV 6 +#define PERL_constant_ISPVN 7 +#define PERL_constant_ISSV 8 +#define PERL_constant_ISUNDEF 9 +#define PERL_constant_ISUV 10 +#define PERL_constant_ISYES 11 + +#ifndef NVTYPE +typedef double NV; /* 5.6 and later define NVTYPE, and typedef NV to it. */ +#endif +#ifndef aTHX_ +#define aTHX_ /* 5.6 or later define this for threading support. */ +#endif +#ifndef pTHX_ +#define pTHX_ /* 5.6 or later define this for threading support. */ +#endif + +static int +constant (pTHX_ const char *name, STRLEN len, IV *iv_return) { + /* Initially switch on the length of the name. */ + /* When generated this function returned values for the list of names given + in this section of perl code. Rather than manually editing these functions + to add or remove constants, which would result in this comment and section + of code becoming inaccurate, we recommend that you edit this section of + code, and use it to regenerate a new set of constant functions which you + then use to replace the originals. + + Regenerate these constant functions by feeding this entire source file to + perl -x + +#!/usr/bin/perl -w +use ExtUtils::Constant qw (constant_types C_constant XS_constant); + +my $types = {map {($_, 1)} qw(IV)}; +my @names = (qw(), + {name=>"GAIM_DEBUG_ALL", type=>"IV", macro=>"1"}, + {name=>"GAIM_DEBUG_ERROR", type=>"IV", macro=>"1"}, + {name=>"GAIM_DEBUG_FATAL", type=>"IV", macro=>"1"}, + {name=>"GAIM_DEBUG_INFO", type=>"IV", macro=>"1"}, + {name=>"GAIM_DEBUG_MISC", type=>"IV", macro=>"1"}, + {name=>"GAIM_DEBUG_WARNING", type=>"IV", macro=>"1"}); + +print constant_types(); # macro defs +foreach (C_constant ("Gaim::DebugLevel", 'constant', 'IV', $types, undef, 3, @names) ) { + print $_, "\n"; # C constant subs +} +print "#### XS Section:\n"; +print XS_constant ("Gaim::DebugLevel", $types); +__END__ + */ + + switch (len) { + case 14: + if (memEQ(name, "GAIM_DEBUG_ALL", 14)) { + *iv_return = GAIM_DEBUG_ALL; + return PERL_constant_ISIV; + } + break; + case 15: + /* Names all of length 15. */ + /* GAIM_DEBUG_INFO GAIM_DEBUG_MISC */ + /* Offset 11 gives the best switch position. */ + switch (name[11]) { + case 'I': + if (memEQ(name, "GAIM_DEBUG_INFO", 15)) { + /* ^ */ + *iv_return = GAIM_DEBUG_INFO; + return PERL_constant_ISIV; + } + break; + case 'M': + if (memEQ(name, "GAIM_DEBUG_MISC", 15)) { + /* ^ */ + *iv_return = GAIM_DEBUG_MISC; + return PERL_constant_ISIV; + } + break; + } + break; + case 16: + /* Names all of length 16. */ + /* GAIM_DEBUG_ERROR GAIM_DEBUG_FATAL */ + /* Offset 11 gives the best switch position. */ + switch (name[11]) { + case 'E': + if (memEQ(name, "GAIM_DEBUG_ERROR", 16)) { + /* ^ */ + *iv_return = GAIM_DEBUG_ERROR; + return PERL_constant_ISIV; + } + break; + case 'F': + if (memEQ(name, "GAIM_DEBUG_FATAL", 16)) { + /* ^ */ + *iv_return = GAIM_DEBUG_FATAL; + return PERL_constant_ISIV; + } + break; + } + break; + case 18: + if (memEQ(name, "GAIM_DEBUG_WARNING", 18)) { + *iv_return = GAIM_DEBUG_WARNING; + return PERL_constant_ISIV; + } + break; + } + return PERL_constant_NOTFOUND; +} + diff --git a/libpurple/plugins/perl/common/fallback/const-xs.inc b/libpurple/plugins/perl/common/fallback/const-xs.inc new file mode 100644 index 0000000000..45384ff63b --- /dev/null +++ b/libpurple/plugins/perl/common/fallback/const-xs.inc @@ -0,0 +1,88 @@ +void +constant(sv) + PREINIT: +#ifdef dXSTARG + dXSTARG; /* Faster if we have it. */ +#else + dTARGET; +#endif + STRLEN len; + int type; + IV iv; + /* NV nv; Uncomment this if you need to return NVs */ + /* const char *pv; Uncomment this if you need to return PVs */ + INPUT: + SV * sv; + const char * s = SvPV(sv, len); + PPCODE: + /* Change this to constant(aTHX_ s, len, &iv, &nv); + if you need to return both NVs and IVs */ + type = constant(aTHX_ s, len, &iv); + /* Return 1 or 2 items. First is error message, or undef if no error. + Second, if present, is found value */ + switch (type) { + case PERL_constant_NOTFOUND: + sv = sv_2mortal(newSVpvf("%s is not a valid Gaim::DebugLevel macro", s)); + PUSHs(sv); + break; + case PERL_constant_NOTDEF: + sv = sv_2mortal(newSVpvf( + "Your vendor has not defined Gaim::DebugLevel macro %s, used", s)); + PUSHs(sv); + break; + case PERL_constant_ISIV: + EXTEND(SP, 1); + PUSHs(&PL_sv_undef); + PUSHi(iv); + break; + /* Uncomment this if you need to return NOs + case PERL_constant_ISNO: + EXTEND(SP, 1); + PUSHs(&PL_sv_undef); + PUSHs(&PL_sv_no); + break; */ + /* Uncomment this if you need to return NVs + case PERL_constant_ISNV: + EXTEND(SP, 1); + PUSHs(&PL_sv_undef); + PUSHn(nv); + break; */ + /* Uncomment this if you need to return PVs + case PERL_constant_ISPV: + EXTEND(SP, 1); + PUSHs(&PL_sv_undef); + PUSHp(pv, strlen(pv)); + break; */ + /* Uncomment this if you need to return PVNs + case PERL_constant_ISPVN: + EXTEND(SP, 1); + PUSHs(&PL_sv_undef); + PUSHp(pv, iv); + break; */ + /* Uncomment this if you need to return SVs + case PERL_constant_ISSV: + EXTEND(SP, 1); + PUSHs(&PL_sv_undef); + PUSHs(sv); + break; */ + /* Uncomment this if you need to return UNDEFs + case PERL_constant_ISUNDEF: + break; */ + /* Uncomment this if you need to return UVs + case PERL_constant_ISUV: + EXTEND(SP, 1); + PUSHs(&PL_sv_undef); + PUSHu((UV)iv); + break; */ + /* Uncomment this if you need to return YESs + case PERL_constant_ISYES: + EXTEND(SP, 1); + PUSHs(&PL_sv_undef); + PUSHs(&PL_sv_yes); + break; */ + default: + sv = sv_2mortal(newSVpvf( + "Unexpected return type %d while processing Gaim::DebugLevel macro %s, used", + type, s)); + PUSHs(sv); + } diff --git a/libpurple/plugins/perl/common/module.h b/libpurple/plugins/perl/common/module.h new file mode 100644 index 0000000000..53357c1a8f --- /dev/null +++ b/libpurple/plugins/perl/common/module.h @@ -0,0 +1,278 @@ + + +typedef struct group *Gaim__Group; + +#define group perl_group + +#include +#ifdef _WIN32 +#undef pipe +#endif +#include +#include +#include + +#undef group + +#include "../perl-common.h" + +#include "account.h" +#include "accountopt.h" +#include "blist.h" +#include "buddyicon.h" +#include "cipher.h" +#include "cmds.h" +#include "connection.h" +#include "conversation.h" +#include "core.h" +#include "debug.h" +#include "desktopitem.h" +#include "eventloop.h" +#include "ft.h" +#ifdef GAIM_GTKPERL +#include "gtkaccount.h" +#include "gtkblist.h" +#include "gtkconn.h" +#include "gtkconv.h" +#include "gtkutils.h" +#endif +#include "imgstore.h" +#include "network.h" +#include "notify.h" +#include "plugin.h" +#include "pluginpref.h" +#include "pounce.h" +#include "prefs.h" +#include "privacy.h" +#include "prpl.h" +#include "proxy.h" +#include "request.h" +#include "roomlist.h" +#include "savedstatuses.h" +#include "server.h" +#include "signals.h" +#include "sound.h" +#include "sslconn.h" +#include "status.h" +#include "stringref.h" +/* Ewww. perl has it's own util.h which is in the include path :( */ +#include "libgaim/util.h" +#include "value.h" +#include "xmlnode.h" + +/* account.h */ +typedef GaimAccount * Gaim__Account; +typedef GaimAccountOption * Gaim__Account__Option; +typedef GaimAccountUiOps * Gaim__Account__UiOps; +typedef GaimAccountUserSplit * Gaim__Account__UserSplit; + +/* blist.h */ +typedef GaimBlistNode * Gaim__BuddyList__Node; +typedef GaimBlistNodeFlags Gaim__BuddyList__NodeFlags; +typedef GaimBlistUiOps * Gaim__BuddyList__UiOps; +typedef GaimBuddyList * Gaim__BuddyList; +typedef GaimBuddy * Gaim__BuddyList__Buddy; +typedef GaimChat * Gaim__BuddyList__Chat; +typedef GaimContact * Gaim__BuddyList__Contact; +typedef GaimGroup * Gaim__BuddyList__Group; + +/* buddyicon.h */ +typedef GaimBuddyIcon * Gaim__Buddy__Icon; + +/* cipher.h */ +typedef GaimCipher * Gaim__Cipher; +typedef GaimCipherCaps Gaim__CipherCaps; +typedef GaimCipherContext * Gaim__Cipher__Context; +typedef GaimCipherOps * Gaim__Cipher__Ops; + +/* cmds.h */ +typedef GaimCmdFlag Gaim__Cmd__Flag; +typedef GaimCmdId Gaim__Cmd__Id; +typedef GaimCmdPriority Gaim__Cmd__Priority; +typedef GaimCmdRet Gaim__Cmd__Ret; + +/* connection.h */ +typedef GaimConnection * Gaim__Connection; +typedef GaimConnectionFlags Gaim__ConnectionFlags; +typedef GaimConnectionState Gaim__ConnectionState; +typedef GaimConnectionUiOps * Gaim__Connection__UiOps; + +/* conversation.h */ +typedef GaimConversationType Gaim__ConversationType; +typedef GaimConvUpdateType Gaim__ConvUpdateType; +typedef GaimTypingState Gaim__TypingState; +typedef GaimMessageFlags Gaim__MessageFlags; +typedef GaimConvChatBuddyFlags Gaim__ConvChatBuddyFlags; +typedef GaimConversation * Gaim__Conversation; +typedef GaimConversationUiOps * Gaim__Conversation__UiOps; +typedef GaimConvIm * Gaim__Conversation__IM; +typedef GaimConvChat * Gaim__Conversation__Chat; +typedef GaimConvChatBuddy * Gaim__Conversation__ChatBuddy; + +/* core.h */ + +typedef GaimCore * Gaim__Core; +typedef GaimCoreUiOps * Gaim__Core__UiOps; + +/* debug.h */ +typedef GaimDebugLevel Gaim__DebugLevel; + +/* desktopitem.h */ +typedef GaimDesktopItem * Gaim__DesktopItem; +typedef GaimDesktopItemType Gaim__DesktopItemType; + +/* eventloop.h */ +typedef GaimInputCondition * Gaim__InputCondition; +typedef GaimEventLoopUiOps * Gaim__EventLoopUiOps; + +/* ft.h */ +typedef GaimXfer * Gaim__Xfer; +typedef GaimXferType Gaim__XferType; +typedef GaimXferStatusType Gaim__XferStatusType; +typedef GaimXferUiOps * Gaim__XferUiOps; + +#ifdef GAIM_GTKPERL +/* gtkblish.h */ +typedef GaimGtkBuddyList * Gaim__GTK__BuddyList; +typedef GaimStatusIconSize Gaim__StatusIconSize; + +/* gtkutils.h */ +typedef GaimButtonOrientation Gaim__ButtonOrientation; +typedef GaimButtonStyle Gaim__ButtonStyle; +#ifndef _WIN32 +typedef GaimBrowserPlace Gaim__BrowserPlace; +#endif /* _WIN32 */ + +/* gtkconv.h */ +typedef GaimUnseenState Gaim__UnseenState; +typedef GaimGtkConversation * Gaim__GTK__Conversation; +typedef GdkPixbuf * Gaim__GDK__Pixbuf; +typedef GtkWidget * Gaim__GTK__Widget; + +/* gtkutils.h */ +typedef GtkFileSelection * Gaim__GTK__FileSelection; +typedef GtkSelectionData * Gaim__GTK__SelectionData; +typedef GtkTextView * Gaim__GTK__TextView; + +/* gtkconn.h */ +#endif + +/* imgstore.h */ +typedef GaimStoredImage * Gaim__StoredImage; + +/* log.h */ +typedef GaimLog * Gaim__Log; +typedef GaimLogCommonLoggerData * Gaim__LogCommonLoggerData; +typedef GaimLogLogger * Gaim__Log__Logger; +typedef GaimLogReadFlags * Gaim__Log__ReadFlags; +typedef GaimLogSet * Gaim__LogSet; +typedef GaimLogType Gaim__LogType; + +/* network.h */ +typedef GaimNetworkListenData * Gaim__NetworkListenData; +typedef GaimNetworkListenCallback Gaim__NetworkListenCallback; + +/* notify.h */ +typedef GaimNotifyCloseCallback Gaim__NotifyCloseCallback; +typedef GaimNotifyMsgType Gaim__NotifyMsgType; +typedef GaimNotifySearchButtonType Gaim__NotifySearchButtonType; +typedef GaimNotifySearchResults * Gaim__NotifySearchResults; +typedef GaimNotifySearchColumn * Gaim__NotifySearchColumn; +typedef GaimNotifySearchButton * Gaim__NotifySearchButton; +typedef GaimNotifyType Gaim__NotifyType; +typedef GaimNotifyUiOps * Gaim__NotifyUiOps; +typedef GaimNotifyUserInfo * Gaim__NotifyUserInfo; +typedef GaimNotifyUserInfoEntry * Gaim__NotifyUserInfoEntry; + +/* plugin.h */ +typedef GaimPlugin * Gaim__Plugin; +typedef GaimPluginAction * Gaim__Plugin__Action; +typedef GaimPluginInfo * Gaim__PluginInfo; +typedef GaimPluginLoaderInfo * Gaim__PluginLoaderInfo; +typedef GaimPluginType Gaim__PluginType; +typedef GaimPluginUiInfo * Gaim__PluginUiInfo; + +/* pluginpref.h */ +typedef GaimPluginPref * Gaim__PluginPref; +typedef GaimPluginPrefFrame * Gaim__PluginPref__Frame; +typedef GaimPluginPrefType Gaim__PluginPrefType; + +/* pounce.h */ +typedef GaimPounce * Gaim__Pounce; +typedef GaimPounceEvent Gaim__PounceEvent; + +/* prefs.h */ +typedef GaimPrefType Gaim__PrefType; + +/* privacy.h */ +typedef GaimPrivacyType Gaim__PrivacyType; +typedef GaimPrivacyUiOps * Gaim__Privacy__UiOps; + +/* proxy.h */ +typedef GaimProxyInfo * Gaim__ProxyInfo; +typedef GaimProxyType Gaim__ProxyType; + +/* prpl.h */ +typedef GaimBuddyIconSpec * Gaim__Buddy__Icon__Spec; +typedef GaimIconScaleRules Gaim__IconScaleRules; +typedef GaimPluginProtocolInfo * Gaim__PluginProtocolInfo; +typedef GaimProtocolOptions Gaim__ProtocolOptions; + +/* request.h */ +typedef GaimRequestField * Gaim__Request__Field; +typedef GaimRequestFields * Gaim__Request__Fields; +typedef GaimRequestFieldGroup * Gaim__Request__Field__Group; +typedef GaimRequestFieldType Gaim__RequestFieldType; +typedef GaimRequestType Gaim__RequestType; +typedef GaimRequestUiOps * Gaim__Request__UiOps; + +/* roomlist.h */ +typedef GaimRoomlist * Gaim__Roomlist; +typedef GaimRoomlistField * Gaim__Roomlist__Field; +typedef GaimRoomlistFieldType Gaim__RoomlistFieldType; +typedef GaimRoomlistRoom * Gaim__Roomlist__Room; +typedef GaimRoomlistRoomType Gaim__RoomlistRoomType; +typedef GaimRoomlistUiOps * Gaim__Roomlist__UiOps; + +/* savedstatuses.h */ +typedef GaimSavedStatus * Gaim__SavedStatus; +typedef GaimSavedStatusSub * Gaim__SavedStatusSub; + +/* sound.h */ +typedef GaimSoundEventID Gaim__SoundEventID; +typedef GaimSoundUiOps * Gaim__Sound__UiOps; + +/* sslconn.h */ +typedef GaimInputCondition * Gaim__Input__Condition; +typedef GaimSslConnection * Gaim__Ssl__Connection; +typedef GaimSslErrorType Gaim__SslErrorType; +typedef GaimSslOps * Gaim__Ssl__Ops; + +/* status.h */ +typedef GaimPresence * Gaim__Presence; +typedef GaimPresenceContext Gaim__PresenceContext; +typedef GaimStatus * Gaim__Status; +typedef GaimStatusAttr * Gaim__StatusAttr; +typedef GaimStatusPrimitive Gaim__StatusPrimitive; +typedef GaimStatusType * Gaim__StatusType; + +/* stringref.h */ +typedef GaimStringref * Gaim__Stringref; + +/* util.h */ +typedef GaimInfoFieldFormatCallback Gaim__Util__InfoFieldFormatCallback; +typedef GaimUtilFetchUrlData Gaim__Util__FetchUrlData; +typedef GaimMenuAction * Gaim__Menu__Action; + +/* value.h */ +typedef GaimValue * Gaim__Value; + +/* xmlnode.h */ +typedef xmlnode * Gaim__XMLNode; +typedef XMLNodeType XMLNode__Type; + +/* other */ +typedef void * Gaim__Handle; + +typedef gchar gchar_own; + diff --git a/libpurple/plugins/perl/common/typemap b/libpurple/plugins/perl/common/typemap new file mode 100644 index 0000000000..e1eba3412f --- /dev/null +++ b/libpurple/plugins/perl/common/typemap @@ -0,0 +1,209 @@ +TYPEMAP +guint T_IV +gint T_IV +const gint * T_PTR +const guint * T_PTR +const guint8 * T_PTR +guint8 T_IV +guint8 * T_PTR +time_t T_IV +gboolean T_BOOL +gpointer T_GaimObj +gconstpointer T_PTR +const gchar * T_PV +const char * T_PV +const char ** T_PTR +char ** T_PTR +gchar T_IV +gchar * T_PV +gchar_own * T_GCHAR_OWN +guchar T_IV +guchar * T_PTR +guchar ** T_PTR +const guchar * T_PV +char * T_PV +int * T_PTR +long * T_PTR +size_t * T_PTR +GCallback T_PTR +va_list T_PTR +GString * T_PTR +GData * T_PTR +GData ** T_PTR +const unsigned char * T_PTR +struct tm * T_PTR +const struct tm * T_PTR +xmlnode * T_PTR +const xmlnode * T_PTR +gssize T_IV +const void * T_PTR + +Gaim::Account T_GaimObj +Gaim::Account::Option T_GaimObj +Gaim::Account::UiOps T_GaimObj +Gaim::Account::UserSplit T_GaimObj + +Gaim::Buddy::Icon T_GaimObj +Gaim::Buddy::Icon::Spec T_GaimObj +Gaim::BuddyList T_GaimObj +Gaim::BuddyList::Buddy T_GaimObj +Gaim::BuddyList::Chat T_GaimObj +Gaim::BuddyList::Contact T_GaimObj +Gaim::BuddyList::Group T_GaimObj +Gaim::BuddyList::Node T_GaimObj +Gaim::BuddyList::NodeFlags T_IV +Gaim::BuddyList::UiOps T_GaimObj + +Gaim::Cipher T_GaimObj +Gaim::CipherCaps T_IV +Gaim::Cipher::Ops T_GaimObj +Gaim::Cipher::Context T_GaimObj +Gaim::Cmd::Flag T_IV +Gaim::Cmd::Id T_IV +Gaim::Cmd::Priority T_IV +Gaim::Cmd::Ret T_IV +Gaim::Connection T_GaimObj +Gaim::Connection::UiOps T_GaimObj +Gaim::Conversation T_GaimObj +Gaim::Conversation::Chat T_GaimObj +Gaim::Conversation::ChatBuddy T_GaimObj +Gaim::Conversation::IM T_GaimObj +Gaim::Conversation::UiOps T_GaimObj +Gaim::Core T_GaimObj +Gaim::Core::UiOps T_GaimObj + +Gaim::Desktop::Item T_GaimObj +Gaim::DesktopItemType T_IV + +Gaim::Handle T_GaimObj + +Gaim::IconScaleRules T_IV + +Gaim::Log T_GaimObj +Gaim::LogType T_IV +Gaim::Log::CommonLoggerData T_GaimObj +Gaim::Log::Logger T_GaimObj +Gaim::Log::ReadFlags T_GaimObj +Gaim::Log::Set T_GaimObj + +Gaim::Menu::Action T_GaimObj + +Gaim::NetworkListenData T_GaimObj +Gaim::NetworkListenCallback T_PTR + +Gaim::NotifyCloseCallback T_PTR +Gaim::NotifyMsgType T_IV +Gaim::NotifySearchButtonType T_IV +Gaim::NotifySearchResults T_GaimObj +Gaim::NotifySearchColumn T_GaimObj +Gaim::NotifySearchButton T_GaimObj +Gaim::NotifyType T_IV +Gaim::NotifyUiOps T_GaimObj +Gaim::NotifyUserInfo T_GaimObj +Gaim::NotifyUserInfoEntry T_GaimObj + +Gaim::Plugin T_GaimObj +Gaim::PluginType T_IV +Gaim::PluginUiInfo T_GaimObj +Gaim::Plugin::Action T_GaimObj +Gaim::Plugin::Info T_GaimObj +Gaim::Plugin::Loader::Info T_GaimObj +Gaim::Plugin::Protocol::Info T_GaimObj +Gaim::PrefType T_IV +Gaim::PluginPref T_GaimObj +Gaim::PluginPrefType T_IV +Gaim::PluginPref::Frame T_GaimObj +Gaim::Pounce T_GaimObj +Gaim::PounceEvent T_IV +Gaim::Presence T_GaimObj +Gaim::PrivacyType T_IV +Gaim::Privacy::UiOps T_GaimObj +Gaim::ProtocolOptions T_IV +Gaim::ProxyInfo T_GaimObj +Gaim::ProxyType T_IV + +Gaim::RequestFieldType T_IV +Gaim::RequestType T_IV +Gaim::Request::Field T_GaimObj +Gaim::Request::Fields T_GaimObj +Gaim::Request::Field::Group T_GaimObj +Gaim::Request::UiOps T_GaimObj + +Gaim::Roomlist T_GaimObj +Gaim::Roomlist::Room T_GaimObj +Gaim::Roomlist::Field T_GaimObj +Gaim::Roomlist::UiOps T_GaimObj +Gaim::RoomlistFieldType T_IV +Gaim::RoomlistRoomType T_IV + +Gaim::SavedStatus T_GaimObj +Gaim::SavedStatusSub T_GaimObj +Gaim::SoundEventID T_IV +Gaim::Sound::UiOps T_GaimObj + +Gaim::Input::Condition T_GaimObj +Gaim::SslErrorType T_IV +Gaim::Ssl::Connection T_GaimObj +Gaim::Ssl::Ops T_GaimObj + +Gaim::Presence T_GaimObj +Gaim::PresenceContext T_IV +Gaim::Status T_GaimObj +Gaim::StatusAttr T_GaimObj +Gaim::StatusPrimitive T_IV +Gaim::StatusType T_GaimObj +const Gaim::StatusType T_GaimObj + +Gaim::StoredImage T_GaimObj +Gaim::Stringref T_GaimObj +Gaim::Util::FetchUrlData T_PTR +Gaim::Util::InfoFieldFormatCallback T_PTR +Gaim::Value T_GaimObj + +Gaim::Xfer T_GaimObj +Gaim::XferType T_IV +Gaim::XferStatusType T_IV +Gaim::XferUiOps T_IV + +Gaim::XMLNode T_GaimObj +XMLNode::Type T_IV + +/* enums */ + +/* cipher.h */ + +/* blist.h */ + +/* debug.h */ +Gaim::DebugLevel T_IV + +/* conversation.h */ +Gaim::ConvChatBuddyFlags T_IV +Gaim::ConvUpdateType T_IV +Gaim::ConversationType T_IV +Gaim::MessageFlags T_IV +Gaim::TypingState T_IV +Gaim::UnseenState T_IV + +/* connection.h */ +Gaim::ConnectionFlags T_IV +Gaim::ConnectionState T_IV + +INPUT + +T_GaimObj + $var = gaim_perl_ref_object($arg) + +OUTPUT + +T_GaimObj + $arg = gaim_perl_bless_object($var, \"$type\"); + +T_GCHAR_OWN + /* used when we can directly own the returned string. */ + /* we have to copy in the case when perl's malloc != gtk's malloc, + * so best copy all the time. */ + sv_setpv ((SV*)$arg, $var); + SvUTF8_on ($arg); + g_free ($var); + diff --git a/libpurple/plugins/perl/libgaimperl.c b/libpurple/plugins/perl/libgaimperl.c new file mode 100644 index 0000000000..e0c25c3a66 --- /dev/null +++ b/libpurple/plugins/perl/libgaimperl.c @@ -0,0 +1,9 @@ +#include +void __attribute__ ((constructor)) my_init(void); + +void __attribute__ ((constructor)) my_init() { + /* Very evil hack...puts perl.so's symbols in the global table + * but does not create a circular dependancy because g_module_open + * will only open the library once. */ + g_module_open("perl.so", 0); +} diff --git a/libpurple/plugins/perl/perl-common.c b/libpurple/plugins/perl/perl-common.c new file mode 100644 index 0000000000..b4daee5f92 --- /dev/null +++ b/libpurple/plugins/perl/perl-common.c @@ -0,0 +1,618 @@ +#include "debug.h" +#include "value.h" + +#include "perl-common.h" + +extern PerlInterpreter *my_perl; + +static GHashTable *object_stashes = NULL; + +void gaim_perl_normalize_script_name(char *name) +{ + char *c; + + c = strrchr(name, '.'); + + if (c != NULL) + *c = '\0'; + + for (c = name; *c != '\0'; c++) { + if (*c != '_' && !g_ascii_isalnum(*c)) + *c = '_'; + } +} + +static int +magic_free_object(pTHX_ SV *sv, MAGIC *mg) +{ + sv_setiv(sv, 0); + + return 0; +} + +static MGVTBL vtbl_free_object = +{ + NULL, NULL, NULL, NULL, magic_free_object, NULL, NULL +}; + +static SV * +create_sv_ptr(void *object) +{ + SV *sv; + + sv = newSViv((IV)object); + + sv_magic(sv, NULL, '~', NULL, 0); + + SvMAGIC(sv)->mg_private = 0x1551; /* HF */ + SvMAGIC(sv)->mg_virtual = &vtbl_free_object; + + return sv; +} + +SV * +newSVGChar(const char *str) +{ + SV *sv; + + if (str == NULL) + return &PL_sv_undef; + + sv = newSVpv(str, 0); + SvUTF8_on(sv); + + return sv; +} + +SV * +gaim_perl_bless_object(void *object, const char *stash_name) +{ + HV *stash; + HV *hv; + + if (object == NULL) + return NULL; + + if (object_stashes == NULL) { + object_stashes = g_hash_table_new(g_direct_hash, g_direct_equal); + } + + stash = gv_stashpv(stash_name, 1); + + hv = newHV(); + hv_store(hv, "_gaim", 5, create_sv_ptr(object), 0); + + return sv_bless(newRV_noinc((SV *)hv), stash); +} + +gboolean +gaim_perl_is_ref_object(SV *o) +{ + SV **sv; + HV *hv; + + hv = hvref(o); + + if (hv != NULL) { + sv = hv_fetch(hv, "_gaim", 5, 0); + + if (sv != NULL) + return TRUE; + } + + return FALSE; +} + +void * +gaim_perl_ref_object(SV *o) +{ + SV **sv; + HV *hv; + void *p; + + if (o == NULL) + return NULL; + + hv = hvref(o); + + if (hv == NULL) + return NULL; + + sv = hv_fetch(hv, "_gaim", 5, 0); + + if (sv == NULL) + croak("variable is damaged"); + + p = GINT_TO_POINTER(SvIV(*sv)); + + return p; +} + +/* + 2003/02/06: execute_perl modified by Mark Doliner + Pass parameters by pushing them onto the stack rather than + passing an array of strings. This way, perl scripts can + modify the parameters and we can get the changed values + and then shoot ourselves. I mean, uh, use them. + + 2001/06/14: execute_perl replaced by Martin Persson + previous use of perl_eval leaked memory, replaced with + a version that uses perl_call instead + + 30/11/2002: execute_perl modified by Eric Timme + args changed to char** so that we can have preparsed + arguments again, and many headaches ensued! This essentially + means we replaced one hacked method with a messier hacked + method out of perceived necessity. Formerly execute_perl + required a single char_ptr, and it would insert it into an + array of character pointers and NULL terminate the new array. + Now we have to pass in pre-terminated character pointer arrays + to accomodate functions that want to pass in multiple arguments. + + Previously arguments were preparsed because an argument list + was constructed in the form 'arg one','arg two' and was + executed via a call like &funcname(arglist) (see .59.x), so + the arglist was magically pre-parsed because of the method. + With Martin Persson's change to perl_call we now need to + use a null terminated list of character pointers for arguments + if we wish them to be parsed. Lacking a better way to allow + for both single arguments and many I created a NULL terminated + array in every function that called execute_perl and passed + that list into the function. In the former version a single + character pointer was passed in, and was placed into an array + of character pointers with two elements, with a NULL element + tacked onto the back, but this method no longer seemed prudent. + + Enhancements in the future might be to get rid of pre-declaring + the array sizes? I am not comfortable enough with this + subject to attempt it myself and hope it to stand the test + of time. +*/ +int +execute_perl(const char *function, int argc, char **args) +{ + int count = 0, i, ret_value = 1; + SV *sv_args[argc]; + STRLEN na; + dSP; + PERL_SET_CONTEXT(my_perl); + /* + * Set up the perl environment, push arguments onto the + * perl stack, then call the given function + */ + SPAGAIN; + ENTER; + SAVETMPS; + PUSHMARK(sp); + + for (i = 0; i < argc; i++) { + if (args[i]) { + sv_args[i] = sv_2mortal(newSVpv(args[i], 0)); + XPUSHs(sv_args[i]); + } + } + + PUTBACK; + PERL_SET_CONTEXT(my_perl); + count = call_pv(function, G_EVAL | G_SCALAR); + SPAGAIN; + + /* + * Check for "die," make sure we have 1 argument, and set our + * return value. + */ + if (SvTRUE(ERRSV)) { + gaim_debug(GAIM_DEBUG_ERROR, "perl", + "Perl function %s exited abnormally: %s\n", + function, SvPV(ERRSV, na)); + POPs; + } else if (count != 1) { + /* + * This should NEVER happen. G_SCALAR ensures that we WILL + * have 1 parameter. + */ + gaim_debug(GAIM_DEBUG_ERROR, "perl", + "Perl error from %s: expected 1 return value, " + "but got %d\n", function, count); + } else + ret_value = POPi; + + /* Check for changed arguments */ + for (i = 0; i < argc; i++) { + if (args[i] && strcmp(args[i], SvPVX(sv_args[i]))) { + /* + * Shizzel. So the perl script changed one of the parameters, + * and we want this change to affect the original parameters. + * args[i] is just a temporary little list of pointers. We don't + * want to free args[i] here because the new parameter doesn't + * overwrite the data that args[i] points to. That is done by + * the function that called execute_perl. I'm not explaining this + * very well. See, it's aggregate... Oh, but if 2 perl scripts + * both modify the data, _that's_ a memleak. This is really kind + * of hackish. I should fix it. Look how long this comment is. + * Holy crap. + */ + args[i] = g_strdup(SvPV(sv_args[i], na)); + } + } + + PUTBACK; + FREETMPS; + LEAVE; + + return ret_value; +} + +#if 0 +gboolean +gaim_perl_value_from_sv(GaimValue *value, SV *sv) +{ + switch (gaim_value_get_type(value)) + { + case GAIM_TYPE_CHAR: + if ((tmp = SvGChar(sv)) != NULL) + gaim_value_set_char(value, tmp[0]); + else + return FALSE; + break; + + case GAIM_TYPE_UCHAR: + if ((tmp = SvPV_nolen(sv)) != NULL) + gaim_value_set_uchar(value, tmp[0]); + else + return FALSE; + break; + + case GAIM_TYPE_BOOLEAN: + gaim_value_set_boolean(value, SvTRUE(sv)); + break; + + case GAIM_TYPE_INT: + gaim_value_set_int(value, SvIV(sv)); + break; + + case GAIM_TYPE_UINT: + gaim_value_set_uint(value, SvIV(sv)); + break; + + case GAIM_TYPE_LONG: + gaim_value_set_long(value, SvIV(sv)); + break; + + case GAIM_TYPE_ULONG: + gaim_value_set_ulong(value, SvIV(sv)); + break; + + case GAIM_TYPE_INT64: + gaim_value_set_int64(value, SvIV(sv)); + break; + + case GAIM_TYPE_UINT64: + gaim_value_set_uint64(value, SvIV(sv)); + break; + + case GAIM_TYPE_STRING: + gaim_value_set_string(value, SvGChar(sv)); + break; + + case GAIM_TYPE_POINTER: + gaim_value_set_pointer(value, (void *)SvIV(sv)); + break; + + case GAIM_TYPE_BOXED: + if (!strcmp(gaim_value_get_specific_type(value), "SV")) + gaim_value_set_boxed(value, (sv == &PL_sv_undef ? NULL : sv)); + else + gaim_value_set_boxed(value, sv); + break; + + default: + return FALSE; + } + + return TRUE; +} + +SV * +gaim_perl_sv_from_value(const GaimValue *value, va_list list) +{ + switch (gaim_value_get_type(value)) + { + case GAIM_TYPE_BOOLEAN: + return newSViv(gaim_value_get_boolean(value)); + break; + + case GAIM_TYPE_INT: + return newSViv(gaim_value_get_int(value)); + break; + + case GAIM_TYPE_UINT: + return newSVuv(gaim_value_get_uint(value)); + break; + + case GAIM_TYPE_LONG: + return newSViv(gaim_value_get_long(value)); + break; + + case GAIM_TYPE_ULONG: + return newSVuv(gaim_value_get_ulong(value)); + break; + + case GAIM_TYPE_INT64: + return newSViv(gaim_value_get_int64(value)); + break; + + case GAIM_TYPE_UINT64: + return newSVuv(gaim_value_get_int64(value)); + break; + + case GAIM_TYPE_STRING: + return newSVGChar(gaim_value_get_string(value)); + break; + + case GAIM_TYPE_POINTER: + return newSViv((IV)gaim_value_get_pointer(value)); + break; + + case GAIM_TYPE_BOXED: + if (!strcmp(gaim_value_get_specific_type(value), "SV")) + { + SV *sv = (SV *)gaim_perl_get_boxed(value); + + return (sv == NULL ? &PL_sv_undef : sv); + } + + /* Uh.. I dunno. Try this? */ + return sv_2mortal(gaim_perl_bless_object( + gaim_perl_get_boxed(value), + gaim_value_get_specific_type(value))); + + default: + return FALSE; + } + + return TRUE; +} +#endif + +void * +gaim_perl_data_from_sv(GaimValue *value, SV *sv) +{ + STRLEN na; + + switch (gaim_value_get_type(value)) { + case GAIM_TYPE_BOOLEAN: return (void *)SvIV(sv); + case GAIM_TYPE_INT: return (void *)SvIV(sv); + case GAIM_TYPE_UINT: return (void *)SvUV(sv); + case GAIM_TYPE_LONG: return (void *)SvIV(sv); + case GAIM_TYPE_ULONG: return (void *)SvUV(sv); + case GAIM_TYPE_INT64: return (void *)SvIV(sv); + case GAIM_TYPE_UINT64: return (void *)SvUV(sv); + case GAIM_TYPE_STRING: return g_strdup((void *)SvPV(sv, na)); + case GAIM_TYPE_POINTER: return (void *)SvIV(sv); + case GAIM_TYPE_BOXED: return (void *)SvIV(sv); + + default: + return NULL; + } + + return NULL; +} + +static SV * +gaim_perl_sv_from_subtype(const GaimValue *value, void *arg) +{ + const char *stash = NULL; + + switch (gaim_value_get_subtype(value)) { + case GAIM_SUBTYPE_ACCOUNT: + stash = "Gaim::Account"; + break; + case GAIM_SUBTYPE_BLIST: + stash = "Gaim::BuddyList"; + break; + case GAIM_SUBTYPE_BLIST_BUDDY: + stash = "Gaim::BuddyList::Buddy"; + break; + case GAIM_SUBTYPE_BLIST_GROUP: + stash = "Gaim::BuddyList::Group"; + break; + case GAIM_SUBTYPE_BLIST_CHAT: + stash = "Gaim::BuddyList::Chat"; + break; + case GAIM_SUBTYPE_BUDDY_ICON: + stash = "Gaim::Buddy::Icon"; + break; + case GAIM_SUBTYPE_CONNECTION: + stash = "Gaim::Connection"; + break; + case GAIM_SUBTYPE_CONVERSATION: + stash = "Gaim::Conversation"; + break; + case GAIM_SUBTYPE_PLUGIN: + stash = "Gaim::Plugin"; + break; + case GAIM_SUBTYPE_BLIST_NODE: + stash = "Gaim::BuddyList::Node"; + break; + case GAIM_SUBTYPE_CIPHER: + stash = "Gaim::Cipher"; + break; + case GAIM_SUBTYPE_STATUS: + stash = "Gaim::Status"; + break; + case GAIM_SUBTYPE_LOG: + stash = "Gaim::Log"; + break; + case GAIM_SUBTYPE_XFER: + stash = "Gaim::Xfer"; + break; + case GAIM_SUBTYPE_XMLNODE: + stash = "Gaim::XMLNode"; + break; + + default: + stash = "Gaim"; /* ? */ + } + + return sv_2mortal(gaim_perl_bless_object(arg, stash)); +} + +SV * +gaim_perl_sv_from_vargs(const GaimValue *value, va_list *args, void ***copy_arg) +{ + if (gaim_value_is_outgoing(value)) { + switch (gaim_value_get_type(value)) { + case GAIM_TYPE_SUBTYPE: + if ((*copy_arg = va_arg(*args, void **)) == NULL) + return &PL_sv_undef; + + return gaim_perl_sv_from_subtype(value, *(void **)*copy_arg); + + case GAIM_TYPE_BOOLEAN: + if ((*copy_arg = (void *)va_arg(*args, gboolean *)) == NULL) + return &PL_sv_undef; + + return newSViv(*(gboolean *)*copy_arg); + + case GAIM_TYPE_INT: + if ((*copy_arg = (void *)va_arg(*args, int *)) == NULL) + return &PL_sv_undef; + + return newSViv(*(int *)*copy_arg); + + case GAIM_TYPE_UINT: + if ((*copy_arg = (void *)va_arg(*args, unsigned int *)) == NULL) + return &PL_sv_undef; + + return newSVuv(*(unsigned int *)*copy_arg); + + case GAIM_TYPE_LONG: + if ((*copy_arg = (void *)va_arg(*args, long *)) == NULL) + return &PL_sv_undef; + + return newSViv(*(long *)*copy_arg); + + case GAIM_TYPE_ULONG: + if ((*copy_arg = (void *)va_arg(*args, + unsigned long *)) == NULL) + return &PL_sv_undef; + + return newSVuv(*(unsigned long *)*copy_arg); + + case GAIM_TYPE_INT64: + if ((*copy_arg = (void *)va_arg(*args, gint64 *)) == NULL) + return &PL_sv_undef; + + return newSViv(*(gint64 *)*copy_arg); + + case GAIM_TYPE_UINT64: + if ((*copy_arg = (void *)va_arg(*args, guint64 *)) == NULL) + return &PL_sv_undef; + + return newSVuv(*(guint64 *)*copy_arg); + + case GAIM_TYPE_STRING: + if ((*copy_arg = (void *)va_arg(*args, char **)) == NULL) + return &PL_sv_undef; + + return newSVGChar(*(char **)*copy_arg); + + case GAIM_TYPE_POINTER: + if ((*copy_arg = va_arg(*args, void **)) == NULL) + return &PL_sv_undef; + + return newSViv((IV)*(void **)*copy_arg); + + case GAIM_TYPE_BOXED: + /* Uh.. I dunno. Try this? */ + if ((*copy_arg = va_arg(*args, void **)) == NULL) + return &PL_sv_undef; + + return sv_2mortal(gaim_perl_bless_object( + *(void **)*copy_arg, + gaim_value_get_specific_type(value))); + + default: + /* If this happens, things are going to get screwed up... */ + return NULL; + } + } else { + switch (gaim_value_get_type(value)) { + case GAIM_TYPE_SUBTYPE: + if ((*copy_arg = va_arg(*args, void *)) == NULL) + return &PL_sv_undef; + + return gaim_perl_sv_from_subtype(value, *copy_arg); + + case GAIM_TYPE_BOOLEAN: + *copy_arg = GINT_TO_POINTER( va_arg(*args, gboolean) ); + + return newSViv((gboolean)GPOINTER_TO_INT(*copy_arg)); + + case GAIM_TYPE_INT: + *copy_arg = GINT_TO_POINTER( va_arg(*args, int) ); + + return newSViv(GPOINTER_TO_INT(*copy_arg)); + + case GAIM_TYPE_UINT: + *copy_arg = GUINT_TO_POINTER(va_arg(*args, unsigned int)); + + return newSVuv(GPOINTER_TO_UINT(*copy_arg)); + + case GAIM_TYPE_LONG: + *copy_arg = (void *)va_arg(*args, long); + + return newSViv((long)*copy_arg); + + case GAIM_TYPE_ULONG: + *copy_arg = (void *)va_arg(*args, unsigned long); + + return newSVuv((unsigned long)*copy_arg); + + case GAIM_TYPE_INT64: +#if 0 + /* XXX This yells and complains. */ + *copy_arg = va_arg(*args, gint64); + + return newSViv(*copy_arg); +#endif + break; + + case GAIM_TYPE_UINT64: + /* XXX This also yells and complains. */ +#if 0 + *copy_arg = (void *)va_arg(*args, guint64); + + return newSVuv(*copy_arg); +#endif + break; + + case GAIM_TYPE_STRING: + if ((*copy_arg = (void *)va_arg(*args, char *)) == NULL) + return &PL_sv_undef; + + return newSVGChar((char *)*copy_arg); + + case GAIM_TYPE_POINTER: + if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL) + return &PL_sv_undef; + + return newSViv((IV)*copy_arg); + + case GAIM_TYPE_BOXED: + /* Uh.. I dunno. Try this? */ + if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL) + return &PL_sv_undef; + + return sv_2mortal(gaim_perl_bless_object(*copy_arg, + gaim_value_get_specific_type(value))); + + default: + /* If this happens, things are going to get screwed up... */ + return NULL; + } + } + + return NULL; +} diff --git a/libpurple/plugins/perl/perl-common.h b/libpurple/plugins/perl/perl-common.h new file mode 100644 index 0000000000..13ef0fa283 --- /dev/null +++ b/libpurple/plugins/perl/perl-common.h @@ -0,0 +1,61 @@ +#ifndef _GAIM_PERL_COMMON_H_ +#define _GAIM_PERL_COMMON_H_ + +#include +#ifdef _WIN32 +#undef pipe +#endif +#include +#include +#include + +#include "plugin.h" +#include "value.h" + +#define is_hvref(o) \ + ((o) && SvROK(o) && SvRV(o) && (SvTYPE(SvRV(o)) == SVt_PVHV)) + +#define hvref(o) \ + (is_hvref(o) ? (HV *)SvRV(o) : NULL); + +#define GAIM_PERL_BOOT_PROTO(x) \ + void boot_Gaim__##x(pTHX_ CV *cv); + +#define GAIM_PERL_BOOT(x) \ + gaim_perl_callXS(boot_Gaim__##x, cv, mark) + +typedef struct +{ + GaimPlugin *plugin; + char *package; + char *load_sub; + char *unload_sub; + char *prefs_sub; +#ifdef GAIM_GTKPERL + char *gtk_prefs_sub; +#endif + char *plugin_action_sub; +} GaimPerlScript; + +void gaim_perl_normalize_script_name(char *name); + +SV *newSVGChar(const char *str); + +void gaim_perl_callXS(void (*subaddr)(pTHX_ CV *cv), CV *cv, SV **mark); +void gaim_perl_bless_plain(const char *stash, void *object); +SV *gaim_perl_bless_object(void *object, const char *stash); +gboolean gaim_perl_is_ref_object(SV *o); +void *gaim_perl_ref_object(SV *o); + +int execute_perl(const char *function, int argc, char **args); + +#if 0 +gboolean gaim_perl_value_from_sv(GaimValue *value, SV *sv); +SV *gaim_perl_sv_from_value(const GaimValue *value); +#endif + +void *gaim_perl_data_from_sv(GaimValue *value, SV *sv); +SV *gaim_perl_sv_from_vargs(const GaimValue *value, va_list *args, + void ***copy_arg); + +#endif /* _GAIM_PERL_COMMON_H_ */ diff --git a/libpurple/plugins/perl/perl-handlers.c b/libpurple/plugins/perl/perl-handlers.c new file mode 100644 index 0000000000..1d6b61d4c6 --- /dev/null +++ b/libpurple/plugins/perl/perl-handlers.c @@ -0,0 +1,636 @@ +#include "perl-common.h" +#include "perl-handlers.h" + +#include "debug.h" +#include "signals.h" + +extern PerlInterpreter *my_perl; +static GList *cmd_handlers = NULL; +static GList *signal_handlers = NULL; +static GList *timeout_handlers = NULL; + +/* perl < 5.8.0 doesn't define PERL_MAGIC_ext */ +#ifndef PERL_MAGIC_ext +#define PERL_MAGIC_ext '~' +#endif + +void +gaim_perl_plugin_action_cb(GaimPluginAction *action) +{ + SV **callback; + HV *hv = NULL; + gchar *hvname; + GaimPlugin *plugin; + GaimPerlScript *gps; + dSP; + + plugin = action->plugin; + gps = (GaimPerlScript *)plugin->info->extra_info; + hvname = g_strdup_printf("%s::plugin_actions", gps->package); + hv = get_hv(hvname, FALSE); + g_free(hvname); + + if (hv == NULL) + croak("No plugin_actions hash found in \"%s\" plugin.", gaim_plugin_get_name(plugin)); + + ENTER; + SAVETMPS; + + callback = hv_fetch(hv, action->label, strlen(action->label), 0); + + if (callback == NULL || *callback == NULL) + croak("No plugin_action function named \"%s\" in \"%s\" plugin.", action->label, gaim_plugin_get_name(plugin)); + + PUSHMARK(sp); + XPUSHs(gaim_perl_bless_object(gps->plugin, "Gaim::Plugin")); + PUTBACK; + + call_sv(*callback, G_VOID | G_DISCARD); + SPAGAIN; + + PUTBACK; + FREETMPS; + LEAVE; +} + +GList * +gaim_perl_plugin_actions(GaimPlugin *plugin, gpointer context) +{ + GList *l = NULL; + GaimPerlScript *gps; + int i = 0, count = 0; + dSP; + + gps = (GaimPerlScript *)plugin->info->extra_info; + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(sv_2mortal(gaim_perl_bless_object(plugin, "Gaim::Plugin"))); + /* XXX This *will* cease working correctly if context gets changed to + * ever be able to hold anything other than a GaimConnection */ + if (context != NULL) + XPUSHs(sv_2mortal(gaim_perl_bless_object(context, + "Gaim::Connection"))); + else + XPUSHs(&PL_sv_undef); + PUTBACK; + + count = call_pv(gps->plugin_action_sub, G_ARRAY); + + SPAGAIN; + + if (count == 0) + croak("The plugin_actions sub didn't return anything.\n"); + + for (i = 0; i < count; i++) { + SV *sv; + gchar *label; + GaimPluginAction *act = NULL; + + sv = POPs; + label = SvPV_nolen(sv); + /* XXX I think this leaks, but doing it without the strdup + * just showed garbage */ + act = gaim_plugin_action_new(g_strdup(label), gaim_perl_plugin_action_cb); + l = g_list_prepend(l, act); + } + + PUTBACK; + FREETMPS; + LEAVE; + + return l; +} + +#ifdef GAIM_GTKPERL +GtkWidget * +gaim_perl_gtk_get_plugin_frame(GaimPlugin *plugin) +{ + SV * sv; + int count; + MAGIC *mg; + GtkWidget *ret; + GaimPerlScript *gps; + dSP; + + gps = (GaimPerlScript *)plugin->info->extra_info; + + ENTER; + SAVETMPS; + + count = call_pv(gps->gtk_prefs_sub, G_SCALAR | G_NOARGS); + if (count != 1) + croak("call_pv: Did not return the correct number of values.\n"); + + /* the frame was created in a perl sub and is returned */ + SPAGAIN; + + /* We have a Gtk2::Frame on top of the stack */ + sv = POPs; + + /* The magic field hides the pointer to the actual GtkWidget */ + mg = mg_find(SvRV(sv), PERL_MAGIC_ext); + ret = (GtkWidget *)mg->mg_ptr; + + PUTBACK; + FREETMPS; + LEAVE; + + return ret; +} +#endif + +GaimPluginPrefFrame * +gaim_perl_get_plugin_frame(GaimPlugin *plugin) +{ + /* Sets up the Perl Stack for our call back into the script to run the + * plugin_pref... sub */ + int count; + GaimPerlScript *gps; + GaimPluginPrefFrame *ret_frame; + dSP; + + gps = (GaimPerlScript *)plugin->info->extra_info; + + ENTER; + SAVETMPS; + /* Some perl magic to run perl_plugin_pref_frame_SV perl sub and + * return the frame */ + PUSHMARK(SP); + PUTBACK; + + count = call_pv(gps->prefs_sub, G_SCALAR | G_NOARGS); + + SPAGAIN; + + if (count != 1) + croak("call_pv: Did not return the correct number of values.\n"); + /* the frame was created in a perl sub and is returned */ + ret_frame = (GaimPluginPrefFrame *)gaim_perl_ref_object(POPs); + + /* Tidy up the Perl stack */ + PUTBACK; + FREETMPS; + LEAVE; + + return ret_frame; +} + +static void +destroy_timeout_handler(GaimPerlTimeoutHandler *handler) +{ + timeout_handlers = g_list_remove(timeout_handlers, handler); + + if (handler->callback != NULL) + SvREFCNT_dec(handler->callback); + + if (handler->data != NULL) + SvREFCNT_dec(handler->data); + + g_free(handler); +} + +static void +destroy_signal_handler(GaimPerlSignalHandler *handler) +{ + signal_handlers = g_list_remove(signal_handlers, handler); + + if (handler->callback != NULL) + SvREFCNT_dec(handler->callback); + + if (handler->data != NULL) + SvREFCNT_dec(handler->data); + + g_free(handler->signal); + g_free(handler); +} + +static int +perl_timeout_cb(gpointer data) +{ + GaimPerlTimeoutHandler *handler = (GaimPerlTimeoutHandler *)data; + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(sp); + XPUSHs((SV *)handler->data); + PUTBACK; + call_sv(handler->callback, G_EVAL | G_SCALAR); + SPAGAIN; + + PUTBACK; + FREETMPS; + LEAVE; + + destroy_timeout_handler(handler); + + return 0; +} + +typedef void *DATATYPE; + +static void * +perl_signal_cb(va_list args, void *data) +{ + GaimPerlSignalHandler *handler = (GaimPerlSignalHandler *)data; + void *ret_val = NULL; + int i; + int count; + int value_count; + GaimValue *ret_value, **values; + SV **sv_args; + DATATYPE **copy_args; + STRLEN na; + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(sp); + + gaim_signal_get_values(handler->instance, handler->signal, + &ret_value, &value_count, &values); + + sv_args = g_new(SV *, value_count); + copy_args = g_new(void **, value_count); + + for (i = 0; i < value_count; i++) { + sv_args[i] = gaim_perl_sv_from_vargs(values[i], + (va_list*)&args, + ©_args[i]); + + XPUSHs(sv_args[i]); + } + + XPUSHs((SV *)handler->data); + + PUTBACK; + + if (ret_value != NULL) { + count = call_sv(handler->callback, G_EVAL | G_SCALAR); + + SPAGAIN; + + if (count != 1) + croak("Uh oh! call_sv returned %i != 1", i); + else + ret_val = gaim_perl_data_from_sv(ret_value, POPs); + } else { + call_sv(handler->callback, G_SCALAR); + + SPAGAIN; + } + + if (SvTRUE(ERRSV)) { + gaim_debug_error("perl", + "Perl function exited abnormally: %s\n", + SvPV(ERRSV, na)); + } + + /* See if any parameters changed. */ + for (i = 0; i < value_count; i++) { + if (gaim_value_is_outgoing(values[i])) { + switch (gaim_value_get_type(values[i])) { + case GAIM_TYPE_BOOLEAN: + *((gboolean *)copy_args[i]) = SvIV(sv_args[i]); + break; + + case GAIM_TYPE_INT: + *((int *)copy_args[i]) = SvIV(sv_args[i]); + break; + + case GAIM_TYPE_UINT: + *((unsigned int *)copy_args[i]) = SvUV(sv_args[i]); + break; + + case GAIM_TYPE_LONG: + *((long *)copy_args[i]) = SvIV(sv_args[i]); + break; + + case GAIM_TYPE_ULONG: + *((unsigned long *)copy_args[i]) = SvUV(sv_args[i]); + break; + + case GAIM_TYPE_INT64: + *((gint64 *)copy_args[i]) = SvIV(sv_args[i]); + break; + + case GAIM_TYPE_UINT64: + *((guint64 *)copy_args[i]) = SvUV(sv_args[i]); + break; + + case GAIM_TYPE_STRING: + if (strcmp(*((char **)copy_args[i]), SvPVX(sv_args[i]))) { + g_free(*((char **)copy_args[i])); + *((char **)copy_args[i]) = + g_strdup(SvPV(sv_args[i], na)); + } + break; + + case GAIM_TYPE_POINTER: + *((void **)copy_args[i]) = (void *)SvIV(sv_args[i]); + break; + + case GAIM_TYPE_BOXED: + *((void **)copy_args[i]) = (void *)SvIV(sv_args[i]); + break; + + default: + break; + } + +#if 0 + *((void **)copy_args[i]) = gaim_perl_data_from_sv(values[i], + sv_args[i]); +#endif + } + } + + PUTBACK; + FREETMPS; + LEAVE; + + g_free(sv_args); + g_free(copy_args); + + gaim_debug_misc("perl", "ret_val = %p\n", ret_val); + + return ret_val; +} + +static GaimPerlSignalHandler * +find_signal_handler(GaimPlugin *plugin, void *instance, const char *signal) +{ + GaimPerlSignalHandler *handler; + GList *l; + + for (l = signal_handlers; l != NULL; l = l->next) { + handler = (GaimPerlSignalHandler *)l->data; + + if (handler->plugin == plugin && + handler->instance == instance && + !strcmp(handler->signal, signal)) { + return handler; + } + } + + return NULL; +} + +void +gaim_perl_timeout_add(GaimPlugin *plugin, int seconds, SV *callback, SV *data) +{ + GaimPerlTimeoutHandler *handler; + + if (plugin == NULL) { + croak("Invalid handle in adding perl timeout handler.\n"); + return; + } + + handler = g_new0(GaimPerlTimeoutHandler, 1); + + handler->plugin = plugin; + handler->callback = (callback != NULL && callback != &PL_sv_undef + ? newSVsv(callback) : NULL); + handler->data = (data != NULL && data != &PL_sv_undef + ? newSVsv(data) : NULL); + + timeout_handlers = g_list_append(timeout_handlers, handler); + + handler->iotag = g_timeout_add(seconds * 1000, perl_timeout_cb, handler); +} + +void +gaim_perl_timeout_clear_for_plugin(GaimPlugin *plugin) +{ + GaimPerlTimeoutHandler *handler; + GList *l, *l_next; + + for (l = timeout_handlers; l != NULL; l = l_next) { + l_next = l->next; + + handler = (GaimPerlTimeoutHandler *)l->data; + + if (handler->plugin == plugin) + destroy_timeout_handler(handler); + } +} + +void +gaim_perl_timeout_clear(void) +{ + while (timeout_handlers != NULL) + destroy_timeout_handler(timeout_handlers->data); +} + +void +gaim_perl_signal_connect(GaimPlugin *plugin, void *instance, + const char *signal, SV *callback, SV *data, + int priority) +{ + GaimPerlSignalHandler *handler; + + handler = g_new0(GaimPerlSignalHandler, 1); + handler->plugin = plugin; + handler->instance = instance; + handler->signal = g_strdup(signal); + handler->callback = (callback != NULL && + callback != &PL_sv_undef ? newSVsv(callback) + : NULL); + handler->data = (data != NULL && + data != &PL_sv_undef ? newSVsv(data) : NULL); + + signal_handlers = g_list_append(signal_handlers, handler); + + gaim_signal_connect_priority_vargs(instance, signal, plugin, + GAIM_CALLBACK(perl_signal_cb), + handler, priority); +} + +void +gaim_perl_signal_disconnect(GaimPlugin *plugin, void *instance, + const char *signal) +{ + GaimPerlSignalHandler *handler; + + handler = find_signal_handler(plugin, instance, signal); + + if (handler == NULL) { + croak("Invalid signal handler information in " + "disconnecting a perl signal handler.\n"); + return; + } + + destroy_signal_handler(handler); +} + +void +gaim_perl_signal_clear_for_plugin(GaimPlugin *plugin) +{ + GaimPerlSignalHandler *handler; + GList *l, *l_next; + + for (l = signal_handlers; l != NULL; l = l_next) { + l_next = l->next; + + handler = (GaimPerlSignalHandler *)l->data; + + if (handler->plugin == plugin) + destroy_signal_handler(handler); + } +} + +void +gaim_perl_signal_clear(void) +{ + while (signal_handlers != NULL) + destroy_signal_handler(signal_handlers->data); +} + +static GaimCmdRet +perl_cmd_cb(GaimConversation *conv, const gchar *command, + gchar **args, gchar **error, void *data) +{ + int i = 0, count, ret_value = GAIM_CMD_RET_OK; + SV *cmdSV, *tmpSV, *convSV; + GaimPerlCmdHandler *handler = (GaimPerlCmdHandler *)data; + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + + /* Push the conversation onto the perl stack */ + convSV = sv_2mortal(gaim_perl_bless_object(conv, "Gaim::Conversation")); + XPUSHs(convSV); + + /* Push the command string onto the perl stack */ + cmdSV = newSVpv(command, 0); + cmdSV = sv_2mortal(cmdSV); + XPUSHs(cmdSV); + + /* Push the data onto the perl stack */ + XPUSHs((SV *)handler->data); + + /* Push any arguments we may have */ + for (i = 0; args[i] != NULL; i++) { + /* XXX The mortality of these created SV's should prevent + * memory issues, if I read/understood everything correctly... + */ + tmpSV = newSVpv(args[i], 0); + tmpSV = sv_2mortal(tmpSV); + XPUSHs(tmpSV); + } + + PUTBACK; + count = call_sv(handler->callback, G_EVAL|G_SCALAR); + + if (count != 1) + croak("call_sv: Did not return the correct number of values.\n"); + + SPAGAIN; + + ret_value = POPi; + + PUTBACK; + FREETMPS; + LEAVE; + + return ret_value; +} + +GaimCmdId +gaim_perl_cmd_register(GaimPlugin *plugin, const gchar *command, + const gchar *args, GaimCmdPriority priority, + GaimCmdFlag flag, const gchar *prpl_id, SV *callback, + const gchar *helpstr, SV *data) +{ + GaimPerlCmdHandler *handler; + + handler = g_new0(GaimPerlCmdHandler, 1); + handler->plugin = plugin; + handler->cmd = g_strdup(command); + handler->prpl_id = g_strdup(prpl_id); + + if (callback != NULL && callback != &PL_sv_undef) + handler->callback = newSVsv(callback); + else + handler->callback = NULL; + + if (data != NULL && data != &PL_sv_undef) + handler->data = newSVsv(data); + else + handler->data = NULL; + + cmd_handlers = g_list_append(cmd_handlers, handler); + + handler->id = gaim_cmd_register(command, args, priority, flag, prpl_id, + GAIM_CMD_FUNC(perl_cmd_cb), helpstr, + handler); + + return handler->id; +} + +static void +destroy_cmd_handler(GaimPerlCmdHandler *handler) +{ + cmd_handlers = g_list_remove(cmd_handlers, handler); + + if (handler->callback != NULL) + SvREFCNT_dec(handler->callback); + + if (handler->data != NULL) + SvREFCNT_dec(handler->data); + + g_free(handler->cmd); + g_free(handler->prpl_id); + g_free(handler); +} + +void +gaim_perl_cmd_clear_for_plugin(GaimPlugin *plugin) +{ + GList *l, *l_next; + + for (l = cmd_handlers; l != NULL; l = l_next) { + GaimPerlCmdHandler *handler = (GaimPerlCmdHandler *)l->data; + + l_next = l->next; + + if (handler->plugin == plugin) + destroy_cmd_handler(handler); + } +} + +static GaimPerlCmdHandler * +find_cmd_handler(GaimCmdId id) +{ + GList *l; + + for (l = cmd_handlers; l != NULL; l = l->next) { + GaimPerlCmdHandler *handler = (GaimPerlCmdHandler *)l->data; + + if (handler->id == id) + return handler; + } + + return NULL; +} + +void +gaim_perl_cmd_unregister(GaimCmdId id) +{ + GaimPerlCmdHandler *handler; + + handler = find_cmd_handler(id); + + if (handler == NULL) { + croak("Invalid command id in removing a perl command handler.\n"); + return; + } + + gaim_cmd_unregister(id); + destroy_cmd_handler(handler); +} diff --git a/libpurple/plugins/perl/perl-handlers.h b/libpurple/plugins/perl/perl-handlers.h new file mode 100644 index 0000000000..9f0232fc68 --- /dev/null +++ b/libpurple/plugins/perl/perl-handlers.h @@ -0,0 +1,71 @@ +#ifndef _GAIM_PERL_HANDLERS_H_ +#define _GAIM_PERL_HANDLERS_H_ + +#include "cmds.h" +#include "plugin.h" +#include "prefs.h" +#include "pluginpref.h" +#ifdef GAIM_GTKPERL +#include "gtkplugin.h" +#include "gtkutils.h" +#endif + +typedef struct +{ + GaimCmdId id; + SV *callback; + SV *data; + char *prpl_id; + char *cmd; + GaimPlugin *plugin; +} GaimPerlCmdHandler; + +typedef struct +{ + SV *callback; + SV *data; + GaimPlugin *plugin; + int iotag; + +} GaimPerlTimeoutHandler; + +typedef struct +{ + char *signal; + SV *callback; + SV *data; + void *instance; + GaimPlugin *plugin; + +} GaimPerlSignalHandler; + +void gaim_perl_plugin_action_cb(GaimPluginAction * gpa); +GList *gaim_perl_plugin_actions(GaimPlugin *plugin, gpointer context); + +GaimPluginPrefFrame *gaim_perl_get_plugin_frame(GaimPlugin *plugin); + +#ifdef GAIM_GTKPERL +GtkWidget *gaim_perl_gtk_get_plugin_frame(GaimPlugin *plugin); +#endif + +void gaim_perl_timeout_add(GaimPlugin *plugin, int seconds, SV *callback, + SV *data); +void gaim_perl_timeout_clear_for_plugin(GaimPlugin *plugin); +void gaim_perl_timeout_clear(void); + +void gaim_perl_signal_connect(GaimPlugin *plugin, void *instance, + const char *signal, SV *callback, + SV *data, int priority); +void gaim_perl_signal_disconnect(GaimPlugin *plugin, void *instance, + const char *signal); +void gaim_perl_signal_clear_for_plugin(GaimPlugin *plugin); +void gaim_perl_signal_clear(void); + +GaimCmdId gaim_perl_cmd_register(GaimPlugin *plugin, const gchar *cmd, + const gchar *args, GaimCmdPriority priority, + GaimCmdFlag flag, const gchar *prpl_id, + SV *callback, const gchar *helpstr, SV *data); +void gaim_perl_cmd_unregister(GaimCmdId id); +void gaim_perl_cmd_clear_for_plugin(GaimPlugin *plugin); + +#endif /* _GAIM_PERL_HANDLERS_H_ */ diff --git a/libpurple/plugins/perl/perl.c b/libpurple/plugins/perl/perl.c new file mode 100644 index 0000000000..45d6e18eda --- /dev/null +++ b/libpurple/plugins/perl/perl.c @@ -0,0 +1,611 @@ +/* + * gaim + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifdef HAVE_CONFIG_H +#include +# ifdef HAVE_LIMITS_H +# include +# ifndef NAME_MAX +# define NAME_MAX _POSIX_NAME_MAX +# endif +# endif +#endif + +#ifdef DEBUG +# undef DEBUG +#endif + +#undef PACKAGE + +#define group perl_group + +#ifdef _WIN32 +/* This took me an age to figure out.. without this __declspec(dllimport) + * will be ignored. + */ +# define HASATTRIBUTE +#endif + +#include + +#ifndef _SEM_SEMUN_UNDEFINED +# define HAS_UNION_SEMUN +#endif + +#include +#include + +#ifndef _WIN32 +# include +#endif + +#undef PACKAGE + +#ifndef _WIN32 +# include +#else + /* We're using perl's win32 port of this */ +# define dirent direct +#endif + +#undef group + +/* perl module support */ +#ifdef OLD_PERL +extern void boot_DynaLoader _((CV * cv)); +#else +extern void boot_DynaLoader _((pTHX_ CV * cv)); /* perl is so wacky */ +#endif + +#undef _ +#ifdef DEBUG +# undef DEBUG +#endif +#ifdef _WIN32 +# undef pipe +#endif + +#ifdef _WIN32 +#define _WIN32DEP_H_ +#endif +#include "internal.h" +#include "debug.h" +#include "plugin.h" +#include "signals.h" +#include "version.h" + +#include "perl-common.h" +#include "perl-handlers.h" + +#define PERL_PLUGIN_ID "core-perl" + +PerlInterpreter *my_perl = NULL; + +static GaimPluginUiInfo ui_info = +{ + gaim_perl_get_plugin_frame, + 0, /* page_num (Reserved) */ + NULL /* frame (Reserved) */ +}; + +#ifdef GAIM_GTKPERL +static GaimGtkPluginUiInfo gtk_ui_info = +{ + gaim_perl_gtk_get_plugin_frame, + 0 /* page_num (Reserved) */ +}; +#endif + +static void +#ifdef OLD_PERL +xs_init() +#else +xs_init(pTHX) +#endif +{ + char *file = __FILE__; + + /* This one allows dynamic loading of perl modules in perl scripts by + * the 'use perlmod;' construction */ + newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); +} + +static void +perl_init(void) +{ + /* changed the name of the variable from load_file to perl_definitions + * since now it does much more than defining the load_file sub. + * Moreover, deplaced the initialisation to the xs_init function. + * (TheHobbit) */ + char *perl_args[] = { "", "-e", "0", "-w" }; + char perl_definitions[] = + { + /* We use to function one to load a file the other to execute + * the string obtained from the first and holding the file + * contents. This allows to have a really local $/ without + * introducing temp variables to hold the old value. Just a + * question of style:) */ + "package Gaim::PerlLoader;" + "use Symbol;" + + "sub load_file {" + "my $f_name=shift;" + "local $/=undef;" + "open FH,$f_name or return \"__FAILED__\";" + "$_=;" + "close FH;" + "return $_;" + "}" + + "sub destroy_package {" + "eval { $_[0]->UNLOAD() if $_[0]->can('UNLOAD'); };" + "Symbol::delete_package($_[0]);" + "}" + + "sub load_n_eval {" + "my ($f_name, $package) = @_;" + "destroy_package($package);" + "my $strin=load_file($f_name);" + "return 2 if($strin eq \"__FAILED__\");" + "my $eval = qq{package $package; $strin;};" + + "{" + " eval $eval;" + "}" + + "if($@) {" + /*" #something went wrong\n"*/ + "die(\"Errors loading file $f_name: $@\");" + "}" + + "return 0;" + "}" + }; + + my_perl = perl_alloc(); + PERL_SET_CONTEXT(my_perl); + PL_perl_destruct_level = 1; + perl_construct(my_perl); +#ifdef DEBUG + perl_parse(my_perl, xs_init, 4, perl_args, NULL); +#else + perl_parse(my_perl, xs_init, 3, perl_args, NULL); +#endif +#ifdef HAVE_PERL_EVAL_PV + eval_pv(perl_definitions, TRUE); +#else + perl_eval_pv(perl_definitions, TRUE); /* deprecated */ +#endif + perl_run(my_perl); +} + +static void +perl_end(void) +{ + if (my_perl == NULL) + return; + + PL_perl_destruct_level = 1; + PERL_SET_CONTEXT(my_perl); + perl_eval_pv( + "foreach my $lib (@DynaLoader::dl_modules) {" + "if ($lib =~ /^Gaim\\b/) {" + "$lib .= '::deinit();';" + "eval $lib;" + "}" + "}", + TRUE); + + PL_perl_destruct_level = 1; + PERL_SET_CONTEXT(my_perl); + perl_destruct(my_perl); + perl_free(my_perl); + my_perl = NULL; +} + +void +gaim_perl_callXS(void (*subaddr)(pTHX_ CV *cv), CV *cv, SV **mark) +{ + dSP; + + PUSHMARK(mark); + (*subaddr)(aTHX_ cv); + + PUTBACK; +} + +static gboolean +probe_perl_plugin(GaimPlugin *plugin) +{ + /* XXX This would be much faster if I didn't create a new + * PerlInterpreter every time I probed a plugin */ + + PerlInterpreter *prober = perl_alloc(); + char *argv[] = {"", plugin->path }; + gboolean status = TRUE; + HV *plugin_info; + PERL_SET_CONTEXT(prober); + PL_perl_destruct_level = 1; + perl_construct(prober); + + perl_parse(prober, xs_init, 2, argv, NULL); + + perl_run(prober); + + plugin_info = perl_get_hv("PLUGIN_INFO", FALSE); + + if (plugin_info == NULL) + status = FALSE; + else if (!hv_exists(plugin_info, "perl_api_version", + strlen("perl_api_version")) || + !hv_exists(plugin_info, "name", strlen("name")) || + !hv_exists(plugin_info, "load", strlen("load"))) { + /* Not a valid plugin. */ + + status = FALSE; + } else { + SV **key; + int perl_api_ver; + + key = hv_fetch(plugin_info, "perl_api_version", + strlen("perl_api_version"), 0); + + perl_api_ver = SvIV(*key); + + if (perl_api_ver != 2) + status = FALSE; + else { + GaimPluginInfo *info; + GaimPerlScript *gps; + char *basename; + STRLEN len; + + info = g_new0(GaimPluginInfo, 1); + gps = g_new0(GaimPerlScript, 1); + + info->magic = GAIM_PLUGIN_MAGIC; + info->major_version = GAIM_MAJOR_VERSION; + info->minor_version = GAIM_MINOR_VERSION; + info->type = GAIM_PLUGIN_STANDARD; + + info->dependencies = g_list_append(info->dependencies, + PERL_PLUGIN_ID); + + gps->plugin = plugin; + + basename = g_path_get_basename(plugin->path); + gaim_perl_normalize_script_name(basename); + gps->package = g_strdup_printf("Gaim::Script::%s", + basename); + g_free(basename); + + /* We know this one exists. */ + key = hv_fetch(plugin_info, "name", strlen("name"), 0); + info->name = g_strdup(SvPV(*key, len)); + /* Set id here in case we don't find one later. */ + info->id = g_strdup(SvPV(*key, len)); + +#ifdef GAIM_GTKPERL + if ((key = hv_fetch(plugin_info, "GTK_UI", + strlen("GTK_UI"), 0))) + info->ui_requirement = GAIM_GTK_PLUGIN_TYPE; +#endif + + if ((key = hv_fetch(plugin_info, "url", + strlen("url"), 0))) + info->homepage = g_strdup(SvPV(*key, len)); + + if ((key = hv_fetch(plugin_info, "author", + strlen("author"), 0))) + info->author = g_strdup(SvPV(*key, len)); + + if ((key = hv_fetch(plugin_info, "summary", + strlen("summary"), 0))) + info->summary = g_strdup(SvPV(*key, len)); + + if ((key = hv_fetch(plugin_info, "description", + strlen("description"), 0))) + info->description = g_strdup(SvPV(*key, len)); + + if ((key = hv_fetch(plugin_info, "version", + strlen("version"), 0))) + info->version = g_strdup(SvPV(*key, len)); + + /* We know this one exists. */ + key = hv_fetch(plugin_info, "load", strlen("load"), 0); + gps->load_sub = g_strdup_printf("%s::%s", gps->package, + SvPV(*key, len)); + + if ((key = hv_fetch(plugin_info, "unload", + strlen("unload"), 0))) + gps->unload_sub = g_strdup_printf("%s::%s", + gps->package, + SvPV(*key, len)); + + if ((key = hv_fetch(plugin_info, "id", + strlen("id"), 0))) { + g_free(info->id); + info->id = g_strdup_printf("perl-%s", + SvPV(*key, len)); + } + + /********************************************************/ + /* Only one of the next two options should be present */ + /* */ + /* prefs_info - Uses non-GUI (read GTK) gaim API calls */ + /* and creates a GaimPluginPrefInfo type. */ + /* */ + /* gtk_prefs_info - Requires gtk2-perl be installed by */ + /* the user and he must create a */ + /* GtkWidget the user and he must */ + /* create a GtkWidget representing the */ + /* plugin preferences page. */ + /********************************************************/ + if ((key = hv_fetch(plugin_info, "prefs_info", + strlen("prefs_info"), 0))) { + /* key now is the name of the Perl sub that + * will create a frame for us */ + gps->prefs_sub = g_strdup_printf("%s::%s", + gps->package, + SvPV(*key, len)); + info->prefs_info = &ui_info; + } + +#ifdef GAIM_GTKPERL + if ((key = hv_fetch(plugin_info, "gtk_prefs_info", + strlen("gtk_prefs_info"), 0))) { + /* key now is the name of the Perl sub that + * will create a frame for us */ + gps->gtk_prefs_sub = g_strdup_printf("%s::%s", + gps->package, + SvPV(*key, len)); + info->ui_info = >k_ui_info; + } +#endif + + if ((key = hv_fetch(plugin_info, "plugin_action_sub", + strlen("plugin_action_sub"), 0))) { + gps->plugin_action_sub = g_strdup_printf("%s::%s", + gps->package, + SvPV(*key, len)); + info->actions = gaim_perl_plugin_actions; + } + + plugin->info = info; + info->extra_info = gps; + + status = gaim_plugin_register(plugin); + } + } + + PL_perl_destruct_level = 1; + PERL_SET_CONTEXT(prober); + perl_destruct(prober); + perl_free(prober); + return status; +} + +static gboolean +load_perl_plugin(GaimPlugin *plugin) +{ + GaimPerlScript *gps = (GaimPerlScript *)plugin->info->extra_info; + char *atmp[3] = { plugin->path, NULL, NULL }; + + if (gps == NULL || gps->load_sub == NULL) + return FALSE; + + gaim_debug(GAIM_DEBUG_INFO, "perl", "Loading perl script\n"); + + if (my_perl == NULL) + perl_init(); + + plugin->handle = gps; + + atmp[1] = gps->package; + + PERL_SET_CONTEXT(my_perl); + execute_perl("Gaim::PerlLoader::load_n_eval", 2, atmp); + + { + dSP; + PERL_SET_CONTEXT(my_perl); + SPAGAIN; + ENTER; + SAVETMPS; + PUSHMARK(sp); + XPUSHs(sv_2mortal(gaim_perl_bless_object(plugin, + "Gaim::Plugin"))); + PUTBACK; + + perl_call_pv(gps->load_sub, G_EVAL | G_SCALAR); + SPAGAIN; + + if (SvTRUE(ERRSV)) { + STRLEN len; + + gaim_debug(GAIM_DEBUG_ERROR, "perl", + "Perl function %s exited abnormally: %s\n", + gps->load_sub, SvPV(ERRSV, len)); + } + + PUTBACK; + FREETMPS; + LEAVE; + } + + return TRUE; +} + +static void +destroy_package(const char *package) +{ + dSP; + PERL_SET_CONTEXT(my_perl); + SPAGAIN; + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(package, strlen(package)))); + PUTBACK; + + perl_call_pv("Gaim::PerlLoader::destroy_package", + G_VOID | G_EVAL | G_DISCARD); + + SPAGAIN; + + PUTBACK; + FREETMPS; + LEAVE; +} + +static gboolean +unload_perl_plugin(GaimPlugin *plugin) +{ + GaimPerlScript *gps = (GaimPerlScript *)plugin->info->extra_info; + + if (gps == NULL) + return FALSE; + + gaim_debug(GAIM_DEBUG_INFO, "perl", "Unloading perl script\n"); + + if (gps->unload_sub != NULL) { + dSP; + PERL_SET_CONTEXT(my_perl); + SPAGAIN; + ENTER; + SAVETMPS; + PUSHMARK(sp); + XPUSHs(sv_2mortal(gaim_perl_bless_object(plugin, + "Gaim::Plugin"))); + PUTBACK; + + perl_call_pv(gps->unload_sub, G_EVAL | G_SCALAR); + SPAGAIN; + + if (SvTRUE(ERRSV)) { + STRLEN len; + + gaim_debug(GAIM_DEBUG_ERROR, "perl", + "Perl function %s exited abnormally: %s\n", + gps->load_sub, SvPV(ERRSV, len)); + } + + PUTBACK; + FREETMPS; + LEAVE; + } + + gaim_perl_cmd_clear_for_plugin(plugin); + gaim_perl_signal_clear_for_plugin(plugin); + gaim_perl_timeout_clear_for_plugin(plugin); + + destroy_package(gps->package); + + return TRUE; +} + +static void +destroy_perl_plugin(GaimPlugin *plugin) +{ + if (plugin->info != NULL) { + GaimPerlScript *gps; + + g_free(plugin->info->name); + g_free(plugin->info->version); + g_free(plugin->info->summary); + g_free(plugin->info->description); + g_free(plugin->info->author); + g_free(plugin->info->homepage); + + gps = (GaimPerlScript *)plugin->info->extra_info; + if (gps != NULL) { + g_free(gps->load_sub); + g_free(gps->unload_sub); + g_free(gps->package); + g_free(gps->prefs_sub); +#ifdef GAIM_GTKPERL + g_free(gps->gtk_prefs_sub); +#endif + g_free(gps); + plugin->info->extra_info = NULL; + } + } +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + return TRUE; +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + perl_end(); + + return TRUE; +} + +static GaimPluginLoaderInfo loader_info = +{ + NULL, /**< exts */ + probe_perl_plugin, /**< probe */ + load_perl_plugin, /**< load */ + unload_perl_plugin, /**< unload */ + destroy_perl_plugin /**< destroy */ +}; + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_LOADER, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + PERL_PLUGIN_ID, /**< id */ + N_("Perl Plugin Loader"), /**< name */ + VERSION, /**< version */ + N_("Provides support for loading perl plugins."), /**< summary */ + N_("Provides support for loading perl plugins."), /**< description */ + "Christian Hammond ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + &loader_info, /**< extra_info */ + NULL, + NULL +}; + +static void +init_plugin(GaimPlugin *plugin) +{ + loader_info.exts = g_list_append(loader_info.exts, "pl"); +} + +GAIM_INIT_PLUGIN(perl, init_plugin, info) diff --git a/libpurple/plugins/perl/scripts/account.pl b/libpurple/plugins/perl/scripts/account.pl new file mode 100644 index 0000000000..e4bc2ca282 --- /dev/null +++ b/libpurple/plugins/perl/scripts/account.pl @@ -0,0 +1,122 @@ +$MODULE_NAME = "Account Functions Test"; + +use Gaim; + +# All the information Gaim gets about our nifty plugin +%PLUGIN_INFO = ( + perl_api_version => 2, + name => "Perl: $MODULE_NAME", + version => "0.1", + summary => "Test plugin for the Perl interpreter.", + description => "Implements a set of test proccedures to ensure all " . + "functions that work in the C API still work in the " . + "Perl plugin interface. As XSUBs are added, this " . + "*should* be updated to test the changes. " . + "Furthermore, this will function as the tutorial perl " . + "plugin.", + author => "John H. Kelm ", + url => "http://sourceforge.net/users/johnhkelm/", + + load => "plugin_load", + unload => "plugin_unload" +); + + + # These names must already exist + my $USERNAME = "johnhkelm2"; + + # We will create these on load then destroy them on unload + my $TEST_NAME = "perlTestName"; + my $PROTOCOL_ID = "prpl-oscar"; + + +sub plugin_init { + return %PLUGIN_INFO; +} + + +# This is the sub defined in %PLUGIN_INFO to be called when the plugin is loaded +# Note: The plugin has a reference to itself on top of the argument stack. +sub plugin_load { + my $plugin = shift; + print "#" x 80 . "\n\n"; + Gaim::Debug::info($MODULE_NAME, "plugin_load() - Testing $MODULE_NAME Started."); + print "\n\n"; + + + ################################# + # # + # Gaim::Account::Option # + # # + ################################# + + print "Testing: Gaim::Account::Option::new()...\n"; + $acc_opt = Gaim::Account::Option->new(1, "TEXT", "pref_name"); + $acc_opt2 = Gaim::Account::Option->bool_new("TeXt", "MYprefName", 1); + + ################################# + # # + # Gaim::Account # + # # + ################################# + + + print "Testing: Gaim::Account::new()... "; + $account = Gaim::Account->new($TEST_NAME, $PROTOCOL_ID); + if ($account) { print "ok.\n"; } else { print "fail.\n"; } + + print "Testing: Gaim::Accounts::add()..."; + Gaim::Accounts::add($account); + print "pending find...\n"; + + print "Testing: Gaim::Accounts::find()..."; + $account = Gaim::Accounts::find($TEST_NAME, $PROTOCOL_ID); + if ($account) { print "ok.\n"; } else { print "fail.\n"; } + + print "Testing: Gaim::Account::get_username()... "; + $user_name = $account->get_username(); + if ($user_name) { + print "Success: $user_name.\n"; + } else { + print "Failed!\n"; + } + + print "Testing: Gaim::Account::is_connected()... "; + if ($account->is_connected()) { + print " Connected.\n"; + } else { + print " Disconnected.\n"; + } + + print "Testing: Gaim::Accounts::get_active_status()... "; + if ($account->get_active_status()) { + print "Okay.\n"; + } else { + print "Failed!\n"; + } + + $account = Gaim::Accounts::find($USERNAME, $PROTOCOL_ID); + print "Testing: Gaim::Accounts::connect()...pending...\n"; + + $account->set_status("available", TRUE); + $account->connect(); + + print "\n\n"; + Gaim::Debug::info($MODULE_NAME, "plugin_load() - Testing $MODULE_NAME Completed.\n"); + print "\n\n" . "#" x 80 . "\n\n"; +} + +sub plugin_unload { + my $plugin = shift; + + print "#" x 80 . "\n\n"; + Gaim::Debug::info($MODULE_NAME, "plugin_unload() - Testing $MODULE_NAME Started.\n"); + print "\n\n"; + + ######### TEST CODE HERE ########## + + print "\n\n"; + Gaim::Debug::info($MODULE_NAME, "plugin_unload() - Testing $MODULE_NAME Completed.\n"); + print "\n\n" . "#" x 80 . "\n\n"; +} + diff --git a/libpurple/plugins/perl/scripts/buddy_list.pl b/libpurple/plugins/perl/scripts/buddy_list.pl new file mode 100644 index 0000000000..3aef6487ae --- /dev/null +++ b/libpurple/plugins/perl/scripts/buddy_list.pl @@ -0,0 +1,107 @@ +$MODULE_NAME = "Buddy List Test"; + +use Gaim; + +# All the information Gaim gets about our nifty plugin +%PLUGIN_INFO = ( + perl_api_version => 2, + name => "Perl: $MODULE_NAME", + version => "0.1", + summary => "Test plugin for the Perl interpreter.", + description => "Implements a set of test proccedures to ensure all functions that work in the C API still work in the Perl plugin interface. As XSUBs are added, this *should* be updated to test the changes. Furthermore, this will function as the tutorial perl plugin.", + author => "John H. Kelm ", + url => "http://sourceforge.net/users/johnhkelm/", + + load => "plugin_load", + unload => "plugin_unload" +); + + + # These names must already exist + my $USERNAME = "johnhkelm2"; + + # We will create these on load then destroy them on unload + my $TEST_GROUP = "UConn Buddies"; + my $TEST_NAME = "johnhkelm"; + my $TEST_ALIAS = "John Kelm"; + my $PROTOCOL_ID = "prpl-oscar"; + + +sub plugin_init { + return %PLUGIN_INFO; +} + + +# This is the sub defined in %PLUGIN_INFO to be called when the plugin is loaded +# Note: The plugin has a reference to itself on top of the argument stack. +sub plugin_load { + my $plugin = shift; + + # This is how we get an account to use in the following tests. You should replace the username + # with an existing user + $account = Gaim::Accounts::find($USERNAME, $PROTOCOL_ID); + + # Testing a find function: Note Gaim::Find not Gaim::Buddy:find! + # Furthermore, this should work the same for chats and groups + Gaim::Debug::info($MODULE_NAME, "Testing: Gaim::Find::buddy()..."); + $buddy = Gaim::Find::buddy($account, $TEST_NAME); + Gaim::Debug::info("", ($buddy ? "ok." : "fail.") . "\n"); + + # If you should need the handle for some reason, here is how you do it + Gaim::Debug::info($MODULE_NAME, "Testing: Gaim::BuddyList::get_handle()..."); + $handle = Gaim::BuddyList::get_handle(); + Gaim::Debug::info("", ($handle ? "ok." : "fail.") . "\n"); + + # This gets the Gaim::BuddyList and references it by $blist + Gaim::Debug::info($MODULE_NAME, "Testing: Gaim::get_blist()..."); + $blist = Gaim::get_blist(); + Gaim::Debug::info("", ($blist ? "ok." : "fail.") . "\n"); + + # This is how you would add a buddy named $TEST_NAME" with the alias $TEST_ALIAS + Gaim::Debug::info($MODULE_NAME, "Testing: Gaim::BuddyList::Buddy::new..."); + $buddy = Gaim::BuddyList::Buddy::new($account, $TEST_NAME, $TEST_ALIAS); + Gaim::Debug::info("", ($buddy ? "ok." : "fail.") . "\n"); + + # Here we add the new buddy '$buddy' to the group $TEST_GROUP + # so first we must find the group + Gaim::Debug::info($MODULE_NAME, "Testing: Gaim::Find::group..."); + $group = Gaim::Find::group($TEST_GROUP); + Gaim::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 + Gaim::Debug::info($MODULE_NAME, "Testing: Gaim::BuddyList::add_buddy...\n"); + Gaim::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 + Gaim::Debug::info($MODULE_NAME, "Testing: Gaim::Find::buddies...\n"); + @buddy_array = Gaim::Find::buddies($account, undef); + if (@buddy_array) { + Gaim::Debug::info($MODULE_NAME, "Buddies in list (" . @buddy_array . "): \n"); + foreach $bud (@buddy_array) { + Gaim::Debug::info($MODULE_NAME, Gaim::BuddyList::Buddy::get_name($bud) . "\n"); + } + } +} + +sub plugin_unload { + my $plugin = shift; + + print "#" x 80 . "\n\n"; + ######### TEST CODE HERE ########## + + print "Testing: Gaim::Find::buddy()..."; + $buddy = Gaim::Find::buddy($account, $TEST_NAME . TEST); + if ($buddy) { + print "ok.\n"; + print "Testing: Gaim::BuddyList::remove_buddy()..."; + Gaim::BuddyList::remove_buddy($buddy); + if (Gaim::Find::buddy($account, $TEST_NAME . TEST)) { print "fail.\n"; } else { print "ok.\n"; } + } else { print "fail.\n"; } + + + print "\n\n" . "#" x 80 . "\n\n"; +} + diff --git a/libpurple/plugins/perl/scripts/conversation.pl b/libpurple/plugins/perl/scripts/conversation.pl new file mode 100644 index 0000000000..a04273f2be --- /dev/null +++ b/libpurple/plugins/perl/scripts/conversation.pl @@ -0,0 +1,119 @@ +$MODULE_NAME = "Conversation Test"; + +use Gaim; + +# All the information Gaim gets about our nifty plugin +%PLUGIN_INFO = ( + perl_api_version => 2, + name => "Perl: $MODULE_NAME", + version => "0.1", + summary => "Test plugin for the Perl interpreter.", + description => "Implements a set of test proccedures to ensure all " . + "functions that work in the C API still work in the " . + "Perl plugin interface. As XSUBs are added, this " . + "*should* be updated to test the changes. " . + "Furthermore, this will function as the tutorial perl " . + "plugin.", + author => "John H. Kelm ", + url => "http://sourceforge.net/users/johnhkelm/", + + load => "plugin_load", + unload => "plugin_unload" +); + + + # These names must already exist + my $GROUP = "UIUC Buddies"; + my $USERNAME = "johnhkelm2"; + + # We will create these on load then destroy them on unload + my $TEST_GROUP = "UConn Buddies"; + my $TEST_NAME = "johnhkelm"; + my $TEST_ALIAS = "John Kelm"; + my $PROTOCOL_ID = "prpl-oscar"; + + +sub plugin_init { + return %PLUGIN_INFO; +} + + +# This is the sub defined in %PLUGIN_INFO to be called when the plugin is loaded +# Note: The plugin has a reference to itself on top of the argument stack. +sub plugin_load { + my $plugin = shift; + print "#" x 80 . "\n\n"; + + print "PERL: Finding account.\n"; + $account = Gaim::Accounts::find($USERNAME, $PROTOCOL_ID); + + ######### TEST CODE HERE ########## + # First we create two new conversations. + print "Testing Gaim::Conversation::new()..."; + $conv1 = Gaim::Conversation->new(1, $account, "Test Conversation 1"); + if ($conv1) { print "ok.\n"; } else { print "fail.\n"; } + + print "Testing Gaim::Conversation::new()..."; + $conv2 = Gaim::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 Gaim::Conversation::Window + print "Testing Gaim::Conversation::Window::new()...\n"; + $win = Gaim::Conversation::Window::new(); + + # The third thing to do is to add the two conversations to the windows. + # The subroutine add_conversation() returns the number of conversations + # present in the window. + print "Testing Gaim::Conversation::Window::add_conversation()..."; + $conv_count = $conv1->add_conversation(); + if ($conv_count) { + print "ok..." . $conv_count . " conversations...\n"; + } else { + print "fail.\n"; + } + + print "Testing Gaim::Conversation::Window::add_conversation()..."; + $conv_count = $win->add_conversation($conv2); + if ($conv_count) { + print "ok..." . $conv_count . " conversations...\n"; + } else { + print "fail.\n"; + } + + # Now the window is displayed to the user. + print "Testing Gaim::Conversation::Window::show()...\n"; + $win->show(); + + # Use get_im_data() to get a handle for the conversation + print "Testing Gaim::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 Gaim::Conversation::IM::send()...\n"; + $im->send("Message Test."); + + print "Testing Gaim::Conversation::IM::write()...\n"; + $im->write("SENDER", "Message Test.", 0, 0); + + print "#" x 80 . "\n\n"; +} + +sub plugin_unload { + my $plugin = shift; + + print "#" x 80 . "\n\n"; + ######### TEST CODE HERE ########## + + print "Testing Gaim::Conversation::Window::get_conversation_count()...\n"; + $conv_count = $win->get_conversation_count(); + print "...and it returned $conv_count.\n"; + if ($conv_count > 0) { + print "Testing Gaim::Conversation::Window::destroy()...\n"; + $win->destroy(); + } + + print "\n\n" . "#" x 80 . "\n\n"; +} + diff --git a/libpurple/plugins/perl/scripts/count_down.pl b/libpurple/plugins/perl/scripts/count_down.pl new file mode 100644 index 0000000000..d1a22fcded --- /dev/null +++ b/libpurple/plugins/perl/scripts/count_down.pl @@ -0,0 +1,89 @@ +use Gaim; + +%PLUGIN_INFO = ( + perl_api_version => 2, + name => "Countdown Info Timer", + version => "0.1", + summary => "Makes a countdown in days from today.", + description => "Long description coming....", + author => "John H. Kelm ", + url => "http://gaim.sourceforge.net/", + + load => "plugin_load", + unload => "plugin_unload" +); + + $GLOBAL_TEST_VAR = "STUFF!"; + +sub plugin_unload { + my $plugin = shift; +} + +sub plugin_init { + return %PLUGIN_INFO; +} + + +sub plugin_load { + my $plugin = shift; + + # Retrieve all the accounts + @accounts = Gaim::Accounts::get_all(); + + print "NUM OF ACCS: " . $accounts . "\n"; + # Search each account's user info for our tag + foreach $acc (@accounts) { + print "IN ACCOUNTS\n"; + $user_info = $acc->get_user_info(); + print "USER INFO 1: " . $user_info . "\n"; + # Find and replace + $user_info =~ /countdown([0-9]+).([0-9]+).([0-9]+)/; + print "Found: " .$1 . " " . $2 . " " . $3 . "\n"; + $days = count_days($1, $2, $3); + $user_info =~ s/countdown(\d\d\d\d).(\d\d).(\d\d)/$days/; + print "USER INFO 2: " . $user_info . "\n"; + # $acc->set_user_info($user_info); + + } + + eval ' + use Gtk2 \'-init\'; + use Glib; + $window = Gtk2::Window->new(\'toplevel\'); + $window->set_border_width(10); + $button = Gtk2::Button->new("Hello World"); + $button->signal_connect(clicked => \&hello, $window); + + $window->add($button); + $button->show; + $window->show; + # Gtk2->main; + + 0; + + '; warn $@ if $@; +} + +sub hello { + my ($widget, $window) = @_; + print "Called from sub hello!\n "; + print "Test var: " . $GLOBAL_TEST_VAR . " \n"; + @accounts = Gaim::Accounts::get_all(); + $acc = $accounts[0]; + $user_info = $acc->get_user_info(); + print "USER INFO from sub hello: " . $user_info . "\n"; + $window->destroy; +} + +sub count_days { + ($year, $month, $day) = @_; + + + eval ' + use Time::Local; + $future = timelocal(0,0,0,$day,$month-1,$year); + '; warn $@ if $@; + $today = time(); + $days = int(($future - $today)/(60*60*24)); + return $days; +} diff --git a/libpurple/plugins/perl/scripts/gtk_frame_test.pl b/libpurple/plugins/perl/scripts/gtk_frame_test.pl new file mode 100644 index 0000000000..5569b39368 --- /dev/null +++ b/libpurple/plugins/perl/scripts/gtk_frame_test.pl @@ -0,0 +1,66 @@ +$MODULE_NAME = "GTK Frame Test"; + +use Gaim; + +%PLUGIN_INFO = ( + perl_api_version => 2, + name => " Perl: $MODULE_NAME", + version => "0.1", + summary => "Test plugin for the Perl interpreter.", + description => "Implements a set of test proccedures to ensure all functions that work in the C API still work in the Perl plugin interface. As XSUBs are added, this *should* be updated to test the changes. Furthermore, this will function as the tutorial perl plugin.", + author => "John H. Kelm ", + url => "http://gaim.sourceforge.net/", + + GTK_UI => TRUE, + gtk_prefs_info => "foo", + load => "plugin_load", + unload => "plugin_unload", +); + + +sub plugin_init { + return %PLUGIN_INFO; +} + +sub button_cb { + my $widget = shift; + my $data = shift; + print "Clicked button with message: " . $data . "\n"; +} + +sub foo { + eval ' + use Glib; + use Gtk2 \'-init\'; + + $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; +} + +sub plugin_load { + my $plugin = shift; + print "#" x 80 . "\n"; + + + ######### TEST CODE HERE ########## + + print "$MODULE_NAME: Loading...\n"; + + + Gaim::debug_info("plugin_load()", "Testing $MODULE_NAME Completed."); + print "#" x 80 . "\n\n"; +} + +sub plugin_unload { + +} diff --git a/libpurple/plugins/perl/scripts/plugin_action.pl b/libpurple/plugins/perl/scripts/plugin_action.pl new file mode 100644 index 0000000000..04f28edb08 --- /dev/null +++ b/libpurple/plugins/perl/scripts/plugin_action.pl @@ -0,0 +1,58 @@ +$MODULE_NAME = "Plugin Action Test Plugin"; +use Gaim; + +sub plugin_init { + return %PLUGIN_INFO; +} + +sub plugin_load { + my $plugin = shift; +} + +sub plugin_unload { + my $plugin = shift; +} + +sub fun1 { + print "1\n"; +} + +sub fun2 { + print "2\n"; +} + +sub fun3 { + print "3\n"; +} + +%plugin_actions = ( + "Action 1" => \&fun1, + "Action 2" => \&fun2, + "Action 3" => \&fun3 +# "Action 1" => sub { print "1\n"; }, +# "Action 2" => sub { print "2\n"; }, +# "Action 3" => sub { print "3\n"; } +); + +sub plugin_action_names { + foreach $key (keys %plugin_actions) { + push @array, $key; + } + + return @array; +} + +# All the information Gaim gets about our nifty plugin +%PLUGIN_INFO = ( + perl_api_version => 2, + name => "Perl: $MODULE_NAME", + version => "0.1", + summary => "Test plugin for the Perl interpreter.", + description => "Just a basic test plugin template.", + author => "Etan Reisner ", + url => "http://sourceforge.net/users/deryni9/", + + load => "plugin_load", + unload => "plugin_unload", + plugin_action_sub => "plugin_action_names" +); diff --git a/libpurple/plugins/perl/scripts/plugin_pref.pl b/libpurple/plugins/perl/scripts/plugin_pref.pl new file mode 100644 index 0000000000..4a763356b4 --- /dev/null +++ b/libpurple/plugins/perl/scripts/plugin_pref.pl @@ -0,0 +1,94 @@ +$MODULE_NAME = "Prefs Functions Test"; +use Gaim; +# All the information Gaim gets about our nifty plugin +%PLUGIN_INFO = ( + perl_api_version => 2, + name => "Perl: $MODULE_NAME", + version => "0.1", + summary => "Test plugin for the Perl interpreter.", + description => "Implements a set of test proccedures to ensure all " . + "functions that work in the C API still work in the " . + "Perl plugin interface. As XSUBs are added, this " . + "*should* be updated to test the changes. " . + "Furthermore, this will function as the tutorial perl " . + "plugin.", + author => "John H. Kelm ", + url => "http://sourceforge.net/users/johnhkelm/", + + load => "plugin_load", + unload => "plugin_unload", + prefs_info => "foo" +); + + # These names must already exist + my $GROUP = "UIUC Buddies"; + my $USERNAME = "johnhkelm2"; + + # We will create these on load then destroy them on unload + my $TEST_GROUP = "perlTestGroup"; + my $TEST_NAME = "perlTestName"; + my $TEST_ALIAS = "perlTestAlias"; + my $PROTOCOL_ID = "prpl-oscar"; + +sub foo { + $frame = Gaim::PluginPref::Frame->new(); + + $ppref = Gaim::PluginPref->new_with_label("boolean"); + $frame->add($ppref); + + $ppref = Gaim::PluginPref->new_with_name_and_label( + "/plugins/core/perl_test/bool", "Boolean Preference"); + $frame->add($ppref); + + + $ppref = Gaim::PluginPref->new_with_name_and_label( + "/plugins/core/perl_test/choice", "Choice Preference"); + $ppref->set_type(1); + $ppref->add_choice("ch0", $frame); + $ppref->add_choice("ch1", $frame); + $frame->add($ppref); + + $ppref = Gaim::PluginPref->new_with_name_and_label( + "/plugins/core/perl_test/text", "Text Box Preference"); + $ppref->set_max_length(16); + $frame->add($ppref); + + return $frame; +} + +sub plugin_init { + + return %PLUGIN_INFO; +} + + +# This is the sub defined in %PLUGIN_INFO to be called when the plugin is loaded +# Note: The plugin has a reference to itself on top of the argument stack. +sub plugin_load { + my $plugin = shift; + print "#" x 80 . "\n\n"; + + + ######### TEST CODE HERE ########## + + Gaim::Prefs::add_none("/plugins/core/perl_test"); + Gaim::Prefs::add_bool("/plugins/core/perl_test/bool", 1); + Gaim::Prefs::add_string("/plugins/core/perl_test/choice", "ch1"); + Gaim::Prefs::add_string("/plugins/core/perl_test/text", "Foobar"); + + + print "\n\n" . "#" x 80 . "\n\n"; +} + +sub plugin_unload { + my $plugin = shift; + + print "#" x 80 . "\n\n"; + + + ######### TEST CODE HERE ########## + + + print "\n\n" . "#" x 80 . "\n\n"; +} + diff --git a/libpurple/plugins/perl/scripts/request.pl b/libpurple/plugins/perl/scripts/request.pl new file mode 100644 index 0000000000..1bed69f28f --- /dev/null +++ b/libpurple/plugins/perl/scripts/request.pl @@ -0,0 +1,109 @@ +$MODULE_NAME = "Request Functions Test"; + +use Gaim; + +# All the information Gaim gets about our nifty plugin +%PLUGIN_INFO = ( + perl_api_version => 2, + name => "Perl: $MODULE_NAME", + version => "0.1", + summary => "Test plugin for the Perl interpreter.", + description => "Implements a set of test proccedures to ensure all functions that work in the C API still work in the Perl plugin interface. As XSUBs are added, this *should* be updated to test the changes. Furthermore, this will function as the tutorial perl plugin.", + author => "John H. Kelm ", + url => "http://sourceforge.net/users/johnhkelm/", + + load => "plugin_load", + unload => "plugin_unload", + plugin_action_sub => "plugin_action_names" +); + +%plugin_actions = ( + "Plugin Action Test Label" => \&plugin_action_test, +); + +sub plugin_action_names { + foreach $key (keys %plugin_actions) { + push @array, $key; + } + + return @array; +} + +sub plugin_init { + return %PLUGIN_INFO; +} + +sub ok_cb_test { + $fields = shift; + + Gaim::Debug::info($MODULE_NAME, "plugin_action_cb_test: BEGIN\n"); + Gaim::Debug::info($MODULE_NAME, "ok_cb_test: BEGIN\n"); + Gaim::Debug::info($MODULE_NAME, "ok_cb_test: Button Click\n"); + Gaim::Debug::info($MODULE_NAME, "ok_cb_test: Field Type: $fields \n"); + $account = Gaim::Request::Fields::get_account($fields, "acct_test"); + Gaim::Debug::info($MODULE_NAME, "ok_cb_test: Username of selected account: " . Gaim::Account::get_username($account) . "\n"); + $int = Gaim::Request::Fields::get_integer($fields, "int_test"); + Gaim::Debug::info($MODULE_NAME, "ok_cb_test: Integer Value: $int \n"); + $choice = Gaim::Request::Fields::get_choice($fields, "ch_test"); + Gaim::Debug::info($MODULE_NAME, "ok_cb_test: Choice Value: $choice \n"); + Gaim::Debug::info($MODULE_NAME, "ok_cb_test: END\n"); +} + +sub cancel_cb_test { + Gaim::Debug::info($MODULE_NAME, "cancel_cb_test: Button Click\n"); +} + +sub plugin_action_test { + $plugin = shift; + Gaim::Debug::info($MODULE_NAME, "plugin_action_cb_test: BEGIN\n"); + plugin_request($plugin); + Gaim::Debug::info($MODULE_NAME, "plugin_action_cb_test: END\n"); +} + +sub plugin_load { + my $plugin = shift; + ######### TEST CODE HERE ########## + + +} + +sub plugin_request { + $group = Gaim::Request::Field::Group::new("Group Name"); + $field = Gaim::Request::Field::account_new("acct_test", "Account Text", undef); + Gaim::Request::Field::account_set_show_all($field, 0); + Gaim::Request::Field::Group::add_field($group, $field); + + $field = Gaim::Request::Field::int_new("int_test", "Integer Text", 33); + Gaim::Request::Field::Group::add_field($group, $field); + + # Test field choice + $field = Gaim::Request::Field::choice_new("ch_test", "Choice Text", 1); + Gaim::Request::Field::choice_add($field, "Choice 0"); + Gaim::Request::Field::choice_add($field, "Choice 1"); + Gaim::Request::Field::choice_add($field, "Choice 2"); + + Gaim::Request::Field::Group::add_field($group, $field); + + + $request = Gaim::Request::Fields::new(); + Gaim::Request::Fields::add_group($request, $group); + + Gaim::Request::fields( + $plugin, + "Request Title!", + "Primary Title", + "Secondary Title", + $request, + "Ok Text", "ok_cb_test", + "Cancel Text", "cancel_cb_test"); +} + +sub plugin_unload { + my $plugin = shift; + Gaim::Debug::info($MODULE_NAME, "#" x 80 . "\n"); + ######### TEST CODE HERE ########## + + + Gaim::Debug::info($MODULE_NAME, "\n" . "#" x 80 . "\n"); +} + diff --git a/libpurple/plugins/pluginpref_example.c b/libpurple/plugins/pluginpref_example.c new file mode 100644 index 0000000000..961c4d08da --- /dev/null +++ b/libpurple/plugins/pluginpref_example.c @@ -0,0 +1,162 @@ +/* + * PluginPref Example Plugin + * + * Copyright (C) 2004, Gary Kramlich + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifndef GAIM_PLUGINS +# define GAIM_PLUGINS +#endif + +#include "internal.h" + +#include "plugin.h" +#include "pluginpref.h" +#include "prefs.h" +#include "version.h" + +static GaimPluginPrefFrame * +get_plugin_pref_frame(GaimPlugin *plugin) { + GaimPluginPrefFrame *frame; + GaimPluginPref *ppref; + + frame = gaim_plugin_pref_frame_new(); + + ppref = gaim_plugin_pref_new_with_label("boolean"); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/pluginpref_example/bool", + "boolean pref"); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_label("integer"); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/pluginpref_example/int", + "integer pref"); + gaim_plugin_pref_set_bounds(ppref, 0, 255); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/pluginpref_example/int_choice", + "integer choice"); + gaim_plugin_pref_set_type(ppref, GAIM_PLUGIN_PREF_CHOICE); + gaim_plugin_pref_add_choice(ppref, "One", GINT_TO_POINTER(1)); + gaim_plugin_pref_add_choice(ppref, "Two", GINT_TO_POINTER(2)); + gaim_plugin_pref_add_choice(ppref, "Four", GINT_TO_POINTER(4)); + gaim_plugin_pref_add_choice(ppref, "Eight", GINT_TO_POINTER(8)); + gaim_plugin_pref_add_choice(ppref, "Sixteen", GINT_TO_POINTER(16)); + gaim_plugin_pref_add_choice(ppref, "Thirty Two", GINT_TO_POINTER(32)); + gaim_plugin_pref_add_choice(ppref, "Sixty Four", GINT_TO_POINTER(64)); + gaim_plugin_pref_add_choice(ppref, "One Hundred Twenty Eight", GINT_TO_POINTER(128)); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_label("string"); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/pluginpref_example/string", + "string pref"); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/pluginpref_example/masked_string", + "masked string"); + gaim_plugin_pref_set_masked(ppref, TRUE); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/pluginpref_example/max_string", + "string pref\n(max length of 16)"); + gaim_plugin_pref_set_max_length(ppref, 16); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label( + "/plugins/core/pluginpref_example/string_choice", + "string choice"); + gaim_plugin_pref_set_type(ppref, GAIM_PLUGIN_PREF_CHOICE); + gaim_plugin_pref_add_choice(ppref, "red", "red"); + gaim_plugin_pref_add_choice(ppref, "orange", "orange"); + gaim_plugin_pref_add_choice(ppref, "yellow", "yellow"); + gaim_plugin_pref_add_choice(ppref, "green", "green"); + gaim_plugin_pref_add_choice(ppref, "blue", "blue"); + gaim_plugin_pref_add_choice(ppref, "purple", "purple"); + gaim_plugin_pref_frame_add(frame, ppref); + + return frame; +} + +static GaimPluginUiInfo prefs_info = { + get_plugin_pref_frame, + 0, /* page_num (Reserved) */ + NULL /* frame (Reserved) */ +}; + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + "core-pluginpref_example", /**< id */ + "Pluginpref Example", /**< name */ + VERSION, /**< version */ + /** summary */ + "An example of how to use pluginprefs", + /** description */ + "An example of how to use pluginprefs", + "Gary Kramlich ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + NULL, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + &prefs_info, /**< prefs_info */ + NULL +}; + +static void +init_plugin(GaimPlugin *plugin) +{ + gaim_prefs_add_none("/plugins/core/pluginpref_example"); + gaim_prefs_add_bool("/plugins/core/pluginpref_example/bool", TRUE); + gaim_prefs_add_int("/plugins/core/pluginpref_example/int", 0); + gaim_prefs_add_int("/plugins/core/pluginpref_example/int_choice", 1); + gaim_prefs_add_string("/plugins/core/pluginpref_example/string", + "string"); + gaim_prefs_add_string("/plugins/core/pluginpref_example/max_string", + "max length string"); + gaim_prefs_add_string("/plugins/core/pluginpref_example/masked_string", "masked"); + gaim_prefs_add_string("/plugins/core/pluginpref_example/string_choice", "red"); +} + +GAIM_INIT_PLUGIN(ppexample, init_plugin, info) diff --git a/libpurple/plugins/psychic.c b/libpurple/plugins/psychic.c new file mode 100644 index 0000000000..134f777400 --- /dev/null +++ b/libpurple/plugins/psychic.c @@ -0,0 +1,164 @@ + + +#include "internal.h" + +#include "account.h" +#include "blist.h" +#include "conversation.h" +#include "debug.h" +#include "signals.h" +#include "status.h" +#include "version.h" + +#include "plugin.h" +#include "pluginpref.h" +#include "prefs.h" + + +#define PLUGIN_ID "core-psychic" +#define PLUGIN_NAME N_("Psychic Mode") +#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, Jabber, Sametime, and Yahoo!") +#define PLUGIN_AUTHOR "Christopher O'Brien " + + +#define PREFS_BASE "/plugins/core/psychic" +#define PREF_BUDDIES PREFS_BASE "/buddies_only" +#define PREF_NOTICE PREFS_BASE "/show_notice" +#define PREF_STATUS PREFS_BASE "/activate_online" +#define PREF_RAISE PREFS_BASE "/raise_conv" + + +static void +buddy_typing_cb(GaimAccount *acct, const char *name, void *data) { + GaimConversation *gconv; + + if(gaim_prefs_get_bool(PREF_STATUS) && + ! gaim_status_is_available(gaim_account_get_active_status(acct))) { + gaim_debug_info("psychic", "not available, doing nothing\n"); + return; + } + + if(gaim_prefs_get_bool(PREF_BUDDIES) && + ! gaim_find_buddy(acct, name)) { + gaim_debug_info("psychic", "not in blist, doing nothing\n"); + return; + } + + gconv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, acct); + if(! gconv) { + gaim_debug_info("psychic", "no previous conversation exists\n"); + gconv = gaim_conversation_new(GAIM_CONV_TYPE_IM, acct, name); + + if(gaim_prefs_get_bool(PREF_RAISE)) { + gaim_conversation_present(gconv); + } + + if(gaim_prefs_get_bool(PREF_NOTICE)) { + + /* This is a quote from Star Wars. You should probably not + translate it literally. If you can't find a fitting cultural + reference in your language, consider translating something + like this instead: "You feel a new message coming." */ + gaim_conversation_write(gconv, NULL, + _("You feel a disturbance in the force..."), + GAIM_MESSAGE_SYSTEM | GAIM_MESSAGE_NO_LOG | GAIM_MESSAGE_ACTIVE_ONLY, + time(NULL)); + } + + gaim_conv_im_set_typing_state(GAIM_CONV_IM(gconv), GAIM_TYPING); + } +} + + +static GaimPluginPrefFrame * +get_plugin_pref_frame(GaimPlugin *plugin) { + + GaimPluginPrefFrame *frame; + GaimPluginPref *pref; + + frame = gaim_plugin_pref_frame_new(); + + pref = gaim_plugin_pref_new_with_name(PREF_BUDDIES); + gaim_plugin_pref_set_label(pref, _("Only enable for users on" + " the buddy list")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name(PREF_STATUS); + gaim_plugin_pref_set_label(pref, _("Disable when away")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name(PREF_NOTICE); + gaim_plugin_pref_set_label(pref, _("Display notification message in" + " conversations")); + gaim_plugin_pref_frame_add(frame, pref); + + pref = gaim_plugin_pref_new_with_name(PREF_RAISE); + gaim_plugin_pref_set_label(pref, _("Raise psychic conversations")); + gaim_plugin_pref_frame_add(frame, pref); + + return frame; +} + + +static gboolean +plugin_load(GaimPlugin *plugin) { + + void *convs_handle; + convs_handle = gaim_conversations_get_handle(); + + gaim_signal_connect(convs_handle, "buddy-typing", plugin, + GAIM_CALLBACK(buddy_typing_cb), NULL); + + return TRUE; +} + + +static GaimPluginUiInfo prefs_info = { + get_plugin_pref_frame, + 0, /* page_num (Reserved) */ + NULL, /* frame (Reserved) */ +}; + + +static GaimPluginInfo info = { + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + PLUGIN_ID, /**< id */ + PLUGIN_NAME, /**< name */ + VERSION, /**< version */ + PLUGIN_SUMMARY, /**< summary */ + PLUGIN_DESC, /**< description */ + PLUGIN_AUTHOR, /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + &prefs_info, /**< prefs_info */ + NULL, /**< actions */ +}; + + +static void +init_plugin(GaimPlugin *plugin) { + gaim_prefs_add_none(PREFS_BASE); + gaim_prefs_add_bool(PREF_BUDDIES, FALSE); + gaim_prefs_add_bool(PREF_NOTICE, TRUE); + gaim_prefs_add_bool(PREF_STATUS, TRUE); +} + + +GAIM_INIT_PLUGIN(psychic, init_plugin, info) diff --git a/libpurple/plugins/signals-test.c b/libpurple/plugins/signals-test.c new file mode 100644 index 0000000000..7bac24131d --- /dev/null +++ b/libpurple/plugins/signals-test.c @@ -0,0 +1,708 @@ +/* + * Signals test 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#define SIGNAL_TEST_PLUGIN_ID "core-signals-test" + +#include + +#include "internal.h" +#include "cipher.h" +#include "connection.h" +#include "conversation.h" +#include "core.h" +#include "debug.h" +#include "ft.h" +#include "signals.h" +#include "version.h" +#include "status.h" +#include "sound.h" + +/************************************************************************** + * Account subsystem signal callbacks + **************************************************************************/ +static void +account_connecting_cb(GaimAccount *account, void *data) +{ + gaim_debug_misc("signals test", "account-connecting (%s)\n", + gaim_account_get_username(account)); +} + +static void +account_setting_info_cb(GaimAccount *account, const char *info, void *data) +{ + gaim_debug_misc("signals test", "account-setting-info (%s, %s)\n", + gaim_account_get_username(account), info); +} + +static void +account_set_info_cb(GaimAccount *account, const char *info, void *data) +{ + gaim_debug_misc("signals test", "account-set-info (%s, %s)\n", + gaim_account_get_username(account), info); +} + +static void +account_status_changed(GaimAccount *account, GaimStatus *old, GaimStatus *new, + gpointer data) +{ + gaim_debug_misc("signals test", "account-status-changed (%s, %s, %s)\n", + gaim_account_get_username(account), + gaim_status_get_name(old), + gaim_status_get_name(new)); +} + +static void +account_alias_changed(GaimAccount *account, const char *old, gpointer data) +{ + gaim_debug_misc("signals test", "account-alias-changed (%s, %s, %s)\n", + gaim_account_get_username(account), + old, gaim_account_get_alias(account)); +} + +/************************************************************************** + * Buddy Icons signal callbacks + **************************************************************************/ +static void +buddy_icon_changed_cb(GaimBuddy *buddy) +{ + gaim_debug_misc("signals test", "buddy icon changed (%s)\n", + gaim_buddy_get_name(buddy)); +} + +/************************************************************************** + * Buddy List subsystem signal callbacks + **************************************************************************/ +static void +buddy_status_changed_cb(GaimBuddy *buddy, GaimStatus *old_status, + GaimStatus *status, void *data) +{ + gaim_debug_misc("signals test", "buddy-status-changed (%s %s to %s)\n", + buddy->name, gaim_status_get_id(old_status), + gaim_status_get_id(status)); +} + +static void +buddy_idle_changed_cb(GaimBuddy *buddy, gboolean old_idle, gboolean idle, + void *data) +{ + gaim_debug_misc("signals test", "buddy-idle-changed (%s %s)\n", + buddy->name, old_idle ? "unidled" : "idled"); +} + +static void +buddy_signed_on_cb(GaimBuddy *buddy, void *data) +{ + gaim_debug_misc("signals test", "buddy-signed-on (%s)\n", buddy->name); +} + +static void +buddy_signed_off_cb(GaimBuddy *buddy, void *data) +{ + gaim_debug_misc("signals test", "buddy-signed-off (%s)\n", buddy->name); +} + +static void +buddy_added_cb(GaimBuddy *buddy, void *data) +{ + gaim_debug_misc("signals test", "buddy_added_cb (%s)\n", gaim_buddy_get_name(buddy)); +} + +static void +buddy_removed_cb(GaimBuddy *buddy, void *data) +{ + gaim_debug_misc("signals test", "buddy_removed_cb (%s)\n", gaim_buddy_get_name(buddy)); +} + +static void +blist_node_aliased(GaimBlistNode *node, const char *old_alias) +{ + GaimContact *p = (GaimContact *)node; + GaimBuddy *b = (GaimBuddy *)node; + GaimChat *c = (GaimChat *)node; + GaimGroup *g = (GaimGroup *)node; + + if (GAIM_BLIST_NODE_IS_CONTACT(node)) + gaim_debug_misc("signals test", "blist-node-aliased (Contact: %s, %s)\n", p->alias, old_alias); + else if (GAIM_BLIST_NODE_IS_BUDDY(node)) + gaim_debug_misc("signals test", "blist-node-aliased (Buddy: %s, %s)\n", b->name, old_alias); + else if (GAIM_BLIST_NODE_IS_CHAT(node)) + gaim_debug_misc("signals test", "blist-node-aliased (Chat: %s, %s)\n", c->alias, old_alias); + else if (GAIM_BLIST_NODE_IS_GROUP(node)) + gaim_debug_misc("signals test", "blist-node-aliased (Group: %s, %s)\n", g->name, old_alias); + else + gaim_debug_misc("signals test", "blist-node-aliased (UNKNOWN: %d, %s)\n", node->type, old_alias); + +} + +static void +blist_node_extended_menu_cb(GaimBlistNode *node, void *data) +{ + GaimContact *p = (GaimContact *)node; + GaimBuddy *b = (GaimBuddy *)node; + GaimChat *c = (GaimChat *)node; + GaimGroup *g = (GaimGroup *)node; + + if (GAIM_BLIST_NODE_IS_CONTACT(node)) + gaim_debug_misc("signals test", "blist-node-extended-menu (Contact: %s)\n", p->alias); + else if (GAIM_BLIST_NODE_IS_BUDDY(node)) + gaim_debug_misc("signals test", "blist-node-extended-menu (Buddy: %s)\n", b->name); + else if (GAIM_BLIST_NODE_IS_CHAT(node)) + gaim_debug_misc("signals test", "blist-node-extended-menu (Chat: %s)\n", c->alias); + else if (GAIM_BLIST_NODE_IS_GROUP(node)) + gaim_debug_misc("signals test", "blist-node-extended-menu (Group: %s)\n", g->name); + else + gaim_debug_misc("signals test", "blist-node-extended-menu (UNKNOWN: %d)\n", node->type); + +} + + +/************************************************************************** + * Connection subsystem signal callbacks + **************************************************************************/ +static void +signing_on_cb(GaimConnection *gc, void *data) +{ + gaim_debug_misc("signals test", "signing-on (%s)\n", + gaim_account_get_username(gaim_connection_get_account(gc))); +} + +static void +signed_on_cb(GaimConnection *gc, void *data) +{ + gaim_debug_misc("signals test", "signed-on (%s)\n", + gaim_account_get_username(gaim_connection_get_account(gc))); +} + +static void +signing_off_cb(GaimConnection *gc, void *data) +{ + gaim_debug_misc("signals test", "signing-off (%s)\n", + gaim_account_get_username(gaim_connection_get_account(gc))); +} + +static void +signed_off_cb(GaimConnection *gc, void *data) +{ + gaim_debug_misc("signals test", "signed-off (%s)\n", + gaim_account_get_username(gaim_connection_get_account(gc))); +} + +/************************************************************************** + * Conversation subsystem signal callbacks + **************************************************************************/ +static gboolean +writing_im_msg_cb(GaimAccount *account, const char *who, char **buffer, + GaimConversation *conv, GaimMessageFlags flags, void *data) +{ + gaim_debug_misc("signals test", "writing-im-msg (%s, %s, %s)\n", + gaim_account_get_username(account), gaim_conversation_get_name(conv), *buffer); + + return FALSE; + +} + +static void +wrote_im_msg_cb(GaimAccount *account, const char *who, const char *buffer, + GaimConversation *conv, GaimMessageFlags flags, void *data) +{ + gaim_debug_misc("signals test", "wrote-im-msg (%s, %s, %s)\n", + gaim_account_get_username(account), gaim_conversation_get_name(conv), buffer); +} + +static void +sending_im_msg_cb(GaimAccount *account, char *recipient, char **buffer, void *data) +{ + gaim_debug_misc("signals test", "sending-im-msg (%s, %s, %s)\n", + gaim_account_get_username(account), recipient, *buffer); + +} + +static void +sent_im_msg_cb(GaimAccount *account, const char *recipient, const char *buffer, void *data) +{ + gaim_debug_misc("signals test", "sent-im-msg (%s, %s, %s)\n", + gaim_account_get_username(account), recipient, buffer); +} + +static gboolean +receiving_im_msg_cb(GaimAccount *account, char **sender, char **buffer, + GaimConversation *conv, GaimMessageFlags *flags, void *data) +{ + gaim_debug_misc("signals test", "receiving-im-msg (%s, %s, %s, %s, %d)\n", + gaim_account_get_username(account), *sender, *buffer, + (conv != NULL) ? gaim_conversation_get_name(conv) : "(null)", *flags); + + return FALSE; +} + +static void +received_im_msg_cb(GaimAccount *account, char *sender, char *buffer, + GaimConversation *conv, GaimMessageFlags flags, void *data) +{ + gaim_debug_misc("signals test", "received-im-msg (%s, %s, %s, %s, %d)\n", + gaim_account_get_username(account), sender, buffer, + (conv != NULL) ? gaim_conversation_get_name(conv) : "(null)", flags); +} + +static gboolean +writing_chat_msg_cb(GaimAccount *account, const char *who, char **buffer, + GaimConversation *conv, GaimMessageFlags flags, void *data) +{ + gaim_debug_misc("signals test", "writing-chat-msg (%s, %s)\n", + gaim_conversation_get_name(conv), *buffer); + + return FALSE; +} + +static void +wrote_chat_msg_cb(GaimAccount *account, const char *who, const char *buffer, + GaimConversation *conv, GaimMessageFlags flags, void *data) +{ + gaim_debug_misc("signals test", "wrote-chat-msg (%s, %s)\n", + gaim_conversation_get_name(conv), buffer); +} + +static gboolean +sending_chat_msg_cb(GaimAccount *account, char **buffer, int id, void *data) +{ + gaim_debug_misc("signals test", "sending-chat-msg (%s, %s, %d)\n", + gaim_account_get_username(account), *buffer, id); + + return FALSE; +} + +static void +sent_chat_msg_cb(GaimAccount *account, const char *buffer, int id, void *data) +{ + gaim_debug_misc("signals test", "sent-chat-msg (%s, %s, %d)\n", + gaim_account_get_username(account), buffer, id); +} + +static gboolean +receiving_chat_msg_cb(GaimAccount *account, char **sender, char **buffer, + GaimConversation *chat, GaimMessageFlags *flags, void *data) +{ + gaim_debug_misc("signals test", + "receiving-chat-msg (%s, %s, %s, %s, %d)\n", + gaim_account_get_username(account), *sender, *buffer, + gaim_conversation_get_name(chat), *flags); + + return FALSE; +} + +static void +received_chat_msg_cb(GaimAccount *account, char *sender, char *buffer, + GaimConversation *chat, GaimMessageFlags flags, void *data) +{ + gaim_debug_misc("signals test", + "received-chat-msg (%s, %s, %s, %s, %d)\n", + gaim_account_get_username(account), sender, buffer, + gaim_conversation_get_name(chat), flags); +} + +static void +conversation_created_cb(GaimConversation *conv, void *data) +{ + gaim_debug_misc("signals test", "conversation-created (%s)\n", + gaim_conversation_get_name(conv)); +} + +static void +deleting_conversation_cb(GaimConversation *conv, void *data) +{ + gaim_debug_misc("signals test", "deleting-conversation (%s)\n", + gaim_conversation_get_name(conv)); +} + +static void +buddy_typing_cb(GaimAccount *account, const char *name, void *data) +{ + gaim_debug_misc("signals test", "buddy-typing (%s, %s)\n", + gaim_account_get_username(account), name); +} + +static void +buddy_typing_stopped_cb(GaimAccount *account, const char *name, void *data) +{ + gaim_debug_misc("signals test", "buddy-typing-stopped (%s, %s)\n", + gaim_account_get_username(account), name); +} + +static gboolean +chat_buddy_joining_cb(GaimConversation *conv, const char *user, + GaimConvChatBuddyFlags flags, void *data) +{ + gaim_debug_misc("signals test", "chat-buddy-joining (%s, %s, %d)\n", + gaim_conversation_get_name(conv), user, flags); + + return FALSE; +} + +static void +chat_buddy_joined_cb(GaimConversation *conv, const char *user, + GaimConvChatBuddyFlags flags, gboolean new_arrival, void *data) +{ + gaim_debug_misc("signals test", "chat-buddy-joined (%s, %s, %d, %d)\n", + gaim_conversation_get_name(conv), user, flags, new_arrival); +} + +static void +chat_buddy_flags_cb(GaimConversation *conv, const char *user, + GaimConvChatBuddyFlags oldflags, GaimConvChatBuddyFlags newflags, void *data) +{ + gaim_debug_misc("signals test", "chat-buddy-flags (%s, %s, %d, %d)\n", + gaim_conversation_get_name(conv), user, oldflags, newflags); +} + +static gboolean +chat_buddy_leaving_cb(GaimConversation *conv, const char *user, + const char *reason, void *data) +{ + gaim_debug_misc("signals test", "chat-buddy-leaving (%s, %s, %s)\n", + gaim_conversation_get_name(conv), user, reason); + + return FALSE; +} + +static void +chat_buddy_left_cb(GaimConversation *conv, const char *user, + const char *reason, void *data) +{ + gaim_debug_misc("signals test", "chat-buddy-left (%s, %s, %s)\n", + gaim_conversation_get_name(conv), user, reason); +} + +static void +chat_inviting_user_cb(GaimConversation *conv, const char *name, + char **reason, void *data) +{ + gaim_debug_misc("signals test", "chat-inviting-user (%s, %s, %s)\n", + gaim_conversation_get_name(conv), name, *reason); +} + +static void +chat_invited_user_cb(GaimConversation *conv, const char *name, + const char *reason, void *data) +{ + gaim_debug_misc("signals test", "chat-invited-user (%s, %s, %s)\n", + gaim_conversation_get_name(conv), name, reason); +} + +static gint +chat_invited_cb(GaimAccount *account, const char *inviter, + const char *room_name, const char *message, + const GHashTable *components, void *data) +{ + gaim_debug_misc("signals test", "chat-invited (%s, %s, %s, %s)\n", + gaim_account_get_username(account), inviter, + room_name, message); + + return 0; +} + +static void +chat_joined_cb(GaimConversation *conv, void *data) +{ + gaim_debug_misc("signals test", "chat-joined (%s)\n", + gaim_conversation_get_name(conv)); +} + +static void +chat_left_cb(GaimConversation *conv, void *data) +{ + gaim_debug_misc("signals test", "chat-left (%s)\n", + gaim_conversation_get_name(conv)); +} + +static void +chat_topic_changed_cb(GaimConversation *conv, const char *who, + const char *topic, void *data) +{ + gaim_debug_misc("signals test", + "chat-topic-changed (%s topic changed to: \"%s\" by %s)\n", + gaim_conversation_get_name(conv), topic, + (who) ? who : "unknown"); +} +/************************************************************************** + * Ciphers signal callbacks + **************************************************************************/ +static void +cipher_added_cb(GaimCipher *cipher, void *data) { + gaim_debug_misc("signals test", "cipher %s added\n", + gaim_cipher_get_name(cipher)); +} + +static void +cipher_removed_cb(GaimCipher *cipher, void *data) { + gaim_debug_misc("signals test", "cipher %s removed\n", + gaim_cipher_get_name(cipher)); +} + +/************************************************************************** + * Core signal callbacks + **************************************************************************/ +static void +quitting_cb(void *data) +{ + gaim_debug_misc("signals test", "quitting ()\n"); +} + +/************************************************************************** + * File transfer signal callbacks + **************************************************************************/ +static void +ft_recv_accept_cb(GaimXfer *xfer, gpointer data) { + gaim_debug_misc("signals test", "file receive accepted\n"); +} + +static void +ft_send_accept_cb(GaimXfer *xfer, gpointer data) { + gaim_debug_misc("signals test", "file send accepted\n"); +} + +static void +ft_recv_start_cb(GaimXfer *xfer, gpointer data) { + gaim_debug_misc("signals test", "file receive started\n"); +} + +static void +ft_send_start_cb(GaimXfer *xfer, gpointer data) { + gaim_debug_misc("signals test", "file send started\n"); +} + +static void +ft_recv_cancel_cb(GaimXfer *xfer, gpointer data) { + gaim_debug_misc("signals test", "file receive canceled\n"); +} + +static void +ft_send_cancel_cb(GaimXfer *xfer, gpointer data) { + gaim_debug_misc("signals test", "file send canceled\n"); +} + +static void +ft_recv_complete_cb(GaimXfer *xfer, gpointer data) { + gaim_debug_misc("signals test", "file receive completed\n"); +} + +static void +ft_send_complete_cb(GaimXfer *xfer, gpointer data) { + gaim_debug_misc("signals test", "file send completed\n"); +} + +/************************************************************************** + * Sound signal callbacks + **************************************************************************/ +static int +sound_playing_event_cb(GaimSoundEventID event, const GaimAccount *account) { + if (account != NULL) + gaim_debug_misc("signals test", "sound playing event: %d for account: %s\n", + event, gaim_account_get_username(account)); + else + gaim_debug_misc("signals test", "sound playing event: %d\n", event); + + return 0; +} + +/************************************************************************** + * Plugin stuff + **************************************************************************/ +static gboolean +plugin_load(GaimPlugin *plugin) +{ + void *core_handle = gaim_get_core(); + void *blist_handle = gaim_blist_get_handle(); + void *conn_handle = gaim_connections_get_handle(); + void *conv_handle = gaim_conversations_get_handle(); + void *accounts_handle = gaim_accounts_get_handle(); + void *ciphers_handle = gaim_ciphers_get_handle(); + void *ft_handle = gaim_xfers_get_handle(); + void *sound_handle = gaim_sounds_get_handle(); + + /* Accounts subsystem signals */ + gaim_signal_connect(accounts_handle, "account-connecting", + plugin, GAIM_CALLBACK(account_connecting_cb), NULL); + gaim_signal_connect(accounts_handle, "account-setting-info", + plugin, GAIM_CALLBACK(account_setting_info_cb), NULL); + gaim_signal_connect(accounts_handle, "account-set-info", + plugin, GAIM_CALLBACK(account_set_info_cb), NULL); + gaim_signal_connect(accounts_handle, "account-status-changed", + plugin, GAIM_CALLBACK(account_status_changed), NULL); + gaim_signal_connect(accounts_handle, "account-alias-changed", + plugin, GAIM_CALLBACK(account_alias_changed), NULL); + + /* Buddy List subsystem signals */ + gaim_signal_connect(blist_handle, "buddy-status-changed", + plugin, GAIM_CALLBACK(buddy_status_changed_cb), NULL); + gaim_signal_connect(blist_handle, "buddy-idle-changed", + plugin, GAIM_CALLBACK(buddy_idle_changed_cb), NULL); + gaim_signal_connect(blist_handle, "buddy-signed-on", + plugin, GAIM_CALLBACK(buddy_signed_on_cb), NULL); + gaim_signal_connect(blist_handle, "buddy-signed-off", + plugin, GAIM_CALLBACK(buddy_signed_off_cb), NULL); + gaim_signal_connect(blist_handle, "buddy-added", + plugin, GAIM_CALLBACK(buddy_added_cb), NULL); + gaim_signal_connect(blist_handle, "buddy-removed", + plugin, GAIM_CALLBACK(buddy_removed_cb), NULL); + gaim_signal_connect(blist_handle, "buddy-icon-changed", + plugin, GAIM_CALLBACK(buddy_icon_changed_cb), NULL); + gaim_signal_connect(blist_handle, "blist-node-aliased", + plugin, GAIM_CALLBACK(blist_node_aliased), NULL); + gaim_signal_connect(blist_handle, "blist-node-extended-menu", + plugin, GAIM_CALLBACK(blist_node_extended_menu_cb), NULL); + + /* Connection subsystem signals */ + gaim_signal_connect(conn_handle, "signing-on", + plugin, GAIM_CALLBACK(signing_on_cb), NULL); + gaim_signal_connect(conn_handle, "signed-on", + plugin, GAIM_CALLBACK(signed_on_cb), NULL); + gaim_signal_connect(conn_handle, "signing-off", + plugin, GAIM_CALLBACK(signing_off_cb), NULL); + gaim_signal_connect(conn_handle, "signed-off", + plugin, GAIM_CALLBACK(signed_off_cb), NULL); + + /* Conversations subsystem signals */ + gaim_signal_connect(conv_handle, "writing-im-msg", + plugin, GAIM_CALLBACK(writing_im_msg_cb), NULL); + gaim_signal_connect(conv_handle, "wrote-im-msg", + plugin, GAIM_CALLBACK(wrote_im_msg_cb), NULL); + gaim_signal_connect(conv_handle, "sending-im-msg", + plugin, GAIM_CALLBACK(sending_im_msg_cb), NULL); + gaim_signal_connect(conv_handle, "sent-im-msg", + plugin, GAIM_CALLBACK(sent_im_msg_cb), NULL); + gaim_signal_connect(conv_handle, "receiving-im-msg", + plugin, GAIM_CALLBACK(receiving_im_msg_cb), NULL); + gaim_signal_connect(conv_handle, "received-im-msg", + plugin, GAIM_CALLBACK(received_im_msg_cb), NULL); + gaim_signal_connect(conv_handle, "writing-chat-msg", + plugin, GAIM_CALLBACK(writing_chat_msg_cb), NULL); + gaim_signal_connect(conv_handle, "wrote-chat-msg", + plugin, GAIM_CALLBACK(wrote_chat_msg_cb), NULL); + gaim_signal_connect(conv_handle, "sending-chat-msg", + plugin, GAIM_CALLBACK(sending_chat_msg_cb), NULL); + gaim_signal_connect(conv_handle, "sent-chat-msg", + plugin, GAIM_CALLBACK(sent_chat_msg_cb), NULL); + gaim_signal_connect(conv_handle, "receiving-chat-msg", + plugin, GAIM_CALLBACK(receiving_chat_msg_cb), NULL); + gaim_signal_connect(conv_handle, "received-chat-msg", + plugin, GAIM_CALLBACK(received_chat_msg_cb), NULL); + gaim_signal_connect(conv_handle, "conversation-created", + plugin, GAIM_CALLBACK(conversation_created_cb), NULL); + gaim_signal_connect(conv_handle, "deleting-conversation", + plugin, GAIM_CALLBACK(deleting_conversation_cb), NULL); + gaim_signal_connect(conv_handle, "buddy-typing", + plugin, GAIM_CALLBACK(buddy_typing_cb), NULL); + gaim_signal_connect(conv_handle, "buddy-typing-stopped", + plugin, GAIM_CALLBACK(buddy_typing_stopped_cb), NULL); + gaim_signal_connect(conv_handle, "chat-buddy-joining", + plugin, GAIM_CALLBACK(chat_buddy_joining_cb), NULL); + gaim_signal_connect(conv_handle, "chat-buddy-joined", + plugin, GAIM_CALLBACK(chat_buddy_joined_cb), NULL); + gaim_signal_connect(conv_handle, "chat-buddy-flags", + plugin, GAIM_CALLBACK(chat_buddy_flags_cb), NULL); + gaim_signal_connect(conv_handle, "chat-buddy-leaving", + plugin, GAIM_CALLBACK(chat_buddy_leaving_cb), NULL); + gaim_signal_connect(conv_handle, "chat-buddy-left", + plugin, GAIM_CALLBACK(chat_buddy_left_cb), NULL); + gaim_signal_connect(conv_handle, "chat-inviting-user", + plugin, GAIM_CALLBACK(chat_inviting_user_cb), NULL); + gaim_signal_connect(conv_handle, "chat-invited-user", + plugin, GAIM_CALLBACK(chat_invited_user_cb), NULL); + gaim_signal_connect(conv_handle, "chat-invited", + plugin, GAIM_CALLBACK(chat_invited_cb), NULL); + gaim_signal_connect(conv_handle, "chat-joined", + plugin, GAIM_CALLBACK(chat_joined_cb), NULL); + gaim_signal_connect(conv_handle, "chat-left", + plugin, GAIM_CALLBACK(chat_left_cb), NULL); + gaim_signal_connect(conv_handle, "chat-topic-changed", + plugin, GAIM_CALLBACK(chat_topic_changed_cb), NULL); + + /* Ciphers signals */ + gaim_signal_connect(ciphers_handle, "cipher-added", + plugin, GAIM_CALLBACK(cipher_added_cb), NULL); + gaim_signal_connect(ciphers_handle, "cipher-removed", + plugin, GAIM_CALLBACK(cipher_removed_cb), NULL); + + /* Core signals */ + gaim_signal_connect(core_handle, "quitting", + plugin, GAIM_CALLBACK(quitting_cb), NULL); + + /* File transfer signals */ + gaim_signal_connect(ft_handle, "file-recv-accept", + plugin, GAIM_CALLBACK(ft_recv_accept_cb), NULL); + gaim_signal_connect(ft_handle, "file-recv-start", + plugin, GAIM_CALLBACK(ft_recv_start_cb), NULL); + gaim_signal_connect(ft_handle, "file-recv-cancel", + plugin, GAIM_CALLBACK(ft_recv_cancel_cb), NULL); + gaim_signal_connect(ft_handle, "file-recv-complete", + plugin, GAIM_CALLBACK(ft_recv_complete_cb), NULL); + gaim_signal_connect(ft_handle, "file-send-accept", + plugin, GAIM_CALLBACK(ft_send_accept_cb), NULL); + gaim_signal_connect(ft_handle, "file-send-start", + plugin, GAIM_CALLBACK(ft_send_start_cb), NULL); + gaim_signal_connect(ft_handle, "file-send-cancel", + plugin, GAIM_CALLBACK(ft_send_cancel_cb), NULL); + gaim_signal_connect(ft_handle, "file-send-complete", + plugin, GAIM_CALLBACK(ft_send_complete_cb), NULL); + + /* Sound signals */ + gaim_signal_connect(sound_handle, "playing-sound-event", plugin, + GAIM_CALLBACK(sound_playing_event_cb), NULL); + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + SIGNAL_TEST_PLUGIN_ID, /**< id */ + N_("Signals Test"), /**< name */ + 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 ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, + NULL +}; + +static void +init_plugin(GaimPlugin *plugin) +{ +} + +GAIM_INIT_PLUGIN(signalstest, init_plugin, info) diff --git a/libpurple/plugins/simple.c b/libpurple/plugins/simple.c new file mode 100644 index 0000000000..fb5fb7fa1c --- /dev/null +++ b/libpurple/plugins/simple.c @@ -0,0 +1,61 @@ +#include "internal.h" +#include "debug.h" +#include "plugin.h" +#include "version.h" + +/** Plugin id : type-author-name (to guarantee uniqueness) */ +#define SIMPLE_PLUGIN_ID "core-ewarmenhoven-simple" + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + gaim_debug(GAIM_DEBUG_INFO, "simple", "simple plugin loaded.\n"); + + return TRUE; +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + gaim_debug(GAIM_DEBUG_INFO, "simple", "simple plugin unloaded.\n"); + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + SIMPLE_PLUGIN_ID, /**< id */ + N_("Simple Plugin"), /**< name */ + VERSION, /**< version */ + /** summary */ + N_("Tests to see that most things are working."), + /** description */ + N_("Tests to see that most things are working."), + "Eric Warmenhoven ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, + NULL +}; + +static void +init_plugin(GaimPlugin *plugin) +{ +} + +GAIM_INIT_PLUGIN(simple, init_plugin, info) diff --git a/libpurple/plugins/ssl/Makefile.am b/libpurple/plugins/ssl/Makefile.am new file mode 100644 index 0000000000..dbdabdaf1f --- /dev/null +++ b/libpurple/plugins/ssl/Makefile.am @@ -0,0 +1,35 @@ +EXTRA_DIST = \ + Makefile.mingw + +plugindir = $(libdir)/gaim + +ssl_la_LDFLAGS = -module -avoid-version +ssl_gnutls_la_LDFLAGS = -module -avoid-version +ssl_nss_la_LDFLAGS = -module -avoid-version + +if PLUGINS + +plugin_LTLIBRARIES = \ + ssl.la \ + ssl-gnutls.la \ + ssl-nss.la + +ssl_la_SOURCES = ssl.c +ssl_gnutls_la_SOURCES = ssl-gnutls.c +ssl_nss_la_SOURCES = ssl-nss.c + +ssl_la_LIBADD = $(GLIB_LIBS) +ssl_gnutls_la_LIBADD = $(GLIB_LIBS) $(GNUTLS_LIBS) +ssl_nss_la_LIBADD = $(GLIB_LIBS) $(NSS_LIBS) + +endif # PLUGINS + +AM_CPPFLAGS = \ + -DDATADIR=\"$(datadir)\" \ + -DLIBDIR=\"$(libdir)/gaim/\" \ + -I$(top_srcdir)/libpurple \ + $(DEBUG_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(PLUGIN_CFLAGS) \ + $(NSS_CFLAGS) \ + $(GNUTLS_CFLAGS) diff --git a/libpurple/plugins/ssl/Makefile.mingw b/libpurple/plugins/ssl/Makefile.mingw new file mode 100644 index 0000000000..03023644e0 --- /dev/null +++ b/libpurple/plugins/ssl/Makefile.mingw @@ -0,0 +1,95 @@ +# +# Makefile.mingw +# +# Description: Makefile for ssl plugin. +# + +GAIM_TOP := ../../.. +include $(GAIM_TOP)/libgaim/win32/global.mak + +## +## VARIABLE DEFINITIONS +## +TARGET = ssl +TARGET_NSS = ssl-nss + +NEEDED_DLLS = \ + $(NSS_TOP)/lib/freebl3.dll \ + $(NSS_TOP)/lib/nss3.dll \ + $(NSS_TOP)/lib/nssckbi.dll \ + $(NSS_TOP)/lib/softokn3.dll \ + $(NSS_TOP)/lib/ssl3.dll \ + $(NSPR_TOP)/lib/nspr4.dll \ + $(NSPR_TOP)/lib/plc4.dll \ + $(NSPR_TOP)/lib/plds4.dll + +## +## INCLUDE PATHS +## +INCLUDE_PATHS += -I. \ + -I$(GTK_TOP)/include \ + -I$(GTK_TOP)/include/glib-2.0 \ + -I$(GTK_TOP)/lib/glib-2.0/include \ + -I$(GAIM_LIB_TOP) \ + -I$(GAIM_LIB_TOP)/win32 \ + -I$(GAIM_TOP) \ + -I$(NSS_TOP)/include \ + -I$(NSPR_TOP)/include + +LIB_PATHS = -L$(GTK_TOP)/lib \ + -L$(GAIM_LIB_TOP) \ + -L$(NSS_TOP)/lib \ + -L$(NSPR_TOP)/lib + +## +## SOURCES, OBJECTS +## +C_SRC = ssl.c +C_SRC_NSS = ssl-nss.c +OBJECTS = $(C_SRC:%.c=%.o) +OBJECTS_NSS = $(C_SRC_NSS:%.c=%.o) + +## +## LIBRARIES +## +LIBS = \ + -lglib-2.0 \ + -lws2_32 \ + -lintl \ + -lgaim \ + -lnss3 \ + -lnspr4 \ + -lssl3 + +include $(GAIM_COMMON_RULES) + +## +## TARGET DEFINITIONS +## +.PHONY: all install clean + +all: $(TARGET).dll $(TARGET_NSS).dll + +install: all $(GAIM_INSTALL_PLUGINS_DIR) $(GAIM_INSTALL_DIR) + cp $(TARGET).dll $(GAIM_INSTALL_PLUGINS_DIR) + cp $(TARGET_NSS).dll $(GAIM_INSTALL_PLUGINS_DIR) + cp $(NEEDED_DLLS) $(GAIM_INSTALL_DIR) + +$(OBJECTS) $(OBJECTS_NSS): $(GAIM_CONFIG_H) + +## +## BUILD DLL +## +$(TARGET).dll: $(GAIM_LIBGAIM_DLL).a $(OBJECTS) + $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll + +$(TARGET_NSS).dll: $(GAIM_LIBGAIM_DLL) $(OBJECTS_NSS) + $(CC) -shared $(OBJECTS_NSS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_NSS).dll + +## +## CLEAN RULES +## +clean: + rm -f $(OBJECTS) $(OBJECTS_NSS) $(TARGET).dll $(TARGET_NSS).dll + +include $(GAIM_COMMON_TARGETS) diff --git a/libpurple/plugins/ssl/ssl-gnutls.c b/libpurple/plugins/ssl/ssl-gnutls.c new file mode 100644 index 0000000000..0d08dc33d7 --- /dev/null +++ b/libpurple/plugins/ssl/ssl-gnutls.c @@ -0,0 +1,264 @@ +/** + * @file ssl-gnutls.c GNUTLS SSL plugin. + * + * gaim + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "internal.h" +#include "debug.h" +#include "plugin.h" +#include "sslconn.h" +#include "version.h" + +#define SSL_GNUTLS_PLUGIN_ID "ssl-gnutls" + +#ifdef HAVE_GNUTLS + +#include + +typedef struct +{ + gnutls_session session; + guint handshake_handler; +} GaimSslGnutlsData; + +#define GAIM_SSL_GNUTLS_DATA(gsc) ((GaimSslGnutlsData *)gsc->private_data) + +static gnutls_certificate_client_credentials xcred; + +static void +ssl_gnutls_init_gnutls(void) +{ + gnutls_global_init(); + + gnutls_certificate_allocate_credentials(&xcred); + gnutls_certificate_set_x509_trust_file(xcred, "ca.pem", + GNUTLS_X509_FMT_PEM); +} + +static gboolean +ssl_gnutls_init(void) +{ + return TRUE; +} + +static void +ssl_gnutls_uninit(void) +{ + gnutls_global_deinit(); + + gnutls_certificate_free_credentials(xcred); +} + + +static void ssl_gnutls_handshake_cb(gpointer data, gint source, + GaimInputCondition cond) +{ + GaimSslConnection *gsc = data; + GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc); + ssize_t ret; + + gaim_debug_info("gnutls", "Handshaking\n"); + ret = gnutls_handshake(gnutls_data->session); + + if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) + return; + + gaim_input_remove(gnutls_data->handshake_handler); + gnutls_data->handshake_handler = 0; + + if(ret != 0) { + gaim_debug_error("gnutls", "Handshake failed. Error %d\n", ret); + + if(gsc->error_cb != NULL) + gsc->error_cb(gsc, GAIM_SSL_HANDSHAKE_FAILED, + gsc->connect_cb_data); + + gaim_ssl_close(gsc); + } else { + gaim_debug_info("gnutls", "Handshake complete\n"); + + gsc->connect_cb(gsc->connect_cb_data, gsc, cond); + } + +} + + +static void +ssl_gnutls_connect(GaimSslConnection *gsc) +{ + GaimSslGnutlsData *gnutls_data; + static const int cert_type_priority[2] = { GNUTLS_CRT_X509, 0 }; + + gnutls_data = g_new0(GaimSslGnutlsData, 1); + gsc->private_data = gnutls_data; + + gnutls_init(&gnutls_data->session, GNUTLS_CLIENT); + gnutls_set_default_priority(gnutls_data->session); + + gnutls_certificate_type_set_priority(gnutls_data->session, + cert_type_priority); + + gnutls_credentials_set(gnutls_data->session, GNUTLS_CRD_CERTIFICATE, + xcred); + + gnutls_transport_set_ptr(gnutls_data->session, GINT_TO_POINTER(gsc->fd)); + + gnutls_data->handshake_handler = gaim_input_add(gsc->fd, + GAIM_INPUT_READ, ssl_gnutls_handshake_cb, gsc); + + ssl_gnutls_handshake_cb(gsc, gsc->fd, GAIM_INPUT_READ); +} + +static void +ssl_gnutls_close(GaimSslConnection *gsc) +{ + GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc); + + if(!gnutls_data) + return; + + if(gnutls_data->handshake_handler) + gaim_input_remove(gnutls_data->handshake_handler); + + gnutls_bye(gnutls_data->session, GNUTLS_SHUT_RDWR); + + gnutls_deinit(gnutls_data->session); + + g_free(gnutls_data); + gsc->private_data = NULL; +} + +static size_t +ssl_gnutls_read(GaimSslConnection *gsc, void *data, size_t len) +{ + GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc); + ssize_t s; + + s = gnutls_record_recv(gnutls_data->session, data, len); + + if(s == GNUTLS_E_AGAIN || s == GNUTLS_E_INTERRUPTED) { + s = -1; + errno = EAGAIN; + } else if(s < 0) { + gaim_debug_error("gnutls", "receive failed: %d\n", s); + s = 0; + } + + return s; +} + +static size_t +ssl_gnutls_write(GaimSslConnection *gsc, const void *data, size_t len) +{ + GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc); + ssize_t s = 0; + + /* XXX: when will gnutls_data be NULL? */ + if(gnutls_data) + s = gnutls_record_send(gnutls_data->session, data, len); + + if(s == GNUTLS_E_AGAIN || s == GNUTLS_E_INTERRUPTED) { + s = -1; + errno = EAGAIN; + } else if(s < 0) { + gaim_debug_error("gnutls", "send failed: %d\n", s); + s = 0; + } + + return s; +} + +static GaimSslOps ssl_ops = +{ + ssl_gnutls_init, + ssl_gnutls_uninit, + ssl_gnutls_connect, + ssl_gnutls_close, + ssl_gnutls_read, + ssl_gnutls_write +}; + +#endif /* HAVE_GNUTLS */ + +static gboolean +plugin_load(GaimPlugin *plugin) +{ +#ifdef HAVE_GNUTLS + if(!gaim_ssl_get_ops()) { + gaim_ssl_set_ops(&ssl_ops); + } + + /* Init GNUTLS now so others can use it even if sslconn never does */ + ssl_gnutls_init_gnutls(); + + return TRUE; +#else + return FALSE; +#endif +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ +#ifdef HAVE_GNUTLS + if(gaim_ssl_get_ops() == &ssl_ops) { + gaim_ssl_set_ops(NULL); + } +#endif + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + GAIM_PLUGIN_FLAG_INVISIBLE, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + SSL_GNUTLS_PLUGIN_ID, /**< id */ + N_("GNUTLS"), /**< name */ + VERSION, /**< version */ + /** summary */ + N_("Provides SSL support through GNUTLS."), + /** description */ + N_("Provides SSL support through GNUTLS."), + "Christian Hammond ", + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, /**< prefs_info */ + NULL /**< actions */ +}; + +static void +init_plugin(GaimPlugin *plugin) +{ +} + +GAIM_INIT_PLUGIN(ssl_gnutls, init_plugin, info) diff --git a/libpurple/plugins/ssl/ssl-nss.c b/libpurple/plugins/ssl/ssl-nss.c new file mode 100644 index 0000000000..019b89db1a --- /dev/null +++ b/libpurple/plugins/ssl/ssl-nss.c @@ -0,0 +1,433 @@ +/** + * @file ssl-nss.c Mozilla NSS SSL plugin. + * + * gaim + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "internal.h" +#include "debug.h" +#include "plugin.h" +#include "sslconn.h" +#include "version.h" + +#define SSL_NSS_PLUGIN_ID "ssl-nss" + +#ifdef HAVE_NSS + +#undef HAVE_LONG_LONG /* Make Mozilla less angry. If angry, Mozilla SMASH! */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + PRFileDesc *fd; + PRFileDesc *in; + guint handshake_handler; + +} GaimSslNssData; + +#define GAIM_SSL_NSS_DATA(gsc) ((GaimSslNssData *)gsc->private_data) + +static const PRIOMethods *_nss_methods = NULL; +static PRDescIdentity _identity; + +/* Thank you, Evolution */ +static void +set_errno(int code) +{ + /* FIXME: this should handle more. */ + switch (code) { + case PR_INVALID_ARGUMENT_ERROR: + errno = EINVAL; + break; + case PR_PENDING_INTERRUPT_ERROR: + errno = EINTR; + break; + case PR_IO_PENDING_ERROR: + errno = EAGAIN; + break; + case PR_WOULD_BLOCK_ERROR: + errno = EAGAIN; + /*errno = EWOULDBLOCK; */ + break; + case PR_IN_PROGRESS_ERROR: + errno = EINPROGRESS; + break; + case PR_ALREADY_INITIATED_ERROR: + errno = EALREADY; + break; + case PR_NETWORK_UNREACHABLE_ERROR: + errno = EHOSTUNREACH; + break; + case PR_CONNECT_REFUSED_ERROR: + errno = ECONNREFUSED; + break; + case PR_CONNECT_TIMEOUT_ERROR: + case PR_IO_TIMEOUT_ERROR: + errno = ETIMEDOUT; + break; + case PR_NOT_CONNECTED_ERROR: + errno = ENOTCONN; + break; + case PR_CONNECT_RESET_ERROR: + errno = ECONNRESET; + break; + case PR_IO_ERROR: + default: + errno = EIO; + break; + } +} + +static void +ssl_nss_init_nss(void) +{ + char *lib; + PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + NSS_NoDB_Init(NULL); + + /* TODO: Fix this so autoconf does the work trying to find this lib. */ +#ifndef _WIN32 + lib = g_strdup(BR_LIBDIR("/libnssckbi.so")); +#else + lib = g_strdup("nssckbi.dll"); +#endif + SECMOD_AddNewModule("Builtins", lib, 0, 0); + g_free(lib); + NSS_SetDomesticPolicy(); + + _identity = PR_GetUniqueIdentity("Gaim"); + _nss_methods = PR_GetDefaultIOMethods(); +} + +static SECStatus +ssl_auth_cert(void *arg, PRFileDesc *socket, PRBool checksig, + PRBool is_server) +{ + return SECSuccess; + +#if 0 + CERTCertificate *cert; + void *pinArg; + SECStatus status; + + cert = SSL_PeerCertificate(socket); + pinArg = SSL_RevealPinArg(socket); + + status = CERT_VerifyCertNow((CERTCertDBHandle *)arg, cert, checksig, + certUsageSSLClient, pinArg); + + if (status != SECSuccess) { + gaim_debug_error("nss", "CERT_VerifyCertNow failed\n"); + CERT_DestroyCertificate(cert); + return status; + } + + CERT_DestroyCertificate(cert); + return SECSuccess; +#endif +} + +static SECStatus +ssl_bad_cert(void *arg, PRFileDesc *socket) +{ + SECStatus status = SECFailure; + PRErrorCode err; + + if (arg == NULL) + return status; + + *(PRErrorCode *)arg = err = PORT_GetError(); + + switch (err) + { + case SEC_ERROR_INVALID_AVA: + case SEC_ERROR_INVALID_TIME: + case SEC_ERROR_BAD_SIGNATURE: + case SEC_ERROR_EXPIRED_CERTIFICATE: + case SEC_ERROR_UNKNOWN_ISSUER: + case SEC_ERROR_UNTRUSTED_CERT: + case SEC_ERROR_CERT_VALID: + case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: + case SEC_ERROR_CRL_EXPIRED: + case SEC_ERROR_CRL_BAD_SIGNATURE: + case SEC_ERROR_EXTENSION_VALUE_INVALID: + case SEC_ERROR_CA_CERT_INVALID: + case SEC_ERROR_CERT_USAGES_INVALID: + case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION: + status = SECSuccess; + break; + + default: + status = SECFailure; + break; + } + + gaim_debug_error("nss", "Bad certificate: %d\n", err); + + return status; +} + +static gboolean +ssl_nss_init(void) +{ + return TRUE; +} + +static void +ssl_nss_uninit(void) +{ + PR_Cleanup(); + + _nss_methods = NULL; +} + +static void +ssl_nss_handshake_cb(gpointer data, int fd, GaimInputCondition cond) +{ + GaimSslConnection *gsc = (GaimSslConnection *)data; + GaimSslNssData *nss_data = gsc->private_data; + + /* I don't think this the best way to do this... + * It seems to work because it'll eventually use the cached value + */ + if(SSL_ForceHandshake(nss_data->in) != SECSuccess) { + set_errno(PR_GetError()); + if (errno == EAGAIN || errno == EWOULDBLOCK) + return; + + gaim_debug_error("nss", "Handshake failed %d\n", PR_GetError()); + + if (gsc->error_cb != NULL) + gsc->error_cb(gsc, GAIM_SSL_HANDSHAKE_FAILED, gsc->connect_cb_data); + + gaim_ssl_close(gsc); + + return; + } + + gaim_input_remove(nss_data->handshake_handler); + nss_data->handshake_handler = 0; + + gsc->connect_cb(gsc->connect_cb_data, gsc, cond); +} + +static void +ssl_nss_connect(GaimSslConnection *gsc) +{ + GaimSslNssData *nss_data = g_new0(GaimSslNssData, 1); + PRSocketOptionData socket_opt; + + gsc->private_data = nss_data; + + nss_data->fd = PR_ImportTCPSocket(gsc->fd); + + if (nss_data->fd == NULL) + { + gaim_debug_error("nss", "nss_data->fd == NULL!\n"); + + if (gsc->error_cb != NULL) + gsc->error_cb(gsc, GAIM_SSL_CONNECT_FAILED, gsc->connect_cb_data); + + gaim_ssl_close((GaimSslConnection *)gsc); + + return; + } + + socket_opt.option = PR_SockOpt_Nonblocking; + socket_opt.value.non_blocking = PR_TRUE; + + if (PR_SetSocketOption(nss_data->fd, &socket_opt) != PR_SUCCESS) + gaim_debug_warning("nss", "unable to set socket into non-blocking mode: %d\n", PR_GetError()); + + nss_data->in = SSL_ImportFD(NULL, nss_data->fd); + + if (nss_data->in == NULL) + { + gaim_debug_error("nss", "nss_data->in == NUL!\n"); + + if (gsc->error_cb != NULL) + gsc->error_cb(gsc, GAIM_SSL_CONNECT_FAILED, gsc->connect_cb_data); + + gaim_ssl_close((GaimSslConnection *)gsc); + + return; + } + + SSL_OptionSet(nss_data->in, SSL_SECURITY, PR_TRUE); + SSL_OptionSet(nss_data->in, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); + + SSL_AuthCertificateHook(nss_data->in, + (SSLAuthCertificate)ssl_auth_cert, + (void *)CERT_GetDefaultCertDB()); + SSL_BadCertHook(nss_data->in, (SSLBadCertHandler)ssl_bad_cert, NULL); + + if(gsc->host) + SSL_SetURL(nss_data->in, gsc->host); + +#if 0 + /* This seems like it'd the be the correct way to implement the + nonblocking stuff, but it doesn't seem to work */ + SSL_HandshakeCallback(nss_data->in, + (SSLHandshakeCallback) ssl_nss_handshake_cb, gsc); +#endif + SSL_ResetHandshake(nss_data->in, PR_FALSE); + + nss_data->handshake_handler = gaim_input_add(gsc->fd, + GAIM_INPUT_READ, ssl_nss_handshake_cb, gsc); + + ssl_nss_handshake_cb(gsc, gsc->fd, GAIM_INPUT_READ); +} + +static void +ssl_nss_close(GaimSslConnection *gsc) +{ + GaimSslNssData *nss_data = GAIM_SSL_NSS_DATA(gsc); + + if(!nss_data) + return; + + if (nss_data->in) PR_Close(nss_data->in); + /* if (nss_data->fd) PR_Close(nss_data->fd); */ + + if (nss_data->handshake_handler) + gaim_input_remove(nss_data->handshake_handler); + + g_free(nss_data); + gsc->private_data = NULL; +} + +static size_t +ssl_nss_read(GaimSslConnection *gsc, void *data, size_t len) +{ + ssize_t ret; + GaimSslNssData *nss_data = GAIM_SSL_NSS_DATA(gsc); + + ret = PR_Read(nss_data->in, data, len); + + if (ret == -1) + set_errno(PR_GetError()); + + return ret; +} + +static size_t +ssl_nss_write(GaimSslConnection *gsc, const void *data, size_t len) +{ + ssize_t ret; + GaimSslNssData *nss_data = GAIM_SSL_NSS_DATA(gsc); + + if(!nss_data) + return 0; + + ret = PR_Write(nss_data->in, data, len); + + if (ret == -1) + set_errno(PR_GetError()); + + return ret; +} + +static GaimSslOps ssl_ops = +{ + ssl_nss_init, + ssl_nss_uninit, + ssl_nss_connect, + ssl_nss_close, + ssl_nss_read, + ssl_nss_write +}; + +#endif /* HAVE_NSS */ + + +static gboolean +plugin_load(GaimPlugin *plugin) +{ +#ifdef HAVE_NSS + if (!gaim_ssl_get_ops()) { + gaim_ssl_set_ops(&ssl_ops); + } + + /* Init NSS now, so others can use it even if sslconn never does */ + ssl_nss_init_nss(); + + return TRUE; +#else + return FALSE; +#endif +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ +#ifdef HAVE_NSS + if (gaim_ssl_get_ops() == &ssl_ops) { + gaim_ssl_set_ops(NULL); + } +#endif + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + GAIM_PLUGIN_FLAG_INVISIBLE, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + SSL_NSS_PLUGIN_ID, /**< id */ + N_("NSS"), /**< name */ + VERSION, /**< version */ + /** summary */ + N_("Provides SSL support through Mozilla NSS."), + /** description */ + N_("Provides SSL support through Mozilla NSS."), + "Christian Hammond ", + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, /**< prefs_info */ + NULL /**< actions */ +}; + +static void +init_plugin(GaimPlugin *plugin) +{ +} + +GAIM_INIT_PLUGIN(ssl_nss, init_plugin, info) diff --git a/libpurple/plugins/ssl/ssl.c b/libpurple/plugins/ssl/ssl.c new file mode 100644 index 0000000000..740ce5f562 --- /dev/null +++ b/libpurple/plugins/ssl/ssl.c @@ -0,0 +1,118 @@ +/** + * @file ssl.c Main SSL plugin + * + * gaim + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "internal.h" +#include "debug.h" +#include "plugin.h" +#include "sslconn.h" +#include "version.h" + +#define SSL_PLUGIN_ID "core-ssl" + +static GaimPlugin *ssl_plugin = NULL; + +static gboolean +probe_ssl_plugins(GaimPlugin *my_plugin) +{ + GaimPlugin *plugin; + GList *l; + + ssl_plugin = NULL; + + for (l = gaim_plugins_get_all(); l != NULL; l = l->next) + { + plugin = (GaimPlugin *)l->data; + + if (plugin == my_plugin) + continue; + + if (plugin->info != NULL && plugin->info->id != NULL && + strncmp(plugin->info->id, "ssl-", 4) == 0) + { + if (gaim_plugin_is_loaded(plugin) || gaim_plugin_load(plugin)) + { + ssl_plugin = plugin; + + break; + } + } + } + + return (ssl_plugin != NULL); +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + return probe_ssl_plugins(plugin); +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + if (ssl_plugin != NULL && + g_list_find(gaim_plugins_get_loaded(), ssl_plugin) != NULL) + { + gaim_plugin_unload(ssl_plugin); + } + + ssl_plugin = NULL; + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + GAIM_PLUGIN_FLAG_INVISIBLE, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + SSL_PLUGIN_ID, /**< id */ + N_("SSL"), /**< name */ + VERSION, /**< version */ + /** summary */ + N_("Provides a wrapper around SSL support libraries."), + /** description */ + N_("Provides a wrapper around SSL support libraries."), + "Christian Hammond ", + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, + NULL +}; + +static void +init_plugin(GaimPlugin *plugin) +{ +} + +GAIM_INIT_PLUGIN(ssl, init_plugin, info) diff --git a/libpurple/plugins/statenotify.c b/libpurple/plugins/statenotify.c new file mode 100644 index 0000000000..fcaf67c540 --- /dev/null +++ b/libpurple/plugins/statenotify.c @@ -0,0 +1,172 @@ +#include "internal.h" + +#include "blist.h" +#include "conversation.h" +#include "debug.h" +#include "signals.h" +#include "version.h" + +#include "plugin.h" +#include "pluginpref.h" +#include "prefs.h" + +#define STATENOTIFY_PLUGIN_ID "core-statenotify" + +static void +write_status(GaimBuddy *buddy, const char *message) +{ + GaimConversation *conv; + const char *who; + char buf[256]; + char *escaped; + + conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, + buddy->name, buddy->account); + + if (conv == NULL) + return; + g_return_if_fail(conv->type == GAIM_CONV_TYPE_IM); + + who = gaim_buddy_get_alias(buddy); + escaped = g_markup_escape_text(who, -1); + + g_snprintf(buf, sizeof(buf), message, escaped); + g_free(escaped); + + gaim_conv_im_write(conv->u.im, NULL, buf, GAIM_MESSAGE_SYSTEM | GAIM_MESSAGE_ACTIVE_ONLY, time(NULL)); +} + +static void +buddy_status_changed_cb(GaimBuddy *buddy, GaimStatus *old_status, + GaimStatus *status, void *data) +{ + gboolean available, old_available; + + available = gaim_status_is_available(status); + old_available = gaim_status_is_available(old_status); + + if (gaim_prefs_get_bool("/plugins/core/statenotify/notify_away")) { + if (available && !old_available) + write_status(buddy, _("%s is no longer away.")); + else if (!available && old_available) + write_status(buddy, _("%s has gone away.")); + } +} + +static void +buddy_idle_changed_cb(GaimBuddy *buddy, gboolean old_idle, gboolean idle, + void *data) +{ + if (gaim_prefs_get_bool("/plugins/core/statenotify/notify_idle")) { + if (idle) { + write_status(buddy, _("%s has become idle.")); + } else { + write_status(buddy, _("%s is no longer idle.")); + } + } +} + +static void +buddy_signon_cb(GaimBuddy *buddy, void *data) +{ + if (gaim_prefs_get_bool("/plugins/core/statenotify/notify_signon")) + write_status(buddy, _("%s has signed on.")); +} + +static void +buddy_signoff_cb(GaimBuddy *buddy, void *data) +{ + if (gaim_prefs_get_bool("/plugins/core/statenotify/notify_signon")) + write_status(buddy, _("%s has signed off.")); +} + +static GaimPluginPrefFrame * +get_plugin_pref_frame(GaimPlugin *plugin) +{ + GaimPluginPrefFrame *frame; + GaimPluginPref *ppref; + + frame = gaim_plugin_pref_frame_new(); + + ppref = gaim_plugin_pref_new_with_label(_("Notify When")); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label("/plugins/core/statenotify/notify_away", _("Buddy Goes _Away")); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label("/plugins/core/statenotify/notify_idle", _("Buddy Goes _Idle")); + gaim_plugin_pref_frame_add(frame, ppref); + + ppref = gaim_plugin_pref_new_with_name_and_label("/plugins/core/statenotify/notify_signon", _("Buddy _Signs On/Off")); + gaim_plugin_pref_frame_add(frame, ppref); + + return frame; +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + void *blist_handle = gaim_blist_get_handle(); + + gaim_signal_connect(blist_handle, "buddy-status-changed", plugin, + GAIM_CALLBACK(buddy_status_changed_cb), NULL); + gaim_signal_connect(blist_handle, "buddy-idle-changed", plugin, + GAIM_CALLBACK(buddy_idle_changed_cb), NULL); + gaim_signal_connect(blist_handle, "buddy-signed-on", plugin, + GAIM_CALLBACK(buddy_signon_cb), NULL); + gaim_signal_connect(blist_handle, "buddy-signed-off", plugin, + GAIM_CALLBACK(buddy_signoff_cb), NULL); + + return TRUE; +} + +static GaimPluginUiInfo prefs_info = +{ + get_plugin_pref_frame, + 0, /* page_num (Reserved) */ + NULL /* frame (Reserved) */ +}; + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + STATENOTIFY_PLUGIN_ID, /**< id */ + N_("Buddy State Notification"), /**< name */ + 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 ", /**< author */ + GAIM_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + &prefs_info, /**< prefs_info */ + NULL +}; + +static void +init_plugin(GaimPlugin *plugin) +{ + gaim_prefs_add_none("/plugins/core/statenotify"); + gaim_prefs_add_bool("/plugins/core/statenotify/notify_away", TRUE); + gaim_prefs_add_bool("/plugins/core/statenotify/notify_idle", TRUE); + gaim_prefs_add_bool("/plugins/core/statenotify/notify_signon", TRUE); +} + +GAIM_INIT_PLUGIN(statenotify, init_plugin, info) diff --git a/libpurple/plugins/tcl/Makefile.am b/libpurple/plugins/tcl/Makefile.am new file mode 100644 index 0000000000..b2551b4e11 --- /dev/null +++ b/libpurple/plugins/tcl/Makefile.am @@ -0,0 +1,22 @@ +plugindir = $(libdir)/gaim + +tcl_la_LDFLAGS = -module -avoid-version + +plugin_LTLIBRARIES = tcl.la + +tcl_la_SOURCES = tcl.c tcl_glib.c tcl_glib.h tcl_cmds.c tcl_signals.c tcl_gaim.h \ + tcl_ref.c tcl_cmd.c + +tcl_la_LIBADD = $(GLIB_LIBS) $(TCL_LIBS) $(TK_LIBS) + +EXTRA_DIST = signal-test.tcl Makefile.mingw + +AM_CPPFLAGS = \ + -DVERSION=\"$(VERSION)\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libpurple \ + $(DEBUG_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(PLUGIN_CFLAGS) \ + $(TK_CFLAGS) \ + $(TCL_CFLAGS) diff --git a/libpurple/plugins/tcl/Makefile.mingw b/libpurple/plugins/tcl/Makefile.mingw new file mode 100644 index 0000000000..11d9846ed0 --- /dev/null +++ b/libpurple/plugins/tcl/Makefile.mingw @@ -0,0 +1,77 @@ +# +# Makefile.mingw +# +# Description: Makefile for tcl plugin loader plugin. +# + +GAIM_TOP := ../../.. +include $(GAIM_TOP)/libgaim/win32/global.mak + +TARGET = tcl +TCL_INC_DIR := $(TCL_LIB_TOP)/include +DEFINES += -DHAVE_TK -DUSE_TCL_STUBS -DUSE_TK_STUBS + +## +## INCLUDE PATHS +## +INCLUDE_PATHS += -I. \ + -I$(GAIM_TOP) \ + -I$(GAIM_LIB_TOP) \ + -I$(GAIM_LIB_TOP)/win32 \ + -I$(GTK_TOP)/include \ + -I$(GTK_TOP)/include/glib-2.0 \ + -I$(GTK_TOP)/lib/glib-2.0/include \ + -I$(TCL_INC_DIR) + +LIB_PATHS += -L$(GTK_TOP)/lib \ + -L$(GAIM_LIB_TOP) \ + -L$(TCL_LIB_TOP) + +## +## SOURCES, OBJECTS +## +C_SRC = tcl.c \ + tcl_cmd.c \ + tcl_cmds.c \ + tcl_glib.c \ + tcl_ref.c \ + tcl_signals.c + +OBJECTS = $(C_SRC:%.c=%.o) + +## +## LIBRARIES +## +LIBS = \ + -lglib-2.0 \ + -lws2_32 \ + -lintl \ + -lgaim \ + -ltclstub84 \ + -ltkstub84 + +include $(GAIM_COMMON_RULES) + +## +## TARGET DEFINITIONS +## +.PHONY: all install clean + +all: $(TARGET).dll + +install: all $(GAIM_INSTALL_PLUGINS_DIR) + cp $(TARGET).dll $(GAIM_INSTALL_PLUGINS_DIR) + +$(OBJECTS): $(GAIM_CONFIG_H) + +$(TARGET).dll: $(GAIM_LIBGAIM_DLL).a $(OBJECTS) + $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll + +## +## CLEAN RULES +## +clean: + rm -rf $(OBJECTS) + rm -rf $(TARGET).dll + +include $(GAIM_COMMON_TARGETS) diff --git a/libpurple/plugins/tcl/signal-test.tcl b/libpurple/plugins/tcl/signal-test.tcl new file mode 100644 index 0000000000..de6ddaf6fe --- /dev/null +++ b/libpurple/plugins/tcl/signal-test.tcl @@ -0,0 +1,110 @@ +gaim::signal connect [gaim::account handle] account-away { account state message } { + gaim::debug -info "tcl signal" "account-away [gaim::account username $account] \"$state\" \"$message\"" +} + +gaim::signal connect [gaim::account handle] account-connecting { account } { + gaim::debug -info "tcl signal" "account-connecting [gaim::account username $account]" +} + +gaim::signal connect [gaim::account handle] account-set-info { account info } { + gaim::debug -info "tcl signal" "account-set-info [gaim::account username $account] $info" +} + +gaim::signal connect [gaim::account handle] account-setting-info { account info } { + gaim::debug -info "tcl signal" "account-set-info [gaim::account username $account] $info" +} + +gaim::signal connect [gaim::buddy handle] buddy-away { buddy } { + gaim::debug -info "tcl signal" "buddy-away [gaim::account username [lindex $buddy 2]] [lindex $buddy 1]" +} + +gaim::signal connect [gaim::buddy handle] buddy-back { buddy } { + gaim::debug -info "tcl signal" "buddy-back [gaim::account username [lindex $buddy 2]] [lindex $buddy 1]" +} + +gaim::signal connect [gaim::buddy handle] buddy-idle { buddy } { + gaim::debug -info "tcl signal" "buddy-idle [gaim::account username [lindex $buddy 2]] [lindex $buddy 1]" +} + +gaim::signal connect [gaim::buddy handle] buddy-unidle { buddy } { + gaim::debug -info "tcl signal" "buddy-unidle [gaim::account username [lindex $buddy 2]] [lindex $buddy 1]" +} + +gaim::signal connect [gaim::buddy handle] buddy-signed-on { buddy } { + gaim::debug -info "tcl signal" "buddy-signed-on [gaim::account username [lindex $buddy 2]] [lindex $buddy 1]" +} + +gaim::signal connect [gaim::buddy handle] buddy-signed-off { buddy } { + gaim::debug -info "tcl signal" "buddy-signed-off [gaim::account username [lindex $buddy 2]] [lindex $buddy 1]" +} + +gaim::signal connect [gaim::core handle] quitting {} { + gaim::debug -info "tcl signal" "quitting" +} + +gaim::signal connect [gaim::conversation handle] receiving-chat-msg { account who what id flags } { + gaim::debug -info "tcl signal" "receiving-chat-msg [gaim::account username $account] $id $flags $who \"$what\"" + return 0 +} + +gaim::signal connect [gaim::conversation handle] receiving-im-msg { account who what id flags } { + gaim::debug -info "tcl signal" "receiving-im-msg [gaim::account username $account] $id $flags $who \"$what\"" + return 0 +} + +gaim::signal connect [gaim::conversation handle] received-chat-msg { account who what id flags } { + gaim::debug -info "tcl signal" "received-chat-msg [gaim::account username $account] $id $flags $who \"$what\"" +} + +gaim::signal connect [gaim::conversation handle] received-im-msg { account who what id flags } { + gaim::debug -info "tcl signal" "received-im-msg [gaim::account username $account] $id $flags $who \"$what\"" +} + +gaim::signal connect [gaim::conversation handle] sending-chat-msg { account what id } { + gaim::debug -info "tcl signal" "sending-chat-msg [gaim::account username $account] $id \"$what\"" + return 0 +} + +gaim::signal connect [gaim::conversation handle] sending-im-msg { account who what } { + gaim::debug -info "tcl signal" "sending-im-msg [gaim::account username $account] $who \"$what\"" + return 0 +} + +gaim::signal connect [gaim::conversation handle] sent-chat-msg { account id what } { + gaim::debug -info "tcl signal" "sent-chat-msg [gaim::account username $account] $id \"$what\"" +} + +gaim::signal connect [gaim::conversation handle] sent-im-msg { account who what } { + gaim::debug -info "tcl signal" "sent-im-msg [gaim::account username $account] $who \"$what\"" +} + +gaim::signal connect [gaim::connection handle] signed-on { gc } { + gaim::debug -info "tcl signal" "signed-on [gaim::account username [gaim::connection account $gc]]" +} + +gaim::signal connect [gaim::connection handle] signed-off { gc } { + gaim::debug -info "tcl signal" "signed-off [gaim::account username [gaim::connection account $gc]]" +} + +gaim::signal connect [gaim::connection handle] signing-on { gc } { + gaim::debug -info "tcl signal" "signing-on [gaim::account username [gaim::connection account $gc]]" +} + +if { 0 } { +gaim::signal connect signing-off { + gaim::debug -info "tcl signal" "signing-off [gaim::account username [gaim::connection account $event::gc]]" +} + +gaim::signal connect update-idle { + gaim::debug -info "tcl signal" "update-idle" +} +} + +proc plugin_init { } { + list "Tcl Signal Test" \ + "$gaim::version" \ + "Tests Tcl signal handlers" \ + "Debugs a ridiculous amount of signal information." \ + "Ethan Blanton " \ + "http://gaim.sourceforge.net/" +} diff --git a/libpurple/plugins/tcl/tcl.c b/libpurple/plugins/tcl/tcl.c new file mode 100644 index 0000000000..67260b1f3a --- /dev/null +++ b/libpurple/plugins/tcl/tcl.c @@ -0,0 +1,527 @@ +/** + * @file tcl.c Gaim Tcl plugin bindings + * + * gaim + * + * Copyright (C) 2003 Ethan Blanton + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include + +#ifdef HAVE_TK +#include +#endif + +#include +#include +#include +#include +#include + +#include "tcl_glib.h" +#include "tcl_gaim.h" + +#include "internal.h" +#include "connection.h" +#include "plugin.h" +#include "signals.h" +#include "debug.h" +#include "util.h" +#include "version.h" + +struct tcl_plugin_data { + GaimPlugin *plugin; + Tcl_Interp *interp; +}; + +GaimStringref *GaimTclRefAccount; +GaimStringref *GaimTclRefConnection; +GaimStringref *GaimTclRefConversation; +GaimStringref *GaimTclRefPointer; +GaimStringref *GaimTclRefPlugin; +GaimStringref *GaimTclRefPresence; +GaimStringref *GaimTclRefStatus; +GaimStringref *GaimTclRefStatusAttr; +GaimStringref *GaimTclRefStatusType; +GaimStringref *GaimTclRefXfer; + +static GHashTable *tcl_plugins = NULL; + +GaimPlugin *_tcl_plugin; + +static gboolean tcl_loaded = FALSE; + +GaimPlugin *tcl_interp_get_plugin(Tcl_Interp *interp) +{ + struct tcl_plugin_data *data; + + if (tcl_plugins == NULL) + return NULL; + + data = g_hash_table_lookup(tcl_plugins, (gpointer)interp); + return data != NULL ? data->plugin : NULL; +} + +static int tcl_init_interp(Tcl_Interp *interp) +{ + char *rcfile; + char init[] = + "namespace eval ::gaim {\n" + " namespace export account buddy connection conversation\n" + " namespace export core debug notify prefs send_im\n" + " namespace export signal unload\n" + " namespace eval _callback { }\n" + "\n" + " proc conv_send { account who text } {\n" + " set gc [gaim::account connection $account]\n" + " set convo [gaim::conversation new $account $who]\n" + " set myalias [gaim::account alias $account]\n" + "\n" + " if {![string length $myalias]} {\n" + " set myalias [gaim::account username $account]\n" + " }\n" + "\n" + " gaim::send_im $gc $who $text\n" + " gaim::conversation write $convo send $myalias $text\n" + " }\n" + "}\n" + "\n" + "proc bgerror { message } {\n" + " global errorInfo\n" + " gaim::notify -error \"Tcl Error\" \"Tcl Error: $message\" \"$errorInfo\"\n" + "}\n"; + + if (Tcl_EvalEx(interp, init, -1, TCL_EVAL_GLOBAL) != TCL_OK) { + return 1; + } + + Tcl_SetVar(interp, "argc", "0", TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "argv0", "gaim", TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "tcl_interactive", "0", TCL_GLOBAL_ONLY); + rcfile = g_strdup_printf("%s" G_DIR_SEPARATOR_S "tclrc", gaim_user_dir()); + Tcl_SetVar(interp, "tcl_rcFileName", rcfile, TCL_GLOBAL_ONLY); + g_free(rcfile); + + Tcl_SetVar(interp, "::gaim::version", VERSION, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "::gaim::user_dir", gaim_user_dir(), TCL_GLOBAL_ONLY); +#ifdef HAVE_TK + Tcl_SetVar(interp, "::gaim::tk_available", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar(interp, "::gaim::tk_available", "0", TCL_GLOBAL_ONLY); +#endif /* HAVE_TK */ + + Tcl_CreateObjCommand(interp, "::gaim::account", tcl_cmd_account, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::buddy", tcl_cmd_buddy, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::cmd", tcl_cmd_cmd, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::connection", tcl_cmd_connection, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::conversation", tcl_cmd_conversation, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::core", tcl_cmd_core, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::debug", tcl_cmd_debug, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::notify", tcl_cmd_notify, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::prefs", tcl_cmd_prefs, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::presence", tcl_cmd_presence, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::send_im", tcl_cmd_send_im, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::signal", tcl_cmd_signal, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::status", tcl_cmd_status, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::status_attr", tcl_cmd_status_attr, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::status_type", tcl_cmd_status_type, (ClientData)NULL, NULL); + Tcl_CreateObjCommand(interp, "::gaim::unload", tcl_cmd_unload, (ClientData)NULL, NULL); + + return 0; +} + +static Tcl_Interp *tcl_create_interp() +{ + Tcl_Interp *interp; + + interp = Tcl_CreateInterp(); + if (Tcl_Init(interp) == TCL_ERROR) { + Tcl_DeleteInterp(interp); + return NULL; + } + + if (tcl_init_interp(interp)) { + Tcl_DeleteInterp(interp); + return NULL; + } + Tcl_StaticPackage(interp, "gaim", tcl_init_interp, NULL); + + return interp; +} + +static gboolean tcl_probe_plugin(GaimPlugin *plugin) +{ + GaimPluginInfo *info; + Tcl_Interp *interp; + Tcl_Parse parse; + Tcl_Obj *result, **listitems; + struct stat st; + FILE *fp; + char *buf, *cur; + const char *next; + int len, found = 0, err = 0, nelems; + gboolean status = FALSE; + if ((fp = g_fopen(plugin->path, "r")) == NULL) + return FALSE; + if (fstat(fileno(fp), &st)) { + fclose(fp); + return FALSE; + } + len = st.st_size; + + buf = g_malloc(len + 1); + + cur = buf; + while (fgets(cur, GPOINTER_TO_INT(buf) - (buf - cur), fp)) { + cur += strlen(cur); + if (feof(fp)) + break; + } + + if (ferror(fp)) { + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "error reading %s (%s)\n", plugin->path, strerror(errno)); + g_free(buf); + fclose(fp); + return FALSE; + } + + fclose(fp); + + if ((interp = tcl_create_interp()) == NULL) { + return FALSE; + } + + next = buf; + do { + if (Tcl_ParseCommand(interp, next, len, 0, &parse) == TCL_ERROR) { + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "parse error in %s: %s\n", plugin->path, + Tcl_GetString(Tcl_GetObjResult(interp))); + err = 1; + break; + } + if (parse.tokenPtr[0].type == TCL_TOKEN_SIMPLE_WORD + && !strncmp(parse.tokenPtr[0].start, "proc", parse.tokenPtr[0].size)) { + if (!strncmp(parse.tokenPtr[2].start, "plugin_init", parse.tokenPtr[2].size)) { + if (Tcl_EvalEx(interp, parse.commandStart, parse.commandSize, TCL_EVAL_GLOBAL) != TCL_OK) { + Tcl_FreeParse(&parse); + break; + } + found = 1; + /* We'll continue parsing the file, just in case */ + } + } + len -= (parse.commandStart + parse.commandSize) - next; + next = parse.commandStart + parse.commandSize; + Tcl_FreeParse(&parse); + } while (len); + + if (found && !err) { + if (Tcl_EvalEx(interp, "plugin_init", -1, TCL_EVAL_GLOBAL) == TCL_OK) { + result = Tcl_GetObjResult(interp); + if (Tcl_ListObjGetElements(interp, result, &nelems, &listitems) == TCL_OK) { + if ((nelems == 6) || (nelems == 7)) { + info = g_new0(GaimPluginInfo, 1); + + info->magic = GAIM_PLUGIN_MAGIC; + info->major_version = GAIM_MAJOR_VERSION; + info->minor_version = GAIM_MINOR_VERSION; + info->type = GAIM_PLUGIN_STANDARD; + info->dependencies = g_list_append(info->dependencies, "core-tcl"); + + info->name = g_strdup(Tcl_GetString(listitems[0])); + info->version = g_strdup(Tcl_GetString(listitems[1])); + info->summary = g_strdup(Tcl_GetString(listitems[2])); + info->description = g_strdup(Tcl_GetString(listitems[3])); + info->author = g_strdup(Tcl_GetString(listitems[4])); + info->homepage = g_strdup(Tcl_GetString(listitems[5])); + + if (nelems == 6) + info->id = g_strdup_printf("tcl-%s", Tcl_GetString(listitems[0])); + else if (nelems == 7) + info->id = g_strdup_printf("tcl-%s", Tcl_GetString(listitems[6])); + + plugin->info = info; + + if (gaim_plugin_register(plugin)) + status = TRUE; + } + } + } + } + + Tcl_DeleteInterp(interp); + g_free(buf); + return status; +} + +static gboolean tcl_load_plugin(GaimPlugin *plugin) +{ + struct tcl_plugin_data *data; + Tcl_Interp *interp; + Tcl_Obj *result; + + plugin->extra = NULL; + + if ((interp = tcl_create_interp()) == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Could not initialize Tcl interpreter\n"); + return FALSE; + } + + Tcl_SourceRCFile(interp); + + if (Tcl_EvalFile(interp, plugin->path) != TCL_OK) { + result = Tcl_GetObjResult(interp); + gaim_debug(GAIM_DEBUG_ERROR, "tcl", + "Error evaluating %s: %s\n", plugin->path, + Tcl_GetString(result)); + Tcl_DeleteInterp(interp); + return FALSE; + } + + Tcl_Preserve((ClientData)interp); + + data = g_new0(struct tcl_plugin_data, 1); + data->plugin = plugin; + data->interp = interp; + plugin->extra = data; + + g_hash_table_insert(tcl_plugins, (gpointer)interp, (gpointer)data); + + return TRUE; +} + +static gboolean tcl_unload_plugin(GaimPlugin *plugin) +{ + struct tcl_plugin_data *data; + + if (plugin == NULL) + return TRUE; + + data = plugin->extra; + + if (data != NULL) { + g_hash_table_remove(tcl_plugins, (gpointer)(data->interp)); + gaim_signals_disconnect_by_handle(data->interp); + tcl_cmd_cleanup(data->interp); + tcl_signal_cleanup(data->interp); + Tcl_Release((ClientData)data->interp); + Tcl_DeleteInterp(data->interp); + g_free(data); + } + + return TRUE; +} + +static void tcl_destroy_plugin(GaimPlugin *plugin) +{ + if (plugin->info != NULL) { + g_free(plugin->info->id); + g_free(plugin->info->name); + g_free(plugin->info->version); + g_free(plugin->info->description); + g_free(plugin->info->author); + g_free(plugin->info->homepage); + } + + return; +} + +static gboolean tcl_load(GaimPlugin *plugin) +{ + if(!tcl_loaded) + return FALSE; + tcl_glib_init(); + tcl_cmd_init(); + tcl_signal_init(); + gaim_tcl_ref_init(); + + GaimTclRefAccount = gaim_stringref_new("Account"); + GaimTclRefConnection = gaim_stringref_new("Connection"); + GaimTclRefConversation = gaim_stringref_new("Conversation"); + GaimTclRefPointer = gaim_stringref_new("Pointer"); + GaimTclRefPlugin = gaim_stringref_new("Plugin"); + GaimTclRefPresence = gaim_stringref_new("Presence"); + GaimTclRefStatus = gaim_stringref_new("Status"); + GaimTclRefStatusAttr = gaim_stringref_new("StatusAttr"); + GaimTclRefStatusType = gaim_stringref_new("StatusType"); + GaimTclRefXfer = gaim_stringref_new("Xfer"); + + tcl_plugins = g_hash_table_new(g_direct_hash, g_direct_equal); + +#ifdef HAVE_TK + Tcl_StaticPackage(NULL, "Tk", Tk_Init, Tk_SafeInit); +#endif /* HAVE_TK */ + + return TRUE; +} + +static gboolean tcl_unload(GaimPlugin *plugin) +{ + g_hash_table_destroy(tcl_plugins); + tcl_plugins = NULL; + + gaim_stringref_unref(GaimTclRefAccount); + gaim_stringref_unref(GaimTclRefConnection); + gaim_stringref_unref(GaimTclRefConversation); + gaim_stringref_unref(GaimTclRefPointer); + gaim_stringref_unref(GaimTclRefPlugin); + gaim_stringref_unref(GaimTclRefPresence); + gaim_stringref_unref(GaimTclRefStatus); + gaim_stringref_unref(GaimTclRefStatusAttr); + gaim_stringref_unref(GaimTclRefStatusType); + gaim_stringref_unref(GaimTclRefXfer); + + return TRUE; +} + +static GaimPluginLoaderInfo tcl_loader_info = +{ + NULL, + tcl_probe_plugin, + tcl_load_plugin, + tcl_unload_plugin, + tcl_destroy_plugin, +}; + +static GaimPluginInfo tcl_info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_LOADER, + NULL, + 0, + NULL, + GAIM_PRIORITY_DEFAULT, + "core-tcl", + N_("Tcl Plugin Loader"), + VERSION, + N_("Provides support for loading Tcl plugins"), + N_("Provides support for loading Tcl plugins"), + "Ethan Blanton ", + GAIM_WEBSITE, + tcl_load, + tcl_unload, + NULL, + NULL, + &tcl_loader_info, + NULL, + NULL +}; + +#ifdef _WIN32 +typedef Tcl_Interp* (CALLBACK* LPFNTCLCREATEINTERP)(void); +typedef void (CALLBACK* LPFNTKINIT)(Tcl_Interp*); + +LPFNTCLCREATEINTERP wtcl_CreateInterp = NULL; +LPFNTKINIT wtk_Init = NULL; +#undef Tcl_CreateInterp +#define Tcl_CreateInterp wtcl_CreateInterp +#undef Tk_Init +#define Tk_Init wtk_Init + +static gboolean tcl_win32_init() { + const char regkey[] = "SOFTWARE\\ActiveState\\ActiveTcl\\"; + char *version = NULL; + gboolean retval = FALSE; + + if ((version = wgaim_read_reg_string(HKEY_LOCAL_MACHINE, regkey, "CurrentVersion")) + || (version = wgaim_read_reg_string(HKEY_CURRENT_USER, regkey, "CurrentVersion"))) { + char *path; + char *regkey2; + + regkey2 = g_strdup_printf("%s%s\\", regkey, version); + if ((path = wgaim_read_reg_string(HKEY_LOCAL_MACHINE, regkey2, NULL)) || (path = wgaim_read_reg_string(HKEY_CURRENT_USER, regkey2, NULL))) { + char *tclpath; + char *tkpath; + + gaim_debug(GAIM_DEBUG_INFO, "tcl", "Loading ActiveTCL version %s from \"%s\"\n", version, path); + + tclpath = g_build_filename(path, "bin", "tcl84.dll", NULL); + tkpath = g_build_filename(path, "bin", "tk84.dll", NULL); + + if(!(wtcl_CreateInterp = (LPFNTCLCREATEINTERP) wgaim_find_and_loadproc(tclpath, "Tcl_CreateInterp"))) { + gaim_debug(GAIM_DEBUG_INFO, "tcl", "tcl_win32_init error loading Tcl_CreateInterp\n"); + } else { + if(!(wtk_Init = (LPFNTKINIT) wgaim_find_and_loadproc(tkpath, "Tk_Init"))) { + HMODULE mod; + gaim_debug(GAIM_DEBUG_INFO, "tcl", "tcl_win32_init error loading Tk_Init\n"); + if((mod = GetModuleHandle("tcl84.dll"))) + FreeLibrary(mod); + } else { + retval = TRUE; + } + } + g_free(tclpath); + g_free(tkpath); + } + g_free(path); + g_free(regkey2); + } + + g_free(version); + + if (!retval) + gaim_debug(GAIM_DEBUG_INFO, "tcl", _("Unable to detect ActiveTCL installation. If you wish to use TCL plugins, install ActiveTCL from http://www.activestate.com\n")); + + return retval; +} + +#endif /* _WIN32 */ + +static void tcl_init_plugin(GaimPlugin *plugin) +{ +#ifdef USE_TCL_STUBS + Tcl_Interp *interp = NULL; +#endif + _tcl_plugin = plugin; + +#ifdef USE_TCL_STUBS +#ifdef _WIN32 + if(!tcl_win32_init()) + return; +#endif + if(!(interp = Tcl_CreateInterp())) + return; + + if(!Tcl_InitStubs(interp, TCL_VERSION, 0)) { + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Tcl_InitStubs: %s\n", interp->result); + return; + } +#endif + + Tcl_FindExecutable("gaim"); + +#if defined(USE_TK_STUBS) && defined(HAVE_TK) + Tk_Init(interp); + + if(!Tk_InitStubs(interp, TK_VERSION, 0)) { + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Error Tk_InitStubs: %s\n", interp->result); + Tcl_DeleteInterp(interp); + return; + } +#endif + tcl_loaded = TRUE; +#ifdef USE_TCL_STUBS + Tcl_DeleteInterp(interp); +#endif + tcl_loader_info.exts = g_list_append(tcl_loader_info.exts, "tcl"); +} + +GAIM_INIT_PLUGIN(tcl, tcl_init_plugin, tcl_info) diff --git a/libpurple/plugins/tcl/tcl_cmd.c b/libpurple/plugins/tcl/tcl_cmd.c new file mode 100644 index 0000000000..c5f9605e01 --- /dev/null +++ b/libpurple/plugins/tcl/tcl_cmd.c @@ -0,0 +1,190 @@ +/** + * @file tcl_cmd.c Gaim Tcl cmd API + * + * gaim + * + * Copyright (C) 2006 Etan Reisner + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include + +#include "tcl_gaim.h" + +#include "internal.h" +#include "cmds.h" +#include "debug.h" + +static GList *tcl_cmd_callbacks; + +static GaimCmdRet tcl_cmd_callback(GaimConversation *conv, const gchar *cmd, + gchar **args, gchar **errors, + struct tcl_cmd_handler *handler); +static Tcl_Obj *new_cmd_cb_namespace(void); + +void tcl_cmd_init() +{ + tcl_cmd_callbacks = NULL; +} + +void tcl_cmd_handler_free(struct tcl_cmd_handler *handler) +{ + if (handler == NULL) + return; + + Tcl_DecrRefCount(handler->namespace); + g_free(handler); +} + +void tcl_cmd_cleanup(Tcl_Interp *interp) +{ + GList *cur; + struct tcl_cmd_handler *handler; + + for (cur = tcl_cmd_callbacks; cur != NULL; cur = g_list_next(cur)) { + handler = cur->data; + if (handler->interp == interp) { + gaim_cmd_unregister(handler->id); + tcl_cmd_handler_free(handler); + cur->data = NULL; + } + } + tcl_cmd_callbacks = g_list_remove_all(tcl_cmd_callbacks, NULL); +} + +GaimCmdId tcl_cmd_register(struct tcl_cmd_handler *handler) +{ + int id; + GString *proc; + + if ((id = gaim_cmd_register(Tcl_GetString(handler->cmd), + handler->args, handler->priority, + handler->flags, handler->prpl_id, + GAIM_CMD_FUNC(tcl_cmd_callback), + handler->helpstr, (void *)handler)) == 0) + return 0; + + handler->namespace = new_cmd_cb_namespace (); + Tcl_IncrRefCount(handler->namespace); + proc = g_string_new(""); + g_string_append_printf(proc, "namespace eval %s { proc cb { conv cmd arglist } { %s } }", + Tcl_GetString(handler->namespace), + Tcl_GetString(handler->proc)); + if (Tcl_Eval(handler->interp, proc->str) != TCL_OK) { + Tcl_DecrRefCount(handler->namespace); + g_string_free(proc, TRUE); + return 0; + } + g_string_free(proc, TRUE); + + tcl_cmd_callbacks = g_list_append(tcl_cmd_callbacks, (gpointer)handler); + + return id; +} + +void tcl_cmd_unregister(GaimCmdId id, Tcl_Interp *interp) +{ + GList *cur; + GString *cmd; + gboolean found = FALSE; + struct tcl_cmd_handler *handler; + + for (cur = tcl_cmd_callbacks; cur != NULL; cur = g_list_next(cur)) { + handler = cur->data; + if (handler->interp == interp && handler->id == id) { + gaim_cmd_unregister(id); + cmd = g_string_sized_new(64); + g_string_printf(cmd, "namespace delete %s", + Tcl_GetString(handler->namespace)); + Tcl_EvalEx(interp, cmd->str, -1, TCL_EVAL_GLOBAL); + tcl_cmd_handler_free(handler); + g_string_free(cmd, TRUE); + cur->data = NULL; + found = TRUE; + break; + } + } + + if (found) + tcl_cmd_callbacks = g_list_remove_all(tcl_cmd_callbacks, NULL); +} + +static GaimCmdRet tcl_cmd_callback(GaimConversation *conv, const gchar *cmd, + gchar **args, gchar **errors, + struct tcl_cmd_handler *handler) +{ + int retval, error, i; + Tcl_Obj *command, *arg, *tclargs, *result; + + command = Tcl_NewListObj(0, NULL); + Tcl_IncrRefCount(command); + + /* The callback */ + arg = Tcl_DuplicateObj(handler->namespace); + Tcl_AppendStringsToObj(arg, "::cb", NULL); + Tcl_ListObjAppendElement(handler->interp, command, arg); + + /* The conversation */ + arg = gaim_tcl_ref_new(GaimTclRefConversation, conv); + Tcl_ListObjAppendElement(handler->interp, command, arg); + + /* The command */ + arg = Tcl_NewStringObj(cmd, -1); + Tcl_ListObjAppendElement(handler->interp, command, arg); + + /* The args list */ + tclargs = Tcl_NewListObj(0, NULL); + for (i = 0; i < handler->nargs; i++) { + arg = Tcl_NewStringObj(args[i], -1); + + Tcl_ListObjAppendElement(handler->interp, tclargs, arg); + } + Tcl_ListObjAppendElement(handler->interp, command, tclargs); + + if ((error = Tcl_EvalObjEx(handler->interp, command, + TCL_EVAL_GLOBAL)) != TCL_OK) { + gchar *errorstr; + + errorstr = g_strdup_printf("error evaluating callback: %s\n", + Tcl_GetString(Tcl_GetObjResult(handler->interp))); + gaim_debug(GAIM_DEBUG_ERROR, "tcl", errorstr); + *errors = errorstr; + retval = GAIM_CMD_RET_FAILED; + } else { + result = Tcl_GetObjResult(handler->interp); + if ((error = Tcl_GetIntFromObj(handler->interp, result, + &retval)) != TCL_OK) { + gchar *errorstr; + + errorstr = g_strdup_printf("Error retreiving procedure result: %s\n", + Tcl_GetString(Tcl_GetObjResult(handler->interp))); + gaim_debug(GAIM_DEBUG_ERROR, "tcl", errorstr); + *errors = errorstr; + retval = GAIM_CMD_RET_FAILED; + } + } + + return retval; +} + +static Tcl_Obj *new_cmd_cb_namespace() +{ + char name[32]; + static int cbnum; + + g_snprintf(name, sizeof(name), "::gaim::_cmd_callback::cb_%d", + cbnum++); + return Tcl_NewStringObj(name, -1); +} diff --git a/libpurple/plugins/tcl/tcl_cmds.c b/libpurple/plugins/tcl/tcl_cmds.c new file mode 100644 index 0000000000..5763eeb14d --- /dev/null +++ b/libpurple/plugins/tcl/tcl_cmds.c @@ -0,0 +1,1607 @@ +/** + * @file tcl_cmds.c Commands for the Gaim Tcl plugin bindings + * + * gaim + * + * Copyright (C) 2003 Ethan Blanton + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "internal.h" +#include "conversation.h" +#include "connection.h" +#include "account.h" +#include "server.h" +#include "notify.h" +#include "blist.h" +#include "debug.h" +#include "prefs.h" +#include "core.h" + +#include "tcl_gaim.h" + +static GaimAccount *tcl_validate_account(Tcl_Obj *obj, Tcl_Interp *interp); +static GaimConversation *tcl_validate_conversation(Tcl_Obj *obj, Tcl_Interp *interp); +static GaimConnection *tcl_validate_gc(Tcl_Obj *obj, Tcl_Interp *interp); + +static GaimAccount *tcl_validate_account(Tcl_Obj *obj, Tcl_Interp *interp) +{ + GaimAccount *account; + GList *cur; + + account = gaim_tcl_ref_get(interp, obj, GaimTclRefAccount); + + if (account == NULL) + return NULL; + + for (cur = gaim_accounts_get_all(); cur != NULL; cur = g_list_next(cur)) { + if (account == cur->data) + return account; + } + if (interp != NULL) + Tcl_SetStringObj(Tcl_GetObjResult(interp), "invalid account", -1); + return NULL; +} + +static GaimConversation *tcl_validate_conversation(Tcl_Obj *obj, Tcl_Interp *interp) +{ + GaimConversation *convo; + GList *cur; + + convo = gaim_tcl_ref_get(interp, obj, GaimTclRefConversation); + + if (convo == NULL) + return NULL; + + for (cur = gaim_get_conversations(); cur != NULL; cur = g_list_next(cur)) { + if (convo == cur->data) + return convo; + } + if (interp != NULL) + Tcl_SetStringObj(Tcl_GetObjResult(interp), "invalid conversation", -1); + return NULL; +} + +static GaimConnection *tcl_validate_gc(Tcl_Obj *obj, Tcl_Interp *interp) +{ + GaimConnection *gc; + GList *cur; + + gc = gaim_tcl_ref_get(interp, obj, GaimTclRefConnection); + + if (gc == NULL) + return NULL; + + for (cur = gaim_connections_get_all(); cur != NULL; cur = g_list_next(cur)) { + if (gc == cur->data) + return gc; + } + return NULL; +} + +int tcl_cmd_account(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Obj *result = Tcl_GetObjResult(interp), *list, *elem; + const char *cmds[] = { "alias", "connect", "connection", "disconnect", + "enabled", "find", "handle", "isconnected", + "list", "presence", "protocol", "status", + "status_type", "status_types", "username", + NULL }; + enum { CMD_ACCOUNT_ALIAS, + CMD_ACCOUNT_CONNECT, CMD_ACCOUNT_CONNECTION, + CMD_ACCOUNT_DISCONNECT, CMD_ACCOUNT_ENABLED, CMD_ACCOUNT_FIND, + CMD_ACCOUNT_HANDLE, CMD_ACCOUNT_ISCONNECTED, CMD_ACCOUNT_LIST, + CMD_ACCOUNT_PRESENCE, CMD_ACCOUNT_PROTOCOL, CMD_ACCOUNT_STATUS, + CMD_ACCOUNT_STATUS_TYPE, CMD_ACCOUNT_STATUS_TYPES, + CMD_ACCOUNT_USERNAME } cmd; + const char *listopts[] = { "-all", "-online", NULL }; + enum { CMD_ACCOUNTLIST_ALL, CMD_ACCOUNTLIST_ONLINE } listopt; + const char *alias; + const GList *cur; + GaimAccount *account; + GaimStatus *status; + GaimStatusType *status_type; + GaimValue *value; + char *attr_id; + int error; + int b, i; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + switch (cmd) { + case CMD_ACCOUNT_ALIAS: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "account"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + alias = gaim_account_get_alias(account); + Tcl_SetStringObj(result, alias ? (char *)alias : "", -1); + break; + case CMD_ACCOUNT_CONNECT: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "account"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + if (!gaim_account_is_connected(account)) + gaim_account_connect(account); + Tcl_SetObjResult(interp, + gaim_tcl_ref_new(GaimTclRefConnection, + gaim_account_get_connection(account))); + break; + case CMD_ACCOUNT_CONNECTION: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "account"); + return TCL_ERROR; + } + + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, + gaim_tcl_ref_new(GaimTclRefConnection, + gaim_account_get_connection(account))); + break; + case CMD_ACCOUNT_DISCONNECT: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "account"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + gaim_account_disconnect(account); + break; + case CMD_ACCOUNT_ENABLED: + if (objc != 3 && objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "account ?enabled?"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + if (objc == 3) { + Tcl_SetBooleanObj(result, + gaim_account_get_enabled(account, + gaim_core_get_ui())); + } else { + if ((error = Tcl_GetBooleanFromObj(interp, objv[3], &b)) != TCL_OK) + return TCL_ERROR; + gaim_account_set_enabled(account, gaim_core_get_ui(), b); + } + break; + case CMD_ACCOUNT_FIND: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "username protocol"); + return TCL_ERROR; + } + account = gaim_accounts_find(Tcl_GetString(objv[2]), + Tcl_GetString(objv[3])); + Tcl_SetObjResult(interp, + gaim_tcl_ref_new(GaimTclRefAccount, account)); + break; + case CMD_ACCOUNT_HANDLE: + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + Tcl_SetIntObj(result, (int)gaim_accounts_get_handle()); + break; + case CMD_ACCOUNT_ISCONNECTED: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "account"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + Tcl_SetBooleanObj(result, gaim_account_is_connected(account)); + break; + case CMD_ACCOUNT_LIST: + listopt = CMD_ACCOUNTLIST_ALL; + if (objc > 3) { + Tcl_WrongNumArgs(interp, 2, objv, "?option?"); + return TCL_ERROR; + } + if (objc == 3) { + if ((error = Tcl_GetIndexFromObj(interp, objv[2], listopts, "option", 0, (int *)&listopt)) != TCL_OK) + return error; + } + list = Tcl_NewListObj(0, NULL); + for (cur = gaim_accounts_get_all(); cur != NULL; cur = g_list_next(cur)) { + account = cur->data; + if (listopt == CMD_ACCOUNTLIST_ONLINE && !gaim_account_is_connected(account)) + continue; + elem = gaim_tcl_ref_new(GaimTclRefAccount, account); + Tcl_ListObjAppendElement(interp, list, elem); + } + Tcl_SetObjResult(interp, list); + break; + case CMD_ACCOUNT_PRESENCE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "account"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, gaim_tcl_ref_new(GaimTclRefPresence, + gaim_account_get_presence(account))); + break; + case CMD_ACCOUNT_PROTOCOL: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "account"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, (char *)gaim_account_get_protocol_id(account), -1); + break; + case CMD_ACCOUNT_STATUS: + if (objc < 3) { + Tcl_WrongNumArgs(interp, 2, objv, "account ?status_id name value ...?"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + if (objc == 3) { + Tcl_SetObjResult(interp, + gaim_tcl_ref_new(GaimTclRefStatus, + gaim_account_get_active_status(account))); + } else { + GList *l = NULL; + if (objc % 2) { + Tcl_SetStringObj(result, "name without value setting status", -1); + return TCL_ERROR; + } + status = gaim_account_get_status(account, Tcl_GetString(objv[3])); + if (status == NULL) { + Tcl_SetStringObj(result, "invalid status for account", -1); + return TCL_ERROR; + } + for (i = 4; i < objc; i += 2) { + attr_id = Tcl_GetString(objv[i]); + value = gaim_status_get_attr_value(status, attr_id); + if (value == NULL) { + Tcl_SetStringObj(result, "invalid attribute for account", -1); + return TCL_ERROR; + } + switch (gaim_value_get_type(value)) { + case GAIM_TYPE_BOOLEAN: + error = Tcl_GetBooleanFromObj(interp, objv[i + 1], &b); + if (error != TCL_OK) + return error; + l = g_list_append(l, attr_id); + l = g_list_append(l, GINT_TO_POINTER(b)); + break; + case GAIM_TYPE_INT: + error = Tcl_GetIntFromObj(interp, objv[i + 1], &b); + if (error != TCL_OK) + return error; + l = g_list_append(l, attr_id); + l = g_list_append(l, GINT_TO_POINTER(b)); + break; + case GAIM_TYPE_STRING: + l = g_list_append(l, attr_id); + l = g_list_append(l, Tcl_GetString(objv[i + 1])); + break; + default: + Tcl_SetStringObj(result, "unknown GaimValue type", -1); + return TCL_ERROR; + } + } + gaim_account_set_status_list(account, Tcl_GetString(objv[3]), TRUE, l); + g_list_free(l); + } + break; + case CMD_ACCOUNT_STATUS_TYPE: + if (objc != 4 && objc != 5) { + Tcl_WrongNumArgs(interp, 2, objv, "account ?statustype? ?-primitive primitive?"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + if (objc == 4) { + status_type = gaim_account_get_status_type(account, + Tcl_GetString(objv[3])); + } else { + GaimStatusPrimitive primitive; + if (strcmp(Tcl_GetString(objv[3]), "-primitive")) { + Tcl_SetStringObj(result, "bad option \"", -1); + Tcl_AppendObjToObj(result, objv[3]); + Tcl_AppendToObj(result, + "\": should be -primitive", -1); + return TCL_ERROR; + } + primitive = gaim_primitive_get_type_from_id(Tcl_GetString(objv[4])); + status_type = gaim_account_get_status_type_with_primitive(account, + primitive); + } + if (status_type == NULL) { + Tcl_SetStringObj(result, "status type not found", -1); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, + gaim_tcl_ref_new(GaimTclRefStatusType, + status_type)); + break; + case CMD_ACCOUNT_STATUS_TYPES: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "account"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + list = Tcl_NewListObj(0, NULL); + for (cur = gaim_account_get_status_types(account); cur != NULL; + cur = g_list_next(cur)) { + Tcl_ListObjAppendElement(interp, list, + gaim_tcl_ref_new(GaimTclRefStatusType, + cur->data)); + } + Tcl_SetObjResult(interp, list); + break; + case CMD_ACCOUNT_USERNAME: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "account"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, (char *)gaim_account_get_username(account), -1); + break; + } + + return TCL_OK; +} + +static GaimBlistNode *tcl_list_to_buddy(Tcl_Interp *interp, int count, Tcl_Obj **elems) +{ + GaimBlistNode *node = NULL; + GaimAccount *account; + char *name; + char *type; + + if (count < 3) { + Tcl_SetStringObj(Tcl_GetObjResult(interp), "list too short", -1); + return NULL; + } + + type = Tcl_GetString(elems[0]); + name = Tcl_GetString(elems[1]); + if ((account = tcl_validate_account(elems[2], interp)) == NULL) + return NULL; + + if (!strcmp(type, "buddy")) { + node = (GaimBlistNode *)gaim_find_buddy(account, name); + } else if (!strcmp(type, "group")) { + node = (GaimBlistNode *)gaim_blist_find_chat(account, name); + } + + return node; +} + +int tcl_cmd_buddy(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Obj *list, *tclgroup, *tclgrouplist, *tclcontact, *tclcontactlist, *tclbud, **elems, *result; + const char *cmds[] = { "alias", "handle", "info", "list", NULL }; + enum { CMD_BUDDY_ALIAS, CMD_BUDDY_HANDLE, CMD_BUDDY_INFO, CMD_BUDDY_LIST } cmd; + GaimBuddyList *blist; + GaimBlistNode *node, *gnode, *bnode; + GaimAccount *account; + GaimBuddy *bud; + GaimChat *cnode; + int error, all = 0, count; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + result = Tcl_GetObjResult(interp); + + switch (cmd) { + case CMD_BUDDY_ALIAS: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "buddy"); + return TCL_ERROR; + } + if ((error = Tcl_ListObjGetElements(interp, objv[2], &count, &elems)) != TCL_OK) + return error; + if ((node = tcl_list_to_buddy(interp, count, elems)) == NULL) + return TCL_ERROR; + if (node->type == GAIM_BLIST_CHAT_NODE) + Tcl_SetStringObj(result, ((GaimChat *)node)->alias, -1); + else if (node->type == GAIM_BLIST_BUDDY_NODE) + Tcl_SetStringObj(result, (char *)gaim_buddy_get_alias((GaimBuddy *)node), -1); + return TCL_OK; + break; + case CMD_BUDDY_HANDLE: + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + Tcl_SetIntObj(result, (int)gaim_blist_get_handle()); + break; + case CMD_BUDDY_INFO: + if (objc != 3 && objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "( buddy | account username )"); + return TCL_ERROR; + } + if (objc == 3) { + if ((error = Tcl_ListObjGetElements(interp, objv[2], &count, &elems)) != TCL_OK) + return error; + if (count < 3) { + Tcl_SetStringObj(result, "buddy too short", -1); + return TCL_ERROR; + } + if (strcmp("buddy", Tcl_GetString(elems[0]))) { + Tcl_SetStringObj(result, "invalid buddy", -1); + return TCL_ERROR; + } + if ((account = tcl_validate_account(elems[2], interp)) == NULL) + return TCL_ERROR; + serv_get_info(gaim_account_get_connection(account), Tcl_GetString(elems[1])); + } else { + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + serv_get_info(gaim_account_get_connection(account), Tcl_GetString(objv[3])); + } + break; + case CMD_BUDDY_LIST: + if (objc == 3) { + if (!strcmp("-all", Tcl_GetString(objv[2]))) { + all = 1; + } else { + Tcl_SetStringObj(result, "", -1); + Tcl_AppendStringsToObj(result, "unknown option: ", Tcl_GetString(objv[2]), NULL); + return TCL_ERROR; + } + } + list = Tcl_NewListObj(0, NULL); + blist = gaim_get_blist(); + for (gnode = blist->root; gnode != NULL; gnode = gnode->next) { + tclgroup = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(interp, tclgroup, Tcl_NewStringObj("group", -1)); + Tcl_ListObjAppendElement(interp, tclgroup, + Tcl_NewStringObj(((GaimGroup *)gnode)->name, -1)); + tclgrouplist = Tcl_NewListObj(0, NULL); + for (node = gnode->child; node != NULL; node = node->next) { + switch (node->type) { + case GAIM_BLIST_CONTACT_NODE: + tclcontact = Tcl_NewListObj(0, NULL); + Tcl_IncrRefCount(tclcontact); + Tcl_ListObjAppendElement(interp, tclcontact, Tcl_NewStringObj("contact", -1)); + tclcontactlist = Tcl_NewListObj(0, NULL); + Tcl_IncrRefCount(tclcontactlist); + count = 0; + for (bnode = node->child; bnode != NULL; bnode = bnode ->next) { + if (bnode->type != GAIM_BLIST_BUDDY_NODE) + continue; + bud = (GaimBuddy *)bnode; + if (!all && !gaim_account_is_connected(bud->account)) + continue; + count++; + tclbud = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(interp, tclbud, Tcl_NewStringObj("buddy", -1)); + Tcl_ListObjAppendElement(interp, tclbud, Tcl_NewStringObj(bud->name, -1)); + Tcl_ListObjAppendElement(interp, tclbud, gaim_tcl_ref_new(GaimTclRefAccount, bud->account)); + Tcl_ListObjAppendElement(interp, tclcontactlist, tclbud); + } + if (count) { + Tcl_ListObjAppendElement(interp, tclcontact, tclcontactlist); + Tcl_ListObjAppendElement(interp, tclgrouplist, tclcontact); + } + Tcl_DecrRefCount(tclcontact); + Tcl_DecrRefCount(tclcontactlist); + break; + case GAIM_BLIST_CHAT_NODE: + cnode = (GaimChat *)node; + if (!all && !gaim_account_is_connected(cnode->account)) + continue; + tclbud = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(interp, tclbud, Tcl_NewStringObj("chat", -1)); + Tcl_ListObjAppendElement(interp, tclbud, Tcl_NewStringObj(cnode->alias, -1)); + Tcl_ListObjAppendElement(interp, tclbud, gaim_tcl_ref_new(GaimTclRefAccount, cnode->account)); + Tcl_ListObjAppendElement(interp, tclgrouplist, tclbud); + break; + default: + gaim_debug(GAIM_DEBUG_WARNING, "tcl", "Unexpected buddy type %d", node->type); + continue; + } + } + Tcl_ListObjAppendElement(interp, tclgroup, tclgrouplist); + Tcl_ListObjAppendElement(interp, list, tclgroup); + } + Tcl_SetObjResult(interp, list); + break; + } + + return TCL_OK; +} + +int tcl_cmd_cmd(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + const char *cmds[] = { "register", "unregister", NULL }; + enum { CMD_CMD_REGISTER, CMD_CMD_UNREGISTER } cmd; + struct tcl_cmd_handler *handler; + Tcl_Obj *result = Tcl_GetObjResult(interp); + GaimCmdId id; + int error; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + switch (cmd) { + case CMD_CMD_REGISTER: + if (objc != 9) { + Tcl_WrongNumArgs(interp, 2, objv, "cmd arglist priority flags prpl_id proc helpstr"); + return TCL_ERROR; + } + handler = g_new0(struct tcl_cmd_handler, 1); + handler->cmd = objv[2]; + handler->args = Tcl_GetString(objv[3]); + handler->nargs = strlen(handler->args); + if ((error = Tcl_GetIntFromObj(interp, objv[4], + &handler->priority)) != TCL_OK) { + g_free(handler); + return error; + } + if ((error = Tcl_GetIntFromObj(interp, objv[5], + &handler->flags)) != TCL_OK) { + g_free(handler); + return error; + } + handler->prpl_id = Tcl_GetString(objv[6]); + handler->proc = objv[7]; + handler->helpstr = Tcl_GetString(objv[8]); + handler->interp = interp; + if ((id = tcl_cmd_register(handler)) == 0) { + tcl_cmd_handler_free(handler); + Tcl_SetIntObj(result, 0); + } else { + handler->id = id; + Tcl_SetIntObj(result, id); + } + break; + case CMD_CMD_UNREGISTER: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "id"); + return TCL_ERROR; + } + if ((error = Tcl_GetIntFromObj(interp, objv[2], + (int *)&id)) != TCL_OK) + return error; + tcl_cmd_unregister(id, interp); + break; + } + + return TCL_OK; +} + +int tcl_cmd_connection(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Obj *result = Tcl_GetObjResult(interp), *list, *elem; + const char *cmds[] = { "account", "displayname", "handle", "list", NULL }; + enum { CMD_CONN_ACCOUNT, CMD_CONN_DISPLAYNAME, CMD_CONN_HANDLE, CMD_CONN_LIST } cmd; + int error; + GList *cur; + GaimConnection *gc; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + switch (cmd) { + case CMD_CONN_ACCOUNT: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "gc"); + return TCL_ERROR; + } + if ((gc = tcl_validate_gc(objv[2], interp)) == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, + gaim_tcl_ref_new(GaimTclRefAccount, + gaim_connection_get_account(gc))); + break; + case CMD_CONN_DISPLAYNAME: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "gc"); + return TCL_ERROR; + } + if ((gc = tcl_validate_gc(objv[2], interp)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, (char *)gaim_connection_get_display_name(gc), -1); + break; + case CMD_CONN_HANDLE: + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + Tcl_SetIntObj(result, (int)gaim_connections_get_handle()); + break; + case CMD_CONN_LIST: + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + list = Tcl_NewListObj(0, NULL); + for (cur = gaim_connections_get_all(); cur != NULL; cur = g_list_next(cur)) { + elem = gaim_tcl_ref_new(GaimTclRefConnection, cur->data); + Tcl_ListObjAppendElement(interp, list, elem); + } + Tcl_SetObjResult(interp, list); + break; + } + + return TCL_OK; +} + +int tcl_cmd_conversation(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Obj *list, *elem, *result = Tcl_GetObjResult(interp); + const char *cmds[] = { "find", "handle", "list", "new", "write", "name", "title", "send", NULL }; + enum { CMD_CONV_FIND, CMD_CONV_HANDLE, CMD_CONV_LIST, CMD_CONV_NEW, CMD_CONV_WRITE , CMD_CONV_NAME, CMD_CONV_TITLE, CMD_CONV_SEND } cmd; + const char *styles[] = { "send", "recv", "system", NULL }; + enum { CMD_CONV_WRITE_SEND, CMD_CONV_WRITE_RECV, CMD_CONV_WRITE_SYSTEM } style; + const char *newopts[] = { "-chat", "-im" }; + enum { CMD_CONV_NEW_CHAT, CMD_CONV_NEW_IM } newopt; + GaimConversation *convo; + GaimAccount *account; + GaimConversationType type; + GList *cur; + char *opt, *from, *what; + int error, argsused, flags = 0; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + switch (cmd) { + case CMD_CONV_FIND: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "account name"); + return TCL_ERROR; + } + account = NULL; + if ((account = tcl_validate_account(objv[2], interp)) == NULL) + return TCL_ERROR; + convo = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, + Tcl_GetString(objv[3]), + account); + Tcl_SetObjResult(interp, gaim_tcl_ref_new(GaimTclRefConversation, convo)); + break; + case CMD_CONV_HANDLE: + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + Tcl_SetIntObj(result, (int)gaim_conversations_get_handle()); + break; + case CMD_CONV_LIST: + list = Tcl_NewListObj(0, NULL); + for (cur = gaim_get_conversations(); cur != NULL; cur = g_list_next(cur)) { + elem = gaim_tcl_ref_new(GaimTclRefConversation, cur->data); + Tcl_ListObjAppendElement(interp, list, elem); + } + Tcl_SetObjResult(interp, list); + break; + case CMD_CONV_NEW: + if (objc < 4) { + Tcl_WrongNumArgs(interp, 2, objv, "?options? account name"); + return TCL_ERROR; + } + argsused = 2; + type = GAIM_CONV_TYPE_IM; + while (argsused < objc) { + opt = Tcl_GetString(objv[argsused]); + if (*opt == '-') { + if ((error = Tcl_GetIndexFromObj(interp, objv[argsused], newopts, + "option", 0, (int *)&newopt)) != TCL_OK) + return error; + argsused++; + switch (newopt) { + case CMD_CONV_NEW_CHAT: + type = GAIM_CONV_TYPE_CHAT; + break; + case CMD_CONV_NEW_IM: + type = GAIM_CONV_TYPE_IM; + break; + } + } else { + break; + } + } + if (objc - argsused != 2) { + Tcl_WrongNumArgs(interp, 2, objv, "?options? account name"); + return TCL_ERROR; + } + if ((account = tcl_validate_account(objv[argsused++], interp)) == NULL) + return TCL_ERROR; + convo = gaim_conversation_new(type, account, Tcl_GetString(objv[argsused])); + Tcl_SetObjResult(interp, gaim_tcl_ref_new(GaimTclRefConversation, convo)); + break; + case CMD_CONV_WRITE: + if (objc != 6) { + Tcl_WrongNumArgs(interp, 2, objv, "conversation style from what"); + return TCL_ERROR; + } + if ((convo = tcl_validate_conversation(objv[2], interp)) == NULL) + return TCL_ERROR; + if ((error = Tcl_GetIndexFromObj(interp, objv[3], styles, "style", 0, (int *)&style)) != TCL_OK) + return error; + from = Tcl_GetString(objv[4]); + what = Tcl_GetString(objv[5]); + + switch (style) { + case CMD_CONV_WRITE_SEND: + flags = GAIM_MESSAGE_SEND; + break; + case CMD_CONV_WRITE_RECV: + flags = GAIM_MESSAGE_RECV; + break; + case CMD_CONV_WRITE_SYSTEM: + flags = GAIM_MESSAGE_SYSTEM; + break; + } + if (gaim_conversation_get_type(convo) == GAIM_CONV_TYPE_CHAT) + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), from, what, flags, time(NULL)); + else + gaim_conv_im_write(GAIM_CONV_IM(convo), from, what, flags, time(NULL)); + break; + case CMD_CONV_NAME: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "conversation"); + return TCL_ERROR; + } + + if ((convo = tcl_validate_conversation(objv[2], interp)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, (char *)gaim_conversation_get_name(convo), -1); + break; + case CMD_CONV_TITLE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "conversation"); + return TCL_ERROR; + } + + if ((convo = tcl_validate_conversation(objv[2], interp)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, (char *)gaim_conversation_get_title(convo), -1); + break; + case CMD_CONV_SEND: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "conversation message"); + return TCL_ERROR; + } + if ((convo = tcl_validate_conversation(objv[2], interp)) == NULL) + return TCL_ERROR; + what = Tcl_GetString(objv[3]); + if (gaim_conversation_get_type(convo) == GAIM_CONV_TYPE_CHAT) + gaim_conv_chat_send(GAIM_CONV_CHAT(convo), what); + else + gaim_conv_im_send(GAIM_CONV_IM(convo), what); + break; + } + + return TCL_OK; +} + +int tcl_cmd_core(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Obj *result = Tcl_GetObjResult(interp); + const char *cmds[] = { "handle", "quit", NULL }; + enum { CMD_CORE_HANDLE, CMD_CORE_QUIT } cmd; + int error; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + switch (cmd) { + case CMD_CORE_HANDLE: + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + Tcl_SetIntObj(result, (int)gaim_get_core()); + break; + case CMD_CORE_QUIT: + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + gaim_core_quit(); + break; + } + + return TCL_OK; +} + +int tcl_cmd_debug(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + char *category, *message; + int lev; + const char *levels[] = { "-misc", "-info", "-warning", "-error", NULL }; + GaimDebugLevel levelind[] = { GAIM_DEBUG_MISC, GAIM_DEBUG_INFO, GAIM_DEBUG_WARNING, GAIM_DEBUG_ERROR }; + int error; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, "level category message"); + return TCL_ERROR; + } + + error = Tcl_GetIndexFromObj(interp, objv[1], levels, "debug level", 0, &lev); + if (error != TCL_OK) + return error; + + category = Tcl_GetString(objv[2]); + message = Tcl_GetString(objv[3]); + + gaim_debug(levelind[lev], category, "%s\n", message); + + return TCL_OK; +} + +int tcl_cmd_notify(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + int error, type; + const char *opts[] = { "-error", "-warning", "-info", NULL }; + GaimNotifyMsgType optind[] = { GAIM_NOTIFY_MSG_ERROR, GAIM_NOTIFY_MSG_WARNING, GAIM_NOTIFY_MSG_INFO }; + char *title, *msg1, *msg2; + + if (objc < 4 || objc > 5) { + Tcl_WrongNumArgs(interp, 1, objv, "?type? title primary secondary"); + return TCL_ERROR; + } + + if (objc == 4) { + type = 1; /* Default to warning */ + title = Tcl_GetString(objv[1]); + msg1 = Tcl_GetString(objv[2]); + msg2 = Tcl_GetString(objv[3]); + } else { + error = Tcl_GetIndexFromObj(interp, objv[1], opts, "message type", 0, &type); + if (error != TCL_OK) + return error; + title = Tcl_GetString(objv[2]); + msg1 = Tcl_GetString(objv[3]); + msg2 = Tcl_GetString(objv[4]); + } + + gaim_notify_message(_tcl_plugin, optind[type], title, msg1, msg2, NULL, NULL); + + return TCL_OK; +} + +int tcl_cmd_prefs(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Obj *result, *list, *elem, **elems; + const char *cmds[] = { "get", "set", "type", NULL }; + enum { CMD_PREFS_GET, CMD_PREFS_SET, CMD_PREFS_TYPE } cmd; + /* char *types[] = { "none", "boolean", "int", "string", "stringlist", NULL }; */ + /* enum { TCL_PREFS_NONE, TCL_PREFS_BOOL, TCL_PREFS_INT, TCL_PREFS_STRING, TCL_PREFS_STRINGLIST } type; */ + GaimPrefType preftype; + GList *cur; + int error, intval, nelem, i; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + result = Tcl_GetObjResult(interp); + switch (cmd) { + case CMD_PREFS_GET: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "path"); + return TCL_ERROR; + } + preftype = gaim_prefs_get_type(Tcl_GetString(objv[2])); + switch (preftype) { + case GAIM_PREF_NONE: + Tcl_SetStringObj(result, "pref type none", -1); + return TCL_ERROR; + break; + case GAIM_PREF_BOOLEAN: + Tcl_SetBooleanObj(result, gaim_prefs_get_bool(Tcl_GetString(objv[2]))); + break; + case GAIM_PREF_INT: + Tcl_SetIntObj(result, gaim_prefs_get_int(Tcl_GetString(objv[2]))); + break; + case GAIM_PREF_STRING: + Tcl_SetStringObj(result, (char *)gaim_prefs_get_string(Tcl_GetString(objv[2])), -1); + break; + case GAIM_PREF_STRING_LIST: + cur = gaim_prefs_get_string_list(Tcl_GetString(objv[2])); + list = Tcl_NewListObj(0, NULL); + while (cur != NULL) { + elem = Tcl_NewStringObj((char *)cur->data, -1); + Tcl_ListObjAppendElement(interp, list, elem); + cur = g_list_next(cur); + } + Tcl_SetObjResult(interp, list); + break; + default: + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "tcl does not know about pref type %d\n", preftype); + Tcl_SetStringObj(result, "unknown pref type", -1); + return TCL_ERROR; + } + break; + case CMD_PREFS_SET: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, "path value"); + return TCL_ERROR; + } + preftype = gaim_prefs_get_type(Tcl_GetString(objv[2])); + switch (preftype) { + case GAIM_PREF_NONE: + Tcl_SetStringObj(result, "bad path or pref type none", -1); + return TCL_ERROR; + break; + case GAIM_PREF_BOOLEAN: + if ((error = Tcl_GetBooleanFromObj(interp, objv[3], &intval)) != TCL_OK) + return error; + gaim_prefs_set_bool(Tcl_GetString(objv[2]), intval); + break; + case GAIM_PREF_INT: + if ((error = Tcl_GetIntFromObj(interp, objv[3], &intval)) != TCL_OK) + return error; + gaim_prefs_set_int(Tcl_GetString(objv[2]), intval); + break; + case GAIM_PREF_STRING: + gaim_prefs_set_string(Tcl_GetString(objv[2]), Tcl_GetString(objv[3])); + break; + case GAIM_PREF_STRING_LIST: + if ((error = Tcl_ListObjGetElements(interp, objv[3], &nelem, &elems)) != TCL_OK) + return error; + cur = NULL; + for (i = 0; i < nelem; i++) { + cur = g_list_append(cur, (gpointer)Tcl_GetString(elems[i])); + } + gaim_prefs_set_string_list(Tcl_GetString(objv[2]), cur); + g_list_free(cur); + break; + default: + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "tcl does not know about pref type %d\n", preftype); + return TCL_ERROR; + } + break; + case CMD_PREFS_TYPE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "path"); + return TCL_ERROR; + } + preftype = gaim_prefs_get_type(Tcl_GetString(objv[2])); + switch (preftype) { + case GAIM_PREF_NONE: + Tcl_SetStringObj(result, "none", -1); + break; + case GAIM_PREF_BOOLEAN: + Tcl_SetStringObj(result, "boolean", -1); + break; + case GAIM_PREF_INT: + Tcl_SetStringObj(result, "int", -1); + break; + case GAIM_PREF_STRING: + Tcl_SetStringObj(result, "string", -1); + break; + case GAIM_PREF_STRING_LIST: + Tcl_SetStringObj(result, "stringlist", -1); + break; + default: + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "tcl does not know about pref type %d\n", preftype); + Tcl_SetStringObj(result, "unknown", -1); + } + break; + } + + return TCL_OK; +} + +int tcl_cmd_presence(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + const char *cmds[] = { "account", "active_status", "available", + "chat_user", "context", "conversation", "idle", + "login", "online", "status", "statuses", NULL }; + enum { CMD_PRESENCE_ACCOUNT, CMD_PRESENCE_ACTIVE_STATUS, + CMD_PRESENCE_AVAILABLE, CMD_PRESENCE_CHAT_USER, + CMD_PRESENCE_CONTEXT, CMD_PRESENCE_CONVERSATION, + CMD_PRESENCE_IDLE, CMD_PRESENCE_LOGIN, CMD_PRESENCE_ONLINE, + CMD_PRESENCE_STATUS, CMD_PRESENCE_STATUSES } cmd; + Tcl_Obj *result = Tcl_GetObjResult(interp); + Tcl_Obj *list, *elem; + GaimPresence *presence; + const GList *cur; + int error, idle, idle_time, login_time; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + switch (cmd) { + case CMD_PRESENCE_ACCOUNT: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "presence"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, gaim_tcl_ref_new(GaimTclRefAccount, + gaim_presence_get_account(presence))); + break; + case CMD_PRESENCE_ACTIVE_STATUS: + if (objc != 3 && objc != 4 && objc != 5) { + Tcl_WrongNumArgs(interp, 2, objv, "presence [?status_id? | ?-primitive primitive?]"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + if (objc == 3) { + Tcl_SetObjResult(interp, + gaim_tcl_ref_new(GaimTclRefStatus, + gaim_presence_get_active_status(presence))); + } else if (objc == 4) { + Tcl_SetBooleanObj(result, + gaim_presence_is_status_active(presence, + Tcl_GetString(objv[3]))); + } else { + GaimStatusPrimitive primitive; + if (strcmp(Tcl_GetString(objv[3]), "-primitive")) { + Tcl_SetStringObj(result, "bad option \"", -1); + Tcl_AppendObjToObj(result, objv[3]); + Tcl_AppendToObj(result, + "\": should be -primitive", -1); + return TCL_ERROR; + } + primitive = gaim_primitive_get_type_from_id(Tcl_GetString(objv[4])); + if (primitive == GAIM_STATUS_UNSET) { + Tcl_SetStringObj(result, "invalid primitive ", -1); + Tcl_AppendObjToObj(result, objv[4]); + return TCL_ERROR; + } + Tcl_SetBooleanObj(result, gaim_presence_is_status_primitive_active(presence, primitive)); + break; + } + break; + case CMD_PRESENCE_AVAILABLE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "presence"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + Tcl_SetBooleanObj(result, gaim_presence_is_available(presence)); + break; + case CMD_PRESENCE_CHAT_USER: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "presence"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, gaim_presence_get_chat_user(presence), -1); + break; + case CMD_PRESENCE_CONTEXT: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "presence"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + switch (gaim_presence_get_context(presence)) { + case GAIM_PRESENCE_CONTEXT_UNSET: + Tcl_SetStringObj(result, "unset", -1); + break; + case GAIM_PRESENCE_CONTEXT_ACCOUNT: + Tcl_SetStringObj(result, "account", -1); + break; + case GAIM_PRESENCE_CONTEXT_CONV: + Tcl_SetStringObj(result, "conversation", -1); + break; + case GAIM_PRESENCE_CONTEXT_BUDDY: + Tcl_SetStringObj(result, "buddy", -1); + break; + } + break; + case CMD_PRESENCE_CONVERSATION: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "presence"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, gaim_tcl_ref_new(GaimTclRefConversation, + gaim_presence_get_conversation(presence))); + break; + case CMD_PRESENCE_IDLE: + if (objc < 3 || objc > 5) { + Tcl_WrongNumArgs(interp, 2, objv, "presence ?idle? ?time?"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + if (objc == 3) { + if (gaim_presence_is_idle(presence)) { + idle_time = gaim_presence_get_idle_time (presence); + Tcl_SetIntObj(result, idle_time); + } else { + result = Tcl_NewListObj(0, NULL); + Tcl_SetObjResult(interp, result); + } + break; + } + if ((error = Tcl_GetBooleanFromObj(interp, objv[3], &idle)) != TCL_OK) + return TCL_ERROR; + if (objc == 4) { + gaim_presence_set_idle(presence, idle, time(NULL)); + } else if (objc == 5) { + if ((error = Tcl_GetIntFromObj(interp, + objv[4], + &idle_time)) != TCL_OK) + return TCL_ERROR; + gaim_presence_set_idle(presence, idle, idle_time); + } + break; + case CMD_PRESENCE_LOGIN: + if (objc != 3 && objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "presence ?time?"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + if (objc == 3) { + Tcl_SetIntObj(result, gaim_presence_get_login_time(presence)); + } else { + if ((error == Tcl_GetIntFromObj(interp, + objv[3], + &login_time)) != TCL_OK) + return TCL_ERROR; + gaim_presence_set_login_time(presence, login_time); + } + break; + case CMD_PRESENCE_ONLINE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "presence"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + Tcl_SetBooleanObj(result, gaim_presence_is_online(presence)); + break; + case CMD_PRESENCE_STATUS: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "presence status_id"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, + gaim_tcl_ref_new(GaimTclRefStatus, + gaim_presence_get_status(presence, + Tcl_GetString(objv[3])))); + break; + case CMD_PRESENCE_STATUSES: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "presence"); + return TCL_ERROR; + } + if ((presence = gaim_tcl_ref_get(interp, objv[2], GaimTclRefPresence)) == NULL) + return TCL_ERROR; + list = Tcl_NewListObj(0, NULL); + for (cur = gaim_presence_get_statuses(presence); cur != NULL; + cur = g_list_next(cur)) { + elem = gaim_tcl_ref_new(GaimTclRefStatus, cur->data); + Tcl_ListObjAppendElement(interp, list, elem); + } + Tcl_SetObjResult(interp, list); + break; + } + + return TCL_OK; +} + +int tcl_cmd_send_im(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + GaimConnection *gc; + char *who, *text; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, "gc who text"); + return TCL_ERROR; + } + + if ((gc = tcl_validate_gc(objv[1], interp)) == NULL) + return TCL_ERROR; + + who = Tcl_GetString(objv[2]); + text = Tcl_GetString(objv[3]); + + serv_send_im(gc, who, text, 0); + + return TCL_OK; +} + +int tcl_cmd_signal(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + const char *cmds[] = { "connect", "disconnect", NULL }; + enum { CMD_SIGNAL_CONNECT, CMD_SIGNAL_DISCONNECT } cmd; + struct tcl_signal_handler *handler; + Tcl_Obj *result = Tcl_GetObjResult(interp); + void *instance; + int error; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + switch (cmd) { + case CMD_SIGNAL_CONNECT: + if (objc != 6) { + Tcl_WrongNumArgs(interp, 2, objv, "instance signal args proc"); + return TCL_ERROR; + } + handler = g_new0(struct tcl_signal_handler, 1); + if ((error = Tcl_GetIntFromObj(interp, objv[2], (int *)&handler->instance)) != TCL_OK) { + g_free(handler); + return error; + } + handler->signal = objv[3]; + Tcl_IncrRefCount(handler->signal); + handler->args = objv[4]; + handler->proc = objv[5]; + handler->interp = interp; + if (!tcl_signal_connect(handler)) { + tcl_signal_handler_free(handler); + Tcl_SetIntObj(result, 1); + } else { + Tcl_SetIntObj(result, 0); + } + break; + case CMD_SIGNAL_DISCONNECT: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "instance signal"); + return TCL_ERROR; + } + if ((error = Tcl_GetIntFromObj(interp, objv[2], (int *)&instance)) != TCL_OK) + return error; + tcl_signal_disconnect(instance, Tcl_GetString(objv[3]), interp); + break; + } + + return TCL_OK; +} + +int tcl_cmd_status(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + const char *cmds[] = { "attr", "type", NULL }; + enum { CMD_STATUS_ATTR, CMD_STATUS_TYPE } cmd; + Tcl_Obj *result = Tcl_GetObjResult(interp); + GaimStatus *status; + GaimStatusType *status_type; + GaimValue *value; + const char *attr; + int error, v; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + switch (cmd) { + case CMD_STATUS_ATTR: + if (objc != 4 && objc != 5) { + Tcl_WrongNumArgs(interp, 2, objv, "status attr_id ?value?"); + return TCL_ERROR; + } + if ((status = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatus)) == NULL) + return TCL_ERROR; + attr = Tcl_GetString(objv[3]); + value = gaim_status_get_attr_value(status, attr); + if (value == NULL) { + Tcl_SetStringObj(result, "no such attribute", -1); + return TCL_ERROR; + } + switch (gaim_value_get_type(value)) { + case GAIM_TYPE_BOOLEAN: + if (objc == 4) { + Tcl_SetBooleanObj(result, gaim_value_get_boolean(value)); + } else { + if ((error = Tcl_GetBooleanFromObj(interp, objv[4], &v)) != TCL_OK) + return error; + gaim_status_set_attr_boolean(status, attr, v); + } + break; + case GAIM_TYPE_INT: + if (objc == 4) { + Tcl_SetIntObj(result, gaim_value_get_int(value)); + } else { + if ((error = Tcl_GetIntFromObj(interp, objv[4], &v)) != TCL_OK) + return error; + gaim_status_set_attr_int(status, attr, v ); + } + break; + case GAIM_TYPE_STRING: + if (objc == 4) + Tcl_SetStringObj(result, gaim_value_get_string(value), -1); + else + gaim_status_set_attr_string(status, attr, Tcl_GetString(objv[4])); + break; + default: + Tcl_SetStringObj(result, "attribute has unknown type", -1); + return TCL_ERROR; + } + break; + case CMD_STATUS_TYPE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "status"); + return TCL_ERROR; + } + if ((status = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatus)) == NULL) + return TCL_ERROR; + status_type = gaim_status_get_type(status); + Tcl_SetObjResult(interp, gaim_tcl_ref_new(GaimTclRefStatusType, + status_type)); + break; + } + + return TCL_OK; +} + +int tcl_cmd_status_attr(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + const char *cmds[] = { "id", "name", NULL }; + enum { CMD_STATUS_ATTR_ID, CMD_STATUS_ATTR_NAME } cmd; + Tcl_Obj *result = Tcl_GetObjResult(interp); + GaimStatusAttr *attr; + int error; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + switch (cmd) { + case CMD_STATUS_ATTR_ID: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "attr"); + return TCL_ERROR; + } + if ((attr = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusAttr)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, gaim_status_attr_get_id(attr), -1); + break; + case CMD_STATUS_ATTR_NAME: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "attr"); + return TCL_ERROR; + } + if ((attr = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusAttr)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, gaim_status_attr_get_name(attr), -1); + break; + } + + return TCL_OK; +} + +int tcl_cmd_status_type(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + const char *cmds[] = { "attr", "attrs", "available", "exclusive", "id", + "independent", "name", "primary_attr", + "primitive", "saveable", "user_settable", + NULL }; + enum { CMD_STATUS_TYPE_ATTR, CMD_STATUS_TYPE_ATTRS, + CMD_STATUS_TYPE_AVAILABLE, CMD_STATUS_TYPE_EXCLUSIVE, + CMD_STATUS_TYPE_ID, CMD_STATUS_TYPE_INDEPENDENT, + CMD_STATUS_TYPE_NAME, CMD_STATUS_TYPE_PRIMARY_ATTR, + CMD_STATUS_TYPE_PRIMITIVE, CMD_STATUS_TYPE_SAVEABLE, + CMD_STATUS_TYPE_USER_SETTABLE } cmd; + Tcl_Obj *result = Tcl_GetObjResult(interp); + GaimStatusType *status_type; + Tcl_Obj *list, *elem; + const GList *cur; + int error; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); + return TCL_ERROR; + } + + if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) + return error; + + switch (cmd) { + case CMD_STATUS_TYPE_AVAILABLE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + Tcl_SetBooleanObj(result, gaim_status_type_is_available(status_type)); + break; + case CMD_STATUS_TYPE_ATTR: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype attr"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, + gaim_tcl_ref_new(GaimTclRefStatusAttr, + gaim_status_type_get_attr(status_type, + Tcl_GetStringFromObj(objv[3], NULL)))); + break; + case CMD_STATUS_TYPE_ATTRS: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + list = Tcl_NewListObj(0, NULL); + for (cur = gaim_status_type_get_attrs(status_type); + cur != NULL; cur = g_list_next(cur)) { + elem = gaim_tcl_ref_new(GaimTclRefStatusAttr, cur->data); + Tcl_ListObjAppendElement(interp, list, elem); + } + Tcl_SetObjResult(interp, list); + break; + case CMD_STATUS_TYPE_EXCLUSIVE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + Tcl_SetBooleanObj(result, gaim_status_type_is_exclusive(status_type)); + break; + case CMD_STATUS_TYPE_ID: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, gaim_status_type_get_id(status_type), -1); + break; + case CMD_STATUS_TYPE_INDEPENDENT: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + Tcl_SetBooleanObj(result, gaim_status_type_is_independent(status_type)); + break; + case CMD_STATUS_TYPE_NAME: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, gaim_status_type_get_name(status_type), -1); + break; + case CMD_STATUS_TYPE_PRIMITIVE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, gaim_primitive_get_id_from_type(gaim_status_type_get_primitive(status_type)), -1); + break; + case CMD_STATUS_TYPE_PRIMARY_ATTR: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + Tcl_SetStringObj(result, gaim_status_type_get_primary_attr(status_type), -1); + break; + case CMD_STATUS_TYPE_SAVEABLE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + Tcl_SetBooleanObj(result, gaim_status_type_is_saveable(status_type)); + break; + case CMD_STATUS_TYPE_USER_SETTABLE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "statustype"); + return TCL_ERROR; + } + if ((status_type = gaim_tcl_ref_get(interp, objv[2], GaimTclRefStatusType)) == NULL) + return TCL_ERROR; + Tcl_SetBooleanObj(result, gaim_status_type_is_user_settable(status_type)); + break; + } + + return TCL_OK; +} + +static gboolean unload_self(gpointer data) +{ + GaimPlugin *plugin = data; + gaim_plugin_unload(plugin); + return FALSE; +} + +int tcl_cmd_unload(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + GaimPlugin *plugin; + if (objc != 1) { + Tcl_WrongNumArgs(interp, 1, objv, ""); + return TCL_ERROR; + } + + if ((plugin = tcl_interp_get_plugin(interp)) == NULL) { + /* This isn't exactly OK, but heh. What do you do? */ + return TCL_OK; + } + /* We can't unload immediately, but we can unload at the first + * known safe opportunity. */ + g_idle_add(unload_self, (gpointer)plugin); + + return TCL_OK; +} diff --git a/libpurple/plugins/tcl/tcl_gaim.h b/libpurple/plugins/tcl/tcl_gaim.h new file mode 100644 index 0000000000..90afaf245b --- /dev/null +++ b/libpurple/plugins/tcl/tcl_gaim.h @@ -0,0 +1,116 @@ +/** + * @file tcl_gaim.h Gaim Tcl definitions + * + * gaim + * + * Copyright (C) 2003 Ethan Blanton + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GAIM_TCL_GAIM_H_ +#define _GAIM_TCL_GAIM_H_ + +#include + +#include "internal.h" +#include "cmds.h" +#include "plugin.h" +#include "value.h" +#include "stringref.h" + +struct tcl_signal_handler { + Tcl_Obj *signal; + Tcl_Interp *interp; + + void *instance; + Tcl_Obj *namespace; + /* These following two are temporary during setup */ + Tcl_Obj *args; + Tcl_Obj *proc; + + GaimValue *returntype; + int nargs; + GaimValue **argtypes; +}; + +struct tcl_cmd_handler { + int id; + Tcl_Obj *cmd; + Tcl_Interp *interp; + + Tcl_Obj *namespace; + /* These are temporary during setup */ + const char *args; + int priority; + int flags; + const char *prpl_id; + Tcl_Obj *proc; + const char *helpstr; + + int nargs; +}; + +extern GaimPlugin *_tcl_plugin; + +/* Capitalized this way because these are "types" */ +extern GaimStringref *GaimTclRefAccount; +extern GaimStringref *GaimTclRefConnection; +extern GaimStringref *GaimTclRefConversation; +extern GaimStringref *GaimTclRefPointer; +extern GaimStringref *GaimTclRefPlugin; +extern GaimStringref *GaimTclRefPresence; +extern GaimStringref *GaimTclRefStatus; +extern GaimStringref *GaimTclRefStatusAttr; +extern GaimStringref *GaimTclRefStatusType; +extern GaimStringref *GaimTclRefXfer; + +GaimPlugin *tcl_interp_get_plugin(Tcl_Interp *interp); + +void tcl_signal_init(void); +void tcl_signal_handler_free(struct tcl_signal_handler *handler); +void tcl_signal_cleanup(Tcl_Interp *interp); +gboolean tcl_signal_connect(struct tcl_signal_handler *handler); +void tcl_signal_disconnect(void *instance, const char *signal, Tcl_Interp *interp); + +void tcl_cmd_init(void); +void tcl_cmd_handler_free(struct tcl_cmd_handler *handler); +void tcl_cmd_cleanup(Tcl_Interp *interp); +GaimCmdId tcl_cmd_register(struct tcl_cmd_handler *handler); +void tcl_cmd_unregister(GaimCmdId id, Tcl_Interp *interp); + +void gaim_tcl_ref_init(void); +void *gaim_tcl_ref_get(Tcl_Interp *interp, Tcl_Obj *obj, GaimStringref *type); +Tcl_Obj *gaim_tcl_ref_new(GaimStringref *type, void *value); + +Tcl_ObjCmdProc tcl_cmd_account; +Tcl_ObjCmdProc tcl_cmd_signal_connect; +Tcl_ObjCmdProc tcl_cmd_buddy; +Tcl_ObjCmdProc tcl_cmd_cmd; +Tcl_ObjCmdProc tcl_cmd_connection; +Tcl_ObjCmdProc tcl_cmd_conversation; +Tcl_ObjCmdProc tcl_cmd_core; +Tcl_ObjCmdProc tcl_cmd_debug; +Tcl_ObjCmdProc tcl_cmd_notify; +Tcl_ObjCmdProc tcl_cmd_prefs; +Tcl_ObjCmdProc tcl_cmd_presence; +Tcl_ObjCmdProc tcl_cmd_send_im; +Tcl_ObjCmdProc tcl_cmd_signal; +Tcl_ObjCmdProc tcl_cmd_status; +Tcl_ObjCmdProc tcl_cmd_status_attr; +Tcl_ObjCmdProc tcl_cmd_status_type; +Tcl_ObjCmdProc tcl_cmd_unload; + +#endif /* _GAIM_TCL_GAIM_H_ */ diff --git a/libpurple/plugins/tcl/tcl_glib.c b/libpurple/plugins/tcl/tcl_glib.c new file mode 100644 index 0000000000..e8878c55b7 --- /dev/null +++ b/libpurple/plugins/tcl/tcl_glib.c @@ -0,0 +1,254 @@ +/* + * Tcl/Glib glue + * + * Copyright (C) 2003, 2004, 2006 Ethan Blanton + * + * This file is dual-licensed under the two sets of terms below. You may + * use, redistribute, or modify it pursuant to either the set of conditions + * under "TERMS 1" or "TERMS 2", at your discretion. The DISCLAIMER + * applies to both sets of terms. + * + * TERMS 1 + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * TERMS 2 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must contain the above copyright + * notice and this comment block in their entirety. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice and the text of this comment block in their entirety in + * the documentation and/or other materials provided with the + * distribution. + * + * DISCLAIMER + * + * 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. + */ + +/* + * NOTES + * + * This file was developed for the Gaim project. It inserts the Tcl + * event loop into the glib2 event loop for the purposes of providing + * Tcl bindings in a glib2 (e.g. Gtk2) program. To use it, simply + * link it into your executable, include tcl_glib.h, and call the + * function tcl_glib_init() before creating or using any Tcl + * interpreters. Then go ahead and use Tcl, Tk, whatever to your + * heart's content. + * + * BUGS + * + * tcl_wait_for_event seems to have a bug that makes vwait not work so + * well... I'm not sure why, yet, but I haven't put much time into + * it. Hopefully I will figure it out soon. In the meantime, this + * means that Tk's bgerror function (which is called when there is an + * error in a callback function) causes some Bad Mojo -- you should + * override it with a function that does not use Tk + */ + +#include +#include +#include + +#include "tcl_glib.h" + +struct tcl_file_handler { + int source; + int fd; + int mask; + int pending; + Tcl_FileProc *proc; + ClientData data; +}; + +struct tcl_file_event { + Tcl_Event header; + int fd; +}; + +static guint tcl_timer; +static gboolean tcl_timer_pending; +static GHashTable *tcl_file_handlers; + +static void tcl_set_timer(Tcl_Time *timePtr); +static int tcl_wait_for_event(Tcl_Time *timePtr); +static void tcl_create_file_handler(int fd, int mask, Tcl_FileProc *proc, ClientData data); +static void tcl_delete_file_handler(int fd); + +static gboolean tcl_kick(gpointer data); +static gboolean tcl_file_callback(GIOChannel *source, GIOCondition condition, gpointer data); +static int tcl_file_event_callback(Tcl_Event *event, int flags); + +#undef Tcl_InitNotifier + +ClientData Tcl_InitNotifier() +{ + return NULL; +} + +void tcl_glib_init () +{ + Tcl_NotifierProcs notifier; + + memset(¬ifier, 0, sizeof(notifier)); + + notifier.createFileHandlerProc = tcl_create_file_handler; + notifier.deleteFileHandlerProc = tcl_delete_file_handler; + notifier.setTimerProc = tcl_set_timer; + notifier.waitForEventProc = tcl_wait_for_event; + + Tcl_SetNotifier(¬ifier); + Tcl_SetServiceMode(TCL_SERVICE_ALL); + + tcl_timer_pending = FALSE; + tcl_file_handlers = g_hash_table_new(g_direct_hash, g_direct_equal); +} + +static void tcl_set_timer(Tcl_Time *timePtr) +{ + guint interval; + + if (tcl_timer_pending) + g_source_remove(tcl_timer); + + if (timePtr == NULL) { + tcl_timer_pending = FALSE; + return; + } + + interval = timePtr->sec * 1000 + (timePtr->usec ? timePtr->usec / 1000 : 0); + tcl_timer = g_timeout_add(interval, tcl_kick, NULL); + tcl_timer_pending = TRUE; +} + +static int tcl_wait_for_event(Tcl_Time *timePtr) +{ + if (!timePtr || (timePtr->sec == 0 && timePtr->usec == 0)) { + g_main_context_iteration(NULL, FALSE); + return 1; + } else { + tcl_set_timer(timePtr); + } + + g_main_context_iteration(NULL, TRUE); + + return 1; +} + +static void tcl_create_file_handler(int fd, int mask, Tcl_FileProc *proc, ClientData data) +{ + struct tcl_file_handler *tfh = g_new0(struct tcl_file_handler, 1); + GIOChannel *channel; + GIOCondition cond = 0; + + if (g_hash_table_lookup(tcl_file_handlers, GINT_TO_POINTER(fd))) + tcl_delete_file_handler(fd); + + if (mask & TCL_READABLE) + cond |= G_IO_IN; + if (mask & TCL_WRITABLE) + cond |= G_IO_OUT; + if (mask & TCL_EXCEPTION) + cond |= G_IO_ERR|G_IO_HUP|G_IO_NVAL; + + tfh->fd = fd; + tfh->mask = mask; + tfh->proc = proc; + tfh->data = data; + + channel = g_io_channel_unix_new(fd); + tfh->source = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, tcl_file_callback, tfh, g_free); + g_io_channel_unref(channel); + + g_hash_table_insert(tcl_file_handlers, GINT_TO_POINTER(fd), tfh); + + Tcl_ServiceAll(); +} + +static void tcl_delete_file_handler(int fd) +{ + struct tcl_file_handler *tfh = g_hash_table_lookup(tcl_file_handlers, GINT_TO_POINTER(fd)); + + if (tfh == NULL) + return; + + g_source_remove(tfh->source); + g_hash_table_remove(tcl_file_handlers, GINT_TO_POINTER(fd)); + + Tcl_ServiceAll(); +} + +static gboolean tcl_kick(gpointer data) +{ + tcl_timer_pending = FALSE; + + Tcl_ServiceAll(); + + return FALSE; +} + +static gboolean tcl_file_callback(GIOChannel *source, GIOCondition condition, gpointer data) +{ + struct tcl_file_handler *tfh = data; + struct tcl_file_event *fev; + int mask = 0; + + if (condition & G_IO_IN) + mask |= TCL_READABLE; + if (condition & G_IO_OUT) + mask |= TCL_WRITABLE; + if (condition & (G_IO_ERR|G_IO_HUP|G_IO_NVAL)) + mask |= TCL_EXCEPTION; + + if (!(tfh->mask & (mask & ~tfh->pending))) + return TRUE; + + tfh->pending |= mask; + fev = (struct tcl_file_event *)ckalloc(sizeof(struct tcl_file_event)); + memset(fev, 0, sizeof(struct tcl_file_event)); + fev->header.proc = tcl_file_event_callback; + fev->fd = tfh->fd; + Tcl_QueueEvent((Tcl_Event *)fev, TCL_QUEUE_TAIL); + + Tcl_ServiceAll(); + + return TRUE; +} + +int tcl_file_event_callback(Tcl_Event *event, int flags) +{ + struct tcl_file_handler *tfh; + struct tcl_file_event *fev = (struct tcl_file_event *)event; + int mask; + + if (!(flags & TCL_FILE_EVENTS)) { + return 0; + } + + tfh = g_hash_table_lookup(tcl_file_handlers, GINT_TO_POINTER(fev->fd)); + if (tfh == NULL) + return 1; + + mask = tfh->mask & tfh->pending; + if (mask) + (*tfh->proc)(tfh->data, mask); + tfh->pending = 0; + + return 1; +} diff --git a/libpurple/plugins/tcl/tcl_glib.h b/libpurple/plugins/tcl/tcl_glib.h new file mode 100644 index 0000000000..ebe18d89a1 --- /dev/null +++ b/libpurple/plugins/tcl/tcl_glib.h @@ -0,0 +1,29 @@ +/* + * Tcl/Glib glue + * + * Copyright (C) 2003 Ethan Blanton + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GAIM_TCL_GLIB_H_ +#define _GAIM_TCL_GLIB_H_ + +#include +#include + +void tcl_glib_init(void); + +#endif /* _GAIM_TCL_GLIB_H_ */ diff --git a/libpurple/plugins/tcl/tcl_ref.c b/libpurple/plugins/tcl/tcl_ref.c new file mode 100644 index 0000000000..340e68c72b --- /dev/null +++ b/libpurple/plugins/tcl/tcl_ref.c @@ -0,0 +1,152 @@ +/** + * @file tcl_ref.c Gaim Tcl typed references API + * + * gaim + * + * Copyright (C) 2006 Ethan Blanton + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "tcl_gaim.h" +#include "stringref.h" + +/* Instead of all that internal representation mumbo jumbo, use these + * macros to access the internal representation of a GaimTclRef */ +#define OBJ_REF_TYPE(obj) (obj->internalRep.twoPtrValue.ptr1) +#define OBJ_REF_VALUE(obj) (obj->internalRep.twoPtrValue.ptr2) + +static Tcl_FreeInternalRepProc gaim_tcl_ref_free; +static Tcl_DupInternalRepProc gaim_tcl_ref_dup; +static Tcl_UpdateStringProc gaim_tcl_ref_update; +static Tcl_SetFromAnyProc gaim_tcl_ref_set; + +static Tcl_ObjType gaim_tcl_ref = { + "GaimTclRef", + gaim_tcl_ref_free, + gaim_tcl_ref_dup, + gaim_tcl_ref_update, + gaim_tcl_ref_set +}; + +void gaim_tcl_ref_init() +{ + Tcl_RegisterObjType(&gaim_tcl_ref); +} + +void *gaim_tcl_ref_get(Tcl_Interp *interp, Tcl_Obj *obj, GaimStringref *type) +{ + if (obj->typePtr != &gaim_tcl_ref) { + if (Tcl_ConvertToType(interp, obj, &gaim_tcl_ref) != TCL_OK) + return NULL; + } + if (strcmp(gaim_stringref_value(OBJ_REF_TYPE(obj)), + gaim_stringref_value(type))) { + if (interp) { + Tcl_Obj *error = Tcl_NewStringObj("Bad Gaim reference type: expected ", -1); + Tcl_AppendToObj(error, gaim_stringref_value(type), -1); + Tcl_AppendToObj(error, " but got ", -1); + Tcl_AppendToObj(error, gaim_stringref_value(OBJ_REF_TYPE(obj)), -1); + Tcl_SetObjResult(interp, error); + } + return NULL; + } + return OBJ_REF_VALUE(obj); +} + +Tcl_Obj *gaim_tcl_ref_new(GaimStringref *type, void *value) +{ + Tcl_Obj *obj = Tcl_NewObj(); + obj->typePtr = &gaim_tcl_ref; + OBJ_REF_TYPE(obj) = gaim_stringref_ref(type); + OBJ_REF_VALUE(obj) = value; + Tcl_InvalidateStringRep(obj); + return obj; +} + +static void gaim_tcl_ref_free(Tcl_Obj *obj) +{ + gaim_stringref_unref(OBJ_REF_TYPE(obj)); +} + +static void gaim_tcl_ref_dup(Tcl_Obj *obj1, Tcl_Obj *obj2) +{ + OBJ_REF_TYPE(obj2) = gaim_stringref_ref(OBJ_REF_TYPE(obj1)); + OBJ_REF_VALUE(obj2) = OBJ_REF_VALUE(obj1); +} + +static void gaim_tcl_ref_update(Tcl_Obj *obj) +{ + /* This is ugly on memory, but we pretty much have to either + * do this or guesstimate lengths or introduce a varargs + * function in here ... ugh. */ + char *bytes = g_strdup_printf("gaim-%s:%p", + gaim_stringref_value(OBJ_REF_TYPE(obj)), + OBJ_REF_VALUE(obj)); + + obj->length = strlen(bytes); + obj->bytes = ckalloc(obj->length + 1); + strcpy(obj->bytes, bytes); + g_free(bytes); +} + +/* This isn't as memory-efficient as setting could be, because we + * essentially have to synthesize the Stringref here, where we would + * really rather dup it. Oh, well. */ +static int gaim_tcl_ref_set(Tcl_Interp *interp, Tcl_Obj *obj) +{ + char *bytes = Tcl_GetStringFromObj(obj, NULL); + char *ptr; + GaimStringref *type; + void *value; + + if (strlen(bytes) < 7 + || strncmp(bytes, "gaim-", 5) + || (ptr = strchr(bytes, ':')) == NULL + || (ptr - bytes) == 5) + goto badobject; + + /* Bad Ethan */ + *ptr = '\0'; + type = gaim_stringref_new(bytes + 5); + *ptr = ':'; + ptr++; + + if (sscanf(ptr, "%p", &value) == 0) { + gaim_stringref_unref(type); + goto badobject; + } + + /* At this point we know we have a good object; free the old and + * install our internal representation. */ + if (obj->typePtr != NULL && obj->typePtr->freeIntRepProc != NULL) + obj->typePtr->freeIntRepProc(obj); + + obj->typePtr = &gaim_tcl_ref; + OBJ_REF_TYPE(obj) = type; + OBJ_REF_VALUE(obj) = value; + + return TCL_OK; + +badobject: + if (interp) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj("invalid GaimTclRef representation", -1)); + } + return TCL_ERROR; +} diff --git a/libpurple/plugins/tcl/tcl_signals.c b/libpurple/plugins/tcl/tcl_signals.c new file mode 100644 index 0000000000..08c1ea105d --- /dev/null +++ b/libpurple/plugins/tcl/tcl_signals.c @@ -0,0 +1,396 @@ +/** + * @file tcl_signals.c Gaim Tcl signal API + * + * gaim + * + * Copyright (C) 2003 Ethan Blanton + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include + +#include "tcl_gaim.h" + +#include "internal.h" +#include "connection.h" +#include "conversation.h" +#include "signals.h" +#include "debug.h" +#include "value.h" +#include "core.h" + +static GList *tcl_callbacks; + +static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler); +static Tcl_Obj *new_cb_namespace (void); + +void tcl_signal_init() +{ + tcl_callbacks = NULL; +} + +void tcl_signal_handler_free(struct tcl_signal_handler *handler) +{ + if (handler == NULL) + return; + + Tcl_DecrRefCount(handler->signal); + if (handler->namespace) + Tcl_DecrRefCount(handler->namespace); + g_free(handler); +} + +void tcl_signal_cleanup(Tcl_Interp *interp) +{ + GList *cur; + struct tcl_signal_handler *handler; + + for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) { + handler = cur->data; + if (handler->interp == interp) { + tcl_signal_handler_free(handler); + cur->data = NULL; + } + } + tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); +} + +gboolean tcl_signal_connect(struct tcl_signal_handler *handler) +{ + GString *proc; + + gaim_signal_get_values(handler->instance, + Tcl_GetString(handler->signal), + &handler->returntype, &handler->nargs, + &handler->argtypes); + + tcl_signal_disconnect(handler->interp, Tcl_GetString(handler->signal), + handler->interp); + + if (!gaim_signal_connect_vargs(handler->instance, + Tcl_GetString(handler->signal), + (void *)handler->interp, + GAIM_CALLBACK(tcl_signal_callback), + (void *)handler)) + return FALSE; + + handler->namespace = new_cb_namespace (); + Tcl_IncrRefCount(handler->namespace); + proc = g_string_new(""); + g_string_append_printf(proc, "namespace eval %s { proc cb { %s } { %s } }", + Tcl_GetString(handler->namespace), + Tcl_GetString(handler->args), + Tcl_GetString(handler->proc)); + if (Tcl_Eval(handler->interp, proc->str) != TCL_OK) { + Tcl_DecrRefCount(handler->namespace); + g_string_free(proc, TRUE); + return FALSE; + } + g_string_free(proc, TRUE); + + tcl_callbacks = g_list_append(tcl_callbacks, (gpointer)handler); + + return TRUE; +} + +void tcl_signal_disconnect(void *instance, const char *signal, Tcl_Interp *interp) +{ + GList *cur; + struct tcl_signal_handler *handler; + gboolean found = FALSE; + GString *cmd; + + for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) { + handler = cur->data; + if (handler->interp == interp && handler->instance == instance + && !strcmp(signal, Tcl_GetString(handler->signal))) { + gaim_signal_disconnect(instance, signal, handler->interp, + GAIM_CALLBACK(tcl_signal_callback)); + cmd = g_string_sized_new(64); + g_string_printf(cmd, "namespace delete %s", + Tcl_GetString(handler->namespace)); + Tcl_EvalEx(interp, cmd->str, -1, TCL_EVAL_GLOBAL); + tcl_signal_handler_free(handler); + g_string_free(cmd, TRUE); + cur->data = NULL; + found = TRUE; + break; + } + } + if (found) + tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); +} + +static GaimStringref *ref_type(GaimSubType type) +{ + switch (type) { + case GAIM_SUBTYPE_ACCOUNT: + return GaimTclRefAccount; + case GAIM_SUBTYPE_CONNECTION: + return GaimTclRefConnection; + case GAIM_SUBTYPE_CONVERSATION: + return GaimTclRefConversation; + case GAIM_SUBTYPE_PLUGIN: + return GaimTclRefPlugin; + case GAIM_SUBTYPE_STATUS: + return GaimTclRefStatus; + case GAIM_SUBTYPE_XFER: + return GaimTclRefXfer; + default: + return NULL; + } +} + +static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler) +{ + GString *name, *val; + GaimBlistNode *node; + int error, i; + void *retval = NULL; + Tcl_Obj *cmd, *arg, *result; + void **vals; /* Used for inout parameters */ + char ***strs; + + vals = g_new0(void *, handler->nargs); + strs = g_new0(char **, handler->nargs); + name = g_string_sized_new(32); + val = g_string_sized_new(32); + + cmd = Tcl_NewListObj(0, NULL); + Tcl_IncrRefCount(cmd); + + arg = Tcl_DuplicateObj(handler->namespace); + Tcl_AppendStringsToObj(arg, "::cb", NULL); + Tcl_ListObjAppendElement(handler->interp, cmd, arg); + + for (i = 0; i < handler->nargs; i++) { + if (gaim_value_is_outgoing(handler->argtypes[i])) + g_string_printf(name, "%s::arg%d", + Tcl_GetString(handler->namespace), i); + + switch(gaim_value_get_type(handler->argtypes[i])) { + case GAIM_TYPE_UNKNOWN: /* What? I guess just pass the word ... */ + /* treat this as a pointer, but complain first */ + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "unknown GaimValue type %d\n", + gaim_value_get_type(handler->argtypes[i])); + case GAIM_TYPE_POINTER: + case GAIM_TYPE_OBJECT: + case GAIM_TYPE_BOXED: + /* These are all "pointer" types to us */ + if (gaim_value_is_outgoing(handler->argtypes[i])) + gaim_debug_error("tcl", "pointer types do not currently support outgoing arguments\n"); + arg = gaim_tcl_ref_new(GaimTclRefPointer, va_arg(args, void *)); + break; + case GAIM_TYPE_BOOLEAN: + if (gaim_value_is_outgoing(handler->argtypes[i])) { + vals[i] = va_arg(args, gboolean *); + Tcl_LinkVar(handler->interp, name->str, + (char *)&vals[i], TCL_LINK_BOOLEAN); + arg = Tcl_NewStringObj(name->str, -1); + } else { + arg = Tcl_NewBooleanObj(va_arg(args, gboolean)); + } + break; + case GAIM_TYPE_CHAR: + case GAIM_TYPE_UCHAR: + case GAIM_TYPE_SHORT: + case GAIM_TYPE_USHORT: + case GAIM_TYPE_INT: + case GAIM_TYPE_UINT: + case GAIM_TYPE_LONG: + case GAIM_TYPE_ULONG: + case GAIM_TYPE_ENUM: + /* I should really cast these individually to + * preserve as much information as possible ... + * but heh */ + if (gaim_value_is_outgoing(handler->argtypes[i])) { + vals[i] = va_arg(args, int *); + Tcl_LinkVar(handler->interp, name->str, + vals[i], TCL_LINK_INT); + arg = Tcl_NewStringObj(name->str, -1); + } else { + arg = Tcl_NewIntObj(va_arg(args, int)); + } + break; + case GAIM_TYPE_INT64: + case GAIM_TYPE_UINT64: + /* Tcl < 8.4 doesn't have wide ints, so we have ugly + * ifdefs in here */ + if (gaim_value_is_outgoing(handler->argtypes[i])) { + vals[i] = (void *)va_arg(args, gint64 *); + #if (TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 4) + Tcl_LinkVar(handler->interp, name->str, + vals[i], TCL_LINK_WIDE_INT); + #else + /* This is going to cause weirdness at best, + * but what do you want ... we're losing + * precision */ + Tcl_LinkVar(handler->interp, name->str, + vals[i], TCL_LINK_INT); + #endif /* Tcl >= 8.4 */ + arg = Tcl_NewStringObj(name->str, -1); + } else { + #if (TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 4) + arg = Tcl_NewWideIntObj(va_arg(args, gint64)); + #else + arg = Tcl_NewIntObj((int)va_arg(args, int)); + #endif /* Tcl >= 8.4 */ + } + break; + case GAIM_TYPE_STRING: + if (gaim_value_is_outgoing(handler->argtypes[i])) { + strs[i] = va_arg(args, char **); + if (strs[i] == NULL || *strs[i] == NULL) { + vals[i] = ckalloc(1); + *(char *)vals[i] = '\0'; + } else { + vals[i] = ckalloc(strlen(*strs[i]) + 1); + strcpy(vals[i], *strs[i]); + } + Tcl_LinkVar(handler->interp, name->str, + (char *)&vals[i], TCL_LINK_STRING); + arg = Tcl_NewStringObj(name->str, -1); + } else { + arg = Tcl_NewStringObj(va_arg(args, char *), -1); + } + break; + case GAIM_TYPE_SUBTYPE: + switch (gaim_value_get_subtype(handler->argtypes[i])) { + case GAIM_SUBTYPE_UNKNOWN: + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "subtype unknown\n"); + case GAIM_SUBTYPE_ACCOUNT: + case GAIM_SUBTYPE_CONNECTION: + case GAIM_SUBTYPE_CONVERSATION: + case GAIM_SUBTYPE_STATUS: + case GAIM_SUBTYPE_PLUGIN: + case GAIM_SUBTYPE_XFER: + if (gaim_value_is_outgoing(handler->argtypes[i])) + gaim_debug_error("tcl", "pointer subtypes do not currently support outgoing arguments\n"); + arg = gaim_tcl_ref_new(ref_type(gaim_value_get_subtype(handler->argtypes[i])), va_arg(args, void *)); + break; + case GAIM_SUBTYPE_BLIST: + case GAIM_SUBTYPE_BLIST_BUDDY: + case GAIM_SUBTYPE_BLIST_GROUP: + case GAIM_SUBTYPE_BLIST_CHAT: + /* We're going to switch again for code-deduping */ + if (gaim_value_is_outgoing(handler->argtypes[i])) + node = *va_arg(args, GaimBlistNode **); + else + node = va_arg(args, GaimBlistNode *); + switch (node->type) { + case GAIM_BLIST_GROUP_NODE: + arg = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(handler->interp, arg, + Tcl_NewStringObj("group", -1)); + Tcl_ListObjAppendElement(handler->interp, arg, + Tcl_NewStringObj(((GaimGroup *)node)->name, -1)); + break; + case GAIM_BLIST_CONTACT_NODE: + /* g_string_printf(val, "contact {%s}", Contact Name? ); */ + arg = Tcl_NewStringObj("contact", -1); + break; + case GAIM_BLIST_BUDDY_NODE: + arg = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(handler->interp, arg, + Tcl_NewStringObj("buddy", -1)); + Tcl_ListObjAppendElement(handler->interp, arg, + Tcl_NewStringObj(((GaimBuddy *)node)->name, -1)); + Tcl_ListObjAppendElement(handler->interp, arg, + gaim_tcl_ref_new(GaimTclRefAccount, + ((GaimBuddy *)node)->account)); + break; + case GAIM_BLIST_CHAT_NODE: + arg = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(handler->interp, arg, + Tcl_NewStringObj("chat", -1)); + Tcl_ListObjAppendElement(handler->interp, arg, + Tcl_NewStringObj(((GaimChat *)node)->alias, -1)); + Tcl_ListObjAppendElement(handler->interp, arg, + gaim_tcl_ref_new(GaimTclRefAccount, + ((GaimChat *)node)->account)); + break; + case GAIM_BLIST_OTHER_NODE: + arg = Tcl_NewStringObj("other", -1); + break; + } + break; + } + } + Tcl_ListObjAppendElement(handler->interp, cmd, arg); + } + + /* Call the friggin' procedure already */ + if ((error = Tcl_EvalObjEx(handler->interp, cmd, TCL_EVAL_GLOBAL)) != TCL_OK) { + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "error evaluating callback: %s\n", + Tcl_GetString(Tcl_GetObjResult(handler->interp))); + } else { + result = Tcl_GetObjResult(handler->interp); + /* handle return values -- strings and words only */ + if (handler->returntype) { + if (gaim_value_get_type(handler->returntype) == GAIM_TYPE_STRING) { + retval = (void *)g_strdup(Tcl_GetString(result)); + } else { + if ((error = Tcl_GetIntFromObj(handler->interp, result, (int *)&retval)) != TCL_OK) { + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Error retrieving procedure result: %s\n", + Tcl_GetString(Tcl_GetObjResult(handler->interp))); + retval = NULL; + } + } + } + } + + /* And finally clean up */ + for (i = 0; i < handler->nargs; i++) { + g_string_printf(name, "%s::arg%d", + Tcl_GetString(handler->namespace), i); + if (gaim_value_is_outgoing(handler->argtypes[i]) + && gaim_value_get_type(handler->argtypes[i]) != GAIM_TYPE_SUBTYPE) + Tcl_UnlinkVar(handler->interp, name->str); + + /* We basically only have to deal with strings on the + * way out */ + switch (gaim_value_get_type(handler->argtypes[i])) { + case GAIM_TYPE_STRING: + if (gaim_value_is_outgoing(handler->argtypes[i])) { + if (vals[i] != NULL && *(char **)vals[i] != NULL) { + g_free(*strs[i]); + *strs[i] = g_strdup(vals[i]); + } + ckfree(vals[i]); + } + break; + default: + /* nothing */ + ; + } + } + + g_string_free(name, TRUE); + g_string_free(val, TRUE); + g_free(vals); + g_free(strs); + + return retval; +} + +static Tcl_Obj *new_cb_namespace () +{ + static int cbnum; + char name[32]; + + g_snprintf (name, sizeof(name), "::gaim::_callback::cb_%d", cbnum++); + return Tcl_NewStringObj (name, -1); +} diff --git a/libpurple/plugins/test.pl b/libpurple/plugins/test.pl new file mode 100644 index 0000000000..eea02a46e0 --- /dev/null +++ b/libpurple/plugins/test.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl -w + +use Gaim; + +%PLUGIN_INFO = ( + perl_api_version => 2, + name => 'Test Perl Plugin', + version => '1.0', + summary => 'Provides as a test base for the perl plugin.', + description => 'Provides as a test base for the perl plugin.', + author => 'Christian Hammond ', + url => 'http://gaim.sf.net/', + + load => "plugin_load", + unload => "plugin_unload" +); + +sub account_away_cb { + Gaim::debug_info("perl test plugin", "In account_away_cb\n"); + + my ($account, $state, $message, $data) = @_; + + Gaim::debug_info("perl test plugin", "Account " . + $account->get_username() . " went away.\n"); + Gaim::debug_info("perl test plugin", $data . "\n"); +} + +sub plugin_init { + return %PLUGIN_INFO; +} + +sub plugin_load { + Gaim::debug_info("perl test plugin", "plugin_load\n"); + my $plugin = shift; + + Gaim::debug_info("perl test plugin", "Listing accounts.\n"); + foreach $account (Gaim::accounts()) { + Gaim::debug_info("perl test plugin", $account->get_username() . "\n"); + } + + Gaim::debug_info("perl test plugin", "Listing buddy list.\n"); + foreach $group (Gaim::BuddyList::groups()) { + Gaim::debug_info("perl test plugin", + $group->get_name() . ":\n"); + + foreach $buddy ($group->buddies()) { + Gaim::debug_info("perl test plugin", + " " . $buddy->get_name() . "\n"); + } + } + + Gaim::signal_connect(Gaim::Accounts::handle, "account-away", + $plugin, \&account_away_cb, "test"); +} + +sub plugin_unload { + my $plugin = shift; +} -- cgit v1.2.1