summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStu Tomlinson <nosnilmot@pidgin.im>2007-06-10 01:46:31 +0000
committerStu Tomlinson <nosnilmot@pidgin.im>2007-06-10 01:46:31 +0000
commitef07ec6aad2d6fb99b51ff431a63fd02bafd6c31 (patch)
tree22bf5c2b4334d698ea09f6d62815489ee136b5da
parent22e92bb4ca5808417869d0be5efc18456ad38338 (diff)
parentdc021d935d2c5a07a979f5bffe35b508e8eb0f9a (diff)
downloadpidgin-ef07ec6aad2d6fb99b51ff431a63fd02bafd6c31.tar.gz
propagate from branch 'im.pidgin.pidgin' (head 33ee7eaab74ba7d307aa75fb1e1836fa5c0e91a8)
to branch 'im.pidgin.pidgin.2.1.0' (head 77cd035b22311abd469fb735229589c3d9b2aad6)
-rw-r--r--COPYRIGHT1
-rw-r--r--ChangeLog1
-rw-r--r--configure.ac78
-rw-r--r--libpurple/connection.c2
-rw-r--r--libpurple/dbus-server.c2
-rw-r--r--libpurple/plugins/ssl/ssl-nss.c5
-rw-r--r--libpurple/protocols/Makefile.am2
-rw-r--r--libpurple/protocols/bonjour/bonjour.c7
-rw-r--r--libpurple/protocols/bonjour/buddy.c7
-rw-r--r--libpurple/protocols/bonjour/jabber.c641
-rw-r--r--libpurple/protocols/bonjour/jabber.h14
-rw-r--r--libpurple/protocols/bonjour/mdns_common.c2
-rw-r--r--libpurple/protocols/bonjour/mdns_types.h2
-rw-r--r--libpurple/protocols/bonjour/mdns_win32.c2
-rw-r--r--libpurple/protocols/jabber/Makefile.am7
-rw-r--r--libpurple/protocols/oscar/Makefile.am11
-rw-r--r--libpurple/protocols/oscar/oscar.c2
-rw-r--r--libpurple/protocols/silc/README12
-rw-r--r--libpurple/protocols/silc/TODO8
-rw-r--r--libpurple/protocols/silc/buddy.c646
-rw-r--r--libpurple/protocols/silc/chat.c306
-rw-r--r--libpurple/protocols/silc/ft.c153
-rw-r--r--libpurple/protocols/silc/ops.c1120
-rw-r--r--libpurple/protocols/silc/pk.c104
-rw-r--r--libpurple/protocols/silc/silc.c675
-rw-r--r--libpurple/protocols/silc/silcpurple.h32
-rw-r--r--libpurple/protocols/silc/util.c105
-rw-r--r--libpurple/protocols/silc/wb.c38
-rw-r--r--libpurple/protocols/silc10/Makefile.am36
-rw-r--r--libpurple/protocols/silc10/Makefile.mingw91
-rw-r--r--libpurple/protocols/silc10/README31
-rw-r--r--libpurple/protocols/silc10/TODO8
-rw-r--r--libpurple/protocols/silc10/buddy.c1728
-rw-r--r--libpurple/protocols/silc10/chat.c1456
-rw-r--r--libpurple/protocols/silc10/ft.c412
-rw-r--r--libpurple/protocols/silc10/ops.c2057
-rw-r--r--libpurple/protocols/silc10/pk.c274
-rw-r--r--libpurple/protocols/silc10/silc.c1914
-rw-r--r--libpurple/protocols/silc10/silcpurple.h173
-rw-r--r--libpurple/protocols/silc10/util.c771
-rw-r--r--libpurple/protocols/silc10/wb.c517
-rw-r--r--libpurple/protocols/silc10/wb.h49
-rw-r--r--libpurple/protocols/simple/simple.c2
-rw-r--r--libpurple/win32/libc_interface.c20
-rw-r--r--pidgin/gtkaccount.c21
-rw-r--r--po/POTFILES.in8
46 files changed, 11650 insertions, 1903 deletions
diff --git a/COPYRIGHT b/COPYRIGHT
index 815932fa9e..d150e49f98 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -228,6 +228,7 @@ Fidel Martinez
Lalo Martins
John Matthews
Simo Mattila
+Michal Matyska
Ryan McCabe
Peter McCurdy
Kurt McKee
diff --git a/ChangeLog b/ChangeLog
index d693ea56d9..ece3cc030b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -31,6 +31,7 @@ version 2.0.2 (??/??/????):
* Remove MSN's random "Authorization Failed" dialogs
* Fix MSN to correctly detect incorrect passwords and disable the account
* Get User Info on MSN is now more reliable & accurate
+ * Updated SILC protocol to support SILC Toolkit 1.1 (Pekka Riikonen)
Finch:
* Auto account reconnecting
diff --git a/configure.ac b/configure.ac
index 11bc366abd..7d9bc96ad3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -647,13 +647,14 @@ AC_ARG_WITH(silc-includes, [AC_HELP_STRING([--with-silc-includes=DIR], [compile
AC_ARG_WITH(silc-libs, [AC_HELP_STRING([--with-silc-libs=DIR], [compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"])
SILC_CFLAGS=""
SILC_LIBS=""
+have_silc="no"
if test -n "$with_silc_includes" || test -n "$with_silc_libs"; then
silc_manual_check="yes"
else
silc_manual_check="no"
fi
if test "x$silc_manual_check" = "xno"; then
- PKG_CHECK_MODULES(SILC, silcclient, [
+ PKG_CHECK_MODULES(SILC, [silcclient >= 1.1], [
have_silc="yes"
silcincludes="yes"
silcclient="yes"
@@ -661,16 +662,26 @@ if test "x$silc_manual_check" = "xno"; then
AC_MSG_RESULT(no)
have_silc="no"
])
- dnl If silcclient.pc wasn't found, check for just silc.pc
if test "x$have_silc" = "xno"; then
- PKG_CHECK_MODULES(SILC, silc, [
+ PKG_CHECK_MODULES(SILC, silcclient, [
have_silc="yes"
- silcincludes="yes"
- silcclient="yes"
+ silc10includes="yes"
+ silc10client="yes"
], [
AC_MSG_RESULT(no)
have_silc="no"
])
+ dnl If silcclient.pc wasn't found, check for just silc.pc
+ if test "x$have_silc" = "xno"; then
+ PKG_CHECK_MODULES(SILC, silc, [
+ have_silc="yes"
+ silc10includes="yes"
+ silc10client="yes"
+ ], [
+ AC_MSG_RESULT(no)
+ have_silc="no"
+ ])
+ fi
fi
else
if test "$ac_silc_includes" != "no"; then
@@ -678,7 +689,7 @@ else
fi
CPPFLAGS_save="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
- AC_CHECK_HEADER(silcincludes.h, [silcincludes=yes])
+ AC_CHECK_HEADER(silc.h, [silcincludes=yes])
CPPFLAGS="$CPPFLAGS_save"
if test "$ac_silc_libs" != "no"; then
@@ -686,11 +697,28 @@ else
fi
SILC_LIBS="$SILC_LIBS -lsilc -lsilcclient -lpthread $LIBDL"
AC_CHECK_LIB(silcclient, silc_client_init, [silcclient=yes], , $SILC_LIBS)
+
+ if test "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes"; then
+ have_silc="yes"
+ else
+ CPPFLAGS_save="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
+ AC_CHECK_HEADER(silcincludes.h, [silc10includes=yes])
+ CPPFLAGS="$CPPFLAGS_save"
+
+ SILC_LIBS="$SILC_LIBS -lsilc -lsilcclient -lpthread $LIBDL"
+ AC_CHECK_LIB(silcclient, silc_client_init, [silc10client=yes], , $SILC_LIBS)
+ if test "x$silc10includes" = "xyes" -a "x$silc10client" = "xyes"; then
+ have_silc="yes"
+ fi
+ fi
fi
AC_SUBST(SILC_LIBS)
AC_SUBST(SILC_CFLAGS)
dnl SILC Toolkit >= 1.0.1 has a new MIME API
if test "x$silcclient" = "xyes"; then
+ AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h])
+elif test "x$silc10client" = "xyes"; then
CPPFLAGS_save="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
AC_MSG_CHECKING(for silcmime.h)
@@ -795,7 +823,10 @@ if test "x$howlincludes" != "xyes" -o "x$howllibs" != "xyes"; then
STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'`
fi
if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
- STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc//'`
+ STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc/silc10/'`
+fi
+if test "x$silc10includes" != "xyes" -o "x$silc10client" != "xyes"; then
+ STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc10//'`
fi
AC_SUBST(STATIC_PRPLS)
STATIC_LINK_LIBS=
@@ -803,13 +834,24 @@ extern_init=
load_proto=
for i in $STATIC_PRPLS ; do
dnl Ugly special case for "libsilcpurple.a":
- if test "x$i" = "xsilc"; then
- STATIC_LINK_LIBS="$STATIC_LINK_LIBS protocols/$i/lib${i}purple.a"
+ dnl ... and Ugly special case for multi-protocol oscar
+ if test \( "x$i" = "xoscar" -o "x$i" = "xaim" -o "x$i" = "xicq" \) -a "x$static_oscar" != "xyes"; then
+ STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/oscar/liboscar.a"
+ extern_init="$extern_init extern gboolean purple_init_aim_plugin();"
+ extern_init="$extern_init extern gboolean purple_init_icq_plugin();"
+ load_proto="$load_proto purple_init_aim_plugin();"
+ load_proto="$load_proto purple_init_icq_plugin();"
else
- STATIC_LINK_LIBS="$STATIC_LINK_LIBS protocols/$i/lib$i.a"
+ if test "x$i" = "xsilc"; then
+ STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib${i}purple.a"
+ elif test "x$i" = "xsilc10"; then
+ STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libsilcpurple.a"
+ else
+ STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.a"
+ fi
+ extern_init="$extern_init extern gboolean purple_init_${i}_plugin();"
+ load_proto="$load_proto purple_init_${i}_plugin();"
fi
- extern_init="$extern_init extern gboolean purple_init_${i}_plugin();"
- load_proto="$load_proto purple_init_${i}_plugin();"
case $i in
bonjour) static_bonjour=yes ;;
gg) static_gg=yes ;;
@@ -823,6 +865,7 @@ for i in $STATIC_PRPLS ; do
qq) static_qq=yes ;;
sametime) static_sametime=yes ;;
silc) static_silc=yes ;;
+ silc10) static_silc=yes ;;
simple) static_simple=yes ;;
toc) static_toc=yes ;;
yahoo) static_yahoo=yes ;;
@@ -839,7 +882,7 @@ AM_CONDITIONAL(STATIC_NOVELL, test "x$static_novell" = "xyes")
AM_CONDITIONAL(STATIC_OSCAR, test "x$static_oscar" = "xyes")
AM_CONDITIONAL(STATIC_QQ, test "x$static_qq" = "xyes")
AM_CONDITIONAL(STATIC_SAMETIME, test "x$static_sametime" = "xyes" -a "x$have_meanwhile" = "xyes")
-AM_CONDITIONAL(STATIC_SILC, test "x$static_silc" = "xyes" -a "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes")
+AM_CONDITIONAL(STATIC_SILC, test "x$static_silc" = "xyes" -a "x$have_silc" = "xyes")
AM_CONDITIONAL(STATIC_SIMPLE, test "x$static_simple" = "xyes")
AM_CONDITIONAL(STATIC_TOC, test "x$static_toc" = "xyes")
AM_CONDITIONAL(STATIC_YAHOO, test "x$static_yahoo" = "xyes")
@@ -859,7 +902,10 @@ if test "x$howlincludes" != "xyes" -o "x$howllibs" != "xyes"; then
DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'`
fi
if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
- DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc//'`
+ DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc/silc10/'`
+fi
+if test "x$silc10includes" != "xyes" -o "x$silc10client" != "xyes"; then
+ DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc10//'`
fi
AC_SUBST(DYNAMIC_PRPLS)
for i in $DYNAMIC_PRPLS ; do
@@ -876,6 +922,7 @@ for i in $DYNAMIC_PRPLS ; do
qq) dynamic_qq=yes ;;
sametime) dynamic_sametime=yes ;;
silc) dynamic_silc=yes ;;
+ silc10) dynamic_silc=yes ;;
simple) dynamic_simple=yes ;;
toc) dynamic_toc=yes ;;
yahoo) dynamic_yahoo=yes ;;
@@ -892,7 +939,7 @@ AM_CONDITIONAL(DYNAMIC_NOVELL, test "x$dynamic_novell" = "xyes")
AM_CONDITIONAL(DYNAMIC_OSCAR, test "x$dynamic_oscar" = "xyes")
AM_CONDITIONAL(DYNAMIC_QQ, test "x$dynamic_qq" = "xyes")
AM_CONDITIONAL(DYNAMIC_SAMETIME, test "x$dynamic_sametime" = "xyes" -a "x$have_meanwhile" = "xyes")
-AM_CONDITIONAL(DYNAMIC_SILC, test "x$dynamic_silc" = "xyes" -a "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes")
+AM_CONDITIONAL(DYNAMIC_SILC, test "x$dynamic_silc" = "xyes" -a "x$have_silc" = "xyes")
AM_CONDITIONAL(DYNAMIC_SIMPLE, test "x$dynamic_simple" = "xyes")
AM_CONDITIONAL(DYNAMIC_TOC, test "x$dynamic_toc" = "xyes")
AM_CONDITIONAL(DYNAMIC_YAHOO, test "x$dynamic_yahoo" = "xyes")
@@ -2091,6 +2138,7 @@ AC_OUTPUT([Makefile
libpurple/protocols/qq/Makefile
libpurple/protocols/sametime/Makefile
libpurple/protocols/silc/Makefile
+ libpurple/protocols/silc10/Makefile
libpurple/protocols/simple/Makefile
libpurple/protocols/toc/Makefile
libpurple/protocols/yahoo/Makefile
diff --git a/libpurple/connection.c b/libpurple/connection.c
index a09e917ae6..754c94fd00 100644
--- a/libpurple/connection.c
+++ b/libpurple/connection.c
@@ -436,7 +436,7 @@ purple_connection_error(PurpleConnection *gc, const char *text)
g_return_if_fail(gc != NULL);
if (text == NULL) {
- g_critical("purple_connection_error: check `text != NULL' failed");
+ purple_debug_error("connection", "purple_connection_error: check `text != NULL' failed");
text = _("Unknown error");
}
diff --git a/libpurple/dbus-server.c b/libpurple/dbus-server.c
index 8ea11a8ee0..bf4ea6eb15 100644
--- a/libpurple/dbus-server.c
+++ b/libpurple/dbus-server.c
@@ -121,7 +121,7 @@ purple_dbus_pointer_to_id(gconstpointer node)
{
purple_debug_warning("dbus",
"Need to register an object with the dbus subsystem.\n");
- g_return_val_if_reached(0);
+ return 0;
}
return id;
}
diff --git a/libpurple/plugins/ssl/ssl-nss.c b/libpurple/plugins/ssl/ssl-nss.c
index 21e422870c..a0dcd45ce5 100644
--- a/libpurple/plugins/ssl/ssl-nss.c
+++ b/libpurple/plugins/ssl/ssl-nss.c
@@ -32,7 +32,6 @@
#undef HAVE_LONG_LONG /* Make Mozilla less angry. If angry, Mozilla SMASH! */
#include <nspr.h>
-#include <private/pprio.h>
#include <nss.h>
#include <pk11func.h>
#include <prio.h>
@@ -42,6 +41,10 @@
#include <sslerr.h>
#include <sslproto.h>
+/* This is defined in NSPR's <private/pprio.h>, but to avoid including a
+ * private header we duplicate the prototype here */
+NSPR_API(PRFileDesc*) PR_ImportTCPSocket(PRInt32 osfd);
+
typedef struct
{
PRFileDesc *fd;
diff --git a/libpurple/protocols/Makefile.am b/libpurple/protocols/Makefile.am
index c9097fca71..8377c773eb 100644
--- a/libpurple/protocols/Makefile.am
+++ b/libpurple/protocols/Makefile.am
@@ -1,5 +1,5 @@
EXTRA_DIST = Makefile.mingw
-DIST_SUBDIRS = bonjour gg irc jabber msn novell null oscar qq sametime silc toc simple yahoo zephyr
+DIST_SUBDIRS = bonjour gg irc jabber msn novell null oscar qq sametime silc silc10 toc simple yahoo zephyr
SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS)
diff --git a/libpurple/protocols/bonjour/bonjour.c b/libpurple/protocols/bonjour/bonjour.c
index d4e24b266c..5d2c2c9e53 100644
--- a/libpurple/protocols/bonjour/bonjour.c
+++ b/libpurple/protocols/bonjour/bonjour.c
@@ -267,6 +267,7 @@ static void
bonjour_convo_closed(PurpleConnection *connection, const char *who)
{
PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
+ BonjourBuddy *bb;
if (buddy == NULL)
{
@@ -277,7 +278,9 @@ bonjour_convo_closed(PurpleConnection *connection, const char *who)
return;
}
- bonjour_jabber_close_conversation(((BonjourData*)(connection->proto_data))->jabber_data, buddy);
+ bb = buddy->proto_data;
+ bonjour_jabber_close_conversation(bb->conversation);
+ bb->conversation = NULL;
}
static char *
@@ -320,6 +323,8 @@ bonjour_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboole
static gboolean
plugin_unload(PurplePlugin *plugin)
{
+ /* These shouldn't happen here because they are allocated in _init() */
+
g_free(default_firstname);
g_free(default_lastname);
g_free(default_hostname);
diff --git a/libpurple/protocols/bonjour/buddy.c b/libpurple/protocols/bonjour/buddy.c
index b5d79f324d..e6c04c447b 100644
--- a/libpurple/protocols/bonjour/buddy.c
+++ b/libpurple/protocols/bonjour/buddy.c
@@ -178,11 +178,8 @@ bonjour_buddy_delete(BonjourBuddy *buddy)
g_free(buddy->node);
g_free(buddy->ver);
- if (buddy->conversation != NULL)
- {
- g_free(buddy->conversation->buddy_name);
- g_free(buddy->conversation);
- }
+ bonjour_jabber_close_conversation(buddy->conversation);
+ buddy->conversation = NULL;
#ifdef USE_BONJOUR_APPLE
if (buddy->txt_query != NULL)
diff --git a/libpurple/protocols/bonjour/jabber.c b/libpurple/protocols/bonjour/jabber.c
index 64218ee9f5..f0fcbe7979 100644
--- a/libpurple/protocols/bonjour/jabber.c
+++ b/libpurple/protocols/bonjour/jabber.c
@@ -28,7 +28,6 @@
#endif
#include <sys/types.h>
#include <glib.h>
-#include <glib/gprintf.h>
#include <unistd.h>
#include <fcntl.h>
@@ -45,39 +44,10 @@
#include "bonjour.h"
#include "buddy.h"
-static gint
-_connect_to_buddy(PurpleBuddy *gb)
-{
- gint socket_fd;
- struct sockaddr_in buddy_address;
- BonjourBuddy *bb = gb->proto_data;
-
- purple_debug_info("bonjour", "Connecting to buddy %s at %s:%d.\n",
- purple_buddy_get_name(gb), bb->ip ? bb->ip : "(null)", bb->port_p2pj);
-
- /* Create a socket and make it non-blocking */
- socket_fd = socket(PF_INET, SOCK_STREAM, 0);
- if (socket_fd < 0) {
- purple_debug_warning("bonjour", "Error opening socket: %s\n", strerror(errno));
- return -1;
- }
-
- buddy_address.sin_family = PF_INET;
- buddy_address.sin_port = htons(bb->port_p2pj);
- inet_aton(bb->ip, &(buddy_address.sin_addr));
- memset(&(buddy_address.sin_zero), '\0', 8);
-
- /* TODO: make this nonblocking before connecting */
- if (connect(socket_fd, (struct sockaddr*)&buddy_address, sizeof(struct sockaddr)) == 0)
- fcntl(socket_fd, F_SETFL, O_NONBLOCK);
- else {
- purple_debug_warning("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n", purple_buddy_get_name(gb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, strerror(errno));
- close(socket_fd);
- socket_fd = -1;
- }
-
- return socket_fd;
-}
+#define STREAM_END "</stream:stream>"
+/* TODO: specify version='1.0' and send stream features */
+#define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
+ "<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
#if 0 /* this isn't used anywhere... */
static const char *
@@ -104,6 +74,19 @@ _font_size_purple_to_ichat(int size)
}
#endif
+static BonjourJabberConversation *
+bonjour_jabber_conv_new() {
+
+ BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1);
+ bconv->socket = -1;
+ bconv->tx_buf = purple_circ_buffer_new(512);
+ bconv->tx_handler = -1;
+ bconv->rx_handler = -1;
+
+ return bconv;
+}
+
+
static const char *
_font_size_ichat_to_purple(int size)
{
@@ -125,9 +108,9 @@ _font_size_ichat_to_purple(int size)
}
static void
-_jabber_parse_and_write_message_to_ui(char *message, PurpleConnection *connection, PurpleBuddy *gb)
+_jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleConnection *connection, PurpleBuddy *pb)
{
- xmlnode *message_node, *body_node, *html_node, *events_node;
+ xmlnode *body_node, *html_node, *events_node;
char *body, *html_body = NULL;
const char *ichat_balloon_color = NULL;
const char *ichat_text_color = NULL;
@@ -136,16 +119,9 @@ _jabber_parse_and_write_message_to_ui(char *message, PurpleConnection *connectio
const char *font_color = NULL;
gboolean composing_event = FALSE;
- /* Parsing of the message */
- message_node = xmlnode_from_str(message, strlen(message));
- if (message_node == NULL)
- return;
-
body_node = xmlnode_get_child(message_node, "body");
- if (body_node == NULL) {
- xmlnode_free(message_node);
+ if (body_node == NULL)
return;
- }
body = xmlnode_get_data(body_node);
html_node = xmlnode_get_child(message_node, "html");
@@ -171,11 +147,8 @@ _jabber_parse_and_write_message_to_ui(char *message, PurpleConnection *connectio
font_color = xmlnode_get_attrib(html_body_font_node, "color");
html_body = xmlnode_get_data(html_body_font_node);
if (html_body == NULL)
- {
- gint garbage = -1;
/* This is the kind of formated messages that Purple creates */
- html_body = xmlnode_to_str(html_body_font_node, &garbage);
- }
+ html_body = xmlnode_to_str(html_body_font_node, NULL);
}
}
}
@@ -184,14 +157,11 @@ _jabber_parse_and_write_message_to_ui(char *message, PurpleConnection *connectio
if (events_node != NULL)
{
if (xmlnode_get_child(events_node, "composing") != NULL)
- {
composing_event = TRUE;
- }
if (xmlnode_get_child(events_node, "id") != NULL)
{
/* The user is just typing */
/* TODO: Deal with typing notification */
- xmlnode_free(message_node);
g_free(body);
g_free(html_body);
return;
@@ -207,31 +177,30 @@ _jabber_parse_and_write_message_to_ui(char *message, PurpleConnection *connectio
if (font_size == NULL) font_size = "3";
if (ichat_text_color == NULL) ichat_text_color = "#000000";
if (ichat_balloon_color == NULL) ichat_balloon_color = "#FFFFFF";
- body = g_strconcat("<font face='", font_face, "' size='", font_size, "' color='", ichat_text_color,
- "' back='", ichat_balloon_color, "'>", html_body, "</font>", NULL);
+ body = g_strdup_printf("<font face='%s' size='%s' color='%s' back='%s'>%s</font>",
+ font_face, font_size, ichat_text_color, ichat_balloon_color,
+ html_body);
}
/* TODO: Should we do something with "composing_event" here? */
/* Send the message to the UI */
- serv_got_im(connection, gb->name, body, 0, time(NULL));
+ serv_got_im(connection, pb->name, body, 0, time(NULL));
- /* Free all the strings and nodes (the attributes are freed with their nodes) */
- xmlnode_free(message_node);
g_free(body);
g_free(html_body);
}
struct _check_buddy_by_address_t {
const char *address;
- PurpleBuddy **gb;
+ PurpleBuddy **pb;
BonjourJabber *bj;
};
static void
_check_buddy_by_address(gpointer key, gpointer value, gpointer data)
{
- PurpleBuddy *gb = value;
+ PurpleBuddy *pb = value;
BonjourBuddy *bb;
struct _check_buddy_by_address_t *cbba = data;
@@ -240,11 +209,11 @@ _check_buddy_by_address(gpointer key, gpointer value, gpointer data)
* is the same as the account requesting the check then continue to determine
* whether the buddies IP matches the target IP.
*/
- if (cbba->bj->account == gb->account)
+ if (cbba->bj->account == pb->account)
{
- bb = gb->proto_data;
+ bb = pb->proto_data;
if ((bb != NULL) && (g_ascii_strcasecmp(bb->ip, cbba->address) == 0))
- *(cbba->gb) = gb;
+ *(cbba->pb) = pb;
}
}
@@ -279,24 +248,96 @@ _read_data(gint socket, char **message)
return total_message_length;
}
+static void
+_send_data_write_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ PurpleBuddy *pb = data;
+ BonjourBuddy *bb = pb->proto_data;
+ BonjourJabberConversation *bconv = bb->conversation;
+ int ret, writelen;
+
+ /* TODO: Make sure that the stream has been established before sending */
+
+ writelen = purple_circ_buffer_get_max_read(bconv->tx_buf);
+
+ if (writelen == 0) {
+ purple_input_remove(bconv->tx_handler);
+ bconv->tx_handler = -1;
+ return;
+ }
+
+ ret = send(bconv->socket, bconv->tx_buf->outptr, writelen, 0);
+
+ if (ret < 0 && errno == EAGAIN)
+ return;
+ else if (ret <= 0) {
+ PurpleConversation *conv;
+ const char *error = strerror(errno);
+
+ purple_debug_error("bonjour", "Error sending message to buddy %s error: %s\n",
+ purple_buddy_get_name(pb), error ? error : "(null)");
+
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+ if (conv != NULL)
+ purple_conversation_write(conv, NULL,
+ _("Unable to send message."),
+ PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+ bonjour_jabber_close_conversation(bb->conversation);
+ bb->conversation = NULL;
+ return;
+ }
+
+ purple_circ_buffer_mark_read(bconv->tx_buf, ret);
+}
+
static gint
-_send_data(gint socket, char *message)
+_send_data(PurpleBuddy *pb, char *message)
{
- gint message_len = strlen(message);
- gint partial_sent = 0;
- gchar *partial_message = message;
+ gint ret;
+ int len = strlen(message);
+ BonjourBuddy *bb = pb->proto_data;
+ BonjourJabberConversation *bconv = bb->conversation;
+
+ /* If we're not ready to actually send, append it to the buffer */
+ if (bconv->tx_handler != -1
+ || bconv->connect_data != NULL
+ || !bconv->stream_started
+ || purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
+ ret = -1;
+ errno = EAGAIN;
+ } else {
+ ret = send(bconv->socket, message, len, 0);
+ }
- while ((partial_sent = send(socket, partial_message, message_len, 0)) < message_len)
- {
- if (partial_sent != -1) {
- partial_message += partial_sent;
- message_len -= partial_sent;
- } else {
- return -1;
- }
+ if (ret == -1 && errno == EAGAIN)
+ ret = 0;
+ else if (ret <= 0) {
+ PurpleConversation *conv;
+ const char *error = strerror(errno);
+
+ purple_debug_error("bonjour", "Error sending message to buddy %s error: %s\n",
+ purple_buddy_get_name(pb), error ? error : "(null)");
+
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+ if (conv != NULL)
+ purple_conversation_write(conv, NULL,
+ _("Unable to send message."),
+ PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+ bonjour_jabber_close_conversation(bb->conversation);
+ bb->conversation = NULL;
+ return -1;
+ }
+
+ if (ret < len) {
+ if (bconv->tx_handler == -1)
+ bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
+ _send_data_write_cb, pb);
+ purple_circ_buffer_append(bconv->tx_buf, message + ret, len - ret);
}
- return strlen(message);
+ return ret;
}
static void
@@ -304,17 +345,22 @@ _client_socket_handler(gpointer data, gint socket, PurpleInputCondition conditio
{
char *message = NULL;
gint message_length;
- PurpleBuddy *gb = data;
- PurpleAccount *account = gb->account;
- PurpleConversation *conversation;
- BonjourBuddy *bb = gb->proto_data;
+ PurpleBuddy *pb = data;
+ PurpleAccount *account = pb->account;
+ BonjourBuddy *bb = pb->proto_data;
gboolean closed_conversation = FALSE;
xmlnode *message_node;
/* Read the data from the socket */
if ((message_length = _read_data(socket, &message)) == -1) {
/* There have been an error reading from the socket */
- /* TODO: Shouldn't we handle the error if it isn't EAGAIN? */
+ if (errno != EAGAIN) {
+ bonjour_jabber_close_conversation(bb->conversation);
+ bb->conversation = NULL;
+
+ /* I guess we really don't need to notify the user.
+ * If they try to send another message it'll reconnect */
+ }
return;
} else if (message_length == 0) { /* The other end has closed the socket */
closed_conversation = TRUE;
@@ -330,62 +376,106 @@ _client_socket_handler(gpointer data, gint socket, PurpleInputCondition conditio
/* Parse the message into an XMLnode for analysis */
message_node = xmlnode_from_str(message, strlen(message));
- /* Check if the start of the stream has been received, if not check that the current */
- /* data is the start of the stream */
- if (!(bb->conversation->stream_started))
- {
- /* Check if this is the start of the stream */
- if ((message_node != NULL) &&
- g_ascii_strcasecmp(xmlnode_get_attrib(message_node, "xmlns"), "jabber:client") &&
- (xmlnode_get_attrib(message_node,"xmlns:stream") != NULL))
- {
- bb->conversation->stream_started = TRUE;
- }
- else
- {
- /* TODO: This needs to be nonblocking! */
- if (send(bb->conversation->socket, DOCTYPE, strlen(DOCTYPE), 0) == -1)
- purple_debug_error("bonjour", "Unable to start a conversation with %s\n", bb->name);
- else
- bb->conversation->stream_started = TRUE;
- }
- }
-
/*
* Check that this is not the end of the conversation. This is
* using a magic string, but xmlnode won't play nice when just
* parsing an end tag
*/
- if (purple_str_has_prefix(message, STREAM_END) || (closed_conversation == TRUE)) {
- char *closed_conv_message;
+ if (closed_conversation || purple_str_has_prefix(message, STREAM_END)) {
+ PurpleConversation *conv;
/* Close the socket, clear the watcher and free memory */
- if (bb->conversation != NULL) {
- close(bb->conversation->socket);
- purple_input_remove(bb->conversation->watcher_id);
- g_free(bb->conversation->buddy_name);
- g_free(bb->conversation);
- bb->conversation = NULL;
- }
+ bonjour_jabber_close_conversation(bb->conversation);
+ bb->conversation = NULL;
/* Inform the user that the conversation has been closed */
- conversation = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, gb->name, account);
- closed_conv_message = g_strdup_printf(_("%s has closed the conversation."), gb->name);
- purple_conversation_write(conversation, NULL, closed_conv_message, PURPLE_MESSAGE_SYSTEM, time(NULL));
- g_free(closed_conv_message);
- } else {
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pb->name, account);
+ if (conv != NULL) {
+ char *tmp = g_strdup_printf(_("%s has closed the conversation."), pb->name);
+ purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ g_free(tmp);
+ }
+ } else if (message_node != NULL) {
/* Parse the message to get the data and send to the ui */
- _jabber_parse_and_write_message_to_ui(message, account->gc, gb);
+ _jabber_parse_and_write_message_to_ui(message_node, account->gc, pb);
+ } else {
+ /* TODO: Deal with receiving only a partial message */
}
+ g_free(message);
if (message_node != NULL)
xmlnode_free(message_node);
}
+struct _stream_start_data {
+ char *msg;
+ PurpleInputFunction tx_handler_cb;
+};
+
+static void
+_start_stream(gpointer data, gint source, PurpleInputCondition condition)
+{
+ PurpleBuddy *pb = data;
+ BonjourBuddy *bb = pb->proto_data;
+ struct _stream_start_data *ss = bb->conversation->stream_data;
+ int len, ret;
+
+ len = strlen(ss->msg);
+
+ /* Start Stream */
+ ret = send(source, ss->msg, len, 0);
+
+ if (ret == -1 && errno == EAGAIN)
+ return;
+ else if (ret <= 0) {
+ const char *err = strerror(errno);
+ PurpleConversation *conv;
+
+ purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
+ purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+ if (conv != NULL)
+ purple_conversation_write(conv, NULL,
+ _("Unable to send the message, the conversation couldn't be started."),
+ PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+ bonjour_jabber_close_conversation(bb->conversation);
+ bb->conversation = NULL;
+
+ return;
+ }
+
+ /* This is EXTREMELY unlikely to happen */
+ if (ret < len) {
+ char *tmp = g_strdup(ss->msg + ret);
+ g_free(ss->msg);
+ ss->msg = tmp;
+ return;
+ }
+
+ /* Stream started; process the send buffer if there is one*/
+ purple_input_remove(bb->conversation->tx_handler);
+ bb->conversation->tx_handler= -1;
+
+ bb->conversation->stream_started = TRUE;
+
+ g_free(ss->msg);
+ g_free(ss);
+ bb->conversation->stream_data = NULL;
+
+ if (ss->tx_handler_cb) {
+ bb->conversation->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE,
+ ss->tx_handler_cb, pb);
+ /* We can probably write the data now. */
+ (ss->tx_handler_cb)(pb, source, PURPLE_INPUT_WRITE);
+ }
+}
+
static void
_server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
{
- PurpleBuddy *gb = NULL;
+ PurpleBuddy *pb = NULL;
struct sockaddr_in their_addr; /* connector's address information */
socklen_t sin_size = sizeof(struct sockaddr);
int client_socket;
@@ -407,36 +497,63 @@ _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition co
address_text = inet_ntoa(their_addr.sin_addr);
cbba = g_new0(struct _check_buddy_by_address_t, 1);
cbba->address = address_text;
- cbba->gb = &gb;
+ cbba->pb = &pb;
cbba->bj = data;
g_hash_table_foreach(bl->buddies, _check_buddy_by_address, cbba);
g_free(cbba);
- if (gb == NULL)
+ if (pb == NULL)
{
purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n");
close(client_socket);
return;
}
- bb = gb->proto_data;
+ bb = pb->proto_data;
/* Check if the conversation has been previously started */
if (bb->conversation == NULL)
{
- bb->conversation = g_new(BonjourJabberConversation, 1);
- bb->conversation->socket = client_socket;
- bb->conversation->stream_started = FALSE;
- bb->conversation->buddy_name = g_strdup(gb->name);
- bb->conversation->message_id = 1;
+ int ret, len;
+ char *stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account),
+ purple_buddy_get_name(pb));
+
+ len = strlen(stream_start);
+
+ /* Start the stream */
+ ret = send(client_socket, stream_start, len, 0);
+
+ if (ret == -1 && errno == EAGAIN)
+ ret = 0;
+ else if (ret <= 0) {
+ const char *err = strerror(errno);
+
+ purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
+ purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+
+ close(client_socket);
+ g_free(stream_start);
+
+ return;
+ }
- if (bb->conversation->stream_started == FALSE) {
- /* Start the stream */
- send(bb->conversation->socket, DOCTYPE, strlen(DOCTYPE), 0);
+ bb->conversation = bonjour_jabber_conv_new();
+ bb->conversation->socket = client_socket;
+ bb->conversation->rx_handler = purple_input_add(client_socket,
+ PURPLE_INPUT_READ, _client_socket_handler, pb);
+
+ /* This is unlikely to happen */
+ if (ret < len) {
+ struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
+ ss->msg = g_strdup(stream_start + ret);
+ ss->tx_handler_cb = NULL; /* We have nothing to write yet */
+ bb->conversation->stream_data = ss;
+ /* Finish sending the stream start */
+ bb->conversation->tx_handler = purple_input_add(client_socket,
+ PURPLE_INPUT_WRITE, _start_stream, pb);
+ } else {
bb->conversation->stream_started = TRUE;
}
- /* Open a watcher for the client socket */
- bb->conversation->watcher_id = purple_input_add(client_socket, PURPLE_INPUT_READ,
- _client_socket_handler, gb);
+ g_free(stream_start);
} else {
close(client_socket);
}
@@ -514,134 +631,199 @@ bonjour_jabber_start(BonjourJabber *data)
return data->port;
}
+static void
+_connected_to_buddy(gpointer data, gint source, const gchar *error)
+{
+ PurpleBuddy *pb = data;
+ BonjourBuddy *bb = pb->proto_data;
+ int len, ret;
+ char *stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account), purple_buddy_get_name(pb));
+
+ bb->conversation->connect_data = NULL;
+
+ if (source < 0) {
+ PurpleConversation *conv;
+
+ purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n",
+ purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, error ? error : "(null)");
+
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+ if (conv != NULL)
+ purple_conversation_write(conv, NULL,
+ _("Unable to send the message, the conversation couldn't be started."),
+ PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+ bonjour_jabber_close_conversation(bb->conversation);
+ bb->conversation = NULL;
+ return;
+ }
+
+ len = strlen(stream_start);
+
+ /* Start the stream and send queued messages */
+ ret = send(source, stream_start, len, 0);
+
+ if (ret == -1 && errno == EAGAIN)
+ ret = 0;
+ else if (ret <= 0) {
+ const char *err = strerror(errno);
+ PurpleConversation *conv;
+
+ purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
+ purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+ if (conv != NULL)
+ purple_conversation_write(conv, NULL,
+ _("Unable to send the message, the conversation couldn't be started."),
+ PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+ close(source);
+ bonjour_jabber_close_conversation(bb->conversation);
+ bb->conversation = NULL;
+
+ g_free(stream_start);
+
+ return;
+ }
+
+ bb->conversation->socket = source;
+ bb->conversation->rx_handler = purple_input_add(source,
+ PURPLE_INPUT_READ, _client_socket_handler, pb);
+
+ /* This is unlikely to happen */
+ if (ret < len) {
+ struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
+ ss->msg = g_strdup(stream_start + ret);
+ ss->tx_handler_cb = _send_data_write_cb;
+ bb->conversation->stream_data = ss;
+ /* Finish sending the stream start */
+ bb->conversation->tx_handler = purple_input_add(source,
+ PURPLE_INPUT_WRITE, _start_stream, pb);
+ }
+ /* Process the send buffer */
+ else {
+ bb->conversation->stream_started = TRUE;
+ /* Watch for when we can write the buffered messages */
+ bb->conversation->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE,
+ _send_data_write_cb, pb);
+ /* We can probably write the data now. */
+ _send_data_write_cb(pb, source, PURPLE_INPUT_WRITE);
+ }
+
+ g_free(stream_start);
+}
+
int
bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body)
{
- xmlnode *message_node = NULL;
- gchar *message = NULL;
- gint message_length = -1;
- xmlnode *message_body_node = NULL;
- xmlnode *message_html_node = NULL;
- xmlnode *message_html_body_node = NULL;
- xmlnode *message_html_body_font_node = NULL;
- xmlnode *message_x_node = NULL;
- PurpleBuddy *gb = NULL;
- BonjourBuddy *bb = NULL;
- PurpleConversation *conversation = NULL;
- char *message_from_ui = NULL;
- char *stripped_message = NULL;
- gint ret;
+ xmlnode *message_node, *node, *node2;
+ gchar *message;
+ PurpleBuddy *pb;
+ BonjourBuddy *bb;
+ int ret;
- gb = purple_find_buddy(data->account, to);
- if (gb == NULL) {
+ pb = purple_find_buddy(data->account, to);
+ if (pb == NULL) {
purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
/* You can not send a message to an offline buddy */
return -10000;
}
- bb = (BonjourBuddy *)gb->proto_data;
+ bb = pb->proto_data;
/* Check if there is a previously open conversation */
if (bb->conversation == NULL)
{
- int socket = _connect_to_buddy(gb);
- if (socket < 0)
+ PurpleProxyConnectData *connect_data;
+
+ /* Make sure that the account always has a proxy of "none".
+ * This is kind of dirty, but proxy_connect_none() isn't exposed. */
+ static PurpleProxyInfo *tmp_none_proxy_info = NULL;
+ if (!tmp_none_proxy_info) {
+ tmp_none_proxy_info = purple_proxy_info_new();
+ purple_proxy_info_set_type(tmp_none_proxy_info, PURPLE_PROXY_NONE);
+ }
+ purple_account_set_proxy_info(data->account, tmp_none_proxy_info);
+
+ connect_data =
+ purple_proxy_connect(data->account->gc, data->account, bb->ip,
+ bb->port_p2pj, _connected_to_buddy, pb);
+
+ if (connect_data == NULL) {
+ purple_debug_error("bonjour", "Unable to connect to buddy (%s).\n", to);
return -10001;
+ }
- bb->conversation = g_new(BonjourJabberConversation, 1);
- bb->conversation->socket = socket;
- bb->conversation->stream_started = FALSE;
- bb->conversation->buddy_name = g_strdup(gb->name);
- bb->conversation->watcher_id = purple_input_add(bb->conversation->socket,
- PURPLE_INPUT_READ, _client_socket_handler, gb);
+ bb->conversation = bonjour_jabber_conv_new();
+ bb->conversation->connect_data = connect_data;
+ /* We don't want _send_data() to register the tx_handler;
+ * that neeeds to wait until we're actually connected. */
+ bb->conversation->tx_handler = 0;
}
- /* Enclose the message from the UI within a "font" node */
- message_body_node = xmlnode_new("body");
- stripped_message = purple_markup_strip_html(body);
- xmlnode_insert_data(message_body_node, stripped_message, strlen(stripped_message));
- g_free(stripped_message);
-
- message_from_ui = g_strconcat("<font>", body, "</font>", NULL);
- message_html_body_font_node = xmlnode_from_str(message_from_ui, strlen(message_from_ui));
- g_free(message_from_ui);
+ message_node = xmlnode_new("message");
+ xmlnode_set_attrib(message_node, "to", bb->name);
+ xmlnode_set_attrib(message_node, "from", purple_account_get_username(data->account));
+ xmlnode_set_attrib(message_node, "type", "chat");
- message_html_body_node = xmlnode_new("body");
- xmlnode_insert_child(message_html_body_node, message_html_body_font_node);
+ /* Enclose the message from the UI within a "font" node */
+ node = xmlnode_new_child(message_node, "body");
+ message = purple_markup_strip_html(body);
+ xmlnode_insert_data(node, message, strlen(message));
+ g_free(message);
- message_html_node = xmlnode_new("html");
- xmlnode_set_attrib(message_html_node, "xmlns", "http://www.w3.org/1999/xhtml");
- xmlnode_insert_child(message_html_node, message_html_body_node);
+ node = xmlnode_new_child(message_node, "html");
+ xmlnode_set_namespace(node, "http://www.w3.org/1999/xhtml");
- message_x_node = xmlnode_new("x");
- xmlnode_set_attrib(message_x_node, "xmlns", "jabber:x:event");
- xmlnode_insert_child(message_x_node, xmlnode_new("composing"));
+ node = xmlnode_new_child(node, "body");
+ message = g_strdup_printf("<font>%s</font>", body);
+ node2 = xmlnode_from_str(message, strlen(message));
+ g_free(message);
+ xmlnode_insert_child(node, node2);
- message_node = xmlnode_new("message");
- xmlnode_set_attrib(message_node, "to", ((BonjourBuddy*)(gb->proto_data))->name);
- xmlnode_set_attrib(message_node, "from", data->account->username);
- xmlnode_set_attrib(message_node, "type", "chat");
- xmlnode_insert_child(message_node, message_body_node);
- xmlnode_insert_child(message_node, message_html_node);
- xmlnode_insert_child(message_node, message_x_node);
+ node = xmlnode_new_child(message_node, "x");
+ xmlnode_set_namespace(node, "jabber:x:event");
+ xmlnode_insert_child(node, xmlnode_new("composing"));
- message = xmlnode_to_str(message_node, &message_length);
+ message = xmlnode_to_str(message_node, NULL);
xmlnode_free(message_node);
- /* Check if the stream for the conversation has been started */
- if (bb->conversation->stream_started == FALSE)
- {
- /* Start the stream */
- if (send(bb->conversation->socket, DOCTYPE, strlen(DOCTYPE), 0) == -1)
- {
- purple_debug_error("bonjour", "Unable to start a conversation\n");
- purple_debug_warning("bonjour", "send error: %s\n", strerror(errno));
- conversation = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, data->account);
- purple_conversation_write(conversation, NULL,
- _("Unable to send the message, the conversation couldn't be started."),
- PURPLE_MESSAGE_SYSTEM, time(NULL));
- close(bb->conversation->socket);
- purple_input_remove(bb->conversation->watcher_id);
-
- /* Free all the data related to the conversation */
- g_free(bb->conversation->buddy_name);
- g_free(bb->conversation);
- bb->conversation = NULL;
- g_free(message);
- return 0;
- }
-
- bb->conversation->stream_started = TRUE;
- }
+ ret = _send_data(pb, message) >= 0;
- /* Send the message */
- ret = (_send_data(bb->conversation->socket, message) == -1);
g_free(message);
- if (ret == -1)
- return -10000;
-
- return 1;
+ return ret;
}
void
-bonjour_jabber_close_conversation(BonjourJabber *data, PurpleBuddy *gb)
+bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
{
- BonjourBuddy *bb = (BonjourBuddy*)gb->proto_data;
-
- if (bb->conversation != NULL)
+ if (bconv != NULL)
{
- /* Send the end of the stream to the other end of the conversation */
- send(bb->conversation->socket, STREAM_END, strlen(STREAM_END), 0);
-
/* Close the socket and remove the watcher */
- close(bb->conversation->socket);
- purple_input_remove(bb->conversation->watcher_id);
+ if (bconv->socket >= 0) {
+ /* Send the end of the stream to the other end of the conversation */
+ if (bconv->stream_started)
+ send(bconv->socket, STREAM_END, strlen(STREAM_END), 0);
+ /* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
+ close(bconv->socket);
+ }
+ if (bconv->rx_handler != -1)
+ purple_input_remove(bconv->rx_handler);
+ if (bconv->tx_handler > 0)
+ purple_input_remove(bconv->tx_handler);
/* Free all the data related to the conversation */
- g_free(bb->conversation->buddy_name);
- g_free(bb->conversation);
- bb->conversation = NULL;
+ purple_circ_buffer_destroy(bconv->tx_buf);
+ if (bconv->connect_data != NULL)
+ purple_proxy_connect_cancel(bconv->connect_data);
+ if (bconv->stream_data != NULL) {
+ struct _stream_start_data *ss = bconv->stream_data;
+ g_free(ss->msg);
+ g_free(ss);
+ }
+ g_free(bconv);
}
}
@@ -657,12 +839,15 @@ bonjour_jabber_stop(BonjourJabber *data)
/* Close all the conversation sockets and remove all the watchers after sending end streams */
if (data->account->gc != NULL)
{
- GSList *buddies;
- GSList *l;
+ GSList *buddies, *l;
+
+ buddies = purple_find_buddies(data->account, purple_account_get_username(data->account));
+ for (l = buddies; l; l = l->next) {
+ BonjourBuddy *bb = ((PurpleBuddy*) l->data)->proto_data;
+ bonjour_jabber_close_conversation(bb->conversation);
+ bb->conversation = NULL;
+ }
- buddies = purple_find_buddies(data->account, data->account->username);
- for (l = buddies; l; l = l->next)
- bonjour_jabber_close_conversation(data, l->data);
g_slist_free(buddies);
}
}
diff --git a/libpurple/protocols/bonjour/jabber.h b/libpurple/protocols/bonjour/jabber.h
index d83fd5ffdf..a8bc07894c 100644
--- a/libpurple/protocols/bonjour/jabber.h
+++ b/libpurple/protocols/bonjour/jabber.h
@@ -27,9 +27,7 @@
#define _BONJOUR_JABBER_H_
#include "account.h"
-
-#define STREAM_END "</stream:stream>"
-#define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\">"
+#include "circbuffer.h"
typedef struct _BonjourJabber
{
@@ -42,10 +40,12 @@ typedef struct _BonjourJabber
typedef struct _BonjourJabberConversation
{
gint socket;
- gint watcher_id;
- gchar* buddy_name;
+ guint rx_handler;
+ guint tx_handler;
+ PurpleCircBuffer *tx_buf;
gboolean stream_started;
- gint message_id;
+ PurpleProxyConnectData *connect_data;
+ gpointer stream_data;
} BonjourJabberConversation;
/**
@@ -58,7 +58,7 @@ gint bonjour_jabber_start(BonjourJabber *data);
int bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body);
-void bonjour_jabber_close_conversation(BonjourJabber *data, PurpleBuddy *gb);
+void bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
void bonjour_jabber_stop(BonjourJabber *data);
diff --git a/libpurple/protocols/bonjour/mdns_common.c b/libpurple/protocols/bonjour/mdns_common.c
index 9b6c6afe61..4e9176f294 100644
--- a/libpurple/protocols/bonjour/mdns_common.c
+++ b/libpurple/protocols/bonjour/mdns_common.c
@@ -162,7 +162,7 @@ bonjour_dns_sd_stop(BonjourDnsSd *data)
#ifdef USE_BONJOUR_APPLE
/* hack: for win32, we need to stop listening to the advertisement pipe too */
- purple_input_remove(data->advertisement_fd);
+ purple_input_remove(data->advertisement_handler);
DNSServiceRefDeallocate(data->advertisement);
DNSServiceRefDeallocate(data->browser);
diff --git a/libpurple/protocols/bonjour/mdns_types.h b/libpurple/protocols/bonjour/mdns_types.h
index fa671120ca..e81b447067 100644
--- a/libpurple/protocols/bonjour/mdns_types.h
+++ b/libpurple/protocols/bonjour/mdns_types.h
@@ -38,7 +38,7 @@ typedef struct _BonjourDnsSd
DNSServiceRef advertisement;
DNSServiceRef browser;
- int advertisement_fd; /* hack... windows bonjour is broken, so we have to have this */
+ int advertisement_handler; /* hack... windows bonjour is broken, so we have to have this */
#else /* USE_BONJOUR_HOWL */
sw_discovery session;
sw_discovery_oid session_id;
diff --git a/libpurple/protocols/bonjour/mdns_win32.c b/libpurple/protocols/bonjour/mdns_win32.c
index 0a2546d6f3..b319507ca4 100644
--- a/libpurple/protocols/bonjour/mdns_win32.c
+++ b/libpurple/protocols/bonjour/mdns_win32.c
@@ -280,7 +280,7 @@ _mdns_publish(BonjourDnsSd *data, PublishType type)
{
/* hack: Bonjour on windows is broken. We don't care about the callback but we have to listen anyway */
gint advertisement_fd = DNSServiceRefSockFD(data->advertisement);
- data->advertisement_fd = purple_input_add(advertisement_fd, PURPLE_INPUT_READ, _mdns_handle_event, data->advertisement);
+ data->advertisement_handler = purple_input_add(advertisement_fd, PURPLE_INPUT_READ, _mdns_handle_event, data->advertisement);
}
}
diff --git a/libpurple/protocols/jabber/Makefile.am b/libpurple/protocols/jabber/Makefile.am
index 4adde6118b..b11f77d87a 100644
--- a/libpurple/protocols/jabber/Makefile.am
+++ b/libpurple/protocols/jabber/Makefile.am
@@ -43,15 +43,12 @@ libxmpp_la_LDFLAGS = -module -avoid-version
if STATIC_JABBER
st = -DPURPLE_STATIC_PRPL
-noinst_LIBRARIES = libjabber.a libxmpp.a
+noinst_LIBRARIES = libjabber.a
pkg_LTLIBRARIES =
-libjabber_a_SOURCES = $(JABBERSOURCES)
+libjabber_a_SOURCES = $(JABBERSOURCES) libxmpp.c
libjabber_a_CFLAGS = $(AM_CFLAGS)
-libxmpp_a_SOURCES = libxmpp.c
-libxmpp_a_CFLAGS = $(AM_CFLAGS)
-
else
st =
diff --git a/libpurple/protocols/oscar/Makefile.am b/libpurple/protocols/oscar/Makefile.am
index 6af19bc306..f27907d7a3 100644
--- a/libpurple/protocols/oscar/Makefile.am
+++ b/libpurple/protocols/oscar/Makefile.am
@@ -52,15 +52,10 @@ libicq_la_LDFLAGS = -module -avoid-version
if STATIC_OSCAR
st = -DPURPLE_STATIC_PRPL
-noinst_LIBRARIES = liboscar.a libaim.a libicq.a
-liboscar_a_SOURCES = $(OSCARSOURCES)
-liboscar_a_CFLAGS = $(AM_CFLAGS)
-
-libaim_a_CFLAGS = $(AM_CFLAGS)
-libaim_a_SOURCES = libaim.c
+noinst_LIBRARIES = liboscar.a
-libicq_a_CFLAGS = $(AM_CFLAGS)
-libicq_a_SOURCES = libicq.c
+liboscar_a_SOURCES = $(OSCARSOURCES) libaim.c libicq.c
+liboscar_a_CFLAGS = $(AM_CFLAGS)
else
diff --git a/libpurple/protocols/oscar/oscar.c b/libpurple/protocols/oscar/oscar.c
index ab61e000fc..06a080d531 100644
--- a/libpurple/protocols/oscar/oscar.c
+++ b/libpurple/protocols/oscar/oscar.c
@@ -4386,7 +4386,7 @@ oscar_set_extendedstatus(PurpleConnection *gc)
if (purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE))
data |= AIM_ICQ_STATE_WEBAWARE;
- if (!strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE) || !strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE))
+ if (!strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE))
data |= AIM_ICQ_STATE_NORMAL;
else if (!strcmp(status_id, OSCAR_STATUS_ID_AWAY))
data |= AIM_ICQ_STATE_AWAY;
diff --git a/libpurple/protocols/silc/README b/libpurple/protocols/silc/README
index 90cbdb5025..c8c5ab532f 100644
--- a/libpurple/protocols/silc/README
+++ b/libpurple/protocols/silc/README
@@ -2,19 +2,19 @@ SILC Purple Plugin
==================
This is the Purple protocol plugin of the protocol called Secure Internet
-Live Conferencing (SILC). The implementation will use the SILC Toolkit,
-freely available from the http://silcnet.org/ site, for the actual SILC
+Live Conferencing (SILC). The implementation will use the SILC Toolkit,
+freely available from the http://silcnet.org/ site, for the actual SILC
protocol implementation.
-To include SILC into Purple, one needs to first compile and install
+To include SILC into Purple, one needs to first compile and install
the SILC Toolkit. It is done as follows:
- ./configure --enable-shared
+ ./configure
make
make install
-This will compile shared libraries of the SILC Toolkit. If the --prefix
-is not given to ./configure, the binaries are installed into the
+This will compile shared libraries of the SILC Toolkit. If the --prefix
+is not given to ./configure, the binaries are installed into the
/usr/local/silc directory.
Once the Toolkit is installed one needs to tell Purple's ./configure
diff --git a/libpurple/protocols/silc/TODO b/libpurple/protocols/silc/TODO
index f346334117..81fae64390 100644
--- a/libpurple/protocols/silc/TODO
+++ b/libpurple/protocols/silc/TODO
@@ -1,14 +1,6 @@
Features TODO (maybe)
=====================
-Sending images
- - Sending images to channel too, if libpurple allows it.
-
Preferences
- Add joined channels to buddy list automatically (during
session)
- - Add joined channels to buddy list automatically permanently
-
-Buddy icon
- - After SILC Toolkit 1.0.2 buddy icon support can be added
- (SILC_ATTERIBUTE_USER_ICON).
diff --git a/libpurple/protocols/silc/buddy.c b/libpurple/protocols/silc/buddy.c
index dcde59b420..548ad1c746 100644
--- a/libpurple/protocols/silc/buddy.c
+++ b/libpurple/protocols/silc/buddy.c
@@ -4,7 +4,7 @@
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2004 Pekka Riikonen
+ Copyright (C) 2004 - 2007 Pekka Riikonen
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
@@ -17,7 +17,7 @@
*/
-#include "silcincludes.h"
+#include "silc.h"
#include "silcclient.h"
#include "silcpurple.h"
#include "wb.h"
@@ -29,7 +29,7 @@ silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data);
static void
silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
- gboolean force_local);
+ gboolean force_local);
typedef struct {
char *nick;
@@ -38,10 +38,10 @@ typedef struct {
static void
silcpurple_buddy_keyagr_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList clients,
+ void *context)
{
PurpleConnection *gc = client->application;
SilcPurpleResolve r = context;
@@ -62,21 +62,16 @@ silcpurple_buddy_keyagr_resolved(SilcClient client,
silc_free(r);
}
-typedef struct {
- gboolean responder;
-} *SilcPurpleKeyAgr;
-
static void
silcpurple_buddy_keyagr_cb(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- SilcKeyAgreementStatus status,
- SilcSKEKeyMaterial *key,
- void *context)
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ SilcKeyAgreementStatus status,
+ SilcSKEKeyMaterial key,
+ void *context)
{
PurpleConnection *gc = client->application;
SilcPurple sg = gc->proto_data;
- SilcPurpleKeyAgr a = context;
if (!sg->conn)
return;
@@ -90,13 +85,13 @@ silcpurple_buddy_keyagr_cb(SilcClient client,
/* Set the private key for this client */
silc_client_del_private_message_key(client, conn, client_entry);
silc_client_add_private_message_key_ske(client, conn, client_entry,
- NULL, NULL, key, a->responder);
+ NULL, NULL, key);
silc_ske_free_key_material(key);
-
+
/* Open IM window */
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- client_entry->nickname, sg->account);
+ client_entry->nickname, sg->account);
if (convo) {
/* we don't have windows in the core anymore...but we may want to
* provide some method for asking the UI to show the window
@@ -104,7 +99,7 @@ silcpurple_buddy_keyagr_cb(SilcClient client,
*/
} else {
convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, sg->account,
- client_entry->nickname);
+ client_entry->nickname);
}
g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname);
purple_conversation_set_title(convo, tmp);
@@ -113,7 +108,7 @@ silcpurple_buddy_keyagr_cb(SilcClient client,
case SILC_KEY_AGREEMENT_ERROR:
purple_notify_error(gc, _("Key Agreement"),
- _("Error occurred during key agreement"), NULL);
+ _("Error occurred during key agreement"), NULL);
break;
case SILC_KEY_AGREEMENT_FAILURE:
@@ -122,53 +117,48 @@ silcpurple_buddy_keyagr_cb(SilcClient client,
case SILC_KEY_AGREEMENT_TIMEOUT:
purple_notify_error(gc, _("Key Agreement"),
- _("Timeout during key agreement"), NULL);
+ _("Timeout during key agreement"), NULL);
break;
case SILC_KEY_AGREEMENT_ABORTED:
purple_notify_error(gc, _("Key Agreement"),
- _("Key agreement was aborted"), NULL);
+ _("Key agreement was aborted"), NULL);
break;
case SILC_KEY_AGREEMENT_ALREADY_STARTED:
purple_notify_error(gc, _("Key Agreement"),
- _("Key agreement is already started"), NULL);
+ _("Key agreement is already started"), NULL);
break;
case SILC_KEY_AGREEMENT_SELF_DENIED:
purple_notify_error(gc, _("Key Agreement"),
- _("Key agreement cannot be started with yourself"),
- NULL);
+ _("Key agreement cannot be started with yourself"),
+ NULL);
break;
default:
break;
}
-
- silc_free(a);
}
static void
silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
- gboolean force_local)
+ gboolean force_local)
{
SilcPurple sg = gc->proto_data;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
+ SilcDList clients;
+ SilcClientEntry client_entry;
+ SilcClientConnectionParams params;
char *local_ip = NULL, *remote_ip = NULL;
gboolean local = TRUE;
- char *nickname;
- SilcPurpleKeyAgr a;
+ SilcSocket sock;
if (!sg->conn || !name)
return;
- if (!silc_parse_userfqdn(name, &nickname, NULL))
- return;
-
/* Find client entry */
- clients = silc_client_get_clients_local(sg->client, sg->conn, nickname, name,
- &clients_count);
+ clients = silc_client_get_clients_local(sg->client, sg->conn, name,
+ FALSE);
if (!clients) {
/* Resolve unknown user */
SilcPurpleResolve r = silc_calloc(1, sizeof(*r));
@@ -176,12 +166,14 @@ silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
return;
r->nick = g_strdup(name);
r->gc = gc;
- silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
+ silc_client_get_clients(sg->client, sg->conn, name, NULL,
silcpurple_buddy_keyagr_resolved, r);
- silc_free(nickname);
return;
}
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sg->conn->stream),
+ &sock, NULL, NULL, NULL);
+
/* Resolve the local IP from the outgoing socket connection. We resolve
it to check whether we have a private range IP address or public IP
address. If we have public then we will assume that we are not behind
@@ -196,14 +188,14 @@ silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
Naturally this algorithm does not always get things right. */
- if (silc_net_check_local_by_sock(sg->conn->sock->sock, NULL, &local_ip)) {
+ if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) {
/* Check if the IP is private */
if (!force_local && silcpurple_ip_is_private(local_ip)) {
local = FALSE;
/* Local IP is private, resolve the remote server IP to see whether
we are talking to Internet or just on LAN. */
- if (silc_net_check_host_by_sock(sg->conn->sock->sock, NULL,
+ if (silc_net_check_host_by_sock(sock, NULL,
&remote_ip))
if (silcpurple_ip_is_private(remote_ip))
/* We assume we are in LAN. Let's provide
@@ -218,19 +210,24 @@ silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
if (local && !local_ip)
local_ip = silc_net_localip();
- a = silc_calloc(1, sizeof(*a));
- if (!a)
- return;
- a->responder = local;
+ silc_dlist_start(clients);
+ client_entry = silc_dlist_get(clients);
+
+ memset(&params, 0, sizeof(params));
+ params.timeout_secs = 60;
+ if (local)
+ /* Provide connection point */
+ params.local_ip = local_ip;
/* Send the key agreement request */
- silc_client_send_key_agreement(sg->client, sg->conn, clients[0],
- local ? local_ip : NULL, NULL, 0, 60,
- silcpurple_buddy_keyagr_cb, a);
+ silc_client_send_key_agreement(sg->client, sg->conn, client_entry,
+ &params, sg->public_key,
+ sg->private_key,
+ silcpurple_buddy_keyagr_cb, NULL);
silc_free(local_ip);
silc_free(remote_ip);
- silc_free(clients);
+ silc_client_list_free(sg->client, sg->conn, clients);
}
typedef struct {
@@ -244,8 +241,8 @@ typedef struct {
static void
silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id)
{
- SilcPurpleKeyAgr ai;
SilcClientEntry client_entry;
+ SilcClientConnectionParams params;
if (id != 1)
goto out;
@@ -255,26 +252,27 @@ silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id)
&a->client_id);
if (!client_entry) {
purple_notify_error(a->client->application, _("Key Agreement"),
- _("The remote user is not present in the network any more"),
- NULL);
+ _("The remote user is not present in the network any more"),
+ NULL);
goto out;
}
/* If the hostname was provided by the requestor perform the key agreement
now. Otherwise, we will send him a request to connect to us. */
if (a->hostname) {
- ai = silc_calloc(1, sizeof(*ai));
- if (!ai)
- goto out;
- ai->responder = FALSE;
- silc_client_perform_key_agreement(a->client, a->conn, client_entry,
+ memset(&params, 0, sizeof(params));
+ params.timeout_secs = 60;
+ silc_client_perform_key_agreement(a->client, a->conn,
+ client_entry, &params,
+ a->conn->public_key,
+ a->conn->private_key,
a->hostname, a->port,
- silcpurple_buddy_keyagr_cb, ai);
+ silcpurple_buddy_keyagr_cb, NULL);
} else {
/* Send request. Force us as the point of connection since requestor
did not provide the point of connection. */
silcpurple_buddy_keyagr_do(a->client->application,
- client_entry->nickname, TRUE);
+ client_entry->nickname, TRUE);
}
out:
@@ -283,14 +281,19 @@ silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id)
}
void silcpurple_buddy_keyagr_request(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- const char *hostname, SilcUInt16 port)
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ const char *hostname, SilcUInt16 port,
+ SilcUInt16 protocol)
{
char tmp[128], tmp2[128];
SilcPurpleKeyAgrAsk a;
PurpleConnection *gc = client->application;
+ /* For now Pidgin don't support UDP key agreement */
+ if (protocol == 1)
+ return;
+
g_snprintf(tmp, sizeof(tmp),
_("Key agreement request received from %s. Would you like to "
"perform the key agreement?"), client_entry->nickname);
@@ -304,15 +307,15 @@ void silcpurple_buddy_keyagr_request(SilcClient client,
return;
a->client = client;
a->conn = conn;
- a->client_id = *client_entry->id;
+ a->client_id = client_entry->id;
if (hostname)
a->hostname = strdup(hostname);
a->port = port;
purple_request_action(client->application, _("Key Agreement Request"), tmp,
- hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname,
- NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
- _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb));
+ hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname,
+ NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
+ _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb));
}
static void
@@ -333,9 +336,7 @@ silcpurple_buddy_resetkey(PurpleBlistNode *node, gpointer data)
PurpleBuddy *b;
PurpleConnection *gc;
SilcPurple sg;
- char *nickname;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
+ SilcDList clients;
g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
@@ -343,23 +344,16 @@ silcpurple_buddy_resetkey(PurpleBlistNode *node, gpointer data)
gc = purple_account_get_connection(b->account);
sg = gc->proto_data;
- if (!silc_parse_userfqdn(b->name, &nickname, NULL))
- return;
-
/* Find client entry */
clients = silc_client_get_clients_local(sg->client, sg->conn,
- nickname, b->name,
- &clients_count);
- if (!clients) {
- silc_free(nickname);
+ b->name, FALSE);
+ if (!clients)
return;
- }
- clients[0]->prv_resp = FALSE;
+ silc_dlist_start(clients);
silc_client_del_private_message_key(sg->client, sg->conn,
- clients[0]);
- silc_free(clients);
- silc_free(nickname);
+ silc_dlist_get(clients));
+ silc_client_list_free(sg->client, sg->conn, clients);
}
typedef struct {
@@ -386,8 +380,8 @@ silcpurple_buddy_privkey_cb(SilcPurplePrivkey p, const char *passphrase)
&p->client_id);
if (!client_entry) {
purple_notify_error(p->client->application, _("IM With Password"),
- _("The remote user is not present in the network any more"),
- NULL);
+ _("The remote user is not present in the network any more"),
+ NULL);
silc_free(p);
return;
}
@@ -398,21 +392,16 @@ silcpurple_buddy_privkey_cb(SilcPurplePrivkey p, const char *passphrase)
silc_client_add_private_message_key(p->client, p->conn,
client_entry, NULL, NULL,
(unsigned char *)passphrase,
- strlen(passphrase), FALSE,
- client_entry->prv_resp);
- if (!client_entry->prv_resp)
- silc_client_send_private_message_key_request(p->client,
- p->conn,
- client_entry);
+ strlen(passphrase));
silc_free(p);
}
static void
silcpurple_buddy_privkey_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList clients,
+ void *context)
{
char tmp[256];
@@ -434,42 +423,39 @@ static void
silcpurple_buddy_privkey(PurpleConnection *gc, const char *name)
{
SilcPurple sg = gc->proto_data;
- char *nickname;
SilcPurplePrivkey p;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
+ SilcDList clients;
+ SilcClientEntry client_entry;
if (!name)
return;
- if (!silc_parse_userfqdn(name, &nickname, NULL))
- return;
/* Find client entry */
clients = silc_client_get_clients_local(sg->client, sg->conn,
- nickname, name,
- &clients_count);
+ name, FALSE);
if (!clients) {
- silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
+ silc_client_get_clients(sg->client, sg->conn, name, NULL,
silcpurple_buddy_privkey_resolved,
g_strdup(name));
- silc_free(nickname);
return;
}
+ silc_dlist_start(clients);
+ client_entry = silc_dlist_get(clients);
+
p = silc_calloc(1, sizeof(*p));
if (!p)
return;
p->client = sg->client;
p->conn = sg->conn;
- p->client_id = *clients[0]->id;
+ p->client_id = client_entry->id;
purple_request_input(gc, _("IM With Password"), NULL,
_("Set IM Password"), NULL, FALSE, TRUE, NULL,
_("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb),
_("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb),
gc->account, NULL, NULL, p);
- silc_free(clients);
- silc_free(nickname);
+ silc_client_list_free(sg->client, sg->conn, clients);
}
static void
@@ -498,13 +484,21 @@ typedef struct {
static void
silcpurple_buddy_getkey(PurpleConnection *gc, const char *name);
-static void
-silcpurple_buddy_getkey_cb(SilcPurpleBuddyGetkey g,
- SilcClientCommandReplyContext cmd)
+static SilcBool
+silcpurple_buddy_getkey_cb(SilcClient client, SilcClientConnection conn,
+ SilcCommand command, SilcStatus status,
+ SilcStatus error, void *context, va_list ap)
{
SilcClientEntry client_entry;
- unsigned char *pk;
- SilcUInt32 pk_len;
+ SilcPurpleBuddyGetkey g = context;
+
+ if (status != SILC_STATUS_OK) {
+ purple_notify_error(g->client->application, _("Get Public Key"),
+ _("The remote user is not present in the network any more"),
+ NULL);
+ silc_free(g);
+ return FALSE;
+ }
/* Get the client entry. */
client_entry = silc_client_get_client_by_id(g->client, g->conn,
@@ -514,30 +508,28 @@ silcpurple_buddy_getkey_cb(SilcPurpleBuddyGetkey g,
_("The remote user is not present in the network any more"),
NULL);
silc_free(g);
- return;
+ return FALSE;
}
if (!client_entry->public_key) {
silc_free(g);
- return;
+ return FALSE;
}
/* Now verify the public key */
- pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
silcpurple_verify_public_key(g->client, g->conn, client_entry->nickname,
- SILC_SOCKET_TYPE_CLIENT,
- pk, pk_len, SILC_SKE_PK_TYPE_SILC,
- NULL, NULL);
- silc_free(pk);
+ SILC_CONN_CLIENT, client_entry->public_key,
+ NULL, NULL);
silc_free(g);
+ return TRUE;
}
static void
silcpurple_buddy_getkey_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList clients,
+ void *context)
{
char tmp[256];
@@ -546,7 +538,7 @@ silcpurple_buddy_getkey_resolved(SilcClient client,
_("User %s is not present in the network"),
(const char *)context);
purple_notify_error(client->application, _("Get Public Key"),
- _("Cannot fetch the public key"), tmp);
+ _("Cannot fetch the public key"), tmp);
g_free(context);
return;
}
@@ -561,42 +553,38 @@ silcpurple_buddy_getkey(PurpleConnection *gc, const char *name)
SilcPurple sg = gc->proto_data;
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
+ SilcClientEntry client_entry;
+ SilcDList clients;
SilcPurpleBuddyGetkey g;
- char *nickname;
+ SilcUInt16 cmd_ident;
if (!name)
return;
- if (!silc_parse_userfqdn(name, &nickname, NULL))
- return;
-
/* Find client entry */
- clients = silc_client_get_clients_local(client, conn, nickname, name,
- &clients_count);
+ clients = silc_client_get_clients_local(client, conn, name, FALSE);
if (!clients) {
- silc_client_get_clients(client, conn, nickname, NULL,
+ silc_client_get_clients(client, conn, name, NULL,
silcpurple_buddy_getkey_resolved,
g_strdup(name));
- silc_free(nickname);
return;
}
+ silc_dlist_start(clients);
+ client_entry = silc_dlist_get(clients);
+
/* Call GETKEY */
g = silc_calloc(1, sizeof(*g));
if (!g)
return;
g->client = client;
g->conn = conn;
- g->client_id = *clients[0]->id;
- silc_client_command_call(client, conn, NULL, "GETKEY",
- clients[0]->nickname, NULL);
- silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
- conn->cmd_ident,
- (SilcCommandCb)silcpurple_buddy_getkey_cb, g);
- silc_free(clients);
- silc_free(nickname);
+ g->client_id = client_entry->id;
+ cmd_ident = silc_client_command_call(client, conn, NULL, "GETKEY",
+ client_entry->nickname, NULL);
+ silc_client_command_pending(conn, SILC_COMMAND_GETKEY, cmd_ident,
+ silcpurple_buddy_getkey_cb, g);
+ silc_client_list_free(client, conn, clients);
}
static void
@@ -629,8 +617,7 @@ silcpurple_buddy_showkey(PurpleBlistNode *node, gpointer data)
sg = gc->proto_data;
pkfile = purple_blist_node_get_string(node, "public-key");
- if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) {
+ if (!silc_pkcs_load_public_key(pkfile, &public_key)) {
purple_notify_error(gc,
_("Show Public Key"),
_("Could not load public key"), NULL);
@@ -661,6 +648,7 @@ typedef struct {
PurpleBuddy *b;
unsigned char *offline_pk;
SilcUInt32 offline_pk_len;
+ SilcPublicKey public_key;
unsigned int offline : 1;
unsigned int pubkey_search : 1;
unsigned int init : 1;
@@ -670,10 +658,10 @@ static void
silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id);
static void
silcpurple_add_buddy_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context);
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList clients,
+ void *context);
void silcpurple_get_info(PurpleConnection *gc, const char *who)
{
@@ -735,35 +723,38 @@ silcpurple_add_buddy_pk_no(SilcPurpleBuddyRes r)
g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"),
r->b->name);
purple_notify_error(r->client->application, _("Add Buddy"), tmp,
- _("You cannot receive buddy notifications until you "
- "import his/her public key. You can use the Get Public Key "
- "command to get the public key."));
+ _("You cannot receive buddy notifications until you "
+ "import his/her public key. You can use the Get Public Key "
+ "command to get the public key."));
purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
}
static void
-silcpurple_add_buddy_save(bool success, void *context)
+silcpurple_add_buddy_save(SilcBool success, void *context)
{
SilcPurpleBuddyRes r = context;
PurpleBuddy *b = r->b;
- SilcClient client = r->client;
SilcClientEntry client_entry;
SilcAttributePayload attr;
SilcAttribute attribute;
SilcVCardStruct vcard;
- SilcAttributeObjMime message, extension;
+ SilcMime message = NULL, extension = NULL;
#ifdef SILC_ATTRIBUTE_USER_ICON
- SilcAttributeObjMime usericon;
+ SilcMime usericon = NULL;
#endif
SilcAttributeObjPk serverpk, usersign, serversign;
gboolean usign_success = TRUE, ssign_success = TRUE;
char filename[512], filename2[512], *fingerprint = NULL, *tmp;
SilcUInt32 len;
+ SilcHash hash;
int i;
if (!success) {
/* The user did not trust the public key. */
silcpurple_add_buddy_pk_no(r);
+ silc_free(r->offline_pk);
+ if (r->public_key)
+ silc_pkcs_public_key_free(r->public_key);
silc_free(r);
return;
}
@@ -783,6 +774,8 @@ silcpurple_add_buddy_save(bool success, void *context)
purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
silc_free(fingerprint);
silc_free(r->offline_pk);
+ if (r->public_key)
+ silc_pkcs_public_key_free(r->public_key);
silc_free(r);
return;
}
@@ -791,16 +784,15 @@ silcpurple_add_buddy_save(bool success, void *context)
client_entry = silc_client_get_client_by_id(r->client, r->conn,
&r->client_id);
if (!client_entry) {
+ silc_free(r->offline_pk);
+ silc_pkcs_public_key_free(r->public_key);
+ if (r->public_key)
+ silc_pkcs_public_key_free(r->public_key);
silc_free(r);
return;
}
memset(&vcard, 0, sizeof(vcard));
- memset(&message, 0, sizeof(message));
- memset(&extension, 0, sizeof(extension));
-#ifdef SILC_ATTRIBUTE_USER_ICON
- memset(&usericon, 0, sizeof(usericon));
-#endif
memset(&serverpk, 0, sizeof(serverpk));
memset(&usersign, 0, sizeof(usersign));
memset(&serversign, 0, sizeof(serversign));
@@ -822,21 +814,24 @@ silcpurple_add_buddy_save(bool success, void *context)
break;
case SILC_ATTRIBUTE_STATUS_MESSAGE:
- if (!silc_attribute_get_object(attr, (void *)&message,
- sizeof(message)))
+ message = silc_mime_alloc();
+ if (!silc_attribute_get_object(attr, (void *)message,
+ sizeof(*message)))
continue;
break;
case SILC_ATTRIBUTE_EXTENSION:
- if (!silc_attribute_get_object(attr, (void *)&extension,
- sizeof(extension)))
+ extension = silc_mime_alloc();
+ if (!silc_attribute_get_object(attr, (void *)extension,
+ sizeof(*extension)))
continue;
break;
#ifdef SILC_ATTRIBUTE_USER_ICON
case SILC_ATTRIBUTE_USER_ICON:
- if (!silc_attribute_get_object(attr, (void *)&usericon,
- sizeof(usericon)))
+ usericon = silc_mime_alloc();
+ if (!silc_attribute_get_object(attr, (void *)usericon,
+ sizeof(*usericon)))
continue;
break;
#endif
@@ -872,50 +867,54 @@ silcpurple_add_buddy_save(bool success, void *context)
}
/* Verify the attribute signatures */
+ silc_hash_alloc((const unsigned char *)"sha1", &hash);
if (usersign.data) {
- SilcPKCS pkcs;
unsigned char *verifyd;
SilcUInt32 verify_len;
- silc_pkcs_alloc((unsigned char*)"rsa", &pkcs);
verifyd = silc_attribute_get_verify_data(client_entry->attrs,
FALSE, &verify_len);
- if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){
- if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
- usersign.data,
- usersign.data_len,
- verifyd, verify_len))
- usign_success = FALSE;
- }
+ if (verifyd && !silc_pkcs_verify(client_entry->public_key,
+ usersign.data,
+ usersign.data_len,
+ verifyd, verify_len, hash))
+ usign_success = FALSE;
silc_free(verifyd);
}
- if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) {
+ if (serversign.data) {
SilcPublicKey public_key;
- SilcPKCS pkcs;
+ SilcPKCSType type = 0;
unsigned char *verifyd;
SilcUInt32 verify_len;
- if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len,
- &public_key)) {
- silc_pkcs_alloc((unsigned char *)"rsa", &pkcs);
+ if (!strcmp(serverpk.type, "silc-rsa"))
+ type = SILC_PKCS_SILC;
+ else if (!strcmp(serverpk.type, "ssh-rsa"))
+ type = SILC_PKCS_SSH2;
+ else if (!strcmp(serverpk.type, "x509v3-sign-rsa"))
+ type = SILC_PKCS_X509V3;
+ else if (!strcmp(serverpk.type, "pgp-sign-rsa"))
+ type = SILC_PKCS_OPENPGP;
+
+ if (silc_pkcs_public_key_alloc(type, serverpk.data,
+ serverpk.data_len,
+ &public_key)) {
verifyd = silc_attribute_get_verify_data(client_entry->attrs,
TRUE, &verify_len);
- if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) {
- if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
- serversign.data,
- serversign.data_len,
- verifyd, verify_len))
- ssign_success = FALSE;
- }
+ if (verifyd && !silc_pkcs_verify(public_key,
+ serversign.data,
+ serversign.data_len,
+ verifyd, verify_len,
+ hash))
+ ssign_success = FALSE;
silc_pkcs_public_key_free(public_key);
silc_free(verifyd);
}
}
- fingerprint = silc_fingerprint(client_entry->fingerprint,
- client_entry->fingerprint_len);
+ fingerprint = silc_fingerprint(client_entry->fingerprint, 20);
for (i = 0; i < strlen(fingerprint); i++)
if (fingerprint[i] == ' ')
fingerprint[i] = '_';
@@ -954,46 +953,45 @@ silcpurple_add_buddy_save(bool success, void *context)
}
/* Save status message */
- if (message.mime) {
+ if (message) {
memset(filename2, 0, sizeof(filename2));
g_snprintf(filename2, sizeof(filename2) - 1,
"%s" G_DIR_SEPARATOR_S "status_message.mime",
filename);
- silc_file_writefile(filename2, (char *)message.mime,
- message.mime_len);
+ tmp = (char *)silc_mime_get_data(message, &len);
+ silc_file_writefile(filename2, tmp, len);
+ silc_mime_free(message);
}
/* Save extension data */
- if (extension.mime) {
+ if (extension) {
memset(filename2, 0, sizeof(filename2));
g_snprintf(filename2, sizeof(filename2) - 1,
"%s" G_DIR_SEPARATOR_S "extension.mime",
filename);
- silc_file_writefile(filename2, (char *)extension.mime,
- extension.mime_len);
+ tmp = (char *)silc_mime_get_data(extension, &len);
+ silc_file_writefile(filename2, tmp, len);
+ silc_mime_free(extension);
}
#ifdef SILC_ATTRIBUTE_USER_ICON
/* Save user icon */
- if (usericon.mime) {
- SilcMime m = silc_mime_decode(usericon.mime,
- usericon.mime_len);
- if (m) {
- const char *type = silc_mime_get_field(m, "Content-Type");
- if (!strcmp(type, "image/jpeg") ||
- !strcmp(type, "image/gif") ||
- !strcmp(type, "image/bmp") ||
- !strcmp(type, "image/png")) {
- const unsigned char *data;
- SilcUInt32 data_len;
- data = silc_mime_get_data(m, &data_len);
- if (data) {
- /* TODO: Check if SILC gives us something to use as the checksum instead */
- purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup(data, data_len), data_len, NULL);
- }
+ if (usericon) {
+ const char *type = silc_mime_get_field(usericon, "Content-Type");
+ if (type &&
+ (!strcmp(type, "image/jpeg") ||
+ !strcmp(type, "image/gif") ||
+ !strcmp(type, "image/bmp") ||
+ !strcmp(type, "image/png"))) {
+ const unsigned char *data;
+ SilcUInt32 data_len;
+ data = silc_mime_get_data(usericon, &data_len);
+ if (data) {
+ /* TODO: Check if SILC gives us something to use as the checksum instead */
+ purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup(data, data_len), data_len, NULL);
}
- silc_mime_free(m);
}
+ silc_mime_free(usericon);
}
#endif
}
@@ -1015,7 +1013,11 @@ silcpurple_add_buddy_save(bool success, void *context)
silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey",
filename2, NULL);
+ silc_hash_free(hash);
silc_free(fingerprint);
+ silc_free(r->offline_pk);
+ if (r->public_key)
+ silc_pkcs_public_key_free(r->public_key);
silc_free(r);
}
@@ -1023,11 +1025,9 @@ static void
silcpurple_add_buddy_ask_import(void *user_data, const char *name)
{
SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data;
- SilcPublicKey public_key;
/* Load the public key */
- if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
+ if (!silc_pkcs_load_public_key(name, &r->public_key)) {
silcpurple_add_buddy_ask_pk_cb(r, 0);
purple_notify_error(r->client->application,
_("Add Buddy"), _("Could not load public key"), NULL);
@@ -1035,12 +1035,10 @@ silcpurple_add_buddy_ask_import(void *user_data, const char *name)
}
/* Now verify the public key */
- r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len);
+ r->offline_pk = silc_pkcs_public_key_encode(r->public_key, &r->offline_pk_len);
silcpurple_verify_public_key(r->client, r->conn, r->b->name,
- SILC_SOCKET_TYPE_CLIENT,
- r->offline_pk, r->offline_pk_len,
- SILC_SKE_PK_TYPE_SILC,
- silcpurple_add_buddy_save, r);
+ SILC_CONN_CLIENT, r->public_key,
+ silcpurple_add_buddy_save, r);
}
static void
@@ -1065,9 +1063,9 @@ silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id)
/* Open file selector to select the public key. */
purple_request_file(r->client->application, _("Open..."), NULL, FALSE,
- G_CALLBACK(silcpurple_add_buddy_ask_import),
- G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
- purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+ G_CALLBACK(silcpurple_add_buddy_ask_import),
+ G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
+ purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
}
@@ -1078,20 +1076,29 @@ silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r)
g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
r->b->name);
purple_request_action(r->client->application, _("Add Buddy"), tmp,
- _("To add the buddy you must import his/her public key. "
- "Press Import to import a public key."), 0,
- purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
- _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
- _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
+ _("To add the buddy you must import his/her public key. "
+ "Press Import to import a public key."), 0,
+ purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
+ _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
+ _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
}
-static void
-silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r,
- SilcClientCommandReplyContext cmd)
+static SilcBool
+silcpurple_add_buddy_getkey_cb(SilcClient client, SilcClientConnection conn,
+ SilcCommand command, SilcStatus status,
+ SilcStatus error, void *context, va_list ap)
{
+ SilcPurpleBuddyRes r = context;
SilcClientEntry client_entry;
- unsigned char *pk;
- SilcUInt32 pk_len;
+
+ if (status != SILC_STATUS_OK) {
+ /* The buddy is offline/nonexistent. We will require user
+ to associate a public key with the buddy or the buddy
+ cannot be added. */
+ r->offline = TRUE;
+ silcpurple_add_buddy_ask_pk(r);
+ return FALSE;
+ }
/* Get the client entry. */
client_entry = silc_client_get_client_by_id(r->client, r->conn,
@@ -1102,16 +1109,14 @@ silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r,
cannot be added. */
r->offline = TRUE;
silcpurple_add_buddy_ask_pk(r);
- return;
+ return FALSE;
}
/* Now verify the public key */
- pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname,
- SILC_SOCKET_TYPE_CLIENT,
- pk, pk_len, SILC_SKE_PK_TYPE_SILC,
- silcpurple_add_buddy_save, r);
- silc_free(pk);
+ SILC_CONN_CLIENT, client_entry->public_key,
+ silcpurple_add_buddy_save, r);
+ return TRUE;
}
static void
@@ -1120,6 +1125,7 @@ silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r, PurpleRequestFields *fields
PurpleRequestField *f;
const GList *list;
SilcClientEntry client_entry;
+ SilcDList clients;
f = purple_request_fields_get_field(fields, "list");
list = purple_request_field_list_get_selected(f);
@@ -1131,7 +1137,11 @@ silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r, PurpleRequestFields *fields
}
client_entry = purple_request_field_list_get_data(f, list->data);
- silcpurple_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r);
+ clients = silc_dlist_init();
+ silc_dlist_add(clients, client_entry);
+ silcpurple_add_buddy_resolved(r->client, r->conn, SILC_STATUS_OK,
+ clients, r);
+ silc_dlist_uninit(clients);
}
static void
@@ -1143,16 +1153,14 @@ silcpurple_add_buddy_select_cancel(SilcPurpleBuddyRes r, PurpleRequestFields *fi
}
static void
-silcpurple_add_buddy_select(SilcPurpleBuddyRes r,
- SilcClientEntry *clients,
- SilcUInt32 clients_count)
+silcpurple_add_buddy_select(SilcPurpleBuddyRes r, SilcDList clients)
{
PurpleRequestFields *fields;
PurpleRequestFieldGroup *g;
PurpleRequestField *f;
char tmp[512], tmp2[128];
- int i;
char *fingerprint;
+ SilcClientEntry client_entry;
fields = purple_request_fields_new();
g = purple_request_field_group_new(NULL);
@@ -1161,56 +1169,56 @@ silcpurple_add_buddy_select(SilcPurpleBuddyRes r,
purple_request_field_list_set_multi_select(f, FALSE);
purple_request_fields_add_group(fields, g);
- for (i = 0; i < clients_count; i++) {
+ silc_dlist_start(clients);
+ while ((client_entry = silc_dlist_get(clients))) {
fingerprint = NULL;
- if (clients[i]->fingerprint) {
- fingerprint = silc_fingerprint(clients[i]->fingerprint,
- clients[i]->fingerprint_len);
+ if (*client_entry->fingerprint) {
+ fingerprint = silc_fingerprint(client_entry->fingerprint, 20);
g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint);
}
g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s",
- clients[i]->realname, clients[i]->nickname,
- clients[i]->username, clients[i]->hostname ?
- clients[i]->hostname : "",
+ client_entry->realname, client_entry->nickname,
+ client_entry->username, *client_entry->hostname ?
+ client_entry->hostname : "",
fingerprint ? tmp2 : "");
- purple_request_field_list_add(f, tmp, clients[i]);
+ purple_request_field_list_add(f, tmp, client_entry);
silc_free(fingerprint);
}
purple_request_fields(r->client->application, _("Add Buddy"),
- _("Select correct user"),
- r->pubkey_search
- ? _("More than one user was found with the same public key. Select "
- "the correct user from the list to add to the buddy list.")
- : _("More than one user was found with the same name. Select "
- "the correct user from the list to add to the buddy list."),
- fields,
- _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
- _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
- purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+ _("Select correct user"),
+ r->pubkey_search
+ ? _("More than one user was found with the same public key. Select "
+ "the correct user from the list to add to the buddy list.")
+ : _("More than one user was found with the same name. Select "
+ "the correct user from the list to add to the buddy list."),
+ fields,
+ _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
+ _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
+ purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
}
static void
silcpurple_add_buddy_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList clients,
+ void *context)
{
SilcPurpleBuddyRes r = context;
PurpleBuddy *b = r->b;
SilcAttributePayload pub;
SilcAttributeObjPk userpk;
- unsigned char *pk;
- SilcUInt32 pk_len;
const char *filename;
+ SilcClientEntry client_entry = NULL;
+ SilcUInt16 cmd_ident;
filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
/* If the buddy is offline/nonexistent, we will require user
to associate a public key with the buddy or the buddy
cannot be added. */
- if (!clients_count) {
+ if (!clients) {
if (r->init) {
silc_free(r);
return;
@@ -1228,33 +1236,37 @@ silcpurple_add_buddy_resolved(SilcClient client,
/* If more than one client was found with nickname, we need to verify
from user which one is the correct. */
- if (clients_count > 1 && !r->pubkey_search) {
+ if (silc_dlist_count(clients) > 1 && !r->pubkey_search) {
if (r->init) {
silc_free(r);
return;
}
- silcpurple_add_buddy_select(r, clients, clients_count);
+ silcpurple_add_buddy_select(r, clients);
return;
}
+ silc_dlist_start(clients);
+ client_entry = silc_dlist_get(clients);
+
/* If we searched using public keys and more than one entry was found
the same person is logged on multiple times. */
- if (clients_count > 1 && r->pubkey_search && b->name) {
+ if (silc_dlist_count(clients) > 1 && r->pubkey_search && b->name) {
if (r->init) {
/* Find the entry that closest matches to the
buddy nickname. */
- int i;
- for (i = 0; i < clients_count; i++) {
- if (!strncasecmp(b->name, clients[i]->nickname,
+ SilcClientEntry entry;
+ silc_dlist_start(clients);
+ while ((entry = silc_dlist_get(clients))) {
+ if (!strncasecmp(b->name, entry->nickname,
strlen(b->name))) {
- clients[0] = clients[i];
+ client_entry = entry;
break;
}
}
} else {
/* Verify from user which one is correct */
- silcpurple_add_buddy_select(r, clients, clients_count);
+ silcpurple_add_buddy_select(r, clients);
return;
}
}
@@ -1262,61 +1274,60 @@ silcpurple_add_buddy_resolved(SilcClient client,
/* The client was found. Now get its public key and verify
that before adding the buddy. */
memset(&userpk, 0, sizeof(userpk));
- b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id));
- r->client_id = *clients[0]->id;
+ b->proto_data = silc_memdup(&client_entry->id, sizeof(client_entry->id));
+ r->client_id = client_entry->id;
/* Get the public key from attributes, if not present then
resolve it with GETKEY unless we have it cached already. */
- if (clients[0]->attrs && !clients[0]->public_key) {
- pub = silcpurple_get_attr(clients[0]->attrs,
- SILC_ATTRIBUTE_USER_PUBLIC_KEY);
+ if (client_entry->attrs && !client_entry->public_key) {
+ pub = silcpurple_get_attr(client_entry->attrs,
+ SILC_ATTRIBUTE_USER_PUBLIC_KEY);
if (!pub || !silc_attribute_get_object(pub, (void *)&userpk,
sizeof(userpk))) {
/* Get public key with GETKEY */
- silc_client_command_call(client, conn, NULL,
- "GETKEY", clients[0]->nickname, NULL);
+ cmd_ident =
+ silc_client_command_call(client, conn, NULL,
+ "GETKEY", client_entry->nickname, NULL);
silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
- conn->cmd_ident,
- (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
+ cmd_ident,
+ silcpurple_add_buddy_getkey_cb,
r);
return;
}
- if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len,
- &clients[0]->public_key))
+ if (!silc_pkcs_public_key_alloc(SILC_PKCS_SILC,
+ userpk.data, userpk.data_len,
+ &client_entry->public_key))
return;
silc_free(userpk.data);
- } else if (filename && !clients[0]->public_key) {
- if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key,
- SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(filename, &clients[0]->public_key,
- SILC_PKCS_FILE_BIN)) {
+ } else if (filename && !client_entry->public_key) {
+ if (!silc_pkcs_load_public_key(filename, &client_entry->public_key)) {
/* Get public key with GETKEY */
- silc_client_command_call(client, conn, NULL,
- "GETKEY", clients[0]->nickname, NULL);
+ cmd_ident =
+ silc_client_command_call(client, conn, NULL,
+ "GETKEY", client_entry->nickname, NULL);
silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
- conn->cmd_ident,
- (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
+ cmd_ident,
+ silcpurple_add_buddy_getkey_cb,
r);
return;
}
- } else if (!clients[0]->public_key) {
+ } else if (!client_entry->public_key) {
/* Get public key with GETKEY */
- silc_client_command_call(client, conn, NULL,
- "GETKEY", clients[0]->nickname, NULL);
+ cmd_ident =
+ silc_client_command_call(client, conn, NULL,
+ "GETKEY", client_entry->nickname, NULL);
silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
- conn->cmd_ident,
- (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
+ cmd_ident,
+ silcpurple_add_buddy_getkey_cb,
r);
return;
}
/* We have the public key, verify it. */
- pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len);
- silcpurple_verify_public_key(client, conn, clients[0]->nickname,
- SILC_SOCKET_TYPE_CLIENT,
- pk, pk_len, SILC_SKE_PK_TYPE_SILC,
- silcpurple_add_buddy_save, r);
- silc_free(pk);
+ silcpurple_verify_public_key(client, conn, client_entry->nickname,
+ SILC_CONN_CLIENT,
+ client_entry->public_key,
+ silcpurple_add_buddy_save, r);
}
static void
@@ -1344,10 +1355,7 @@ silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init)
SilcPublicKey public_key;
SilcAttributeObjPk userpk;
- if (!silc_pkcs_load_public_key(filename, &public_key,
- SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(filename, &public_key,
- SILC_PKCS_FILE_BIN))
+ if (!silc_pkcs_load_public_key(filename, &public_key))
return;
/* Get all attributes, and use the public key to search user */
@@ -1632,12 +1640,13 @@ GList *silcpurple_buddy_menu(PurpleBuddy *buddy)
sg->conn,
buddy->proto_data);
- if (client_entry && client_entry->send_key) {
+ if (client_entry &&
+ silc_client_private_message_key_is_set(sg->client,
+ sg->conn, client_entry)) {
act = purple_menu_action_new(_("Reset IM Key"),
PURPLE_CALLBACK(silcpurple_buddy_resetkey),
NULL, NULL);
m = g_list_append(m, act);
-
} else {
act = purple_menu_action_new(_("IM with Key Exchange"),
PURPLE_CALLBACK(silcpurple_buddy_keyagr),
@@ -1690,9 +1699,7 @@ void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
SilcClientConnection conn = sg->conn;
SilcMime mime;
char type[32];
- unsigned char *icon;
const char *t;
- SilcAttributeObjMime obj;
/* Remove */
if (!img) {
@@ -1717,12 +1724,9 @@ void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
silc_mime_add_field(mime, "Content-Type", type);
silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
- obj.mime = icon = silc_mime_encode(mime, &obj.mime_len);
- if (obj.mime)
- silc_client_attribute_add(client, conn,
- SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj));
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_USER_ICON, mime, sizeof(*mime));
- silc_free(icon);
silc_mime_free(mime);
}
#endif
diff --git a/libpurple/protocols/silc/chat.c b/libpurple/protocols/silc/chat.c
index 343a153999..a95bc9d2a5 100644
--- a/libpurple/protocols/silc/chat.c
+++ b/libpurple/protocols/silc/chat.c
@@ -4,7 +4,7 @@
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2004 Pekka Riikonen
+ Copyright (C) 2004 - 2007 Pekka Riikonen
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
@@ -17,7 +17,7 @@
*/
-#include "silcincludes.h"
+#include "silc.h"
#include "silcclient.h"
#include "silcpurple.h"
#include "wb.h"
@@ -61,10 +61,10 @@ silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components);
static void
silcpurple_chat_getinfo_res(SilcClient client,
- SilcClientConnection conn,
- SilcChannelEntry *channels,
- SilcUInt32 channels_count,
- void *context)
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList channels,
+ void *context)
{
GHashTable *components = context;
PurpleConnection *gc = client->application;
@@ -134,13 +134,14 @@ silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components)
}
silc_hash_table_list_reset(&htl);
- if (channel->channel_key)
+ if (channel->cipher)
g_string_append_printf(s, _("<br><b>Channel Cipher:</b> %s"),
- silc_cipher_get_name(channel->channel_key));
+ channel->cipher);
+
if (channel->hmac)
/* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */
g_string_append_printf(s, _("<br><b>Channel HMAC:</b> %s"),
- silc_hmac_get_name(channel->hmac));
+ channel->hmac);
if (channel->topic) {
tmp2 = g_markup_escape_text(channel->topic, -1);
@@ -211,7 +212,7 @@ typedef struct {
SilcPurple sg;
SilcChannelEntry channel;
PurpleChat *c;
- SilcBuffer pubkeys;
+ SilcDList pubkeys;
} *SilcPurpleChauth;
static void
@@ -227,22 +228,21 @@ silcpurple_chat_chpk_add(void *user_data, const char *name)
SilcUInt32 m;
/* Load the public key */
- if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
+ if (!silc_pkcs_load_public_key(name, &public_key)) {
silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
- silc_buffer_free(sgc->pubkeys);
+ silc_dlist_uninit(sgc->pubkeys);
silc_free(sgc);
purple_notify_error(client->application,
- _("Add Channel Public Key"),
- _("Could not load public key"), NULL);
+ _("Add Channel Public Key"),
+ _("Could not load public key"), NULL);
return;
}
- pk = silc_pkcs_public_key_payload_encode(public_key);
+ pk = silc_public_key_payload_encode(public_key);
chpks = silc_buffer_alloc_size(2);
SILC_PUT16_MSB(1, chpks->head);
chpks = silc_argument_payload_encode_one(chpks, pk->data,
- pk->len, 0x00);
+ silc_buffer_len(pk), 0x00);
silc_buffer_free(pk);
m = sgc->channel->mode;
@@ -250,15 +250,20 @@ silcpurple_chat_chpk_add(void *user_data, const char *name)
/* Send CMODE */
SILC_PUT32_MSB(m, mode);
- chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL);
+ chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL);
silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
- ++conn->cmd_ident, 3,
- 1, chidp->data, chidp->len,
+ silcpurple_command_reply, NULL, 3,
+ 1, chidp->data, silc_buffer_len(chidp),
2, mode, sizeof(mode),
- 9, chpks->data, chpks->len);
+ 9, chpks->data, silc_buffer_len(chpks));
silc_buffer_free(chpks);
silc_buffer_free(chidp);
- silc_buffer_free(sgc->pubkeys);
+ if (sgc->pubkeys) {
+ silc_dlist_start(sgc->pubkeys);
+ while ((public_key = silc_dlist_get(sgc->pubkeys)))
+ silc_pkcs_public_key_free(public_key);
+ silc_dlist_uninit(sgc->pubkeys);
+ }
silc_free(sgc);
}
@@ -266,8 +271,16 @@ static void
silcpurple_chat_chpk_cancel(void *user_data, const char *name)
{
SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
+ SilcPublicKey public_key;
+
silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
- silc_buffer_free(sgc->pubkeys);
+
+ if (sgc->pubkeys) {
+ silc_dlist_start(sgc->pubkeys);
+ while ((public_key = silc_dlist_get(sgc->pubkeys)))
+ silc_pkcs_public_key_free(public_key);
+ silc_dlist_uninit(sgc->pubkeys);
+ }
silc_free(sgc);
}
@@ -289,9 +302,9 @@ silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields)
if (!purple_request_field_list_get_selected(f)) {
/* Add new public key */
purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE,
- G_CALLBACK(silcpurple_chat_chpk_add),
- G_CALLBACK(silcpurple_chat_chpk_cancel),
- purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+ G_CALLBACK(silcpurple_chat_chpk_add),
+ G_CALLBACK(silcpurple_chat_chpk_cancel),
+ purple_connection_get_account(sg->gc), NULL, NULL, sgc);
return;
}
@@ -302,13 +315,12 @@ silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields)
public_key = purple_request_field_list_get_data(f, list->data);
if (purple_request_field_list_is_selected(f, list->data)) {
/* Delete this public key */
- pk = silc_pkcs_public_key_payload_encode(public_key);
+ pk = silc_public_key_payload_encode(public_key);
chpks = silc_argument_payload_encode_one(chpks, pk->data,
- pk->len, 0x01);
+ silc_buffer_len(pk), 0x01);
silc_buffer_free(pk);
c++;
}
- silc_pkcs_public_key_free(public_key);
}
if (!c) {
silc_buffer_free(chpks);
@@ -322,15 +334,20 @@ silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields)
/* Send CMODE */
SILC_PUT32_MSB(m, mode);
- chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL);
+ chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL);
silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
- ++conn->cmd_ident, 3,
- 1, chidp->data, chidp->len,
+ silcpurple_command_reply, NULL, 3,
+ 1, chidp->data, silc_buffer_len(chidp),
2, mode, sizeof(mode),
- 9, chpks->data, chpks->len);
+ 9, chpks->data, silc_buffer_len(chpks));
silc_buffer_free(chpks);
silc_buffer_free(chidp);
- silc_buffer_free(sgc->pubkeys);
+ if (sgc->pubkeys) {
+ silc_dlist_start(sgc->pubkeys);
+ while ((public_key = silc_dlist_get(sgc->pubkeys)))
+ silc_pkcs_public_key_free(public_key);
+ silc_dlist_uninit(sgc->pubkeys);
+ }
silc_free(sgc);
}
@@ -339,6 +356,7 @@ silcpurple_chat_chauth_ok(SilcPurpleChauth sgc, PurpleRequestFields *fields)
{
SilcPurple sg = sgc->sg;
PurpleRequestField *f;
+ SilcPublicKey public_key;
const char *curpass, *val;
int set;
@@ -365,19 +383,23 @@ silcpurple_chat_chauth_ok(SilcPurpleChauth sgc, PurpleRequestFields *fields)
purple_blist_node_remove_setting((PurpleBlistNode *)sgc->c, "passphrase");
}
- silc_buffer_free(sgc->pubkeys);
+ if (sgc->pubkeys) {
+ silc_dlist_start(sgc->pubkeys);
+ while ((public_key = silc_dlist_get(sgc->pubkeys)))
+ silc_pkcs_public_key_free(public_key);
+ silc_dlist_uninit(sgc->pubkeys);
+ }
silc_free(sgc);
}
void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
- SilcBuffer channel_pubkeys)
+ SilcDList channel_pubkeys)
{
- SilcUInt16 argc;
- SilcArgumentPayload chpks;
+ SilcPublicKey public_key;
+ SilcSILCPublicKey silc_pubkey;
unsigned char *pk;
- SilcUInt32 pk_len, type;
+ SilcUInt32 pk_len;
char *fingerprint, *babbleprint;
- SilcPublicKey pubkey;
SilcPublicKeyIdentifier ident;
char tmp2[1024], t[512];
PurpleRequestFields *fields;
@@ -399,7 +421,7 @@ void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
g = purple_request_field_group_new(NULL);
f = purple_request_field_string_new("passphrase", _("Channel Passphrase"),
- curpass, FALSE);
+ curpass, FALSE);
purple_request_field_string_set_masked(f, TRUE);
purple_request_field_group_add_field(g, f);
purple_request_fields_add_group(fields, g);
@@ -416,55 +438,49 @@ void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
"is required to be able to join. If channel public keys are set "
"then only users whose public keys are listed are able to join."));
- if (!channel_pubkeys) {
+ if (!channel_pubkeys || !silc_dlist_count(channel_pubkeys)) {
f = purple_request_field_list_new("list", NULL);
purple_request_field_group_add_field(g, f);
purple_request_fields(sg->gc, _("Channel Authentication"),
- _("Channel Authentication"), t, fields,
- _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
- _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
- purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+ _("Channel Authentication"), t, fields,
+ _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
+ _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
+ purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+ if (channel_pubkeys)
+ silc_dlist_uninit(channel_pubkeys);
return;
}
- sgc->pubkeys = silc_buffer_copy(channel_pubkeys);
+ sgc->pubkeys = channel_pubkeys;
g = purple_request_field_group_new(NULL);
f = purple_request_field_list_new("list", NULL);
purple_request_field_group_add_field(g, f);
purple_request_fields_add_group(fields, g);
- SILC_GET16_MSB(argc, channel_pubkeys->data);
- chpks = silc_argument_payload_parse(channel_pubkeys->data + 2,
- channel_pubkeys->len - 2, argc);
- if (!chpks)
- return;
-
- pk = silc_argument_get_first_arg(chpks, &type, &pk_len);
- while (pk) {
+ silc_dlist_start(channel_pubkeys);
+ while ((public_key = silc_dlist_get(channel_pubkeys))) {
+ pk = silc_pkcs_public_key_encode(public_key, &pk_len);
fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
- silc_pkcs_public_key_payload_decode(pk, pk_len, &pubkey);
- ident = silc_pkcs_decode_identifier(pubkey->identifier);
+
+ silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
+ ident = &silc_pubkey->identifier;
g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s",
ident->realname ? ident->realname : ident->username ?
ident->username : "", fingerprint, babbleprint);
- purple_request_field_list_add(f, tmp2, pubkey);
+ purple_request_field_list_add(f, tmp2, public_key);
silc_free(fingerprint);
silc_free(babbleprint);
- silc_pkcs_free_identifier(ident);
- pk = silc_argument_get_next_arg(chpks, &type, &pk_len);
}
purple_request_field_list_set_multi_select(f, FALSE);
purple_request_fields(sg->gc, _("Channel Authentication"),
- _("Channel Authentication"), t, fields,
- _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
- _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
- purple_connection_get_account(sg->gc), NULL, NULL, sgc);
-
- silc_argument_payload_free(chpks);
+ _("Channel Authentication"), t, fields,
+ _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
+ _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
+ purple_connection_get_account(sg->gc), NULL, NULL, sgc);
}
static void
@@ -525,9 +541,9 @@ silcpurple_chat_prv_add(SilcPurpleCharPrv p, PurpleRequestFields *fields)
/* Add private group to buddy list */
g_snprintf(tmp, sizeof(tmp), "%s [Private Group]", name);
- comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
- g_hash_table_replace(comp, g_strdup("channel"), g_strdup(tmp));
- g_hash_table_replace(comp, g_strdup("passphrase"), g_strdup(passphrase));
+ comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+ g_hash_table_replace(comp, "channel", g_strdup(tmp));
+ g_hash_table_replace(comp, "passphrase", g_strdup(passphrase));
cn = purple_chat_new(sg->account, alias, comp);
g = (PurpleGroup *)p->c->node.parent;
@@ -596,9 +612,9 @@ silcpurple_chat_prv(PurpleBlistNode *node, gpointer data)
_("Please enter the %s channel private group name and passphrase."),
p->channel);
purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields,
- _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
- _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
- purple_connection_get_account(gc), NULL, NULL, p);
+ _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
+ _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
+ purple_connection_get_account(gc), NULL, NULL, p);
}
@@ -907,7 +923,7 @@ GList *silcpurple_chat_menu(PurpleChat *chat)
m = g_list_append(m, act);
}
- if (mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (chu && mode & SILC_CHANNEL_UMODE_CHANFO) {
act = purple_menu_action_new(_("Channel Authentication"),
PURPLE_CALLBACK(silcpurple_chat_chauth),
NULL, NULL);
@@ -926,7 +942,7 @@ GList *silcpurple_chat_menu(PurpleChat *chat)
}
}
- if (mode & SILC_CHANNEL_UMODE_CHANOP) {
+ if (chu && mode & SILC_CHANNEL_UMODE_CHANOP) {
act = purple_menu_action_new(_("Set User Limit"),
PURPLE_CALLBACK(silcpurple_chat_ulimit),
NULL, NULL);
@@ -969,7 +985,7 @@ GList *silcpurple_chat_menu(PurpleChat *chat)
}
}
- if (channel) {
+ if (chu && channel) {
SilcPurpleChatWb wb;
wb = silc_calloc(1, sizeof(*wb));
wb->sg = sg;
@@ -986,86 +1002,10 @@ GList *silcpurple_chat_menu(PurpleChat *chat)
/******************************* Joining Etc. ********************************/
-void silcpurple_chat_join_done(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- SilcChannelEntry channel = context;
- PurpleConversation *convo;
- SilcUInt32 retry = SILC_PTR_TO_32(channel->context);
- SilcHashTableList htl;
- SilcChannelUser chu;
- GList *users = NULL, *flags = NULL;
- char tmp[256];
-
- if (!clients && retry < 1) {
- /* Resolving users failed, try again. */
- channel->context = SILC_32_TO_PTR(retry + 1);
- silc_client_get_clients_by_channel(client, conn, channel,
- silcpurple_chat_join_done, channel);
- return;
- }
-
- /* Add channel to Purple */
- channel->context = SILC_32_TO_PTR(++sg->channel_ids);
- serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name);
- convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
- if (!convo)
- return;
-
- /* Add all users to channel */
- silc_hash_table_list(channel->user_list, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE;
- if (!chu->client->nickname)
- continue;
- chu->context = SILC_32_TO_PTR(sg->channel_ids);
-
- if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
- f |= PURPLE_CBFLAGS_FOUNDER;
- if (chu->mode & SILC_CHANNEL_UMODE_CHANOP)
- f |= PURPLE_CBFLAGS_OP;
- users = g_list_append(users, g_strdup(chu->client->nickname));
- flags = g_list_append(flags, GINT_TO_POINTER(f));
-
- if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
- if (chu->client == conn->local_entry)
- g_snprintf(tmp, sizeof(tmp),
- _("You are channel founder on <I>%s</I>"),
- channel->channel_name);
- else
- g_snprintf(tmp, sizeof(tmp),
- _("Channel founder on <I>%s</I> is <I>%s</I>"),
- channel->channel_name, chu->client->nickname);
-
- purple_conversation_write(convo, NULL, tmp,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
-
- }
- }
- silc_hash_table_list_reset(&htl);
-
- purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE);
- g_list_free(users);
- g_list_free(flags);
-
- /* Set topic */
- if (channel->topic)
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic);
-
- /* Set nick */
- purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname);
-}
-
char *silcpurple_get_chat_name(GHashTable *data)
{
return g_strdup(g_hash_table_lookup(data, "channel"));
-}
+}
void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
{
@@ -1073,6 +1013,9 @@ void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
const char *channel, *passphrase, *parentch;
+#if 0
+ PurpleChat *chat;
+#endif
if (!conn)
return;
@@ -1128,6 +1071,22 @@ void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
return;
}
+#if 0
+ /* If the channel is not on buddy list, automatically add it there. */
+ chat = purple_blist_find_chat(sg->account, channel);
+ if (!chat) {
+ data = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ g_hash_table_replace(data, g_strdup("channel"),
+ g_strdup(channel));
+ if (passphrase)
+ g_hash_table_replace(data, g_strdup("passphrase"),
+ g_strdup(passphrase));
+ chat = purple_chat_new(sg->account, NULL, data);
+ purple_blist_add_chat(chat, NULL, NULL);
+ }
+#endif
+
/* XXX We should have other properties here as well:
1. whether to try to authenticate to the channel
1a. with default key,
@@ -1150,7 +1109,7 @@ void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
}
void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
- const char *name)
+ const char *name)
{
SilcPurple sg = gc->proto_data;
SilcClient client = sg->client;
@@ -1264,7 +1223,8 @@ void silcpurple_chat_leave(PurpleConnection *gc, int id)
}
}
-int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags msgflags)
+int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
+ PurpleMessageFlags msgflags)
{
SilcPurple sg = gc->proto_data;
SilcClient client = sg->client;
@@ -1274,10 +1234,13 @@ int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMe
SilcChannelEntry channel = NULL;
SilcChannelPrivateKey key = NULL;
SilcUInt32 flags;
- int ret;
+ int ret = 0;
char *msg2, *tmp;
gboolean found = FALSE;
gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
+#ifdef HAVE_SILCMIME_H
+ SilcDList list;
+#endif
if (!msg || !conn)
return 0;
@@ -1297,7 +1260,7 @@ int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMe
} else if (strlen(msg) > 1 && msg[0] == '/') {
if (!silc_client_command_call(client, conn, msg + 1))
purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
- _("Unknown command"));
+ _("Unknown command"));
g_free(tmp);
return 0;
}
@@ -1346,10 +1309,37 @@ int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMe
channel = chu->channel;
}
+#ifdef HAVE_SILCMIME_H
+ /* Check for images */
+ if (msgflags & PURPLE_MESSAGE_IMAGES) {
+ list = silcpurple_image_message(msg, &flags);
+ if (list) {
+ /* Send one or more MIME message. If more than one, they
+ are MIME fragments due to over large message */
+ SilcBuffer buf;
+
+ silc_dlist_start(list);
+ while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
+ ret =
+ silc_client_send_channel_message(client, conn,
+ channel, key,
+ flags, NULL,
+ buf->data,
+ silc_buffer_len(buf));
+ silc_mime_partial_free(list);
+ g_free(tmp);
+
+ if (ret)
+ serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, msg, time(NULL));
+ return ret;
+ }
+ }
+#endif
+
/* Send channel message */
ret = silc_client_send_channel_message(client, conn, channel, key,
- flags, (unsigned char *)msg2,
- strlen(msg2), TRUE);
+ flags, NULL, (unsigned char *)msg2,
+ strlen(msg2));
if (ret) {
serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, msg,
time(NULL));
diff --git a/libpurple/protocols/silc/ft.c b/libpurple/protocols/silc/ft.c
index 39e74495b7..05fa942305 100644
--- a/libpurple/protocols/silc/ft.c
+++ b/libpurple/protocols/silc/ft.c
@@ -4,7 +4,7 @@
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2004 Pekka Riikonen
+ Copyright (C) 2004 - 2007 Pekka Riikonen
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
@@ -17,7 +17,7 @@
*/
-#include "silcincludes.h"
+#include "silc.h"
#include "silcclient.h"
#include "silcpurple.h"
@@ -74,11 +74,23 @@ silcpurple_ftp_monitor(SilcClient client,
char tmp[256];
if (status == SILC_CLIENT_FILE_MONITOR_CLOSED) {
+ /* All started sessions terminate here */
+ xfer->xfer->data = NULL;
purple_xfer_unref(xfer->xfer);
silc_free(xfer);
return;
}
+ if (status == SILC_CLIENT_FILE_MONITOR_DISCONNECT) {
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Error during file transfer"),
+ _("Remote disconnected"));
+ xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_REMOTE;
+ purple_xfer_update_progress(xfer->xfer);
+ silc_client_file_close(client, conn, session_id);
+ return;
+ }
+
if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT)
return;
@@ -96,17 +108,22 @@ silcpurple_ftp_monitor(SilcClient client,
purple_notify_error(gc, _("Secure File Transfer"),
_("Error during file transfer"),
_("Key agreement failed"));
+ } else if (error == SILC_CLIENT_FILE_TIMEOUT) {
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Error during file transfer"),
+ _("Connection timedout"));
+ } else if (error == SILC_CLIENT_FILE_CONNECT_FAILED) {
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Error during file transfer"),
+ _("Creating connection failed"));
} else if (error == SILC_CLIENT_FILE_UNKNOWN_SESSION) {
purple_notify_error(gc, _("Secure File Transfer"),
_("Error during file transfer"),
_("File transfer session does not exist"));
- } else {
- purple_notify_error(gc, _("Secure File Transfer"),
- _("Error during file transfer"), NULL);
}
+ xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_REMOTE;
+ purple_xfer_update_progress(xfer->xfer);
silc_client_file_close(client, conn, session_id);
- purple_xfer_unref(xfer->xfer);
- silc_free(xfer);
return;
}
@@ -133,6 +150,10 @@ static void
silcpurple_ftp_cancel(PurpleXfer *x)
{
SilcPurpleXfer xfer = x->data;
+
+ if (!xfer)
+ return;
+
xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL;
purple_xfer_update_progress(xfer->xfer);
silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
@@ -143,6 +164,9 @@ silcpurple_ftp_ask_name_cancel(PurpleXfer *x)
{
SilcPurpleXfer xfer = x->data;
+ if (!xfer)
+ return;
+
/* Cancel the transmission */
xfer->completion(NULL, xfer->completion_context);
silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
@@ -154,6 +178,9 @@ silcpurple_ftp_ask_name_ok(PurpleXfer *x)
SilcPurpleXfer xfer = x->data;
const char *name;
+ if (!xfer)
+ return;
+
name = purple_xfer_get_local_filename(x);
g_unlink(name);
xfer->completion(name, xfer->completion_context);
@@ -187,17 +214,57 @@ silcpurple_ftp_request_result(PurpleXfer *x)
SilcPurpleXfer xfer = x->data;
SilcClientFileError status;
PurpleConnection *gc = xfer->sg->gc;
+ SilcClientConnectionParams params;
+ gboolean local = xfer->hostname ? FALSE : TRUE;
+ char *local_ip = NULL, *remote_ip = NULL;
+ SilcSocket sock;
if (purple_xfer_get_status(x) != PURPLE_XFER_STATUS_ACCEPTED)
return;
+ if (!xfer)
+ return;
+
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(xfer->sg->conn->stream),
+ &sock, NULL, NULL, NULL);
+
+ if (local) {
+ /* Do the same magic what we do with key agreement (see silcpurple_buddy.c)
+ to see if we are behind NAT. */
+ if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) {
+ /* Check if the IP is private */
+ if (silcpurple_ip_is_private(local_ip)) {
+ local = TRUE;
+ /* Local IP is private, resolve the remote server IP to see whether
+ we are talking to Internet or just on LAN. */
+ if (silc_net_check_host_by_sock(sock, NULL,
+ &remote_ip))
+ if (silcpurple_ip_is_private(remote_ip))
+ /* We assume we are in LAN. Let's provide the connection point. */
+ local = TRUE;
+ }
+ }
+
+ if (local && !local_ip)
+ local_ip = silc_net_localip();
+ }
+
+ memset(&params, 0, sizeof(params));
+ params.timeout_secs = 60;
+ if (local)
+ /* Provide connection point */
+ params.local_ip = local_ip;
/* Start the file transfer */
status = silc_client_file_receive(xfer->sg->client, xfer->sg->conn,
+ &params, xfer->sg->public_key,
+ xfer->sg->private_key,
silcpurple_ftp_monitor, xfer,
NULL, xfer->session_id,
silcpurple_ftp_ask_name, xfer);
switch (status) {
case SILC_CLIENT_FILE_OK:
+ silc_free(local_ip);
+ silc_free(remote_ip);
return;
break;
@@ -227,6 +294,8 @@ silcpurple_ftp_request_result(PurpleXfer *x)
purple_xfer_unref(xfer->xfer);
g_free(xfer->hostname);
silc_free(xfer);
+ silc_free(local_ip);
+ silc_free(remote_ip);
}
static void
@@ -236,8 +305,8 @@ silcpurple_ftp_request_denied(PurpleXfer *x)
}
void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
- SilcClientEntry client_entry, SilcUInt32 session_id,
- const char *hostname, SilcUInt16 port)
+ SilcClientEntry client_entry, SilcUInt32 session_id,
+ const char *hostname, SilcUInt16 port)
{
PurpleConnection *gc = client->application;
SilcPurple sg = gc->proto_data;
@@ -255,7 +324,7 @@ void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
xfer->hostname = g_strdup(hostname);
xfer->port = port;
xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_RECEIVE,
- xfer->client_entry->nickname);
+ xfer->client_entry->nickname);
if (!xfer->xfer) {
silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
g_free(xfer->hostname);
@@ -277,10 +346,12 @@ static void
silcpurple_ftp_send_cancel(PurpleXfer *x)
{
SilcPurpleXfer xfer = x->data;
+
+ if (!xfer)
+ return;
+
+ /* This call will free all resources */
silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
- purple_xfer_unref(xfer->xfer);
- g_free(xfer->hostname);
- silc_free(xfer);
}
static void
@@ -290,19 +361,26 @@ silcpurple_ftp_send(PurpleXfer *x)
const char *name;
char *local_ip = NULL, *remote_ip = NULL;
gboolean local = TRUE;
+ SilcClientConnectionParams params;
+ SilcSocket sock;
+
+ if (!xfer)
+ return;
name = purple_xfer_get_local_filename(x);
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(xfer->sg->conn->stream),
+ &sock, NULL, NULL, NULL);
+
/* Do the same magic what we do with key agreement (see silcpurple_buddy.c)
to see if we are behind NAT. */
- if (silc_net_check_local_by_sock(xfer->sg->conn->sock->sock,
- NULL, &local_ip)) {
+ if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) {
/* Check if the IP is private */
if (silcpurple_ip_is_private(local_ip)) {
local = FALSE;
/* Local IP is private, resolve the remote server IP to see whether
we are talking to Internet or just on LAN. */
- if (silc_net_check_host_by_sock(xfer->sg->conn->sock->sock, NULL,
+ if (silc_net_check_host_by_sock(sock, NULL,
&remote_ip))
if (silcpurple_ip_is_private(remote_ip))
/* We assume we are in LAN. Let's provide the connection point. */
@@ -313,10 +391,17 @@ silcpurple_ftp_send(PurpleXfer *x)
if (local && !local_ip)
local_ip = silc_net_localip();
+ memset(&params, 0, sizeof(params));
+ params.timeout_secs = 60;
+ if (local)
+ /* Provide connection point */
+ params.local_ip = local_ip;
+
/* Send the file */
silc_client_file_send(xfer->sg->client, xfer->sg->conn,
+ xfer->client_entry, &params,
+ xfer->sg->public_key, xfer->sg->private_key,
silcpurple_ftp_monitor, xfer,
- local_ip, 0, !local, xfer->client_entry,
name, &xfer->session_id);
silc_free(local_ip);
@@ -325,10 +410,10 @@ silcpurple_ftp_send(PurpleXfer *x)
static void
silcpurple_ftp_send_file_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList clients,
+ void *context)
{
PurpleConnection *gc = client->application;
char tmp[256];
@@ -352,38 +437,29 @@ PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name)
SilcPurple sg = gc->proto_data;
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
+ SilcDList clients;
SilcPurpleXfer xfer;
- char *nickname;
g_return_val_if_fail(name != NULL, NULL);
- if (!silc_parse_userfqdn(name, &nickname, NULL))
- return NULL;
-
/* Find client entry */
- clients = silc_client_get_clients_local(client, conn, nickname, name,
- &clients_count);
+ clients = silc_client_get_clients_local(client, conn, name, FALSE);
if (!clients) {
- silc_client_get_clients(client, conn, nickname, NULL,
- silcpurple_ftp_send_file_resolved,
- strdup(name));
- silc_free(nickname);
+ silc_client_get_clients(client, conn, name, NULL,
+ silcpurple_ftp_send_file_resolved,
+ strdup(name));
return NULL;
}
+ silc_dlist_start(clients);
xfer = silc_calloc(1, sizeof(*xfer));
-
g_return_val_if_fail(xfer != NULL, NULL);
xfer->sg = sg;
- xfer->client_entry = clients[0];
+ xfer->client_entry = silc_dlist_get(clients);
xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_SEND,
- xfer->client_entry->nickname);
+ xfer->client_entry->nickname);
if (!xfer->xfer) {
- silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
- g_free(xfer->hostname);
silc_free(xfer);
return NULL;
}
@@ -393,7 +469,6 @@ PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name)
xfer->xfer->data = xfer;
silc_free(clients);
- silc_free(nickname);
return xfer->xfer;
}
diff --git a/libpurple/protocols/silc/ops.c b/libpurple/protocols/silc/ops.c
index fd5a93dab2..6b61e5d11f 100644
--- a/libpurple/protocols/silc/ops.c
+++ b/libpurple/protocols/silc/ops.c
@@ -4,7 +4,7 @@
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2004 Pekka Riikonen
+ Copyright (C) 2004 - 2007 Pekka Riikonen
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
@@ -17,7 +17,7 @@
*/
-#include "silcincludes.h"
+#include "silc.h"
#include "silcclient.h"
#include "silcpurple.h"
#include "imgstore.h"
@@ -26,14 +26,18 @@
static void
silc_channel_message(SilcClient client, SilcClientConnection conn,
SilcClientEntry sender, SilcChannelEntry channel,
- SilcMessagePayload payload, SilcChannelPrivateKey key,
- SilcMessageFlags flags, const unsigned char *message,
+ SilcMessagePayload payload,
+ SilcChannelPrivateKey key, SilcMessageFlags flags,
+ const unsigned char *message,
SilcUInt32 message_len);
static void
silc_private_message(SilcClient client, SilcClientConnection conn,
SilcClientEntry sender, SilcMessagePayload payload,
SilcMessageFlags flags, const unsigned char *message,
SilcUInt32 message_len);
+static void
+silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+ SilcAskPassphrase completion, void *context);
/* Message sent to the application by library. `conn' associates the
message to a specific connection. `conn', however, may be NULL.
@@ -41,23 +45,32 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
The application can for example filter the message according the
type. */
-static void
-silc_say(SilcClient client, SilcClientConnection conn,
- SilcClientMessageType type, char *msg, ...)
+void silc_say(SilcClient client, SilcClientConnection conn,
+ SilcClientMessageType type, char *msg, ...)
{
- /* Nothing */
+ if (type == SILC_CLIENT_MESSAGE_ERROR) {
+ char tmp[256];
+ va_list va;
+
+ va_start(va, msg);
+ silc_vsnprintf(tmp, sizeof(tmp), msg, va);
+ purple_notify_error(NULL, _("Error"), _("Error occurred"), tmp);
+
+ va_end(va);
+ return;
+ }
}
#ifdef HAVE_SILCMIME_H
/* Processes incoming MIME message. Can be private message or channel
- message. */
+ message. Returns TRUE if the message `mime' was displayed. */
-static void
+static SilcBool
silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
- SilcClientEntry sender, SilcChannelEntry channel,
- SilcMessagePayload payload, SilcChannelPrivateKey key,
- SilcMessageFlags flags, SilcMime mime,
- gboolean recursive)
+ SilcClientEntry sender, SilcChannelEntry channel,
+ SilcMessagePayload payload, SilcChannelPrivateKey key,
+ SilcMessageFlags flags, SilcMime mime,
+ gboolean recursive)
{
PurpleConnection *gc = client->application;
SilcPurple sg = gc->proto_data;
@@ -66,9 +79,10 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
SilcUInt32 data_len;
PurpleMessageFlags cflags = 0;
PurpleConversation *convo = NULL;
+ SilcBool ret = FALSE;
if (!mime)
- return;
+ return FALSE;
/* Check for fragmented MIME message */
if (silc_mime_is_partial(mime)) {
@@ -79,12 +93,12 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
mime = silc_mime_assemble(sg->mimeass, mime);
if (!mime)
/* More fragments to come */
- return;
+ return FALSE;
/* Process the complete message */
- silcpurple_mime_message(client, conn, sender, channel,
- payload, key, flags, mime, FALSE);
- return;
+ return silcpurple_mime_message(client, conn, sender, channel,
+ payload, key, flags, mime,
+ FALSE);
}
/* Check for multipart message */
@@ -92,17 +106,33 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
SilcMime p;
const char *mtype;
SilcDList parts = silc_mime_get_multiparts(mime, &mtype);
+ SilcBool ret;
+
+ if (!strcmp(mtype, "mixed")) {
+ /* Contains multiple messages */
+ silc_dlist_start(parts);
+ while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
+ /* Recursively process parts */
+ ret = silcpurple_mime_message(client, conn, sender, channel,
+ payload, key, flags, p, TRUE);
+ }
+ }
- /* Only "mixed" type supported */
- if (strcmp(mtype, "mixed"))
- goto out;
-
- silc_dlist_start(parts);
- while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
- /* Recursively process parts */
- silcpurple_mime_message(client, conn, sender, channel,
- payload, key, flags, p, TRUE);
+ if (!strcmp(mtype, "alternative")) {
+ /* Same message in alternative formats. Kopete sends
+ these. Go in order from last to first. */
+ silc_dlist_end(parts);
+ while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
+ /* Go through the alternatives and display the first
+ one we support. */
+ if (silcpurple_mime_message(client, conn, sender, channel,
+ payload, key, flags, p, TRUE)) {
+ ret = TRUE;
+ break;
+ }
+ }
}
+
goto out;
}
@@ -124,13 +154,14 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
if (channel)
silc_channel_message(client, conn, sender, channel,
- payload, key,
+ payload, key,
SILC_MESSAGE_FLAG_UTF8, data,
data_len);
else
silc_private_message(client, conn, sender, payload,
SILC_MESSAGE_FLAG_UTF8, data,
data_len);
+ ret = TRUE;
goto out;
}
@@ -157,7 +188,7 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
}
if (channel && !convo)
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
+ channel->channel_name, sg->account);
if (channel && !convo)
goto out;
@@ -165,11 +196,11 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
if (imgid) {
cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
g_snprintf(tmp, sizeof(tmp), "<IMG ID=\"%d\">", imgid);
-
+
if (channel)
serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
sender->nickname ?
- sender->nickname :
+ sender->nickname :
"<unknown>", cflags,
tmp, time(NULL));
else
@@ -179,6 +210,7 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
purple_imgstore_unref_by_id(imgid);
cflags = 0;
+ ret = TRUE;
}
goto out;
}
@@ -191,13 +223,15 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
payload, flags, data, data_len);
else
silcpurple_wb_receive(client, conn, sender, payload,
- flags, data, data_len);
+ flags, data, data_len);
+ ret = TRUE;
goto out;
}
out:
if (!recursive)
silc_mime_free(mime);
+ return ret;
}
#endif /* HAVE_SILCMIME_H */
@@ -210,8 +244,9 @@ silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
static void
silc_channel_message(SilcClient client, SilcClientConnection conn,
SilcClientEntry sender, SilcChannelEntry channel,
- SilcMessagePayload payload, SilcChannelPrivateKey key,
- SilcMessageFlags flags, const unsigned char *message,
+ SilcMessagePayload payload,
+ SilcChannelPrivateKey key, SilcMessageFlags flags,
+ const unsigned char *message,
SilcUInt32 message_len)
{
PurpleConnection *gc = client->application;
@@ -236,7 +271,7 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
}
if (!convo)
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
+ channel->channel_name, sg->account);
if (!convo)
return;
@@ -249,9 +284,9 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
/* Process MIME message */
#ifdef HAVE_SILCMIME_H
SilcMime mime;
- mime = silc_mime_decode(message, message_len);
+ mime = silc_mime_decode(NULL, message, message_len);
silcpurple_mime_message(client, conn, sender, channel, payload,
- key, flags, mime, FALSE);
+ key, flags, mime, FALSE);
#else
char type[128], enc[128];
unsigned char *data;
@@ -283,9 +318,7 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
tmp = g_markup_escape_text(msg, -1);
/* Send to Purple */
serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
- sender->nickname ?
- sender->nickname : "<unknown>", 0,
- tmp, time(NULL));
+ sender->nickname, 0, tmp, time(NULL));
g_free(tmp);
g_free(msg);
return;
@@ -293,9 +326,7 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
if (flags & SILC_MESSAGE_FLAG_NOTICE) {
msg = g_strdup_printf("(notice) <I>%s</I> %s",
- sender->nickname ?
- sender->nickname : "<unknown>",
- (const char *)message);
+ sender->nickname, (const char *)message);
if (!msg)
return;
@@ -310,9 +341,7 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
tmp = g_markup_escape_text((const char *)message, -1);
/* Send to Purple */
serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
- sender->nickname ?
- sender->nickname : "<unknown>", 0,
- tmp, time(NULL));
+ sender->nickname, 0, tmp, time(NULL));
g_free(tmp);
}
}
@@ -341,7 +370,7 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
if (sender->nickname)
/* XXX - Should this be PURPLE_CONV_TYPE_IM? */
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
- sender->nickname, sg->account);
+ sender->nickname, sg->account);
if (flags & SILC_MESSAGE_FLAG_SIGNED &&
purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
@@ -352,7 +381,7 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
#ifdef HAVE_SILCMIME_H
/* Process MIME message */
SilcMime mime;
- mime = silc_mime_decode(message, message_len);
+ mime = silc_mime_decode(NULL, message, message_len);
silcpurple_mime_message(client, conn, sender, NULL, payload,
NULL, flags, mime, FALSE);
#else
@@ -364,7 +393,7 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
memset(enc, 0, sizeof(enc));
if (!silc_mime_parse(message, message_len, NULL, 0,
- type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
+ type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
&data_len))
return;
@@ -383,11 +412,9 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
if (!msg)
return;
- tmp = g_markup_escape_text(msg, -1);
/* Send to Purple */
- serv_got_im(gc, sender->nickname ?
- sender->nickname : "<unknown>",
- tmp, 0, time(NULL));
+ tmp = g_markup_escape_text(msg, -1);
+ serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
g_free(msg);
g_free(tmp);
return;
@@ -395,15 +422,13 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) {
msg = g_strdup_printf("(notice) <I>%s</I> %s",
- sender->nickname ?
- sender->nickname : "<unknown>",
- (const char *)message);
+ sender->nickname, (const char *)message);
if (!msg)
return;
/* Send to Purple */
purple_conversation_write(convo, NULL, (const char *)msg,
- PURPLE_MESSAGE_SYSTEM, time(NULL));
+ PURPLE_MESSAGE_SYSTEM, time(NULL));
g_free(msg);
return;
}
@@ -411,9 +436,7 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
if (flags & SILC_MESSAGE_FLAG_UTF8) {
tmp = g_markup_escape_text((const char *)message, -1);
/* Send to Purple */
- serv_got_im(gc, sender->nickname ?
- sender->nickname : "<unknown>",
- tmp, 0, time(NULL));
+ serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
g_free(tmp);
}
}
@@ -447,6 +470,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
char buf[512], buf2[512], *tmp, *name;
SilcNotifyType notify;
PurpleBuddy *b;
+ SilcDList list;
int i;
va_start(va, type);
@@ -460,7 +484,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
case SILC_NOTIFY_TYPE_INVITE:
{
GHashTable *components;
- va_arg(va, SilcChannelEntry);
+ (void)va_arg(va, SilcChannelEntry);
name = va_arg(va, char *);
client_entry = va_arg(va, SilcClientEntry);
@@ -479,7 +503,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
break;
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
+ channel->channel_name, sg->account);
if (!convo)
break;
@@ -487,7 +511,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
g_snprintf(buf, sizeof(buf), "%s@%s",
client_entry->username, client_entry->hostname);
purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo),
- g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE);
+ g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE);
break;
@@ -496,13 +520,13 @@ silc_notify(SilcClient client, SilcClientConnection conn,
channel = va_arg(va, SilcChannelEntry);
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
+ channel->channel_name, sg->account);
if (!convo)
break;
/* Remove user from channel */
purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname, NULL);
+ client_entry->nickname, NULL);
break;
@@ -510,19 +534,16 @@ silc_notify(SilcClient client, SilcClientConnection conn,
client_entry = va_arg(va, SilcClientEntry);
tmp = va_arg(va, char *);
- if (!client_entry->nickname)
- break;
-
/* Remove from all channels */
silc_hash_table_list(client_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- chu->channel->channel_name, sg->account);
+ chu->channel->channel_name, sg->account);
if (!convo)
continue;
purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname,
- tmp);
+ client_entry->nickname,
+ tmp);
}
silc_hash_table_list_reset(&htl);
@@ -537,7 +558,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
channel = va_arg(va, SilcChannelEntry);
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
+ channel->channel_name, sg->account);
if (!convo)
break;
@@ -586,22 +607,22 @@ silc_notify(SilcClient client, SilcClientConnection conn,
}
case SILC_NOTIFY_TYPE_NICK_CHANGE:
client_entry = va_arg(va, SilcClientEntry);
- client_entry2 = va_arg(va, SilcClientEntry);
+ tmp = va_arg(va, char *); /* Old nick */
+ name = va_arg(va, char *); /* New nick */
- if (!strcmp(client_entry->nickname, client_entry2->nickname))
+ if (!strcmp(tmp, name))
break;
/* Change nick on all channels */
- silc_hash_table_list(client_entry2->channels, &htl);
+ silc_hash_table_list(client_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- chu->channel->channel_name, sg->account);
+ chu->channel->channel_name, sg->account);
if (!convo)
continue;
if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname))
purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname,
- client_entry2->nickname);
+ tmp, name);
}
silc_hash_table_list_reset(&htl);
@@ -615,11 +636,11 @@ silc_notify(SilcClient client, SilcClientConnection conn,
(void)va_arg(va, char *);
(void)va_arg(va, char *);
(void)va_arg(va, SilcPublicKey);
- (void)va_arg(va, SilcBuffer);
+ (void)va_arg(va, SilcDList);
channel = va_arg(va, SilcChannelEntry);
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
+ channel->channel_name, sg->account);
if (!convo)
break;
@@ -643,7 +664,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
channel->channel_name);
}
purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
break;
case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
@@ -656,7 +677,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
channel = va_arg(va, SilcChannelEntry);
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
+ channel->channel_name, sg->account);
if (!convo)
break;
@@ -672,19 +693,19 @@ silc_notify(SilcClient client, SilcClientConnection conn,
if (mode) {
silcpurple_get_chumode_string(mode, buf2, sizeof(buf2));
g_snprintf(buf, sizeof(buf),
- _("<I>%s</I> set <I>%s's</I> modes to: %s"), name,
- client_entry2->nickname, buf2);
+ _("<I>%s</I> set <I>%s's</I> modes to: %s"), name,
+ client_entry2->nickname, buf2);
if (mode & SILC_CHANNEL_UMODE_CHANFO)
flags |= PURPLE_CBFLAGS_FOUNDER;
if (mode & SILC_CHANNEL_UMODE_CHANOP)
flags |= PURPLE_CBFLAGS_OP;
} else {
g_snprintf(buf, sizeof(buf),
- _("<I>%s</I> removed all <I>%s's</I> modes"), name,
- client_entry2->nickname);
+ _("<I>%s</I> removed all <I>%s's</I> modes"), name,
+ client_entry2->nickname);
}
purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags);
break;
}
@@ -702,7 +723,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
channel = va_arg(va, SilcChannelEntry);
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
+ channel->channel_name, sg->account);
if (!convo)
break;
@@ -713,15 +734,15 @@ silc_notify(SilcClient client, SilcClientConnection conn,
channel->channel_name, client_entry2->nickname,
tmp ? tmp : "");
purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
} else {
/* Remove user from channel */
g_snprintf(buf, sizeof(buf), _("Kicked by %s (%s)"),
client_entry2->nickname, tmp ? tmp : "");
purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname,
- buf);
+ client_entry->nickname,
+ buf);
}
break;
@@ -732,9 +753,6 @@ silc_notify(SilcClient client, SilcClientConnection conn,
idtype = va_arg(va, int);
entry = va_arg(va, SilcClientEntry);
- if (!client_entry->nickname)
- break;
-
if (client_entry == conn->local_entry) {
if (idtype == SILC_ID_CLIENT) {
client_entry2 = (SilcClientEntry)entry;
@@ -761,7 +779,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
if (!convo)
continue;
purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
- buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
}
silc_hash_table_list_reset(&htl);
@@ -792,7 +810,7 @@ silc_notify(SilcClient client, SilcClientConnection conn,
if (!convo)
continue;
purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
- client_entry->nickname, tmp);
+ client_entry->nickname, tmp);
}
silc_hash_table_list_reset(&htl);
}
@@ -803,33 +821,23 @@ silc_notify(SilcClient client, SilcClientConnection conn,
break;
case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
- {
- int i;
- SilcClientEntry *clients;
- SilcUInt32 clients_count;
-
- (void)va_arg(va, void *);
- clients = va_arg(va, SilcClientEntry *);
- clients_count = va_arg(va, SilcUInt32);
-
- for (i = 0; i < clients_count; i++) {
- if (!clients[i]->nickname)
- break;
+ (void)va_arg(va, void *);
+ list = va_arg(va, SilcDList);
- /* Remove from all channels */
- silc_hash_table_list(clients[i]->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
- convo =
- purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- chu->channel->channel_name, sg->account);
- if (!convo)
- continue;
- purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
- clients[i]->nickname,
- _("Server signoff"));
- }
- silc_hash_table_list_reset(&htl);
+ silc_dlist_start(list);
+ while ((client_entry = silc_dlist_get(list))) {
+ /* Remove from all channels */
+ silc_hash_table_list(client_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chu->channel->channel_name, sg->account);
+ if (!convo)
+ continue;
+ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ client_entry->nickname,
+ _("Server signoff"));
}
+ silc_hash_table_list_reset(&htl);
}
break;
@@ -837,8 +845,8 @@ silc_notify(SilcClient client, SilcClientConnection conn,
{
SilcStatus error = va_arg(va, int);
purple_notify_error(gc, "Error Notify",
- silc_get_status_message(error),
- NULL);
+ silc_get_status_message(error),
+ NULL);
}
break;
@@ -909,8 +917,8 @@ silc_notify(SilcClient client, SilcClientConnection conn,
}
silc_free(b->proto_data);
- b->proto_data = silc_memdup(client_entry->id,
- sizeof(*client_entry->id));
+ b->proto_data = silc_memdup(&client_entry->id,
+ sizeof(client_entry->id));
if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
break;
} else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
@@ -955,19 +963,20 @@ silc_notify(SilcClient client, SilcClientConnection conn,
}
-/* Command handler. This function is called always in the command function.
- If error occurs it will be called as well. `conn' is the associated
- client connection. `cmd_context' is the command context that was
- originally sent to the command. `success' is FALSE if error occurred
- during command. `command' is the command being processed. It must be
- noted that this is not reply from server. This is merely called just
- after application has called the command. Just to tell application
- that the command really was processed. */
+/* Command handler. This function is called always after application has
+ called a command. It will be called to indicate that the command
+ was processed. It will also be called if error occurs while processing
+ the command. The `success' indicates whether the command was sent
+ or if error occurred. The `status' indicates the actual error.
+ The `argc' and `argv' are the command line arguments sent to the
+ command by application. Note that, this is not reply to the command
+ from server, this is merely and indication to application that the
+ command was processed. */
static void
silc_command(SilcClient client, SilcClientConnection conn,
- SilcClientCommandContext cmd_context, bool success,
- SilcCommand command, SilcStatus status)
+ SilcBool success, SilcCommand command, SilcStatus status,
+ SilcUInt32 argc, unsigned char **argv)
{
PurpleConnection *gc = client->application;
SilcPurple sg = gc->proto_data;
@@ -975,8 +984,7 @@ silc_command(SilcClient client, SilcClientConnection conn,
switch (command) {
case SILC_COMMAND_CMODE:
- if (cmd_context->argc == 3 &&
- !strcmp((char *)cmd_context->argv[2], "+C"))
+ if (argc == 3 && !strcmp((char *)argv[2], "+C"))
sg->chpk = TRUE;
else
sg->chpk = FALSE;
@@ -1090,53 +1098,96 @@ silcpurple_whois_more(SilcClientEntry client_entry, gint id)
}
#endif
-/* Command reply handler. This function is called always in the command reply
- function. If error occurs it will be called as well. Normal scenario
- is that it will be called after the received command data has been parsed
- and processed. The function is used to pass the received command data to
- the application.
-
- `conn' is the associated client connection. `cmd_payload' is the command
- payload data received from server and it can be ignored. It is provided
- if the application would like to re-parse the received command data,
- however, it must be noted that the data is parsed already by the library
- thus the payload can be ignored. `success' is FALSE if error occurred.
- In this case arguments are not sent to the application. The `status' is
- the command reply status server returned. The `command' is the command
- reply being processed. The function has variable argument list and each
- command defines the number and type of arguments it passes to the
- application (on error they are not sent). */
+
+/* Command reply handler. Delivers a reply to command that was sent
+ earlier. The `conn' is the associated client connection. The `command'
+ indicates the command reply type. If the `status' other than
+ SILC_STATUS_OK an error occurred. In this case the `error' will indicate
+ the error. It is possible to receive list of command replies and list
+ of errors. In this case the `status' will indicate it is an list entry
+ (the `status' is SILC_STATUS_LIST_START, SILC_STATUS_LIST_ITEM and/or
+ SILC_STATUS_LIST_END).
+
+ The arguments received in `ap' are command specific. See a separate
+ documentation in the Toolkit Reference Manual for the command reply
+ arguments. */
static void
silc_command_reply(SilcClient client, SilcClientConnection conn,
- SilcCommandPayload cmd_payload, bool success,
- SilcCommand command, SilcStatus status, ...)
+ SilcCommand command, SilcStatus status,
+ SilcStatus error, va_list ap)
{
PurpleConnection *gc = client->application;
SilcPurple sg = gc->proto_data;
PurpleConversation *convo;
- va_list vp;
-
- va_start(vp, status);
switch (command) {
case SILC_COMMAND_JOIN:
{
- SilcChannelEntry channel_entry;
+ SilcChannelEntry channel;
+ PurpleConversation *convo;
+ SilcHashTableList *user_list;
+ SilcChannelUser chu;
+ GList *users = NULL, *flags = NULL;
+ char tmp[256], *topic;
- if (!success) {
+ if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"),
- silc_get_status_message(status));
+ silc_get_status_message(error));
return;
}
- (void)va_arg(vp, char *);
- channel_entry = va_arg(vp, SilcChannelEntry);
+ (void)va_arg(ap, char *);
+ channel = va_arg(ap, SilcChannelEntry);
+ (void)va_arg(ap, SilcUInt32);
+ user_list = va_arg(ap, SilcHashTableList *);
+ topic = va_arg(ap, char *);
+
+ /* Add channel to Purple */
+ channel->context = SILC_32_TO_PTR(++sg->channel_ids);
+ serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name);
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (!convo)
+ return;
+
+ /* Add all users to channel */
+ while (silc_hash_table_get(user_list, NULL, (void *)&chu)) {
+ PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE;
+ chu->context = SILC_32_TO_PTR(sg->channel_ids);
+
+ if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
+ f |= PURPLE_CBFLAGS_FOUNDER;
+ if (chu->mode & SILC_CHANNEL_UMODE_CHANOP)
+ f |= PURPLE_CBFLAGS_OP;
+ users = g_list_append(users, g_strdup(chu->client->nickname));
+ flags = g_list_append(flags, GINT_TO_POINTER(f));
+
+ if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (chu->client == conn->local_entry)
+ g_snprintf(tmp, sizeof(tmp),
+ _("You are channel founder on <I>%s</I>"),
+ channel->channel_name);
+ else
+ g_snprintf(tmp, sizeof(tmp),
+ _("Channel founder on <I>%s</I> is <I>%s</I>"),
+ channel->channel_name, chu->client->nickname);
+
+ purple_conversation_write(convo, NULL, tmp,
+ PURPLE_MESSAGE_SYSTEM, time(NULL));
+ }
+ }
+
+ purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE);
+ g_list_free(users);
+ g_list_free(flags);
+
+ /* Set topic */
+ if (topic)
+ purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, topic);
- /* Resolve users on channel */
- silc_client_get_clients_by_channel(client, conn, channel_entry,
- silcpurple_chat_join_done,
- channel_entry);
+ /* Set nick */
+ purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname);
}
break;
@@ -1148,31 +1199,29 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
case SILC_COMMAND_WHOIS:
{
- SilcUInt32 idle, mode;
- SilcBuffer channels, user_modes;
+ SilcUInt32 idle, *user_modes;
+ SilcDList channels;
SilcClientEntry client_entry;
char tmp[1024], *tmp2;
char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
PurpleNotifyUserInfo *user_info;
- if (!success) {
+ if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("User Information"),
_("Cannot get user information"),
- silc_get_status_message(status));
+ silc_get_status_message(error));
break;
}
- client_entry = va_arg(vp, SilcClientEntry);
- if (!client_entry->nickname)
- break;
- (void)va_arg(vp, char *);
- (void)va_arg(vp, char *);
- (void)va_arg(vp, char *);
- channels = va_arg(vp, SilcBuffer);
- mode = va_arg(vp, SilcUInt32);
- idle = va_arg(vp, SilcUInt32);
- (void)va_arg(vp, unsigned char *);
- user_modes = va_arg(vp, SilcBuffer);
+ client_entry = va_arg(ap, SilcClientEntry);
+ (void)va_arg(ap, char *);
+ (void)va_arg(ap, char *);
+ (void)va_arg(ap, char *);
+ channels = va_arg(ap, SilcDList);
+ (void)va_arg(ap, SilcUInt32);
+ idle = va_arg(ap, SilcUInt32);
+ (void)va_arg(ap, unsigned char *);
+ user_modes = va_arg(ap, SilcUInt32 *);
user_info = purple_notify_user_info_new();
tmp2 = g_markup_escape_text(client_entry->nickname, -1);
@@ -1183,22 +1232,20 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2);
g_free(tmp2);
}
- if (client_entry->username) {
- tmp2 = g_markup_escape_text(client_entry->username, -1);
- if (client_entry->hostname) {
- gchar *tmp3;
- tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname);
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
- g_free(tmp3);
- } else
- purple_notify_user_info_add_pair(user_info, _("Username"), tmp2);
- g_free(tmp2);
- }
+ tmp2 = g_markup_escape_text(client_entry->username, -1);
+ if (*client_entry->hostname) {
+ gchar *tmp3;
+ tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname);
+ purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
+ g_free(tmp3);
+ } else
+ purple_notify_user_info_add_pair(user_info, _("Username"), tmp2);
+ g_free(tmp2);
if (client_entry->mode) {
memset(tmp, 0, sizeof(tmp));
silcpurple_get_umode_string(client_entry->mode,
- tmp, sizeof(tmp) - strlen(tmp));
+ tmp, sizeof(tmp) - strlen(tmp));
purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
}
@@ -1240,39 +1287,28 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
g_free(geostr);
}
- if (client_entry->server)
+ if (*client_entry->server)
purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
if (channels && user_modes) {
- SilcUInt32 *umodes;
- SilcDList list =
- silc_channel_payload_parse_list(channels->data,
- channels->len);
- if (list && silc_get_mode_list(user_modes,
- silc_dlist_count(list),
- &umodes)) {
- SilcChannelPayload entry;
- int i = 0;
-
- memset(tmp, 0, sizeof(tmp));
- silc_dlist_start(list);
- while ((entry = silc_dlist_get(list))
- != SILC_LIST_END) {
- SilcUInt32 name_len;
- char *m = silc_client_chumode_char(umodes[i++]);
- char *name = (char *)silc_channel_get_name(entry, &name_len);
- if (m)
- silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m));
- silc_strncat(tmp, sizeof(tmp) - 1, name, name_len);
- silc_strncat(tmp, sizeof(tmp) - 1, " ", 1);
- silc_free(m);
+ SilcChannelPayload entry;
+ int i = 0;
- }
- tmp2 = g_markup_escape_text(tmp, -1);
- purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2);
- g_free(tmp2);
- silc_free(umodes);
+ memset(tmp, 0, sizeof(tmp));
+ silc_dlist_start(channels);
+ while ((entry = silc_dlist_get(channels))) {
+ SilcUInt32 name_len;
+ char *m = silc_client_chumode_char(user_modes[i++]);
+ char *name = (char *)silc_channel_get_name(entry, &name_len);
+ if (m)
+ silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m));
+ silc_strncat(tmp, sizeof(tmp) - 1, name, name_len);
+ silc_strncat(tmp, sizeof(tmp) - 1, " ", 1);
+ silc_free(m);
}
+ tmp2 = g_markup_escape_text(tmp, -1);
+ purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2);
+ g_free(tmp2);
}
if (client_entry->public_key) {
@@ -1297,7 +1333,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
_("OK"), G_CALLBACK(silcpurple_whois_more),
_("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL);
else
-#endif
+#endif /* 0 */
purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL);
purple_notify_user_info_destroy(user_info);
}
@@ -1309,17 +1345,17 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
char *nickname, *realname, *username, *tmp;
PurpleNotifyUserInfo *user_info;
- if (!success) {
+ if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("User Information"),
_("Cannot get user information"),
- silc_get_status_message(status));
+ silc_get_status_message(error));
break;
}
- client_entry = va_arg(vp, SilcClientEntry);
- nickname = va_arg(vp, char *);
- username = va_arg(vp, char *);
- realname = va_arg(vp, char *);
+ client_entry = va_arg(ap, SilcClientEntry);
+ nickname = va_arg(ap, char *);
+ username = va_arg(ap, char *);
+ realname = va_arg(ap, char *);
if (!nickname)
break;
@@ -1334,7 +1370,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
}
if (username) {
tmp = g_markup_escape_text(username, -1);
- if (client_entry && client_entry->hostname) {
+ if (client_entry && *client_entry->hostname) {
gchar *tmp3;
tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname);
purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
@@ -1343,7 +1379,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
g_free(tmp);
}
- if (client_entry && client_entry->server)
+ if (client_entry && *client_entry->server)
purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
@@ -1367,10 +1403,23 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
break;
case SILC_COMMAND_DETACH:
- if (!success) {
- purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
- silc_get_status_message(status));
- return;
+ {
+ const char *file;
+ SilcBuffer detach_data;
+
+ if (status != SILC_STATUS_OK) {
+ purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
+ silc_get_status_message(error));
+ return;
+ }
+
+ detach_data = va_arg(ap, SilcBuffer);
+
+ /* Save the detachment data to file. */
+ file = silcpurple_session_file(purple_account_get_username(sg->account));
+ g_unlink(file);
+ silc_file_writefile(file, (const char *)silc_buffer_data(detach_data),
+ silc_buffer_len(detach_data));
}
break;
@@ -1378,19 +1427,19 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
{
SilcChannelEntry channel;
- if (!success) {
+ if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Topic"), _("Cannot set topic"),
- silc_get_status_message(status));
+ silc_get_status_message(error));
return;
}
- channel = va_arg(vp, SilcChannelEntry);
+ channel = va_arg(ap, SilcChannelEntry);
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- channel->channel_name, sg->account);
+ channel->channel_name, sg->account);
if (!convo) {
purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
- channel->channel_name);
+ channel->channel_name);
break;
}
@@ -1402,39 +1451,37 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
case SILC_COMMAND_NICK:
{
- /* I don't think we should need to do this because the server should
- * be sending a SILC_NOTIFY_TYPE_NICK_CHANGE when we change our own
- * nick, but it isn't, so we deal with it here instead. Stu. */
SilcClientEntry local_entry;
SilcHashTableList htl;
SilcChannelUser chu;
- const char *oldnick;
+ const char *oldnick, *newnick;
- if (!success) {
+ if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Nick"), _("Failed to change nickname"),
- silc_get_status_message(status));
+ silc_get_status_message(error));
return;
}
- local_entry = va_arg(vp, SilcClientEntry);
+ local_entry = va_arg(ap, SilcClientEntry);
+ newnick = va_arg(ap, char *);
/* Change nick on all channels */
silc_hash_table_list(local_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- chu->channel->channel_name, sg->account);
+ chu->channel->channel_name, sg->account);
if (!convo)
continue;
oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo));
- if (strcmp(oldnick, purple_normalize(purple_conversation_get_account(convo), local_entry->nickname))) {
+ if (strcmp(oldnick, purple_normalize(purple_conversation_get_account(convo), newnick))) {
purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
- oldnick, local_entry->nickname);
- purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), local_entry->nickname);
+ oldnick, newnick);
+ purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), newnick);
}
}
silc_hash_table_list_reset(&htl);
- purple_connection_set_display_name(gc, local_entry->nickname);
+ purple_connection_set_display_name(gc, newnick);
}
break;
@@ -1447,34 +1494,34 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
if (sg->roomlist_canceled)
break;
- if (!success) {
+ if (error != SILC_STATUS_OK) {
purple_notify_error(gc, _("Error"), _("Error retrieving room list"),
- silc_get_status_message(status));
+ silc_get_status_message(error));
purple_roomlist_set_in_progress(sg->roomlist, FALSE);
purple_roomlist_unref(sg->roomlist);
sg->roomlist = NULL;
return;
}
- (void)va_arg(vp, SilcChannelEntry);
- name = va_arg(vp, char *);
+ (void)va_arg(ap, SilcChannelEntry);
+ name = va_arg(ap, char *);
if (!name) {
purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"),
- silc_get_status_message(status));
+ _("Network is empty"));
purple_roomlist_set_in_progress(sg->roomlist, FALSE);
purple_roomlist_unref(sg->roomlist);
sg->roomlist = NULL;
return;
}
- topic = va_arg(vp, char *);
- usercount = va_arg(vp, int);
+ topic = va_arg(ap, char *);
+ usercount = va_arg(ap, int);
room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL);
purple_roomlist_room_add_field(sg->roomlist, room, name);
purple_roomlist_room_add_field(sg->roomlist, room,
- SILC_32_TO_PTR(usercount));
+ SILC_32_TO_PTR(usercount));
purple_roomlist_room_add_field(sg->roomlist, room,
- topic ? topic : "");
+ topic ? topic : "");
purple_roomlist_room_add(sg->roomlist, room);
if (status == SILC_STATUS_LIST_END ||
@@ -1490,21 +1537,21 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
{
SilcPublicKey public_key;
- if (!success) {
+ if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Get Public Key"),
- _("Cannot fetch the public key"),
- silc_get_status_message(status));
+ _("Cannot fetch the public key"),
+ silc_get_status_message(error));
return;
}
- (void)va_arg(vp, SilcUInt32);
- (void)va_arg(vp, void *);
- public_key = va_arg(vp, SilcPublicKey);
+ (void)va_arg(ap, SilcUInt32);
+ (void)va_arg(ap, void *);
+ public_key = va_arg(ap, SilcPublicKey);
if (!public_key)
purple_notify_error(gc, _("Get Public Key"),
- _("Cannot fetch the public key"),
- _("No public key was received"));
+ _("Cannot fetch the public key"),
+ _("No public key was received"));
}
break;
@@ -1515,16 +1562,16 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
char *server_info;
char tmp[256];
- if (!success) {
+ if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Server Information"),
- _("Cannot get server information"),
- silc_get_status_message(status));
+ _("Cannot get server information"),
+ silc_get_status_message(error));
return;
}
- (void)va_arg(vp, SilcServerEntry);
- server_name = va_arg(vp, char *);
- server_info = va_arg(vp, char *);
+ (void)va_arg(ap, SilcServerEntry);
+ server_name = va_arg(ap, char *);
+ server_info = va_arg(ap, char *);
if (server_name && server_info) {
g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s",
@@ -1536,94 +1583,73 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
case SILC_COMMAND_STATS:
{
- SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
- my_router_ops, cell_clients, cell_channels, cell_servers,
- clients, channels, servers, routers, server_ops, router_ops;
- SilcUInt32 buffer_length;
- SilcBufferStruct buf;
-
- unsigned char *server_stats;
+ SilcClientStats *stats;
char *msg;
- if (!success) {
+ if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Server Statistics"),
- _("Cannot get server statistics"),
- silc_get_status_message(status));
+ _("Cannot get server statistics"),
+ silc_get_status_message(error));
return;
}
- server_stats = va_arg(vp, unsigned char *);
- buffer_length = va_arg(vp, SilcUInt32);
- if (!server_stats || !buffer_length) {
- purple_notify_error(gc, _("Server Statistics"),
- _("No server statistics available"), NULL);
- break;
- }
- silc_buffer_set(&buf, server_stats, buffer_length);
- silc_buffer_unformat(&buf,
- SILC_STR_UI_INT(&starttime),
- SILC_STR_UI_INT(&uptime),
- SILC_STR_UI_INT(&my_clients),
- SILC_STR_UI_INT(&my_channels),
- SILC_STR_UI_INT(&my_server_ops),
- SILC_STR_UI_INT(&my_router_ops),
- SILC_STR_UI_INT(&cell_clients),
- SILC_STR_UI_INT(&cell_channels),
- SILC_STR_UI_INT(&cell_servers),
- SILC_STR_UI_INT(&clients),
- SILC_STR_UI_INT(&channels),
- SILC_STR_UI_INT(&servers),
- SILC_STR_UI_INT(&routers),
- SILC_STR_UI_INT(&server_ops),
- SILC_STR_UI_INT(&router_ops),
- SILC_STR_END);
+ stats = va_arg(ap, SilcClientStats *);
msg = g_strdup_printf(_("Local server start time: %s\n"
- "Local server uptime: %s\n"
- "Local server clients: %d\n"
- "Local server channels: %d\n"
- "Local server operators: %d\n"
- "Local router operators: %d\n"
- "Local cell clients: %d\n"
- "Local cell channels: %d\n"
- "Local cell servers: %d\n"
- "Total clients: %d\n"
- "Total channels: %d\n"
- "Total servers: %d\n"
- "Total routers: %d\n"
- "Total server operators: %d\n"
- "Total router operators: %d\n"),
- silc_get_time(starttime),
- purple_str_seconds_to_string((int)uptime),
- (int)my_clients, (int)my_channels, (int)my_server_ops, (int)my_router_ops,
- (int)cell_clients, (int)cell_channels, (int)cell_servers,
- (int)clients, (int)channels, (int)servers, (int)routers,
- (int)server_ops, (int)router_ops);
+ "Local server uptime: %s\n"
+ "Local server clients: %d\n"
+ "Local server channels: %d\n"
+ "Local server operators: %d\n"
+ "Local router operators: %d\n"
+ "Local cell clients: %d\n"
+ "Local cell channels: %d\n"
+ "Local cell servers: %d\n"
+ "Total clients: %d\n"
+ "Total channels: %d\n"
+ "Total servers: %d\n"
+ "Total routers: %d\n"
+ "Total server operators: %d\n"
+ "Total router operators: %d\n"),
+ silc_time_string(stats->starttime),
+ purple_str_seconds_to_string((int)stats->uptime),
+ (int)stats->my_clients,
+ (int)stats->my_channels,
+ (int)stats->my_server_ops,
+ (int)stats->my_router_ops,
+ (int)stats->cell_clients,
+ (int)stats->cell_channels,
+ (int)stats->cell_servers,
+ (int)stats->clients,
+ (int)stats->channels,
+ (int)stats->servers,
+ (int)stats->routers,
+ (int)stats->server_ops,
+ (int)stats->router_ops);
purple_notify_info(gc, NULL,
- _("Network Statistics"), msg);
+ _("Network Statistics"), msg);
g_free(msg);
}
break;
case SILC_COMMAND_PING:
{
- if (!success) {
+ if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Ping"), _("Ping failed"),
- silc_get_status_message(status));
+ silc_get_status_message(error));
return;
}
purple_notify_info(gc, _("Ping"), _("Ping reply received from server"),
- NULL);
+ NULL);
}
break;
case SILC_COMMAND_KILL:
- if (!success) {
+ if (status != SILC_STATUS_OK) {
purple_notify_error(gc, _("Kill User"),
- _("Could not kill user"),
- silc_get_status_message(status));
+ _("Could not kill user"),
+ silc_get_status_message(error));
return;
}
break;
@@ -1631,188 +1657,108 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
case SILC_COMMAND_CMODE:
{
SilcChannelEntry channel_entry;
- SilcBuffer channel_pubkeys;
+ SilcDList channel_pubkeys, list;
+ SilcArgumentDecodedList e;
- if (!success)
+ if (status != SILC_STATUS_OK)
return;
- channel_entry = va_arg(vp, SilcChannelEntry);
- (void)va_arg(vp, SilcUInt32);
- (void)va_arg(vp, SilcPublicKey);
- channel_pubkeys = va_arg(vp, SilcBuffer);
+ channel_entry = va_arg(ap, SilcChannelEntry);
+ (void)va_arg(ap, SilcUInt32);
+ (void)va_arg(ap, SilcPublicKey);
+ channel_pubkeys = va_arg(ap, SilcDList);
- if (sg->chpk)
- silcpurple_chat_chauth_show(sg, channel_entry, channel_pubkeys);
- }
- break;
-
- default:
- if (success)
- purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command);
- else
- purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command,
- silc_get_status_message(status));
- break;
- }
-
- va_end(vp);
-}
-
-
-/* Called to indicate that connection was either successfully established
- or connecting failed. This is also the first time application receives
- the SilcClientConnection object which it should save somewhere.
- If the `success' is FALSE the application must always call the function
- silc_client_close_connection. */
+ if (!sg->chpk)
+ break;
-static void
-silc_connected(SilcClient client, SilcClientConnection conn,
- SilcClientConnectionStatus status)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg;
- gboolean reject_watch, block_invites, block_ims;
+ list = silc_dlist_init();
- if (gc == NULL) {
- silc_client_close_connection(client, conn);
- return;
- }
- sg = gc->proto_data;
-
- switch (status) {
- case SILC_CLIENT_CONN_SUCCESS:
- case SILC_CLIENT_CONN_SUCCESS_RESUME:
- purple_connection_set_state(gc, PURPLE_CONNECTED);
-
- /* Send the server our buddy list */
- silcpurple_send_buddylist(gc);
-
- g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
-
- /* Send any UMODEs configured for account */
- reject_watch = purple_account_get_bool(sg->account, "reject-watch", FALSE);
- block_invites = purple_account_get_bool(sg->account, "block-invites", FALSE);
- block_ims = purple_account_get_bool(sg->account, "block-ims", FALSE);
- if (reject_watch || block_invites || block_ims) {
- char m[5];
- g_snprintf(m, sizeof(m), "+%s%s%s",
- reject_watch ? "w" : "",
- block_invites ? "I" : "",
- block_ims ? "P" : "");
- silc_client_command_call(sg->client, sg->conn, NULL,
- "UMODE", m, NULL);
+ if (channel_pubkeys) {
+ silc_dlist_start(channel_pubkeys);
+ while ((e = silc_dlist_get(channel_pubkeys))) {
+ if (e->arg_type == 0x00 ||
+ e->arg_type == 0x03)
+ silc_dlist_add(list, silc_pkcs_public_key_copy(e->argument));
+ }
+ }
+ silcpurple_chat_chauth_show(sg, channel_entry, list);
}
-
- return;
- break;
- case SILC_CLIENT_CONN_ERROR:
- purple_connection_error(gc, _("Error during connecting to SILC Server"));
- g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
break;
- case SILC_CLIENT_CONN_ERROR_KE:
- purple_connection_error(gc, _("Key Exchange failed"));
- break;
-
- case SILC_CLIENT_CONN_ERROR_AUTH:
- purple_connection_error(gc, _("Authentication failed"));
- break;
-
- case SILC_CLIENT_CONN_ERROR_RESUME:
- purple_connection_error(gc,
- _("Resuming detached session failed. "
- "Press Reconnect to create new connection."));
- g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+ case SILC_COMMAND_WATCH:
+ if (status != SILC_STATUS_OK) {
+ purple_notify_error(gc, _("WATCH"), _("Cannot watch user"),
+ silc_get_status_message(error));
+ return;
+ }
break;
- case SILC_CLIENT_CONN_ERROR_TIMEOUT:
- purple_connection_error(gc, _("Connection Timeout"));
+ default:
+ if (status == SILC_STATUS_OK)
+ purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command);
+ else
+ purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command,
+ silc_get_status_message(error));
break;
}
-
- /* Error */
- sg->conn = NULL;
- silc_client_close_connection(client, conn);
}
+/* Generic command reply callback for silc_client_command_send. Simply
+ calls the default command_reply client operation callback */
-/* Called to indicate that connection was disconnected to the server.
- The `status' may tell the reason of the disconnection, and if the
- `message' is non-NULL it may include the disconnection message
- received from server. */
-
-static void
-silc_disconnected(SilcClient client, SilcClientConnection conn,
- SilcStatus status, const char *message)
+SilcBool silcpurple_command_reply(SilcClient client, SilcClientConnection conn,
+ SilcCommand command, SilcStatus status,
+ SilcStatus error, void *context, va_list ap)
{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
-
- if (sg->resuming && !sg->detaching)
- g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
-
- sg->conn = NULL;
-
- /* Close the connection */
- if (!sg->detaching)
- purple_connection_error(gc, _("Disconnected by server"));
- else
- /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
- purple_account_disconnect(purple_connection_get_account(gc));
+ silc_command_reply(client, conn, command, status, error, ap);
+ return TRUE;
}
typedef struct {
- SilcGetAuthMeth completion;
+ union {
+ SilcAskPassphrase ask_pass;
+ SilcGetAuthMeth get_auth;
+ } u;
void *context;
-} *SilcPurpleGetAuthMethod;
-
-/* Callback called when we've received the authentication method information
- from the server after we've requested it. */
+} *SilcPurpleAskPassphrase;
-static void silc_get_auth_method_callback(SilcClient client,
- SilcClientConnection conn,
- SilcAuthMethod auth_meth,
- void *context)
+static void
+silc_ask_auth_password_cb(const unsigned char *passphrase,
+ SilcUInt32 passphrase_len, void *context)
{
- SilcPurpleGetAuthMethod internal = context;
-
- switch (auth_meth) {
- case SILC_AUTH_NONE:
- /* No authentication required. */
- (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
- break;
-
- case SILC_AUTH_PASSWORD:
- /* By returning NULL here the library will ask the passphrase from us
- by calling the silc_ask_passphrase. */
- (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
- break;
-
- case SILC_AUTH_PUBLIC_KEY:
- /* Do not get the authentication data now, the library will generate
- it using our default key, if we do not provide it here. */
- (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
- break;
- }
+ SilcPurpleAskPassphrase internal = context;
+ if (!passphrase || !(*passphrase))
+ internal->u.get_auth(SILC_AUTH_NONE, NULL, 0, internal->context);
+ else
+ internal->u.get_auth(SILC_AUTH_PASSWORD,
+ (unsigned char *)passphrase,
+ passphrase_len, internal->context);
silc_free(internal);
}
/* Find authentication method and authentication data by hostname and
- port. The hostname may be IP address as well. When the authentication
- method has been resolved the `completion' callback with the found
- authentication method and authentication data is called. The `conn'
- may be NULL. */
+ port. The hostname may be IP address as well. The `auth_method' is
+ the authentication method the remote connection requires. It is
+ however possible that remote accepts also some other authentication
+ method. Application should use the method that may have been
+ configured for this connection. If none has been configured it should
+ use the required `auth_method'. If the `auth_method' is
+ SILC_AUTH_NONE, server does not require any authentication or the
+ required authentication method is not known. The `completion'
+ callback must be called to deliver the chosen authentication method
+ and data. The `conn' may be NULL. */
static void
silc_get_auth_method(SilcClient client, SilcClientConnection conn,
char *hostname, SilcUInt16 port,
+ SilcAuthMethod auth_method,
SilcGetAuthMeth completion, void *context)
{
PurpleConnection *gc = client->application;
SilcPurple sg = gc->proto_data;
- SilcPurpleGetAuthMethod internal;
+ SilcPurpleAskPassphrase internal;
const char *password;
/* Progress */
@@ -1821,72 +1767,71 @@ silc_get_auth_method(SilcClient client, SilcClientConnection conn,
else
purple_connection_update_progress(gc, _("Authenticating connection"), 4, 5);
- /* Check configuration if we have this connection configured. If we
- have then return that data immediately, as it's faster way. */
- if (purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) {
- completion(TRUE, SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
+ /* Check configuration if we have this connection configured. */
+ if (auth_method == SILC_AUTH_PUBLIC_KEY &&
+ purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) {
+ completion(SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
return;
}
- password = purple_connection_get_password(gc);
- if (password && *password) {
- completion(TRUE, SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context);
+ if (auth_method == SILC_AUTH_PASSWORD) {
+ password = purple_connection_get_password(gc);
+ if (password && *password) {
+ completion(SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context);
+ return;
+ }
+
+ /* Ask password from user */
+ internal = silc_calloc(1, sizeof(*internal));
+ if (!internal)
+ return;
+ internal->u.get_auth = completion;
+ internal->context = context;
+ silc_ask_passphrase(client, conn, silc_ask_auth_password_cb,
+ internal);
return;
}
- /* Resolve the authentication method from server, as we may not know it. */
- internal = silc_calloc(1, sizeof(*internal));
- if (!internal)
- return;
- internal->completion = completion;
- internal->context = context;
- silc_client_request_authentication_method(client, conn,
- silc_get_auth_method_callback,
- internal);
+ completion(SILC_AUTH_NONE, NULL, 0, context);
}
-/* Verifies received public key. The `conn_type' indicates which entity
- (server, client etc.) has sent the public key. If user decides to trust
- the application may save the key as trusted public key for later
- use. The `completion' must be called after the public key has been
- verified. */
+/* Called to verify received public key. The `conn_type' indicates which
+ entity (server or client) has sent the public key. If user decides to
+ trust the key the application may save the key as trusted public key for
+ later use. The `completion' must be called after the public key has
+ been verified. */
static void
silc_verify_public_key(SilcClient client, SilcClientConnection conn,
- SilcSocketType conn_type, unsigned char *pk,
- SilcUInt32 pk_len, SilcSKEPKType pk_type,
+ SilcConnectionType conn_type,
+ SilcPublicKey public_key,
SilcVerifyPublicKey completion, void *context)
{
PurpleConnection *gc = client->application;
SilcPurple sg = gc->proto_data;
- if (!sg->conn && (conn_type == SILC_SOCKET_TYPE_SERVER ||
- conn_type == SILC_SOCKET_TYPE_ROUTER)) {
+ if (!sg->conn && (conn_type == SILC_CONN_SERVER ||
+ conn_type == SILC_CONN_ROUTER)) {
/* Progress */
if (sg->resuming)
purple_connection_update_progress(gc, _("Resuming session"), 3, 5);
else
purple_connection_update_progress(gc, _("Verifying server public key"),
- 3, 5);
+ 3, 5);
}
/* Verify public key */
- silcpurple_verify_public_key(client, conn, NULL, conn_type, pk,
- pk_len, pk_type, completion, context);
+ silcpurple_verify_public_key(client, conn, NULL, conn_type,
+ public_key, completion, context);
}
-typedef struct {
- SilcAskPassphrase completion;
- void *context;
-} *SilcPurpleAskPassphrase;
-
static void
silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase)
{
if (!passphrase || !(*passphrase))
- internal->completion(NULL, 0, internal->context);
+ internal->u.ask_pass(NULL, 0, internal->context);
else
- internal->completion((unsigned char *)passphrase,
+ internal->u.ask_pass((unsigned char *)passphrase,
strlen(passphrase), internal->context);
silc_free(internal);
}
@@ -1905,97 +1850,32 @@ silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
if (!internal)
return;
- internal->completion = completion;
+ internal->u.ask_pass = completion;
internal->context = context;
purple_request_input(gc, _("Passphrase"), NULL,
- _("Passphrase required"), NULL, FALSE, TRUE, NULL,
- _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
- _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
- purple_connection_get_account(gc), NULL, NULL, internal);
+ _("Passphrase required"), NULL, FALSE, TRUE, NULL,
+ _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
+ _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
+ purple_connection_get_account(gc), NULL, NULL, internal);
}
-/* Notifies application that failure packet was received. This is called
- if there is some protocol active in the client. The `protocol' is the
- protocol context. The `failure' is opaque pointer to the failure
- indication. Note, that the `failure' is protocol dependant and
- application must explicitly cast it to correct type. Usually `failure'
- is 32 bit failure type (see protocol specs for all protocol failure
- types). */
+/* Called to indicate that incoming key agreement request has been
+ received. If the application wants to perform key agreement it may
+ call silc_client_perform_key_agreement to initiate key agreement or
+ silc_client_send_key_agreement to provide connection point to the
+ remote client in case the `hostname' is NULL. If key agreement is
+ not desired this request can be ignored. The `protocol' is either
+ value 0 for TCP or value 1 for UDP. */
static void
-silc_failure(SilcClient client, SilcClientConnection conn,
- SilcProtocol protocol, void *failure)
-{
- PurpleConnection *gc = client->application;
- char buf[128];
-
- memset(buf, 0, sizeof(buf));
-
- if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
- SilcSKEStatus status = (SilcSKEStatus)SILC_PTR_TO_32(failure);
-
- if (status == SILC_SKE_STATUS_BAD_VERSION)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Version mismatch, upgrade your client"));
- if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not trust/support your public key"));
- if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not support proposed KE group"));
- if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not support proposed cipher"));
- if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not support proposed PKCS"));
- if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not support proposed hash function"));
- if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
- g_snprintf(buf, sizeof(buf),
- _("Failure: Remote does not support proposed HMAC"));
- if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
- g_snprintf(buf, sizeof(buf), _("Failure: Incorrect signature"));
- if (status == SILC_SKE_STATUS_INVALID_COOKIE)
- g_snprintf(buf, sizeof(buf), _("Failure: Invalid cookie"));
-
- /* Show the error on the progress bar. A more generic error message
- is going to be showed to user after this in the silc_connected. */
- purple_connection_update_progress(gc, buf, 2, 5);
- }
-
- if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
- SilcUInt32 err = SILC_PTR_TO_32(failure);
-
- if (err == SILC_AUTH_FAILED)
- g_snprintf(buf, sizeof(buf), _("Failure: Authentication failed"));
-
- /* Show the error on the progress bar. A more generic error message
- is going to be showed to user after this in the silc_connected. */
- purple_connection_update_progress(gc, buf, 4, 5);
- }
-}
-
-/* Asks whether the user would like to perform the key agreement protocol.
- This is called after we have received an key agreement packet or an
- reply to our key agreement packet. This returns TRUE if the user wants
- the library to perform the key agreement protocol and FALSE if it is not
- desired (application may start it later by calling the function
- silc_client_perform_key_agreement). If TRUE is returned also the
- `completion' and `context' arguments must be set by the application. */
-
-static bool
silc_key_agreement(SilcClient client, SilcClientConnection conn,
- SilcClientEntry client_entry, const char *hostname,
- SilcUInt16 port, SilcKeyAgreementCallback *completion,
- void **context)
+ SilcClientEntry client_entry,
+ const char *hostname, SilcUInt16 protocol,
+ SilcUInt16 port)
{
- silcpurple_buddy_keyagr_request(client, conn, client_entry, hostname, port);
- *completion = NULL;
- *context = NULL;
- return FALSE;
+ silcpurple_buddy_keyagr_request(client, conn, client_entry,
+ hostname, port, protocol);
}
@@ -2012,39 +1892,7 @@ silc_ftp(SilcClient client, SilcClientConnection conn,
const char *hostname, SilcUInt16 port)
{
silcpurple_ftp_request(client, conn, client_entry, session_id,
- hostname, port);
-}
-
-
-/* Delivers SILC session detachment data indicated by `detach_data' to the
- application. If application has issued SILC_COMMAND_DETACH command
- the client session in the SILC network is not quit. The client remains
- in the network but is detached. The detachment data may be used later
- to resume the session in the SILC Network. The appliation is
- responsible of saving the `detach_data', to for example in a file.
-
- The detachment data can be given as argument to the functions
- silc_client_connect_to_server, or silc_client_add_connection when
- creating connection to remote server, inside SilcClientConnectionParams
- structure. If it is provided the client library will attempt to resume
- the session in the network. After the connection is created
- successfully, the application is responsible of setting the user
- interface for user into the same state it was before detaching (showing
- same channels, channel modes, etc). It can do this by fetching the
- information (like joined channels) from the client library. */
-
-static void
-silc_detach(SilcClient client, SilcClientConnection conn,
- const unsigned char *detach_data, SilcUInt32 detach_data_len)
-{
- PurpleConnection *gc = client->application;
- SilcPurple sg = gc->proto_data;
- const char *file;
-
- /* Save the detachment data to file. */
- file = silcpurple_session_file(purple_account_get_username(sg->account));
- g_unlink(file);
- silc_file_writefile(file, (char *)detach_data, detach_data_len);
+ hostname, port);
}
SilcClientOperations ops = {
@@ -2054,13 +1902,9 @@ SilcClientOperations ops = {
silc_notify,
silc_command,
silc_command_reply,
- silc_connected,
- silc_disconnected,
silc_get_auth_method,
silc_verify_public_key,
silc_ask_passphrase,
- silc_failure,
silc_key_agreement,
- silc_ftp,
- silc_detach
+ silc_ftp
};
diff --git a/libpurple/protocols/silc/pk.c b/libpurple/protocols/silc/pk.c
index 0bd399d6d5..41be306679 100644
--- a/libpurple/protocols/silc/pk.c
+++ b/libpurple/protocols/silc/pk.c
@@ -4,7 +4,7 @@
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2004 Pekka Riikonen
+ Copyright (C) 2004 - 2007 Pekka Riikonen
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
@@ -17,7 +17,7 @@
*/
-#include "silcincludes.h"
+#include "silc.h"
#include "silcclient.h"
#include "silcpurple.h"
@@ -31,18 +31,16 @@ typedef struct {
char *entity_name;
char *fingerprint;
char *babbleprint;
- unsigned char *pk;
- SilcUInt32 pk_len;
- SilcSKEPKType pk_type;
+ SilcPublicKey public_key;
SilcVerifyPublicKey completion;
void *context;
gboolean changed;
} *PublicKeyVerify;
static void silcpurple_verify_ask(const char *entity,
- const char *fingerprint,
- const char *babbleprint,
- PublicKeyVerify verify);
+ const char *fingerprint,
+ const char *babbleprint,
+ PublicKeyVerify verify);
static void silcpurple_verify_cb(PublicKeyVerify verify, gint id)
{
@@ -54,8 +52,8 @@ static void silcpurple_verify_cb(PublicKeyVerify verify, gint id)
verify->completion(TRUE, verify->context);
/* Save the key for future checking */
- silc_pkcs_save_public_key_data(verify->filename, verify->pk,
- verify->pk_len, SILC_PKCS_FILE_PEM);
+ silc_pkcs_save_public_key(verify->filename, verify->public_key,
+ SILC_PKCS_FILE_BASE64);
}
silc_free(verify->filename);
@@ -63,7 +61,7 @@ static void silcpurple_verify_cb(PublicKeyVerify verify, gint id)
silc_free(verify->entity_name);
silc_free(verify->fingerprint);
silc_free(verify->babbleprint);
- silc_free(verify->pk);
+ silc_pkcs_public_key_free(verify->public_key);
silc_free(verify);
}
@@ -74,27 +72,23 @@ static void silcpurple_verify_details_cb(PublicKeyVerify verify)
should have option for the dialogs whether the buttons close them
or not. */
silcpurple_verify_ask(verify->entity, verify->fingerprint,
- verify->babbleprint, verify);
+ verify->babbleprint, verify);
}
static void silcpurple_verify_details(PublicKeyVerify verify, gint id)
{
- SilcPublicKey public_key;
PurpleConnection *gc = verify->client->application;
SilcPurple sg = gc->proto_data;
- silc_pkcs_public_key_decode(verify->pk, verify->pk_len,
- &public_key);
- silcpurple_show_public_key(sg, verify->entity_name, public_key,
- G_CALLBACK(silcpurple_verify_details_cb),
- verify);
- silc_pkcs_public_key_free(public_key);
+ silcpurple_show_public_key(sg, verify->entity_name, verify->public_key,
+ G_CALLBACK(silcpurple_verify_details_cb),
+ verify);
}
static void silcpurple_verify_ask(const char *entity,
- const char *fingerprint,
- const char *babbleprint,
- PublicKeyVerify verify)
+ const char *fingerprint,
+ const char *babbleprint,
+ PublicKeyVerify verify)
{
PurpleConnection *gc = verify->client->application;
char tmp[256], tmp2[256];
@@ -114,18 +108,17 @@ static void silcpurple_verify_ask(const char *entity,
"%s\n%s\n"), entity, fingerprint, babbleprint);
purple_request_action(gc, _("Verify Public Key"), tmp, tmp2,
- PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), entity, NULL, verify, 3,
- _("Yes"), G_CALLBACK(silcpurple_verify_cb),
- _("No"), G_CALLBACK(silcpurple_verify_cb),
- _("_View..."), G_CALLBACK(silcpurple_verify_details));
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_connection_get_account(gc), entity, NULL, verify, 3,
+ _("Yes"), G_CALLBACK(silcpurple_verify_cb),
+ _("No"), G_CALLBACK(silcpurple_verify_cb),
+ _("_View..."), G_CALLBACK(silcpurple_verify_details));
}
void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
- const char *name, SilcSocketType conn_type,
- unsigned char *pk, SilcUInt32 pk_len,
- SilcSKEPKType pk_type,
- SilcVerifyPublicKey completion, void *context)
+ const char *name, SilcConnectionType conn_type,
+ SilcPublicKey public_key,
+ SilcVerifyPublicKey completion, void *context)
{
PurpleConnection *gc = client->application;
int i;
@@ -133,14 +126,18 @@ void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
char *fingerprint, *babbleprint;
struct passwd *pw;
struct stat st;
- char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
- conn_type == SILC_SOCKET_TYPE_ROUTER) ?
+ char *entity = ((conn_type == SILC_CONN_SERVER ||
+ conn_type == SILC_CONN_ROUTER) ?
"server" : "client");
PublicKeyVerify verify;
+ const char *ip, *hostname;
+ SilcUInt16 port;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
- if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+ if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
purple_notify_error(gc, _("Verify Public Key"),
- _("Unsupported public key type"), NULL);
+ _("Unsupported public key type"), NULL);
if (completion)
completion(FALSE, context);
return;
@@ -157,17 +154,22 @@ void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
memset(filename2, 0, sizeof(filename2));
memset(file, 0, sizeof(file));
- if (conn_type == SILC_SOCKET_TYPE_SERVER ||
- conn_type == SILC_SOCKET_TYPE_ROUTER) {
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
+ NULL, &hostname, &ip, &port);
+
+ pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+
+ if (conn_type == SILC_CONN_SERVER ||
+ conn_type == SILC_CONN_ROUTER) {
if (!name) {
g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
- conn->sock->ip, conn->sock->port);
+ ip, port);
g_snprintf(filename, sizeof(filename) - 1,
"%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
silcpurple_silcdir(), entity, file);
g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
- conn->sock->hostname, conn->sock->port);
+ hostname, port);
g_snprintf(filename2, sizeof(filename2) - 1,
"%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
silcpurple_silcdir(), entity, file);
@@ -176,7 +178,7 @@ void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
hostf = filename2;
} else {
g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
- name, conn->sock->port);
+ name, port);
g_snprintf(filename, sizeof(filename) - 1,
"%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
silcpurple_silcdir(), entity, file);
@@ -206,12 +208,10 @@ void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
verify->conn = conn;
verify->filename = strdup(ipf);
verify->entity = strdup(entity);
- verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
- (name ? strdup(name) : strdup(conn->sock->hostname))
+ verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
+ (name ? strdup(name) : strdup(hostname))
: NULL);
- verify->pk = silc_memdup(pk, pk_len);
- verify->pk_len = pk_len;
- verify->pk_type = pk_type;
+ verify->public_key = silc_pkcs_public_key_copy(public_key);
verify->completion = completion;
verify->context = context;
fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
@@ -221,7 +221,7 @@ void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) {
/* Key does not exist, ask user to verify the key and save it */
silcpurple_verify_ask(name ? name : entity,
- fingerprint, babbleprint, verify);
+ fingerprint, babbleprint, verify);
return;
} else {
/* The key already exists, verify it. */
@@ -230,14 +230,8 @@ void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
SilcUInt32 encpk_len;
/* Load the key file, try for both IP filename and hostname filename */
- if (!silc_pkcs_load_public_key(ipf, &public_key,
- SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(ipf, &public_key,
- SILC_PKCS_FILE_BIN) &&
- (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
- SILC_PKCS_FILE_PEM) &&
- !silc_pkcs_load_public_key(hostf, &public_key,
- SILC_PKCS_FILE_BIN)))) {
+ if (!silc_pkcs_load_public_key(ipf, &public_key) &&
+ (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key)))) {
silcpurple_verify_ask(name ? name : entity,
fingerprint, babbleprint, verify);
return;
@@ -266,9 +260,9 @@ void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
silc_free(verify->filename);
silc_free(verify->entity);
silc_free(verify->entity_name);
- silc_free(verify->pk);
silc_free(verify->fingerprint);
silc_free(verify->babbleprint);
+ silc_pkcs_public_key_free(verify->public_key);
silc_free(verify);
}
}
diff --git a/libpurple/protocols/silc/silc.c b/libpurple/protocols/silc/silc.c
index e952f2c414..cea91d893b 100644
--- a/libpurple/protocols/silc/silc.c
+++ b/libpurple/protocols/silc/silc.c
@@ -4,7 +4,7 @@
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2004 - 2005 Pekka Riikonen
+ Copyright (C) 2004 - 2007 Pekka Riikonen
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
@@ -17,7 +17,7 @@
*/
-#include "silcincludes.h"
+#include "silc.h"
#include "silcclient.h"
#include "silcpurple.h"
#include "version.h"
@@ -26,6 +26,15 @@
extern SilcClientOperations ops;
static PurplePlugin *silc_plugin = NULL;
+/* Error log message callback */
+
+static SilcBool silcpurple_log_error(SilcLogType type, char *message,
+ void *context)
+{
+ silc_say(NULL, NULL, SILC_CLIENT_MESSAGE_ERROR, message);
+ return TRUE;
+}
+
static const char *
silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b)
{
@@ -102,8 +111,8 @@ silcpurple_set_status(PurpleAccount *account, PurpleStatus *status)
idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT);
SILC_PUT32_MSB(mode, mb);
silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE,
- ++sg->conn->cmd_ident, 2,
- 1, idp->data, idp->len,
+ silcpurple_command_reply, NULL, 2,
+ 1, idp->data, silc_buffer_len(idp),
2, mb, sizeof(mb));
silc_buffer_free(idp);
}
@@ -115,91 +124,56 @@ static void
silcpurple_keepalive(PurpleConnection *gc)
{
SilcPurple sg = gc->proto_data;
- silc_client_send_packet(sg->client, sg->conn, SILC_PACKET_HEARTBEAT,
- NULL, 0);
+ silc_packet_send(sg->conn->stream, SILC_PACKET_HEARTBEAT, 0,
+ NULL, 0);
}
static gboolean
silcpurple_scheduler(gpointer *context)
{
- SilcPurple sg = (SilcPurple)context;
- silc_client_run_one(sg->client);
+ SilcClient client = (SilcClient)context;
+ silc_client_run_one(client);
return TRUE;
}
static void
-silcpurple_nickname_parse(const char *nickname,
- char **ret_nickname)
-{
- silc_parse_userfqdn(nickname, ret_nickname, NULL);
-}
-
-static void
-silcpurple_login_connected(gpointer data, gint source, const gchar *error_message)
+silcpurple_connect_cb(SilcClient client, SilcClientConnection conn,
+ SilcClientConnectionStatus status, SilcStatus error,
+ const char *message, void *context)
{
- PurpleConnection *gc = data;
+ PurpleConnection *gc = context;
SilcPurple sg;
- SilcClient client;
- SilcClientConnection conn;
- PurpleAccount *account;
- SilcClientConnectionParams params;
- const char *dfile;
-
- g_return_if_fail(gc != NULL);
+ SilcUInt32 mask;
+ char tz[16];
+#ifdef SILC_ATTRIBUTE_USER_ICON
+ PurpleStoredImage *img;
+#endif
+#ifdef HAVE_SYS_UTSNAME_H
+ struct utsname u;
+#endif
sg = gc->proto_data;
- if (source < 0) {
- purple_connection_error(gc, _("Connection failed"));
- return;
- }
+ switch (status) {
+ case SILC_CLIENT_CONN_SUCCESS:
+ case SILC_CLIENT_CONN_SUCCESS_RESUME:
+ sg->conn = conn;
- client = sg->client;
- account = sg->account;
+ /* Connection created successfully */
+ purple_connection_set_state(gc, PURPLE_CONNECTED);
- /* Get session detachment data, if available */
- memset(&params, 0, sizeof(params));
- dfile = silcpurple_session_file(purple_account_get_username(sg->account));
- params.detach_data = (unsigned char *)silc_file_readfile(dfile, &params.detach_data_len);
- if (params.detach_data)
- params.detach_data[params.detach_data_len] = 0;
-
- /* Add connection to SILC client library */
- conn = silc_client_add_connection(
- sg->client, &params,
- (char *)purple_account_get_string(account, "server",
- "silc.silcnet.org"),
- purple_account_get_int(account, "port", 706), sg);
- if (!conn) {
- purple_connection_error(gc, _("Cannot initialize SILC Client connection"));
- gc->proto_data = NULL;
- return;
- }
- sg->conn = conn;
-
- /* Progress */
- if (params.detach_data) {
- purple_connection_update_progress(gc, _("Resuming session"), 2, 5);
- sg->resuming = TRUE;
- } else {
- purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5);
- }
+ /* Send the server our buddy list */
+ silcpurple_send_buddylist(gc);
- /* Perform SILC Key Exchange. The "silc_connected" will be called
- eventually. */
- silc_client_start_key_exchange(sg->client, sg->conn, source);
+ g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
- /* Set default attributes */
- if (!purple_account_get_bool(account, "reject-attrs", FALSE)) {
- SilcUInt32 mask;
- const char *tmp;
-#ifdef SILC_ATTRIBUTE_USER_ICON
- PurpleStoredImage *img;
-#endif
-#ifdef HAVE_SYS_UTSNAME_H
- struct utsname u;
-#endif
+ /* Send any UMODEs configured for account */
+ if (purple_account_get_bool(sg->account, "block-ims", FALSE)) {
+ silc_client_command_call(sg->client, sg->conn, NULL,
+ "UMODE", "+P", NULL);
+ }
+ /* Set default attributes */
mask = SILC_ATTRIBUTE_MOOD_NORMAL;
silc_client_attribute_add(client, conn,
SILC_ATTRIBUTE_STATUS_MOOD,
@@ -222,36 +196,193 @@ silcpurple_login_connected(gpointer data, gint source, const gchar *error_messag
(void *)&dev, sizeof(dev));
}
#endif
-#ifdef _WIN32
- tmp = _tzname[0];
-#else
- tmp = tzname[0];
-#endif
+ silc_timezone(tz, sizeof(tz));
silc_client_attribute_add(client, conn,
SILC_ATTRIBUTE_TIMEZONE,
- (void *)tmp, strlen(tmp));
+ (void *)tz, strlen(tz));
#ifdef SILC_ATTRIBUTE_USER_ICON
/* Set our buddy icon */
- img = purple_buddy_icons_find_account_icon(account);
+ img = purple_buddy_icons_find_account_icon(sg->account);
silcpurple_buddy_set_icon(gc, img);
purple_imgstore_unref(img);
#endif
+
+ return;
+ break;
+
+ case SILC_CLIENT_CONN_DISCONNECTED:
+ /* Disconnected */
+ if (sg->resuming && !sg->detaching)
+ g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+
+ /* Close the connection */
+ if (!sg->detaching)
+ purple_connection_error(gc, _("Disconnected by server"));
+ else
+ /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
+ purple_account_disconnect(purple_connection_get_account(gc));
+ break;
+
+ case SILC_CLIENT_CONN_ERROR:
+ purple_connection_error(gc, _("Error during connecting to SILC Server"));
+ g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+ break;
+
+ case SILC_CLIENT_CONN_ERROR_KE:
+ purple_connection_error(gc, _("Key Exchange failed"));
+ break;
+
+ case SILC_CLIENT_CONN_ERROR_AUTH:
+ purple_connection_error(gc, _("Authentication failed"));
+ break;
+
+ case SILC_CLIENT_CONN_ERROR_RESUME:
+ purple_connection_error(gc,
+ _("Resuming detached session failed. "
+ "Press Reconnect to create new connection."));
+ g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+ break;
+
+ case SILC_CLIENT_CONN_ERROR_TIMEOUT:
+ purple_connection_error(gc, _("Connection Timeout"));
+ break;
+ }
+
+ /* Error */
+ sg->conn = NULL;
+}
+
+static void
+silcpurple_stream_created(SilcSocketStreamStatus status, SilcStream stream,
+ void *context)
+{
+ PurpleConnection *gc = context;
+ SilcPurple sg;
+ SilcClient client;
+ SilcClientConnectionParams params;
+ const char *dfile;
+
+ sg = gc->proto_data;
+
+ if (status != SILC_SOCKET_OK) {
+ purple_connection_error(gc, _("Connection failed"));
+ silc_pkcs_public_key_free(sg->public_key);
+ silc_pkcs_private_key_free(sg->private_key);
+ silc_free(sg);
+ gc->proto_data = NULL;
+ return;
+ }
+
+ client = sg->client;
+
+ /* Progress */
+ if (params.detach_data) {
+ purple_connection_update_progress(gc, _("Resuming session"), 2, 5);
+ sg->resuming = TRUE;
+ } else {
+ purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5);
}
+ /* Get session detachment data, if available */
+ memset(&params, 0, sizeof(params));
+ dfile = silcpurple_session_file(purple_account_get_username(sg->account));
+ params.detach_data = (unsigned char *)silc_file_readfile(dfile, &params.detach_data_len);
+ if (params.detach_data)
+ params.detach_data[params.detach_data_len] = 0;
+ params.ignore_requested_attributes = FALSE;
+ params.pfs = purple_account_get_bool(sg->account, "pfs", FALSE);
+
+ /* Perform SILC Key Exchange. */
+ silc_client_key_exchange(sg->client, &params, sg->public_key,
+ sg->private_key, stream, SILC_CONN_SERVER,
+ silcpurple_connect_cb, gc);
+
silc_free(params.detach_data);
}
static void
-silcpurple_login(PurpleAccount *account)
+silcpurple_login_connected(gpointer data, gint source, const gchar *error_message)
{
+ PurpleConnection *gc = data;
SilcPurple sg;
+
+ g_return_if_fail(gc != NULL);
+
+ sg = gc->proto_data;
+
+ if (source < 0) {
+ purple_connection_error(gc, _("Connection failed"));
+ silc_pkcs_public_key_free(sg->public_key);
+ silc_pkcs_private_key_free(sg->private_key);
+ silc_free(sg);
+ gc->proto_data = NULL;
+ return;
+ }
+
+ /* Wrap socket to TCP stream */
+ silc_socket_tcp_stream_create(source, TRUE, FALSE,
+ sg->client->schedule,
+ silcpurple_stream_created, gc);
+}
+
+static void silcpurple_running(SilcClient client, void *context)
+{
+ PurpleAccount *account = context;
+ PurpleConnection *gc = account->gc;
+ SilcPurple sg;
+ char pkd[256], prd[256];
+
+ sg = silc_calloc(1, sizeof(*sg));
+ if (!sg)
+ return;
+ memset(sg, 0, sizeof(*sg));
+ sg->client = client;
+ sg->gc = gc;
+ sg->account = account;
+ sg->scheduler = SILC_PTR_TO_32(gc->proto_data);
+ gc->proto_data = sg;
+
+ /* Progress */
+ purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5);
+
+ /* Load SILC key pair */
+ g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
+ g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
+ if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
+ (char *)purple_account_get_string(account, "private-key", prd),
+ (gc->password == NULL) ? "" : gc->password,
+ &sg->public_key, &sg->private_key)) {
+ g_snprintf(pkd, sizeof(pkd), _("Could not load SILC key pair"));
+ purple_connection_error(gc, pkd);
+ gc->proto_data = NULL;
+ silc_free(sg);
+ return;
+ }
+
+ /* Connect to the SILC server */
+ if (purple_proxy_connect(gc, account,
+ purple_account_get_string(account, "server",
+ "silc.silcnet.org"),
+ purple_account_get_int(account, "port", 706),
+ silcpurple_login_connected, gc) == NULL)
+ {
+ purple_connection_error(gc, _("Unable to create connection"));
+ gc->proto_data = NULL;
+ silc_free(sg);
+ return;
+ }
+}
+
+static void
+silcpurple_login(PurpleAccount *account)
+{
SilcClient client;
- SilcClientParams params;
PurpleConnection *gc;
- char pkd[256], prd[256];
+ SilcClientParams params;
const char *cipher, *hmac;
- char *realname;
+ char *username, *hostname, *realname, **up;
+ guint scheduler;
int i;
gc = account->gc;
@@ -260,10 +391,7 @@ silcpurple_login(PurpleAccount *account)
gc->proto_data = NULL;
memset(&params, 0, sizeof(params));
- strcat(params.nickname_format, "%n@%h%a");
- params.nickname_parse = silcpurple_nickname_parse;
- params.ignore_requested_attributes =
- purple_account_get_bool(account, "reject-attrs", FALSE);
+ strcat(params.nickname_format, "%n#a");
/* Allocate SILC client */
client = silc_client_alloc(&ops, &params, gc, NULL);
@@ -273,32 +401,28 @@ silcpurple_login(PurpleAccount *account)
}
/* Get username, real name and local hostname for SILC library */
- if (purple_account_get_username(account)) {
- const char *u = purple_account_get_username(account);
- char **up = g_strsplit(u, "@", 2);
- client->username = strdup(up[0]);
- g_strfreev(up);
- } else {
- client->username = silc_get_username();
- purple_account_set_username(account, client->username);
+ if (!purple_account_get_username(account))
+ purple_account_set_username(account, silc_get_username());
+
+ username = (char *)purple_account_get_username(account);
+ up = g_strsplit(username, "@", 2);
+ username = strdup(up[0]);
+ g_strfreev(up);
+
+ if (!purple_account_get_user_info(account)) {
+ purple_account_set_user_info(account, silc_get_real_name());
+ if (!purple_account_get_user_info(account))
+ purple_account_set_user_info(account,
+ "John T. Noname");
}
- realname = silc_get_real_name();
- if (purple_account_get_user_info(account)) {
- client->realname = strdup(purple_account_get_user_info(account));
- free(realname);
- } else if ((silc_get_real_name() != NULL) && (*realname != '\0')) {
- client->realname = realname;
- purple_account_set_user_info(account, client->realname);
- } else {
- free(realname);
- client->realname = strdup(_("John Noname"));
- }
- client->hostname = silc_net_localhost();
+ realname = (char *)purple_account_get_user_info(account);
+ hostname = silc_net_localhost();
- purple_connection_set_display_name(gc, client->username);
+ purple_connection_set_display_name(gc, username);
/* Register requested cipher and HMAC */
- cipher = purple_account_get_string(account, "cipher", SILC_DEFAULT_CIPHER);
+ cipher = purple_account_get_string(account, "cipher",
+ SILC_DEFAULT_CIPHER);
for (i = 0; silc_default_ciphers[i].name; i++)
if (!strcmp(silc_default_ciphers[i].name, cipher)) {
silc_cipher_register(&(silc_default_ciphers[i]));
@@ -312,7 +436,8 @@ silcpurple_login(PurpleAccount *account)
}
/* Init SILC client */
- if (!silc_client_init(client)) {
+ if (!silc_client_init(client, username, hostname, realname,
+ silcpurple_running, account)) {
gc->wants_to_die = TRUE;
purple_connection_error(gc, _("Cannot initialize SILC protocol"));
return;
@@ -321,54 +446,20 @@ silcpurple_login(PurpleAccount *account)
/* Check the ~/.silc dir and create it, and new key pair if necessary. */
if (!silcpurple_check_silc_dir(gc)) {
gc->wants_to_die = TRUE;
- purple_connection_error(gc, _("Cannot find/access ~/.silc directory"));
- return;
- }
-
- /* Progress */
- purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5);
-
- /* Load SILC key pair */
- g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
- g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
- if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
- (char *)purple_account_get_string(account, "private-key", prd),
- (gc->password == NULL) ? "" : gc->password, &client->pkcs,
- &client->public_key, &client->private_key)) {
- g_snprintf(pkd, sizeof(pkd), _("Could not load SILC key pair: %s"), strerror(errno));
- purple_connection_error(gc, pkd);
- return;
- }
-
- sg = silc_calloc(1, sizeof(*sg));
- if (!sg)
- return;
- memset(sg, 0, sizeof(*sg));
- sg->client = client;
- sg->gc = gc;
- sg->account = account;
- gc->proto_data = sg;
-
- /* Connect to the SILC server */
- if (purple_proxy_connect(gc, account,
- purple_account_get_string(account, "server",
- "silc.silcnet.org"),
- purple_account_get_int(account, "port", 706),
- silcpurple_login_connected, gc) == NULL)
- {
- purple_connection_error(gc, _("Unable to create connection"));
+ purple_connection_error(gc, _("Error loading SILC key pair"));
return;
}
/* Schedule SILC using Glib's event loop */
- sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg);
+ scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, client);
+ gc->proto_data = SILC_32_TO_PTR(scheduler);
}
static int
silcpurple_close_final(gpointer *context)
{
SilcPurple sg = (SilcPurple)context;
- silc_client_stop(sg->client);
+ silc_client_stop(sg->client, NULL, NULL);
silc_client_free(sg->client);
#ifdef HAVE_SILCMIME_H
if (sg->mimeass)
@@ -387,7 +478,7 @@ silcpurple_close(PurpleConnection *gc)
/* Send QUIT */
silc_client_command_call(sg->client, sg->conn, NULL,
- "QUIT", "Download this: " PURPLE_WEBSITE, NULL);
+ "QUIT", "Download Pidgin: " PURPLE_WEBSITE, NULL);
if (sg->conn)
silc_client_close_connection(sg->client, sg->conn);
@@ -595,7 +686,7 @@ silcpurple_attrs(PurplePluginAction *action)
gboolean cemail = FALSE, ccall = FALSE, csms = FALSE,
cmms = FALSE, cchat = TRUE, cvideo = FALSE;
gboolean device = TRUE;
- char status[1024];
+ char status[1024], tz[16];
sg = gc->proto_data;
if (!sg)
@@ -722,11 +813,9 @@ silcpurple_attrs(PurplePluginAction *action)
purple_account_get_string(sg->account, "vcard", ""),
FALSE);
purple_request_field_group_add_field(g, f);
-#ifdef _WIN32
- f = purple_request_field_string_new("timezone", _("Timezone"), _tzname[0], FALSE);
-#else
- f = purple_request_field_string_new("timezone", _("Timezone"), tzname[0], FALSE);
-#endif
+
+ silc_timezone(tz, sizeof(tz));
+ f = purple_request_field_string_new("timezone", _("Timezone (UTC)"), tz, FALSE);
purple_request_field_group_add_field(g, f);
purple_request_fields_add_group(fields, g);
@@ -861,12 +950,14 @@ silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields)
if (f)
c = purple_request_field_string_get_value(f);
- identifier = silc_pkcs_encode_identifier((char *)un, (char *)hn,
- (char *)rn, (char *)e, (char *)o, (char *)c);
+ identifier = silc_pkcs_silc_encode_identifier((char *)un, (char *)hn,
+ (char *)rn, (char *)e,
+ (char *)o, (char *)c,
+ NULL);
/* Create the key pair */
if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile,
- identifier, pass1, NULL, &public_key, NULL,
+ identifier, pass1, &public_key, NULL,
FALSE)) {
purple_notify_error(
gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL);
@@ -941,10 +1032,10 @@ silcpurple_create_keypair(PurplePluginAction *action)
purple_request_fields_add_group(fields, g);
purple_request_fields(gc, _("Create New SILC Key Pair"),
- _("Create New SILC Key Pair"), NULL, fields,
- _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
- _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
- gc->account, NULL, NULL, gc);
+ _("Create New SILC Key Pair"), NULL, fields,
+ _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
+ _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
+ gc->account, NULL, NULL, gc);
g_strfreev(u);
silc_free(hostname);
@@ -963,8 +1054,8 @@ silcpurple_change_passwd(PurpleConnection *gc, const char *old, const char *new)
char prd[256];
g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir());
silc_change_private_key_passphrase(purple_account_get_string(gc->account,
- "private-key",
- prd), old, new);
+ "private-key",
+ prd), old, new);
}
static void
@@ -982,15 +1073,12 @@ silcpurple_set_info(PurpleConnection *gc, const char *text)
static GList *
silcpurple_actions(PurplePlugin *plugin, gpointer context)
{
- PurpleConnection *gc = context;
GList *list = NULL;
PurplePluginAction *act;
- if (!purple_account_get_bool(gc->account, "reject-attrs", FALSE)) {
- act = purple_plugin_action_new(_("Online Status"),
- silcpurple_attrs);
- list = g_list_append(list, act);
- }
+ act = purple_plugin_action_new(_("Online Status"),
+ silcpurple_attrs);
+ list = g_list_append(list, act);
act = purple_plugin_action_new(_("Detach From Server"),
silcpurple_detach);
@@ -1028,49 +1116,46 @@ typedef struct {
static void
silcpurple_send_im_resolved(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context)
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList clients,
+ void *context)
{
PurpleConnection *gc = client->application;
SilcPurple sg = gc->proto_data;
SilcPurpleIM im = context;
PurpleConversation *convo;
- char tmp[256], *nickname = NULL;
+ char tmp[256];
SilcClientEntry client_entry;
#ifdef HAVE_SILCMIME_H
SilcDList list;
#endif
convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick,
- sg->account);
+ sg->account);
if (!convo)
return;
if (!clients)
goto err;
- if (clients_count > 1) {
- silc_parse_userfqdn(im->nick, &nickname, NULL);
-
+ if (silc_dlist_count(clients) > 1) {
/* Find the correct one. The im->nick might be a formatted nick
so this will find the correct one. */
clients = silc_client_get_clients_local(client, conn,
- nickname, im->nick,
- &clients_count);
+ im->nick, FALSE);
if (!clients)
goto err;
- client_entry = clients[0];
- silc_free(clients);
- } else {
- client_entry = clients[0];
}
+ silc_dlist_start(clients);
+ client_entry = silc_dlist_get(clients);
+
#ifdef HAVE_SILCMIME_H
/* Check for images */
if (im->gflags & PURPLE_MESSAGE_IMAGES) {
- list = silcpurple_image_message(im->message, (SilcUInt32 *)&im->flags);
+ list = silcpurple_image_message(im->message,
+ (SilcUInt32 *)(void *)&im->flags);
if (list) {
/* Send one or more MIME message. If more than one, they
are MIME fragments due to over large message */
@@ -1079,12 +1164,12 @@ silcpurple_send_im_resolved(SilcClient client,
silc_dlist_start(list);
while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
silc_client_send_private_message(client, conn,
- client_entry, im->flags,
- buf->data, buf->len,
- TRUE);
+ client_entry, im->flags, NULL,
+ buf->data,
+ silc_buffer_len(buf));
silc_mime_partial_free(list);
purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
- im->message, 0, time(NULL));
+ im->message, 0, time(NULL));
goto out;
}
}
@@ -1092,9 +1177,9 @@ silcpurple_send_im_resolved(SilcClient client,
/* Send the message */
silc_client_send_private_message(client, conn, client_entry, im->flags,
- (unsigned char *)im->message, im->message_len, TRUE);
+ NULL, (unsigned char *)im->message, im->message_len);
purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
- im->message, 0, time(NULL));
+ im->message, 0, time(NULL));
goto out;
err:
@@ -1106,19 +1191,19 @@ silcpurple_send_im_resolved(SilcClient client,
g_free(im->nick);
g_free(im->message);
silc_free(im);
- silc_free(nickname);
}
static int
silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
- PurpleMessageFlags flags)
+ PurpleMessageFlags flags)
{
SilcPurple sg = gc->proto_data;
SilcClient client = sg->client;
SilcClientConnection conn = sg->conn;
- SilcClientEntry *clients;
- SilcUInt32 clients_count, mflags;
- char *nickname, *msg, *tmp;
+ SilcDList clients;
+ SilcClientEntry client_entry;
+ SilcUInt32 mflags;
+ char *msg, *tmp;
int ret = 0;
gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
#ifdef HAVE_SILCMIME_H
@@ -1141,14 +1226,9 @@ silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
mflags |= SILC_MESSAGE_FLAG_ACTION;
} else if (strlen(msg) > 1 && msg[0] == '/') {
if (!silc_client_command_call(client, conn, msg + 1))
- purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
- _("Unknown command"));
- g_free(tmp);
- return 0;
- }
-
-
- if (!silc_parse_userfqdn(who, &nickname, NULL)) {
+ purple_notify_error(gc, _("Call Command"),
+ _("Cannot call command"),
+ _("Unknown command"));
g_free(tmp);
return 0;
}
@@ -1157,8 +1237,7 @@ silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
mflags |= SILC_MESSAGE_FLAG_SIGNED;
/* Find client entry */
- clients = silc_client_get_clients_local(client, conn, nickname, who,
- &clients_count);
+ clients = silc_client_get_clients_local(client, conn, who, FALSE);
if (!clients) {
/* Resolve unknown user */
SilcPurpleIM im = silc_calloc(1, sizeof(*im));
@@ -1171,13 +1250,15 @@ silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
im->message_len = strlen(im->message);
im->flags = mflags;
im->gflags = flags;
- silc_client_get_clients(client, conn, nickname, NULL,
+ silc_client_get_clients(client, conn, who, NULL,
silcpurple_send_im_resolved, im);
- silc_free(nickname);
g_free(tmp);
return 0;
}
+ silc_dlist_start(clients);
+ client_entry = silc_dlist_get(clients);
+
#ifdef HAVE_SILCMIME_H
/* Check for images */
if (flags & PURPLE_MESSAGE_IMAGES) {
@@ -1191,27 +1272,25 @@ silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
ret =
silc_client_send_private_message(client, conn,
- clients[0], mflags,
- buf->data, buf->len,
- TRUE);
+ client_entry, mflags, NULL,
+ buf->data,
+ silc_buffer_len(buf));
silc_mime_partial_free(list);
g_free(tmp);
- silc_free(nickname);
- silc_free(clients);
+ silc_client_list_free(client, conn, clients);
return ret;
}
}
#endif
/* Send private message directly */
- ret = silc_client_send_private_message(client, conn, clients[0],
- mflags,
+ ret = silc_client_send_private_message(client, conn, client_entry,
+ mflags, NULL,
(unsigned char *)msg,
- strlen(msg), TRUE);
+ strlen(msg));
g_free(tmp);
- silc_free(nickname);
- silc_free(clients);
+ silc_client_list_free(client, conn, clients);
return ret;
}
@@ -1219,7 +1298,6 @@ silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) {
/* split this single menu building function back into the two
original: one for buddies and one for chats */
-
if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
return silcpurple_chat_menu((PurpleChat *) node);
} else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
@@ -1548,7 +1626,7 @@ static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
return PURPLE_CMD_RET_FAILED;
silc_client_command_call(sg->client, sg->conn, NULL,
- "QUIT", (args && args[0]) ? args[0] : "Download this: " PURPLE_WEBSITE, NULL);
+ "QUIT", (args && args[0]) ? args[0] : "Download Pidgin: " PURPLE_WEBSITE, NULL);
return PURPLE_CMD_RET_OK;
}
@@ -1730,75 +1808,75 @@ static PurplePluginProtocolInfo prpl_info =
OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_SLASH_COMMANDS_NATIVE,
#endif
- NULL, /* user_splits */
- NULL, /* protocol_options */
+ NULL, /* user_splits */
+ NULL, /* protocol_options */
#ifdef SILC_ATTRIBUTE_USER_ICON
{"jpeg,gif,png,bmp", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
#else
NO_BUDDY_ICONS,
#endif
- silcpurple_list_icon, /* list_icon */
- NULL, /* list_emblems */
- silcpurple_status_text, /* status_text */
+ silcpurple_list_icon, /* list_icon */
+ NULL, /* list_emblems */
+ silcpurple_status_text, /* status_text */
silcpurple_tooltip_text, /* tooltip_text */
- silcpurple_away_states, /* away_states */
- silcpurple_blist_node_menu, /* blist_node_menu */
+ silcpurple_away_states, /* away_states */
+ silcpurple_blist_node_menu, /* blist_node_menu */
silcpurple_chat_info, /* chat_info */
- silcpurple_chat_info_defaults,/* chat_info_defaults */
- silcpurple_login, /* login */
- silcpurple_close, /* close */
+ silcpurple_chat_info_defaults, /* chat_info_defaults */
+ silcpurple_login, /* login */
+ silcpurple_close, /* close */
silcpurple_send_im, /* send_im */
silcpurple_set_info, /* set_info */
- NULL, /* send_typing */
+ NULL, /* send_typing */
silcpurple_get_info, /* get_info */
- silcpurple_set_status, /* set_status */
+ silcpurple_set_status, /* set_status */
silcpurple_idle_set, /* set_idle */
silcpurple_change_passwd, /* change_passwd */
silcpurple_add_buddy, /* add_buddy */
- NULL, /* add_buddies */
+ NULL, /* add_buddies */
silcpurple_remove_buddy, /* remove_buddy */
- NULL, /* remove_buddies */
- NULL, /* add_permit */
- NULL, /* add_deny */
- NULL, /* rem_permit */
- NULL, /* rem_deny */
- NULL, /* set_permit_deny */
+ NULL, /* remove_buddies */
+ NULL, /* add_permit */
+ NULL, /* add_deny */
+ NULL, /* rem_permit */
+ NULL, /* rem_deny */
+ NULL, /* set_permit_deny */
silcpurple_chat_join, /* join_chat */
- NULL, /* reject_chat */
+ NULL, /* reject_chat */
silcpurple_get_chat_name, /* get_chat_name */
- silcpurple_chat_invite, /* chat_invite */
- silcpurple_chat_leave, /* chat_leave */
- NULL, /* chat_whisper */
+ silcpurple_chat_invite, /* chat_invite */
+ silcpurple_chat_leave, /* chat_leave */
+ NULL, /* chat_whisper */
silcpurple_chat_send, /* chat_send */
silcpurple_keepalive, /* keepalive */
- NULL, /* register_user */
- NULL, /* get_cb_info */
- NULL, /* get_cb_away */
- NULL, /* alias_buddy */
- NULL, /* group_buddy */
- NULL, /* rename_group */
- NULL, /* buddy_free */
- NULL, /* convo_closed */
- NULL, /* normalize */
+ NULL, /* register_user */
+ NULL, /* get_cb_info */
+ NULL, /* get_cb_away */
+ NULL, /* alias_buddy */
+ NULL, /* group_buddy */
+ NULL, /* rename_group */
+ NULL, /* buddy_free */
+ NULL, /* convo_closed */
+ NULL, /* normalize */
#ifdef SILC_ATTRIBUTE_USER_ICON
- silcpurple_buddy_set_icon, /* set_buddy_icon */
+ silcpurple_buddy_set_icon, /* set_buddy_icon */
#else
NULL,
#endif
- NULL, /* remove_group */
- NULL, /* get_cb_real_name */
- silcpurple_chat_set_topic, /* set_chat_topic */
- NULL, /* find_blist_chat */
- silcpurple_roomlist_get_list, /* roomlist_get_list */
- silcpurple_roomlist_cancel, /* roomlist_cancel */
- NULL, /* roomlist_expand_category */
- NULL, /* can_receive_file */
+ NULL, /* remove_group */
+ NULL, /* get_cb_real_name */
+ silcpurple_chat_set_topic, /* set_chat_topic */
+ NULL, /* find_blist_chat */
+ silcpurple_roomlist_get_list, /* roomlist_get_list */
+ silcpurple_roomlist_cancel, /* roomlist_cancel */
+ NULL, /* roomlist_expand_category */
+ NULL, /* can_receive_file */
silcpurple_ftp_send_file, /* send_file */
silcpurple_ftp_new_xfer, /* new_xfer */
- NULL, /* offline_message */
+ NULL, /* offline_message */
&silcpurple_wb_ops, /* whiteboard_prpl_ops */
- NULL, /* send_raw */
- NULL, /* roomlist_room_serialize */
+ NULL, /* send_raw */
+ NULL, /* roomlist_room_serialize */
/* padding */
NULL,
@@ -1812,29 +1890,29 @@ static PurplePluginInfo info =
PURPLE_PLUGIN_MAGIC,
PURPLE_MAJOR_VERSION,
PURPLE_MINOR_VERSION,
- PURPLE_PLUGIN_PROTOCOL, /**< type */
- NULL, /**< ui_requirement */
- 0, /**< flags */
- NULL, /**< dependencies */
- PURPLE_PRIORITY_DEFAULT, /**< priority */
-
- "prpl-silc", /**< id */
- "SILC", /**< name */
- "1.0", /**< version */
+ PURPLE_PLUGIN_PROTOCOL, /**< type */
+ NULL, /**< ui_requirement */
+ 0, /**< flags */
+ NULL, /**< dependencies */
+ PURPLE_PRIORITY_DEFAULT, /**< priority */
+
+ "prpl-silc", /**< id */
+ "SILC", /**< name */
+ "1.1", /**< version */
/** summary */
N_("SILC Protocol Plugin"),
/** description */
N_("Secure Internet Live Conferencing (SILC) Protocol"),
- "Pekka Riikonen", /**< author */
- "http://silcnet.org/", /**< homepage */
+ "Pekka Riikonen", /**< author */
+ "http://silcnet.org/", /**< homepage */
- NULL, /**< load */
- NULL, /**< unload */
- NULL, /**< destroy */
+ NULL, /**< load */
+ NULL, /**< unload */
+ NULL, /**< destroy */
- NULL, /**< ui_info */
- &prpl_info, /**< extra_info */
- NULL, /**< prefs_info */
+ NULL, /**< ui_info */
+ &prpl_info, /**< extra_info */
+ NULL, /**< prefs_info */
silcpurple_actions,
/* padding */
@@ -1861,18 +1939,18 @@ init_plugin(PurplePlugin *plugin)
/* Account options */
option = purple_account_option_string_new(_("Connect server"),
- "server",
- "silc.silcnet.org");
+ "server",
+ "silc.silcnet.org");
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
option = purple_account_option_int_new(_("Port"), "port", 706);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
option = purple_account_option_string_new(_("Public Key file"),
- "public-key", tmp);
+ "public-key", tmp);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
option = purple_account_option_string_new(_("Private Key file"),
- "private-key", tmp);
+ "private-key", tmp);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
for (i = 0; silc_default_ciphers[i].name; i++) {
@@ -1894,35 +1972,36 @@ init_plugin(PurplePlugin *plugin)
option = purple_account_option_list_new(_("HMAC"), "hmac", list);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_bool_new(_("Public key authentication"),
- "pubkey-auth", FALSE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_bool_new(_("Reject watching by other users"),
- "reject-watch", FALSE);
+ option = purple_account_option_bool_new(_("Use Perfect Forward Secrecy"),
+ "pfs", FALSE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_bool_new(_("Block invites"),
- "block-invites", FALSE);
+
+ option = purple_account_option_bool_new(_("Public key authentication"),
+ "pubkey-auth", FALSE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
option = purple_account_option_bool_new(_("Block IMs without Key Exchange"),
- "block-ims", FALSE);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_bool_new(_("Reject online status attribute requests"),
- "reject-attrs", FALSE);
+ "block-ims", FALSE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
option = purple_account_option_bool_new(_("Block messages to whiteboard"),
- "block-wb", FALSE);
+ "block-wb", FALSE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
option = purple_account_option_bool_new(_("Automatically open whiteboard"),
- "open-wb", FALSE);
+ "open-wb", FALSE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
option = purple_account_option_bool_new(_("Digitally sign and verify all messages"),
- "sign-verify", FALSE);
+ "sign-verify", FALSE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
purple_prefs_remove("/plugins/prpl/silc");
+ silc_log_set_callback(SILC_LOG_ERROR, silcpurple_log_error, NULL);
silcpurple_register_commands();
+#if 0
+silc_log_debug(TRUE);
+silc_log_set_debug_string("*client*");
+#endif
+
#ifdef _WIN32
silc_net_win32_init();
#endif
diff --git a/libpurple/protocols/silc/silcpurple.h b/libpurple/protocols/silc/silcpurple.h
index 86ba056614..c19c6d6223 100644
--- a/libpurple/protocols/silc/silcpurple.h
+++ b/libpurple/protocols/silc/silcpurple.h
@@ -4,7 +4,7 @@
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2004 Pekka Riikonen
+ Copyright (C) 2004 - 2007 Pekka Riikonen
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
@@ -66,6 +66,8 @@ typedef struct {
typedef struct SilcPurpleStruct {
SilcClient client;
SilcClientConnection conn;
+ SilcPublicKey public_key;
+ SilcPrivateKey private_key;
guint scheduler;
PurpleConnection *gc;
@@ -85,27 +87,29 @@ typedef struct SilcPurpleStruct {
} *SilcPurple;
+void silc_say(SilcClient client, SilcClientConnection conn,
+ SilcClientMessageType type, char *msg, ...);
+SilcBool silcpurple_command_reply(SilcClient client, SilcClientConnection conn,
+ SilcCommand command, SilcStatus status,
+ SilcStatus error, void *context, va_list ap);
gboolean silcpurple_check_silc_dir(PurpleConnection *gc);
-void silcpurple_chat_join_done(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- void *context);
const char *silcpurple_silcdir(void);
const char *silcpurple_session_file(const char *account);
void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
- const char *name, SilcSocketType conn_type,
- unsigned char *pk, SilcUInt32 pk_len,
- SilcSKEPKType pk_type,
- SilcVerifyPublicKey completion, void *context);
+ const char *name,
+ SilcConnectionType conn_type,
+ SilcPublicKey public_key,
+ SilcVerifyPublicKey completion,
+ void *context);
GList *silcpurple_buddy_menu(PurpleBuddy *buddy);
void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
void silcpurple_send_buddylist(PurpleConnection *gc);
void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
void silcpurple_buddy_keyagr_request(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- const char *hostname, SilcUInt16 port);
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ const char *hostname, SilcUInt16 port,
+ SilcUInt16 protocol);
void silcpurple_idle_set(PurpleConnection *gc, int idle);
void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full);
char *silcpurple_status_text(PurpleBuddy *b);
@@ -140,7 +144,7 @@ void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic);
PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc);
void silcpurple_roomlist_cancel(PurpleRoomlist *list);
void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
- SilcBuffer channel_pubkeys);
+ SilcDList channel_pubkeys);
void silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
char **contactstr, char **langstr, char **devicestr,
char **tzstr, char **geostr);
diff --git a/libpurple/protocols/silc/util.c b/libpurple/protocols/silc/util.c
index 5310424ee0..84f905188e 100644
--- a/libpurple/protocols/silc/util.c
+++ b/libpurple/protocols/silc/util.c
@@ -4,7 +4,7 @@
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2004 - 2005 Pekka Riikonen
+ Copyright (C) 2004 - 2007 Pekka Riikonen
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
@@ -17,7 +17,7 @@
*/
-#include "silcincludes.h"
+#include "silc.h"
#include "silcclient.h"
#include "silcpurple.h"
#include "imgstore.h"
@@ -206,22 +206,24 @@ gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
if (errno == ENOENT) {
purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
- SILCPURPLE_DEF_PKCS_LEN,
- file_public_key, file_private_key, NULL,
- (gc->password == NULL) ? "" : gc->password,
- NULL, NULL, NULL, FALSE)) {
- purple_debug_error("silc", "Couldn't create key pair\n");
+ SILCPURPLE_DEF_PKCS_LEN,
+ file_public_key,
+ file_private_key, NULL,
+ (gc->password == NULL)
+ ? "" : gc->password,
+ NULL, NULL, FALSE)) {
+ purple_connection_error(gc, _("Cannot create SILC key pair\n"));
return FALSE;
}
if ((g_stat(file_public_key, &st)) == -1) {
purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
- file_public_key, strerror(errno));
+ file_public_key, strerror(errno));
return FALSE;
}
} else {
purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
- file_public_key, strerror(errno));
+ file_public_key, strerror(errno));
return FALSE;
}
}
@@ -237,7 +239,7 @@ gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
if ((fstat(fd, &st)) == -1) {
purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
- file_private_key, strerror(errno));
+ file_private_key, strerror(errno));
close(fd);
return FALSE;
}
@@ -246,18 +248,20 @@ gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
if (errno == ENOENT) {
purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
- SILCPURPLE_DEF_PKCS_LEN,
- file_public_key, file_private_key, NULL,
- (gc->password == NULL) ? "" : gc->password,
- NULL, NULL, NULL, FALSE)) {
- purple_debug_error("silc", "Couldn't create key pair\n");
+ SILCPURPLE_DEF_PKCS_LEN,
+ file_public_key,
+ file_private_key, NULL,
+ (gc->password == NULL)
+ ? "" : gc->password,
+ NULL, NULL, FALSE)) {
+ purple_connection_error(gc, _("Cannot create SILC key pair\n"));
return FALSE;
}
if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
if ((fstat(fd, &st)) == -1) {
purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
- file_private_key, strerror(errno));
+ file_private_key, strerror(errno));
close(fd);
return FALSE;
}
@@ -266,12 +270,12 @@ gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
* will set the permissions */
else if ((g_stat(file_private_key, &st)) == -1) {
purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
- file_private_key, strerror(errno));
+ file_private_key, strerror(errno));
return FALSE;
}
} else {
purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
- file_private_key, strerror(errno));
+ file_private_key, strerror(errno));
return FALSE;
}
}
@@ -323,30 +327,29 @@ uid_t geteuid() {
#endif
void silcpurple_show_public_key(SilcPurple sg,
- const char *name, SilcPublicKey public_key,
- GCallback callback, void *context)
+ const char *name, SilcPublicKey public_key,
+ GCallback callback, void *context)
{
SilcPublicKeyIdentifier ident;
- SilcPKCS pkcs;
+ SilcSILCPublicKey silc_pubkey;
char *fingerprint, *babbleprint;
unsigned char *pk;
SilcUInt32 pk_len, key_len = 0;
GString *s;
char *buf;
- ident = silc_pkcs_decode_identifier(public_key->identifier);
- if (!ident)
- return;
+ /* We support showing only SILC public keys for now */
+ if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC)
+ return;
+
+ silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
+ ident = &silc_pubkey->identifier;
+ key_len = silc_pkcs_public_key_get_len(public_key);
pk = silc_pkcs_public_key_encode(public_key, &pk_len);
fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
- if (silc_pkcs_alloc((unsigned char *)public_key->name, &pkcs)) {
- key_len = silc_pkcs_public_key_set(pkcs, public_key);
- silc_pkcs_free(pkcs);
- }
-
s = g_string_new("");
if (ident->realname)
/* Hint for translators: Please check the tabulator width here and in
@@ -363,8 +366,10 @@ void silcpurple_show_public_key(SilcPurple sg,
g_string_append_printf(s, _("Organization: \t%s\n"), ident->org);
if (ident->country)
g_string_append_printf(s, _("Country: \t%s\n"), ident->country);
- g_string_append_printf(s, _("Algorithm: \t%s\n"), public_key->name);
+ g_string_append_printf(s, _("Algorithm: \t%s\n"), silc_pubkey->pkcs->name);
g_string_append_printf(s, _("Key Length: \t%d bits\n"), (int)key_len);
+ if (ident->version)
+ g_string_append_printf(s, _("Version: \t%s\n"), ident->version);
g_string_append_printf(s, "\n");
g_string_append_printf(s, _("Public Key Fingerprint:\n%s\n\n"), fingerprint);
g_string_append_printf(s, _("Public Key Babbleprint:\n%s"), babbleprint);
@@ -372,15 +377,14 @@ void silcpurple_show_public_key(SilcPurple sg,
buf = g_string_free(s, FALSE);
purple_request_action(sg->gc, _("Public Key Information"),
- _("Public Key Information"),
- buf, 0, purple_connection_get_account(sg->gc),
- NULL, NULL, context, 1, _("Close"), callback);
+ _("Public Key Information"),
+ buf, 0, purple_connection_get_account(sg->gc),
+ NULL, NULL, context, 1, _("Close"), callback);
g_free(buf);
silc_free(fingerprint);
silc_free(babbleprint);
silc_free(pk);
- silc_pkcs_free_identifier(ident);
}
SilcAttributePayload
@@ -400,7 +404,7 @@ silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute)
}
void silcpurple_get_umode_string(SilcUInt32 mode, char *buf,
- SilcUInt32 buf_size)
+ SilcUInt32 buf_size)
{
memset(buf, 0, buf_size);
if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
@@ -435,7 +439,7 @@ void silcpurple_get_umode_string(SilcUInt32 mode, char *buf,
}
void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf,
- SilcUInt32 buf_size)
+ SilcUInt32 buf_size)
{
memset(buf, 0, buf_size);
if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
@@ -482,8 +486,8 @@ void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf,
void
silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
- char **contactstr, char **langstr, char **devicestr,
- char **tzstr, char **geostr)
+ char **contactstr, char **langstr, char **devicestr,
+ char **tzstr, char **geostr)
{
SilcAttributePayload attr;
SilcAttributeMood mood = 0;
@@ -620,23 +624,23 @@ char *silcpurple_file2mime(const char *filename)
ct = strrchr(filename, '.');
if (!ct)
return NULL;
- else if (!g_ascii_strcasecmp(".png", ct))
+ else if (!strcasecmp(".png", ct))
return strdup("image/png");
- else if (!g_ascii_strcasecmp(".jpg", ct))
+ else if (!strcasecmp(".jpg", ct))
return strdup("image/jpeg");
- else if (!g_ascii_strcasecmp(".jpeg", ct))
+ else if (!strcasecmp(".jpeg", ct))
return strdup("image/jpeg");
- else if (!g_ascii_strcasecmp(".gif", ct))
+ else if (!strcasecmp(".gif", ct))
return strdup("image/gif");
- else if (!g_ascii_strcasecmp(".tiff", ct))
+ else if (!strcasecmp(".tiff", ct))
return strdup("image/tiff");
-
+
return NULL;
}
-/* Checks if message has images, and assembles MIME message if it has.
- If only one image is present, creates simple MIME image message. If
- there are multiple images and/or text with images multipart MIME
+/* Checks if message has images, and assembles MIME message if it has.
+ If only one image is present, creates simple MIME image message. If
+ there are multiple images and/or text with images multipart MIME
message is created. */
SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags)
@@ -666,8 +670,9 @@ SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags)
tmp = g_strndup(last, start - last);
text = purple_unescape_html(tmp);
g_free(tmp);
+
/* Add text */
- silc_mime_add_data(p, text, strlen(text));
+ silc_mime_add_data(p, (const unsigned char *)text, strlen(text));
g_free(text);
if (!parts)
@@ -720,7 +725,7 @@ SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags)
"text/plain; charset=utf-8");
/* Add text */
- silc_mime_add_data(p, tmp, strlen(tmp));
+ silc_mime_add_data(p, (const unsigned char *)tmp, strlen(tmp));
g_free(tmp);
if (!parts)
@@ -742,7 +747,7 @@ SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags)
silc_mime_add_field(mime, "MIME-Version", "1.0");
g_snprintf(b, sizeof(b), "b%4X%4X",
(unsigned int)time(NULL),
- silc_dlist_count(parts));
+ silc_dlist_count(parts));
silc_mime_set_multipart(mime, "mixed", b);
silc_dlist_start(parts);
while ((p = silc_dlist_get(parts)) != SILC_LIST_END)
diff --git a/libpurple/protocols/silc/wb.c b/libpurple/protocols/silc/wb.c
index 1c3b49cd45..636d82bd14 100644
--- a/libpurple/protocols/silc/wb.c
+++ b/libpurple/protocols/silc/wb.c
@@ -4,7 +4,7 @@
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2005 Pekka Riikonen
+ Copyright (C) 2005 - 2007 Pekka Riikonen
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
@@ -17,7 +17,7 @@
*/
-#include "silcincludes.h"
+#include "silc.h"
#include "silcclient.h"
#include "silcpurple.h"
#include "wb.h"
@@ -30,7 +30,7 @@
2 bytes height
4 bytes brush color
2 bytes brush size
- n bytes data
+ n bytes data
Data:
@@ -204,7 +204,7 @@ silcpurple_wb_parse(SilcPurpleWb wbs, PurpleWhiteboard *wb,
silc_buffer_pull(&buf, 8);
x = dx;
y = dy;
- while (buf.len > 0) {
+ while (silc_buffer_len(&buf) > 0) {
ret = silc_buffer_unformat(&buf,
SILC_STR_UI_INT(&dx),
SILC_STR_UI_INT(&dy),
@@ -214,7 +214,7 @@ silcpurple_wb_parse(SilcPurpleWb wbs, PurpleWhiteboard *wb,
silc_buffer_pull(&buf, 8);
purple_whiteboard_draw_line(wb, x, y, x + dx, y + dy,
- brush_color, brush_size);
+ brush_color, brush_size);
x += dx;
y += dy;
}
@@ -253,8 +253,8 @@ silcpurple_wb_request_cb(SilcPurpleWbRequest req, gint id)
}
static void
-silcpurple_wb_request(SilcClient client, const unsigned char *message,
- SilcUInt32 message_len, SilcClientEntry sender,
+silcpurple_wb_request(SilcClient client, const unsigned char *message,
+ SilcUInt32 message_len, SilcClientEntry sender,
SilcChannelEntry channel)
{
char tmp[128];
@@ -406,16 +406,16 @@ void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list)
/* Send the message */
if (wbs->type == 0) {
/* Private message */
- silc_client_send_private_message(sg->client, sg->conn,
- wbs->u.client,
- SILC_MESSAGE_FLAG_DATA,
- packet->head, len, TRUE);
+ silc_client_send_private_message(sg->client, sg->conn,
+ wbs->u.client,
+ SILC_MESSAGE_FLAG_DATA, NULL,
+ packet->head, len);
} else if (wbs->type == 1) {
/* Channel message. Channel private keys are not supported. */
silc_client_send_channel_message(sg->client, sg->conn,
wbs->u.channel, NULL,
- SILC_MESSAGE_FLAG_DATA,
- packet->head, len, TRUE);
+ SILC_MESSAGE_FLAG_DATA, NULL,
+ packet->head, len);
}
silc_buffer_free(packet);
@@ -501,16 +501,16 @@ void silcpurple_wb_clear(PurpleWhiteboard *wb)
/* Send the message */
if (wbs->type == 0) {
/* Private message */
- silc_client_send_private_message(sg->client, sg->conn,
- wbs->u.client,
- SILC_MESSAGE_FLAG_DATA,
- packet->head, len, TRUE);
+ silc_client_send_private_message(sg->client, sg->conn,
+ wbs->u.client,
+ SILC_MESSAGE_FLAG_DATA, NULL,
+ packet->head, len);
} else if (wbs->type == 1) {
/* Channel message */
silc_client_send_channel_message(sg->client, sg->conn,
wbs->u.channel, NULL,
- SILC_MESSAGE_FLAG_DATA,
- packet->head, len, TRUE);
+ SILC_MESSAGE_FLAG_DATA, NULL,
+ packet->head, len);
}
silc_buffer_free(packet);
diff --git a/libpurple/protocols/silc10/Makefile.am b/libpurple/protocols/silc10/Makefile.am
new file mode 100644
index 0000000000..df78045d30
--- /dev/null
+++ b/libpurple/protocols/silc10/Makefile.am
@@ -0,0 +1,36 @@
+EXTRA_DIST = README TODO Makefile.mingw
+
+pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+
+SILCSOURCES = silc.c silcpurple.h buddy.c chat.c ft.c ops.c pk.c util.c wb.c wb.h
+
+AM_CFLAGS = $(st)
+
+libsilcpurple_la_LDFLAGS = -module -avoid-version
+
+if STATIC_SILC
+
+st = -DPURPLE_STATIC_PRPL $(SILC_CFLAGS)
+noinst_LIBRARIES = libsilcpurple.a
+pkg_LTLIBRARIES =
+
+libsilcpurple_a_SOURCES = $(SILCSOURCES)
+libsilcpurple_a_CFLAGS = $(AM_CFLAGS)
+libsilcpurple_a_LIBADD = $(SILC_LIBS)
+
+else
+
+st = $(SILC_CFLAGS)
+pkg_LTLIBRARIES = libsilcpurple.la
+noinst_LIBRARIES =
+
+libsilcpurple_la_SOURCES = $(SILCSOURCES)
+libsilcpurple_la_LIBADD = $(GLIB_LIBS) $(SILC_LIBS)
+
+endif
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/libpurple \
+ -I$(top_builddir)/libpurple \
+ $(GLIB_CFLAGS) \
+ $(DEBUG_CFLAGS)
diff --git a/libpurple/protocols/silc10/Makefile.mingw b/libpurple/protocols/silc10/Makefile.mingw
new file mode 100644
index 0000000000..66bcffab68
--- /dev/null
+++ b/libpurple/protocols/silc10/Makefile.mingw
@@ -0,0 +1,91 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for win32 (mingw) version of libsilc protocol plugin
+#
+
+PIDGIN_TREE_TOP := ../../..
+include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+
+TARGET = libsilc
+NEEDED_DLLS = $(SILC_TOOLKIT)/lib/silc.dll \
+ $(SILC_TOOLKIT)/lib/silcclient.dll
+TYPE = PLUGIN
+
+# Static or Plugin...
+ifeq ($(TYPE),STATIC)
+ DEFINES += -DSTATIC
+ DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR)
+else
+ifeq ($(TYPE),PLUGIN)
+ DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR)
+endif
+endif
+
+##
+## 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$(PURPLE_TOP) \
+ -I$(PURPLE_TOP)/win32 \
+ -I$(PIDGIN_TREE_TOP) \
+ -I$(SILC_TOOLKIT)/include
+
+LIB_PATHS += -L$(GTK_TOP)/lib \
+ -L$(PURPLE_TOP) \
+ -L$(SILC_TOOLKIT)/lib
+
+##
+## SOURCES, OBJECTS
+##
+C_SRC = silc.c \
+ buddy.c \
+ chat.c \
+ ft.c \
+ ops.c \
+ pk.c \
+ util.c \
+ wb.c
+
+OBJECTS = $(C_SRC:%.c=%.o)
+
+##
+## LIBRARIES
+##
+LIBS = \
+ -lglib-2.0 \
+ -lws2_32 \
+ -lintl \
+ -lpurple \
+ -lsilc \
+ -lsilcclient
+
+include $(PIDGIN_COMMON_RULES)
+
+##
+## TARGET DEFINITIONS
+##
+.PHONY: all install clean
+
+all: $(TARGET).dll
+
+install: all $(DLL_INSTALL_DIR) $(PURPLE_INSTALL_DIR)
+ cp $(TARGET).dll $(DLL_INSTALL_DIR)
+ cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR)
+
+$(OBJECTS): $(PURPLE_CONFIG_H)
+
+$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS)
+ $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--image-base,0x64000000 -o $(TARGET).dll
+
+##
+## CLEAN RULES
+##
+clean:
+ rm -f $(OBJECTS)
+ rm -f $(TARGET).dll
+
+include $(PIDGIN_COMMON_TARGETS)
diff --git a/libpurple/protocols/silc10/README b/libpurple/protocols/silc10/README
new file mode 100644
index 0000000000..90cbdb5025
--- /dev/null
+++ b/libpurple/protocols/silc10/README
@@ -0,0 +1,31 @@
+SILC Purple Plugin
+==================
+
+This is the Purple protocol plugin of the protocol called Secure Internet
+Live Conferencing (SILC). The implementation will use the SILC Toolkit,
+freely available from the http://silcnet.org/ site, for the actual SILC
+protocol implementation.
+
+To include SILC into Purple, one needs to first compile and install
+the SILC Toolkit. It is done as follows:
+
+ ./configure --enable-shared
+ make
+ make install
+
+This will compile shared libraries of the SILC Toolkit. If the --prefix
+is not given to ./configure, the binaries are installed into the
+/usr/local/silc directory.
+
+Once the Toolkit is installed one needs to tell Purple's ./configure
+script where the SILC Toolkit is located. It is done as simply as:
+
+ ./configure
+
+if pkg-config is installed in your system. If it is isn't it's done as:
+
+ ./configure --with-silc-libs=/path/to/silc/lib
+ --with-silc-includes=/path/to/silc/include
+
+If the SILC Toolkit cannot be found then the SILC protocol plugin will
+not be compiled.
diff --git a/libpurple/protocols/silc10/TODO b/libpurple/protocols/silc10/TODO
new file mode 100644
index 0000000000..285ca58485
--- /dev/null
+++ b/libpurple/protocols/silc10/TODO
@@ -0,0 +1,8 @@
+Features TODO (maybe)
+=====================
+
+Preferences
+ - Add joined channels to buddy list automatically (during
+ session)
+ - Add joined channels to buddy list automatically permanently
+
diff --git a/libpurple/protocols/silc10/buddy.c b/libpurple/protocols/silc10/buddy.c
new file mode 100644
index 0000000000..dcde59b420
--- /dev/null
+++ b/libpurple/protocols/silc10/buddy.c
@@ -0,0 +1,1728 @@
+/*
+
+ silcpurple_buddy.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2004 Pekka Riikonen
+
+ 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; version 2 of the License.
+
+ 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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcpurple.h"
+#include "wb.h"
+
+/***************************** Key Agreement *********************************/
+
+static void
+silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data);
+
+static void
+silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
+ gboolean force_local);
+
+typedef struct {
+ char *nick;
+ PurpleConnection *gc;
+} *SilcPurpleResolve;
+
+static void
+silcpurple_buddy_keyagr_resolved(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ SilcUInt32 clients_count,
+ void *context)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurpleResolve r = context;
+ char tmp[256];
+
+ if (!clients) {
+ g_snprintf(tmp, sizeof(tmp),
+ _("User %s is not present in the network"), r->nick);
+ purple_notify_error(gc, _("Key Agreement"),
+ _("Cannot perform the key agreement"), tmp);
+ silc_free(r->nick);
+ silc_free(r);
+ return;
+ }
+
+ silcpurple_buddy_keyagr_do(gc, r->nick, FALSE);
+ silc_free(r->nick);
+ silc_free(r);
+}
+
+typedef struct {
+ gboolean responder;
+} *SilcPurpleKeyAgr;
+
+static void
+silcpurple_buddy_keyagr_cb(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ SilcKeyAgreementStatus status,
+ SilcSKEKeyMaterial *key,
+ void *context)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ SilcPurpleKeyAgr a = context;
+
+ if (!sg->conn)
+ return;
+
+ switch (status) {
+ case SILC_KEY_AGREEMENT_OK:
+ {
+ PurpleConversation *convo;
+ char tmp[128];
+
+ /* Set the private key for this client */
+ silc_client_del_private_message_key(client, conn, client_entry);
+ silc_client_add_private_message_key_ske(client, conn, client_entry,
+ NULL, NULL, key, a->responder);
+ silc_ske_free_key_material(key);
+
+
+ /* Open IM window */
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+ client_entry->nickname, sg->account);
+ if (convo) {
+ /* we don't have windows in the core anymore...but we may want to
+ * provide some method for asking the UI to show the window
+ purple_conv_window_show(purple_conversation_get_window(convo));
+ */
+ } else {
+ convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, sg->account,
+ client_entry->nickname);
+ }
+ g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname);
+ purple_conversation_set_title(convo, tmp);
+ }
+ break;
+
+ case SILC_KEY_AGREEMENT_ERROR:
+ purple_notify_error(gc, _("Key Agreement"),
+ _("Error occurred during key agreement"), NULL);
+ break;
+
+ case SILC_KEY_AGREEMENT_FAILURE:
+ purple_notify_error(gc, _("Key Agreement"), _("Key Agreement failed"), NULL);
+ break;
+
+ case SILC_KEY_AGREEMENT_TIMEOUT:
+ purple_notify_error(gc, _("Key Agreement"),
+ _("Timeout during key agreement"), NULL);
+ break;
+
+ case SILC_KEY_AGREEMENT_ABORTED:
+ purple_notify_error(gc, _("Key Agreement"),
+ _("Key agreement was aborted"), NULL);
+ break;
+
+ case SILC_KEY_AGREEMENT_ALREADY_STARTED:
+ purple_notify_error(gc, _("Key Agreement"),
+ _("Key agreement is already started"), NULL);
+ break;
+
+ case SILC_KEY_AGREEMENT_SELF_DENIED:
+ purple_notify_error(gc, _("Key Agreement"),
+ _("Key agreement cannot be started with yourself"),
+ NULL);
+ break;
+
+ default:
+ break;
+ }
+
+ silc_free(a);
+}
+
+static void
+silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
+ gboolean force_local)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClientEntry *clients;
+ SilcUInt32 clients_count;
+ char *local_ip = NULL, *remote_ip = NULL;
+ gboolean local = TRUE;
+ char *nickname;
+ SilcPurpleKeyAgr a;
+
+ if (!sg->conn || !name)
+ return;
+
+ if (!silc_parse_userfqdn(name, &nickname, NULL))
+ return;
+
+ /* Find client entry */
+ clients = silc_client_get_clients_local(sg->client, sg->conn, nickname, name,
+ &clients_count);
+ if (!clients) {
+ /* Resolve unknown user */
+ SilcPurpleResolve r = silc_calloc(1, sizeof(*r));
+ if (!r)
+ return;
+ r->nick = g_strdup(name);
+ r->gc = gc;
+ silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
+ silcpurple_buddy_keyagr_resolved, r);
+ silc_free(nickname);
+ return;
+ }
+
+ /* Resolve the local IP from the outgoing socket connection. We resolve
+ it to check whether we have a private range IP address or public IP
+ address. If we have public then we will assume that we are not behind
+ NAT and will provide automatically the point of connection to the
+ agreement. If we have private range address we assume that we are
+ behind NAT and we let the responder provide the point of connection.
+
+ The algorithm also checks the remote IP address of server connection.
+ If it is private range address and we have private range address we
+ assume that we are chatting in LAN and will provide the point of
+ connection.
+
+ Naturally this algorithm does not always get things right. */
+
+ if (silc_net_check_local_by_sock(sg->conn->sock->sock, NULL, &local_ip)) {
+ /* Check if the IP is private */
+ if (!force_local && silcpurple_ip_is_private(local_ip)) {
+ local = FALSE;
+
+ /* Local IP is private, resolve the remote server IP to see whether
+ we are talking to Internet or just on LAN. */
+ if (silc_net_check_host_by_sock(sg->conn->sock->sock, NULL,
+ &remote_ip))
+ if (silcpurple_ip_is_private(remote_ip))
+ /* We assume we are in LAN. Let's provide
+ the connection point. */
+ local = TRUE;
+ }
+ }
+
+ if (force_local)
+ local = TRUE;
+
+ if (local && !local_ip)
+ local_ip = silc_net_localip();
+
+ a = silc_calloc(1, sizeof(*a));
+ if (!a)
+ return;
+ a->responder = local;
+
+ /* Send the key agreement request */
+ silc_client_send_key_agreement(sg->client, sg->conn, clients[0],
+ local ? local_ip : NULL, NULL, 0, 60,
+ silcpurple_buddy_keyagr_cb, a);
+
+ silc_free(local_ip);
+ silc_free(remote_ip);
+ silc_free(clients);
+}
+
+typedef struct {
+ SilcClient client;
+ SilcClientConnection conn;
+ SilcClientID client_id;
+ char *hostname;
+ SilcUInt16 port;
+} *SilcPurpleKeyAgrAsk;
+
+static void
+silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id)
+{
+ SilcPurpleKeyAgr ai;
+ SilcClientEntry client_entry;
+
+ if (id != 1)
+ goto out;
+
+ /* Get the client entry. */
+ client_entry = silc_client_get_client_by_id(a->client, a->conn,
+ &a->client_id);
+ if (!client_entry) {
+ purple_notify_error(a->client->application, _("Key Agreement"),
+ _("The remote user is not present in the network any more"),
+ NULL);
+ goto out;
+ }
+
+ /* If the hostname was provided by the requestor perform the key agreement
+ now. Otherwise, we will send him a request to connect to us. */
+ if (a->hostname) {
+ ai = silc_calloc(1, sizeof(*ai));
+ if (!ai)
+ goto out;
+ ai->responder = FALSE;
+ silc_client_perform_key_agreement(a->client, a->conn, client_entry,
+ a->hostname, a->port,
+ silcpurple_buddy_keyagr_cb, ai);
+ } else {
+ /* Send request. Force us as the point of connection since requestor
+ did not provide the point of connection. */
+ silcpurple_buddy_keyagr_do(a->client->application,
+ client_entry->nickname, TRUE);
+ }
+
+ out:
+ silc_free(a->hostname);
+ silc_free(a);
+}
+
+void silcpurple_buddy_keyagr_request(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ const char *hostname, SilcUInt16 port)
+{
+ char tmp[128], tmp2[128];
+ SilcPurpleKeyAgrAsk a;
+ PurpleConnection *gc = client->application;
+
+ g_snprintf(tmp, sizeof(tmp),
+ _("Key agreement request received from %s. Would you like to "
+ "perform the key agreement?"), client_entry->nickname);
+ if (hostname)
+ g_snprintf(tmp2, sizeof(tmp2),
+ _("The remote user is waiting key agreement on:\n"
+ "Remote host: %s\nRemote port: %d"), hostname, port);
+
+ a = silc_calloc(1, sizeof(*a));
+ if (!a)
+ return;
+ a->client = client;
+ a->conn = conn;
+ a->client_id = *client_entry->id;
+ if (hostname)
+ a->hostname = strdup(hostname);
+ a->port = port;
+
+ purple_request_action(client->application, _("Key Agreement Request"), tmp,
+ hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname,
+ NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
+ _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb));
+}
+
+static void
+silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data)
+{
+ PurpleBuddy *buddy;
+
+ buddy = (PurpleBuddy *)node;
+ silcpurple_buddy_keyagr_do(buddy->account->gc, buddy->name, FALSE);
+}
+
+
+/**************************** Static IM Key **********************************/
+
+static void
+silcpurple_buddy_resetkey(PurpleBlistNode *node, gpointer data)
+{
+ PurpleBuddy *b;
+ PurpleConnection *gc;
+ SilcPurple sg;
+ char *nickname;
+ SilcClientEntry *clients;
+ SilcUInt32 clients_count;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+ b = (PurpleBuddy *) node;
+ gc = purple_account_get_connection(b->account);
+ sg = gc->proto_data;
+
+ if (!silc_parse_userfqdn(b->name, &nickname, NULL))
+ return;
+
+ /* Find client entry */
+ clients = silc_client_get_clients_local(sg->client, sg->conn,
+ nickname, b->name,
+ &clients_count);
+ if (!clients) {
+ silc_free(nickname);
+ return;
+ }
+
+ clients[0]->prv_resp = FALSE;
+ silc_client_del_private_message_key(sg->client, sg->conn,
+ clients[0]);
+ silc_free(clients);
+ silc_free(nickname);
+}
+
+typedef struct {
+ SilcClient client;
+ SilcClientConnection conn;
+ SilcClientID client_id;
+} *SilcPurplePrivkey;
+
+static void
+silcpurple_buddy_privkey(PurpleConnection *gc, const char *name);
+
+static void
+silcpurple_buddy_privkey_cb(SilcPurplePrivkey p, const char *passphrase)
+{
+ SilcClientEntry client_entry;
+
+ if (!passphrase || !(*passphrase)) {
+ silc_free(p);
+ return;
+ }
+
+ /* Get the client entry. */
+ client_entry = silc_client_get_client_by_id(p->client, p->conn,
+ &p->client_id);
+ if (!client_entry) {
+ purple_notify_error(p->client->application, _("IM With Password"),
+ _("The remote user is not present in the network any more"),
+ NULL);
+ silc_free(p);
+ return;
+ }
+
+ /* Set the private message key */
+ silc_client_del_private_message_key(p->client, p->conn,
+ client_entry);
+ silc_client_add_private_message_key(p->client, p->conn,
+ client_entry, NULL, NULL,
+ (unsigned char *)passphrase,
+ strlen(passphrase), FALSE,
+ client_entry->prv_resp);
+ if (!client_entry->prv_resp)
+ silc_client_send_private_message_key_request(p->client,
+ p->conn,
+ client_entry);
+ silc_free(p);
+}
+
+static void
+silcpurple_buddy_privkey_resolved(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ SilcUInt32 clients_count,
+ void *context)
+{
+ char tmp[256];
+
+ if (!clients) {
+ g_snprintf(tmp, sizeof(tmp),
+ _("User %s is not present in the network"),
+ (const char *)context);
+ purple_notify_error(client->application, _("IM With Password"),
+ _("Cannot set IM key"), tmp);
+ g_free(context);
+ return;
+ }
+
+ silcpurple_buddy_privkey(client->application, context);
+ silc_free(context);
+}
+
+static void
+silcpurple_buddy_privkey(PurpleConnection *gc, const char *name)
+{
+ SilcPurple sg = gc->proto_data;
+ char *nickname;
+ SilcPurplePrivkey p;
+ SilcClientEntry *clients;
+ SilcUInt32 clients_count;
+
+ if (!name)
+ return;
+ if (!silc_parse_userfqdn(name, &nickname, NULL))
+ return;
+
+ /* Find client entry */
+ clients = silc_client_get_clients_local(sg->client, sg->conn,
+ nickname, name,
+ &clients_count);
+ if (!clients) {
+ silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
+ silcpurple_buddy_privkey_resolved,
+ g_strdup(name));
+ silc_free(nickname);
+ return;
+ }
+
+ p = silc_calloc(1, sizeof(*p));
+ if (!p)
+ return;
+ p->client = sg->client;
+ p->conn = sg->conn;
+ p->client_id = *clients[0]->id;
+ purple_request_input(gc, _("IM With Password"), NULL,
+ _("Set IM Password"), NULL, FALSE, TRUE, NULL,
+ _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb),
+ _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb),
+ gc->account, NULL, NULL, p);
+
+ silc_free(clients);
+ silc_free(nickname);
+}
+
+static void
+silcpurple_buddy_privkey_menu(PurpleBlistNode *node, gpointer data)
+{
+ PurpleBuddy *buddy;
+ PurpleConnection *gc;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+ buddy = (PurpleBuddy *) node;
+ gc = purple_account_get_connection(buddy->account);
+
+ silcpurple_buddy_privkey(gc, buddy->name);
+}
+
+
+/**************************** Get Public Key *********************************/
+
+typedef struct {
+ SilcClient client;
+ SilcClientConnection conn;
+ SilcClientID client_id;
+} *SilcPurpleBuddyGetkey;
+
+static void
+silcpurple_buddy_getkey(PurpleConnection *gc, const char *name);
+
+static void
+silcpurple_buddy_getkey_cb(SilcPurpleBuddyGetkey g,
+ SilcClientCommandReplyContext cmd)
+{
+ SilcClientEntry client_entry;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+
+ /* Get the client entry. */
+ client_entry = silc_client_get_client_by_id(g->client, g->conn,
+ &g->client_id);
+ if (!client_entry) {
+ purple_notify_error(g->client->application, _("Get Public Key"),
+ _("The remote user is not present in the network any more"),
+ NULL);
+ silc_free(g);
+ return;
+ }
+
+ if (!client_entry->public_key) {
+ silc_free(g);
+ return;
+ }
+
+ /* Now verify the public key */
+ pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
+ silcpurple_verify_public_key(g->client, g->conn, client_entry->nickname,
+ SILC_SOCKET_TYPE_CLIENT,
+ pk, pk_len, SILC_SKE_PK_TYPE_SILC,
+ NULL, NULL);
+ silc_free(pk);
+ silc_free(g);
+}
+
+static void
+silcpurple_buddy_getkey_resolved(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ SilcUInt32 clients_count,
+ void *context)
+{
+ char tmp[256];
+
+ if (!clients) {
+ g_snprintf(tmp, sizeof(tmp),
+ _("User %s is not present in the network"),
+ (const char *)context);
+ purple_notify_error(client->application, _("Get Public Key"),
+ _("Cannot fetch the public key"), tmp);
+ g_free(context);
+ return;
+ }
+
+ silcpurple_buddy_getkey(client->application, context);
+ silc_free(context);
+}
+
+static void
+silcpurple_buddy_getkey(PurpleConnection *gc, const char *name)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcClientEntry *clients;
+ SilcUInt32 clients_count;
+ SilcPurpleBuddyGetkey g;
+ char *nickname;
+
+ if (!name)
+ return;
+
+ if (!silc_parse_userfqdn(name, &nickname, NULL))
+ return;
+
+ /* Find client entry */
+ clients = silc_client_get_clients_local(client, conn, nickname, name,
+ &clients_count);
+ if (!clients) {
+ silc_client_get_clients(client, conn, nickname, NULL,
+ silcpurple_buddy_getkey_resolved,
+ g_strdup(name));
+ silc_free(nickname);
+ return;
+ }
+
+ /* Call GETKEY */
+ g = silc_calloc(1, sizeof(*g));
+ if (!g)
+ return;
+ g->client = client;
+ g->conn = conn;
+ g->client_id = *clients[0]->id;
+ silc_client_command_call(client, conn, NULL, "GETKEY",
+ clients[0]->nickname, NULL);
+ silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
+ conn->cmd_ident,
+ (SilcCommandCb)silcpurple_buddy_getkey_cb, g);
+ silc_free(clients);
+ silc_free(nickname);
+}
+
+static void
+silcpurple_buddy_getkey_menu(PurpleBlistNode *node, gpointer data)
+{
+ PurpleBuddy *buddy;
+ PurpleConnection *gc;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+ buddy = (PurpleBuddy *) node;
+ gc = purple_account_get_connection(buddy->account);
+
+ silcpurple_buddy_getkey(gc, buddy->name);
+}
+
+static void
+silcpurple_buddy_showkey(PurpleBlistNode *node, gpointer data)
+{
+ PurpleBuddy *b;
+ PurpleConnection *gc;
+ SilcPurple sg;
+ SilcPublicKey public_key;
+ const char *pkfile;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+ b = (PurpleBuddy *) node;
+ gc = purple_account_get_connection(b->account);
+ sg = gc->proto_data;
+
+ pkfile = purple_blist_node_get_string(node, "public-key");
+ if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) &&
+ !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) {
+ purple_notify_error(gc,
+ _("Show Public Key"),
+ _("Could not load public key"), NULL);
+ return;
+ }
+
+ silcpurple_show_public_key(sg, b->name, public_key, NULL, NULL);
+ silc_pkcs_public_key_free(public_key);
+}
+
+
+/**************************** Buddy routines *********************************/
+
+/* The buddies are implemented by using the WHOIS and WATCH commands that
+ can be used to search users by their public key. Since nicknames aren't
+ unique in SILC we cannot trust the buddy list using their nickname. We
+ associate public keys to buddies and use those to search and watch
+ in the network.
+
+ The problem is that Purple does not return PurpleBuddy contexts to the
+ callbacks but the buddy names. Naturally, this is not going to work
+ with SILC. But, for now, we have to do what we can... */
+
+typedef struct {
+ SilcClient client;
+ SilcClientConnection conn;
+ SilcClientID client_id;
+ PurpleBuddy *b;
+ unsigned char *offline_pk;
+ SilcUInt32 offline_pk_len;
+ unsigned int offline : 1;
+ unsigned int pubkey_search : 1;
+ unsigned int init : 1;
+} *SilcPurpleBuddyRes;
+
+static void
+silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id);
+static void
+silcpurple_add_buddy_resolved(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ SilcUInt32 clients_count,
+ void *context);
+
+void silcpurple_get_info(PurpleConnection *gc, const char *who)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcClientEntry client_entry;
+ PurpleBuddy *b;
+ const char *filename, *nick = who;
+ char tmp[256];
+
+ if (!who)
+ return;
+ if (strlen(who) > 1 && who[0] == '@')
+ nick = who + 1;
+ if (strlen(who) > 1 && who[0] == '*')
+ nick = who + 1;
+ if (strlen(who) > 2 && who[0] == '*' && who[1] == '@')
+ nick = who + 2;
+
+ b = purple_find_buddy(gc->account, nick);
+ if (b) {
+ /* See if we have this buddy's public key. If we do use that
+ to search the details. */
+ filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
+ if (filename) {
+ /* Call WHOIS. The user info is displayed in the WHOIS
+ command reply. */
+ silc_client_command_call(client, conn, NULL, "WHOIS",
+ "-details", "-pubkey", filename, NULL);
+ return;
+ }
+
+ if (!b->proto_data) {
+ g_snprintf(tmp, sizeof(tmp),
+ _("User %s is not present in the network"), b->name);
+ purple_notify_error(gc, _("User Information"),
+ _("Cannot get user information"), tmp);
+ return;
+ }
+
+ client_entry = silc_client_get_client_by_id(client, conn, b->proto_data);
+ if (client_entry) {
+ /* Call WHOIS. The user info is displayed in the WHOIS
+ command reply. */
+ silc_client_command_call(client, conn, NULL, "WHOIS",
+ client_entry->nickname, "-details", NULL);
+ }
+ } else {
+ /* Call WHOIS just with nickname. */
+ silc_client_command_call(client, conn, NULL, "WHOIS", nick, NULL);
+ }
+}
+
+static void
+silcpurple_add_buddy_pk_no(SilcPurpleBuddyRes r)
+{
+ char tmp[512];
+ g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"),
+ r->b->name);
+ purple_notify_error(r->client->application, _("Add Buddy"), tmp,
+ _("You cannot receive buddy notifications until you "
+ "import his/her public key. You can use the Get Public Key "
+ "command to get the public key."));
+ purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
+}
+
+static void
+silcpurple_add_buddy_save(bool success, void *context)
+{
+ SilcPurpleBuddyRes r = context;
+ PurpleBuddy *b = r->b;
+ SilcClient client = r->client;
+ SilcClientEntry client_entry;
+ SilcAttributePayload attr;
+ SilcAttribute attribute;
+ SilcVCardStruct vcard;
+ SilcAttributeObjMime message, extension;
+#ifdef SILC_ATTRIBUTE_USER_ICON
+ SilcAttributeObjMime usericon;
+#endif
+ SilcAttributeObjPk serverpk, usersign, serversign;
+ gboolean usign_success = TRUE, ssign_success = TRUE;
+ char filename[512], filename2[512], *fingerprint = NULL, *tmp;
+ SilcUInt32 len;
+ int i;
+
+ if (!success) {
+ /* The user did not trust the public key. */
+ silcpurple_add_buddy_pk_no(r);
+ silc_free(r);
+ return;
+ }
+
+ if (r->offline) {
+ /* User is offline. Associate the imported public key with
+ this user. */
+ fingerprint = silc_hash_fingerprint(NULL, r->offline_pk,
+ r->offline_pk_len);
+ for (i = 0; i < strlen(fingerprint); i++)
+ if (fingerprint[i] == ' ')
+ fingerprint[i] = '_';
+ g_snprintf(filename, sizeof(filename) - 1,
+ "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
+ silcpurple_silcdir(), fingerprint);
+ purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename);
+ purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
+ silc_free(fingerprint);
+ silc_free(r->offline_pk);
+ silc_free(r);
+ return;
+ }
+
+ /* Get the client entry. */
+ client_entry = silc_client_get_client_by_id(r->client, r->conn,
+ &r->client_id);
+ if (!client_entry) {
+ silc_free(r);
+ return;
+ }
+
+ memset(&vcard, 0, sizeof(vcard));
+ memset(&message, 0, sizeof(message));
+ memset(&extension, 0, sizeof(extension));
+#ifdef SILC_ATTRIBUTE_USER_ICON
+ memset(&usericon, 0, sizeof(usericon));
+#endif
+ memset(&serverpk, 0, sizeof(serverpk));
+ memset(&usersign, 0, sizeof(usersign));
+ memset(&serversign, 0, sizeof(serversign));
+
+ /* Now that we have the public key and we trust it now we
+ save the attributes of the buddy and update its status. */
+
+ if (client_entry->attrs) {
+ silc_dlist_start(client_entry->attrs);
+ while ((attr = silc_dlist_get(client_entry->attrs))
+ != SILC_LIST_END) {
+ attribute = silc_attribute_get_attribute(attr);
+
+ switch (attribute) {
+ case SILC_ATTRIBUTE_USER_INFO:
+ if (!silc_attribute_get_object(attr, (void *)&vcard,
+ sizeof(vcard)))
+ continue;
+ break;
+
+ case SILC_ATTRIBUTE_STATUS_MESSAGE:
+ if (!silc_attribute_get_object(attr, (void *)&message,
+ sizeof(message)))
+ continue;
+ break;
+
+ case SILC_ATTRIBUTE_EXTENSION:
+ if (!silc_attribute_get_object(attr, (void *)&extension,
+ sizeof(extension)))
+ continue;
+ break;
+
+#ifdef SILC_ATTRIBUTE_USER_ICON
+ case SILC_ATTRIBUTE_USER_ICON:
+ if (!silc_attribute_get_object(attr, (void *)&usericon,
+ sizeof(usericon)))
+ continue;
+ break;
+#endif
+
+ case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
+ if (serverpk.type)
+ continue;
+ if (!silc_attribute_get_object(attr, (void *)&serverpk,
+ sizeof(serverpk)))
+ continue;
+ break;
+
+ case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
+ if (usersign.data)
+ continue;
+ if (!silc_attribute_get_object(attr, (void *)&usersign,
+ sizeof(usersign)))
+ continue;
+ break;
+
+ case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
+ if (serversign.data)
+ continue;
+ if (!silc_attribute_get_object(attr, (void *)&serversign,
+ sizeof(serversign)))
+ continue;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Verify the attribute signatures */
+
+ if (usersign.data) {
+ SilcPKCS pkcs;
+ unsigned char *verifyd;
+ SilcUInt32 verify_len;
+
+ silc_pkcs_alloc((unsigned char*)"rsa", &pkcs);
+ verifyd = silc_attribute_get_verify_data(client_entry->attrs,
+ FALSE, &verify_len);
+ if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){
+ if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
+ usersign.data,
+ usersign.data_len,
+ verifyd, verify_len))
+ usign_success = FALSE;
+ }
+ silc_free(verifyd);
+ }
+
+ if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) {
+ SilcPublicKey public_key;
+ SilcPKCS pkcs;
+ unsigned char *verifyd;
+ SilcUInt32 verify_len;
+
+ if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len,
+ &public_key)) {
+ silc_pkcs_alloc((unsigned char *)"rsa", &pkcs);
+ verifyd = silc_attribute_get_verify_data(client_entry->attrs,
+ TRUE, &verify_len);
+ if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) {
+ if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
+ serversign.data,
+ serversign.data_len,
+ verifyd, verify_len))
+ ssign_success = FALSE;
+ }
+ silc_pkcs_public_key_free(public_key);
+ silc_free(verifyd);
+ }
+ }
+
+ fingerprint = silc_fingerprint(client_entry->fingerprint,
+ client_entry->fingerprint_len);
+ for (i = 0; i < strlen(fingerprint); i++)
+ if (fingerprint[i] == ' ')
+ fingerprint[i] = '_';
+
+ if (usign_success || ssign_success) {
+ struct passwd *pw;
+ struct stat st;
+
+ memset(filename2, 0, sizeof(filename2));
+
+ /* Filename for dir */
+ tmp = fingerprint + strlen(fingerprint) - 9;
+ g_snprintf(filename, sizeof(filename) - 1,
+ "%s" G_DIR_SEPARATOR_S "friends" G_DIR_SEPARATOR_S "%s",
+ silcpurple_silcdir(), tmp);
+
+ pw = getpwuid(getuid());
+ if (!pw)
+ return;
+
+ /* Create dir if it doesn't exist */
+ if ((g_stat(filename, &st)) == -1) {
+ if (errno == ENOENT) {
+ if (pw->pw_uid == geteuid())
+ g_mkdir(filename, 0755);
+ }
+ }
+
+ /* Save VCard */
+ g_snprintf(filename2, sizeof(filename2) - 1,
+ "%s" G_DIR_SEPARATOR_S "vcard", filename);
+ if (vcard.full_name) {
+ tmp = (char *)silc_vcard_encode(&vcard, &len);
+ silc_file_writefile(filename2, tmp, len);
+ silc_free(tmp);
+ }
+
+ /* Save status message */
+ if (message.mime) {
+ memset(filename2, 0, sizeof(filename2));
+ g_snprintf(filename2, sizeof(filename2) - 1,
+ "%s" G_DIR_SEPARATOR_S "status_message.mime",
+ filename);
+ silc_file_writefile(filename2, (char *)message.mime,
+ message.mime_len);
+ }
+
+ /* Save extension data */
+ if (extension.mime) {
+ memset(filename2, 0, sizeof(filename2));
+ g_snprintf(filename2, sizeof(filename2) - 1,
+ "%s" G_DIR_SEPARATOR_S "extension.mime",
+ filename);
+ silc_file_writefile(filename2, (char *)extension.mime,
+ extension.mime_len);
+ }
+
+#ifdef SILC_ATTRIBUTE_USER_ICON
+ /* Save user icon */
+ if (usericon.mime) {
+ SilcMime m = silc_mime_decode(usericon.mime,
+ usericon.mime_len);
+ if (m) {
+ const char *type = silc_mime_get_field(m, "Content-Type");
+ if (!strcmp(type, "image/jpeg") ||
+ !strcmp(type, "image/gif") ||
+ !strcmp(type, "image/bmp") ||
+ !strcmp(type, "image/png")) {
+ const unsigned char *data;
+ SilcUInt32 data_len;
+ data = silc_mime_get_data(m, &data_len);
+ if (data) {
+ /* TODO: Check if SILC gives us something to use as the checksum instead */
+ purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup(data, data_len), data_len, NULL);
+ }
+ }
+ silc_mime_free(m);
+ }
+ }
+#endif
+ }
+
+ /* Save the public key path to buddy properties, as it is used
+ to identify the buddy in the network (and not the nickname). */
+ memset(filename, 0, sizeof(filename));
+ g_snprintf(filename, sizeof(filename) - 1,
+ "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
+ silcpurple_silcdir(), fingerprint);
+ purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename);
+
+ /* Update online status */
+ purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
+
+ /* Finally, start watching this user so we receive its status
+ changes from the server */
+ g_snprintf(filename2, sizeof(filename2) - 1, "+%s", filename);
+ silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey",
+ filename2, NULL);
+
+ silc_free(fingerprint);
+ silc_free(r);
+}
+
+static void
+silcpurple_add_buddy_ask_import(void *user_data, const char *name)
+{
+ SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data;
+ SilcPublicKey public_key;
+
+ /* Load the public key */
+ if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
+ !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
+ silcpurple_add_buddy_ask_pk_cb(r, 0);
+ purple_notify_error(r->client->application,
+ _("Add Buddy"), _("Could not load public key"), NULL);
+ return;
+ }
+
+ /* Now verify the public key */
+ r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len);
+ silcpurple_verify_public_key(r->client, r->conn, r->b->name,
+ SILC_SOCKET_TYPE_CLIENT,
+ r->offline_pk, r->offline_pk_len,
+ SILC_SKE_PK_TYPE_SILC,
+ silcpurple_add_buddy_save, r);
+}
+
+static void
+silcpurple_add_buddy_ask_pk_cancel(void *user_data, const char *name)
+{
+ SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data;
+
+ /* The user did not import public key. The buddy is unusable. */
+ silcpurple_add_buddy_pk_no(r);
+ silc_free(r);
+}
+
+static void
+silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id)
+{
+ if (id != 0) {
+ /* The user did not import public key. The buddy is unusable. */
+ silcpurple_add_buddy_pk_no(r);
+ silc_free(r);
+ return;
+ }
+
+ /* Open file selector to select the public key. */
+ purple_request_file(r->client->application, _("Open..."), NULL, FALSE,
+ G_CALLBACK(silcpurple_add_buddy_ask_import),
+ G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
+ purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+
+}
+
+static void
+silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r)
+{
+ char tmp[512];
+ g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
+ r->b->name);
+ purple_request_action(r->client->application, _("Add Buddy"), tmp,
+ _("To add the buddy you must import his/her public key. "
+ "Press Import to import a public key."), 0,
+ purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
+ _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
+ _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
+}
+
+static void
+silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r,
+ SilcClientCommandReplyContext cmd)
+{
+ SilcClientEntry client_entry;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+
+ /* Get the client entry. */
+ client_entry = silc_client_get_client_by_id(r->client, r->conn,
+ &r->client_id);
+ if (!client_entry || !client_entry->public_key) {
+ /* The buddy is offline/nonexistent. We will require user
+ to associate a public key with the buddy or the buddy
+ cannot be added. */
+ r->offline = TRUE;
+ silcpurple_add_buddy_ask_pk(r);
+ return;
+ }
+
+ /* Now verify the public key */
+ pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
+ silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname,
+ SILC_SOCKET_TYPE_CLIENT,
+ pk, pk_len, SILC_SKE_PK_TYPE_SILC,
+ silcpurple_add_buddy_save, r);
+ silc_free(pk);
+}
+
+static void
+silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r, PurpleRequestFields *fields)
+{
+ PurpleRequestField *f;
+ const GList *list;
+ SilcClientEntry client_entry;
+
+ f = purple_request_fields_get_field(fields, "list");
+ list = purple_request_field_list_get_selected(f);
+ if (!list) {
+ /* The user did not select any user. */
+ silcpurple_add_buddy_pk_no(r);
+ silc_free(r);
+ return;
+ }
+
+ client_entry = purple_request_field_list_get_data(f, list->data);
+ silcpurple_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r);
+}
+
+static void
+silcpurple_add_buddy_select_cancel(SilcPurpleBuddyRes r, PurpleRequestFields *fields)
+{
+ /* The user did not select any user. */
+ silcpurple_add_buddy_pk_no(r);
+ silc_free(r);
+}
+
+static void
+silcpurple_add_buddy_select(SilcPurpleBuddyRes r,
+ SilcClientEntry *clients,
+ SilcUInt32 clients_count)
+{
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *g;
+ PurpleRequestField *f;
+ char tmp[512], tmp2[128];
+ int i;
+ char *fingerprint;
+
+ fields = purple_request_fields_new();
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_list_new("list", NULL);
+ purple_request_field_group_add_field(g, f);
+ purple_request_field_list_set_multi_select(f, FALSE);
+ purple_request_fields_add_group(fields, g);
+
+ for (i = 0; i < clients_count; i++) {
+ fingerprint = NULL;
+ if (clients[i]->fingerprint) {
+ fingerprint = silc_fingerprint(clients[i]->fingerprint,
+ clients[i]->fingerprint_len);
+ g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint);
+ }
+ g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s",
+ clients[i]->realname, clients[i]->nickname,
+ clients[i]->username, clients[i]->hostname ?
+ clients[i]->hostname : "",
+ fingerprint ? tmp2 : "");
+ purple_request_field_list_add(f, tmp, clients[i]);
+ silc_free(fingerprint);
+ }
+
+ purple_request_fields(r->client->application, _("Add Buddy"),
+ _("Select correct user"),
+ r->pubkey_search
+ ? _("More than one user was found with the same public key. Select "
+ "the correct user from the list to add to the buddy list.")
+ : _("More than one user was found with the same name. Select "
+ "the correct user from the list to add to the buddy list."),
+ fields,
+ _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
+ _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
+ purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+}
+
+static void
+silcpurple_add_buddy_resolved(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ SilcUInt32 clients_count,
+ void *context)
+{
+ SilcPurpleBuddyRes r = context;
+ PurpleBuddy *b = r->b;
+ SilcAttributePayload pub;
+ SilcAttributeObjPk userpk;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+ const char *filename;
+
+ filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
+
+ /* If the buddy is offline/nonexistent, we will require user
+ to associate a public key with the buddy or the buddy
+ cannot be added. */
+ if (!clients_count) {
+ if (r->init) {
+ silc_free(r);
+ return;
+ }
+
+ r->offline = TRUE;
+ /* If the user has already associated a public key, try loading it
+ * before prompting the user to load it again */
+ if (filename != NULL)
+ silcpurple_add_buddy_ask_import(r, filename);
+ else
+ silcpurple_add_buddy_ask_pk(r);
+ return;
+ }
+
+ /* If more than one client was found with nickname, we need to verify
+ from user which one is the correct. */
+ if (clients_count > 1 && !r->pubkey_search) {
+ if (r->init) {
+ silc_free(r);
+ return;
+ }
+
+ silcpurple_add_buddy_select(r, clients, clients_count);
+ return;
+ }
+
+ /* If we searched using public keys and more than one entry was found
+ the same person is logged on multiple times. */
+ if (clients_count > 1 && r->pubkey_search && b->name) {
+ if (r->init) {
+ /* Find the entry that closest matches to the
+ buddy nickname. */
+ int i;
+ for (i = 0; i < clients_count; i++) {
+ if (!strncasecmp(b->name, clients[i]->nickname,
+ strlen(b->name))) {
+ clients[0] = clients[i];
+ break;
+ }
+ }
+ } else {
+ /* Verify from user which one is correct */
+ silcpurple_add_buddy_select(r, clients, clients_count);
+ return;
+ }
+ }
+
+ /* The client was found. Now get its public key and verify
+ that before adding the buddy. */
+ memset(&userpk, 0, sizeof(userpk));
+ b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id));
+ r->client_id = *clients[0]->id;
+
+ /* Get the public key from attributes, if not present then
+ resolve it with GETKEY unless we have it cached already. */
+ if (clients[0]->attrs && !clients[0]->public_key) {
+ pub = silcpurple_get_attr(clients[0]->attrs,
+ SILC_ATTRIBUTE_USER_PUBLIC_KEY);
+ if (!pub || !silc_attribute_get_object(pub, (void *)&userpk,
+ sizeof(userpk))) {
+ /* Get public key with GETKEY */
+ silc_client_command_call(client, conn, NULL,
+ "GETKEY", clients[0]->nickname, NULL);
+ silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
+ conn->cmd_ident,
+ (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
+ r);
+ return;
+ }
+ if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len,
+ &clients[0]->public_key))
+ return;
+ silc_free(userpk.data);
+ } else if (filename && !clients[0]->public_key) {
+ if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key,
+ SILC_PKCS_FILE_PEM) &&
+ !silc_pkcs_load_public_key(filename, &clients[0]->public_key,
+ SILC_PKCS_FILE_BIN)) {
+ /* Get public key with GETKEY */
+ silc_client_command_call(client, conn, NULL,
+ "GETKEY", clients[0]->nickname, NULL);
+ silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
+ conn->cmd_ident,
+ (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
+ r);
+ return;
+ }
+ } else if (!clients[0]->public_key) {
+ /* Get public key with GETKEY */
+ silc_client_command_call(client, conn, NULL,
+ "GETKEY", clients[0]->nickname, NULL);
+ silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
+ conn->cmd_ident,
+ (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
+ r);
+ return;
+ }
+
+ /* We have the public key, verify it. */
+ pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len);
+ silcpurple_verify_public_key(client, conn, clients[0]->nickname,
+ SILC_SOCKET_TYPE_CLIENT,
+ pk, pk_len, SILC_SKE_PK_TYPE_SILC,
+ silcpurple_add_buddy_save, r);
+ silc_free(pk);
+}
+
+static void
+silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcPurpleBuddyRes r;
+ SilcBuffer attrs;
+ const char *filename, *name = b->name;
+
+ r = silc_calloc(1, sizeof(*r));
+ if (!r)
+ return;
+ r->client = client;
+ r->conn = conn;
+ r->b = b;
+ r->init = init;
+
+ /* See if we have this buddy's public key. If we do use that
+ to search the details. */
+ filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
+ if (filename) {
+ SilcPublicKey public_key;
+ SilcAttributeObjPk userpk;
+
+ if (!silc_pkcs_load_public_key(filename, &public_key,
+ SILC_PKCS_FILE_PEM) &&
+ !silc_pkcs_load_public_key(filename, &public_key,
+ SILC_PKCS_FILE_BIN))
+ return;
+
+ /* Get all attributes, and use the public key to search user */
+ name = NULL;
+ attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
+ SILC_ATTRIBUTE_SERVICE,
+ SILC_ATTRIBUTE_STATUS_MOOD,
+ SILC_ATTRIBUTE_STATUS_FREETEXT,
+ SILC_ATTRIBUTE_STATUS_MESSAGE,
+ SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
+ SILC_ATTRIBUTE_PREFERRED_CONTACT,
+ SILC_ATTRIBUTE_TIMEZONE,
+ SILC_ATTRIBUTE_GEOLOCATION,
+#ifdef SILC_ATTRIBUTE_USER_ICON
+ SILC_ATTRIBUTE_USER_ICON,
+#endif
+ SILC_ATTRIBUTE_DEVICE_INFO, 0);
+ userpk.type = "silc-rsa";
+ userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len);
+ attrs = silc_attribute_payload_encode(attrs,
+ SILC_ATTRIBUTE_USER_PUBLIC_KEY,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ &userpk, sizeof(userpk));
+ silc_free(userpk.data);
+ silc_pkcs_public_key_free(public_key);
+ r->pubkey_search = TRUE;
+ } else {
+ /* Get all attributes */
+ attrs = silc_client_attributes_request(0);
+ }
+
+ /* Resolve */
+ silc_client_get_clients_whois(client, conn, name, NULL, attrs,
+ silcpurple_add_buddy_resolved, r);
+ silc_buffer_free(attrs);
+}
+
+void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+{
+ silcpurple_add_buddy_i(gc, buddy, FALSE);
+}
+
+void silcpurple_send_buddylist(PurpleConnection *gc)
+{
+ PurpleBuddyList *blist;
+ PurpleBlistNode *gnode, *cnode, *bnode;
+ PurpleBuddy *buddy;
+ PurpleAccount *account;
+
+ account = purple_connection_get_account(gc);
+
+ if ((blist = purple_get_blist()) != NULL)
+ {
+ for (gnode = blist->root; gnode != NULL; gnode = gnode->next)
+ {
+ if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ continue;
+ for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
+ {
+ if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+ continue;
+ for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
+ {
+ if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ continue;
+ buddy = (PurpleBuddy *)bnode;
+ if (purple_buddy_get_account(buddy) == account)
+ silcpurple_add_buddy_i(gc, buddy, TRUE);
+ }
+ }
+ }
+ }
+}
+
+void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
+ PurpleGroup *group)
+{
+ silc_free(buddy->proto_data);
+}
+
+void silcpurple_idle_set(PurpleConnection *gc, int idle)
+
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcAttributeObjService service;
+ const char *server;
+ int port;
+
+ server = purple_account_get_string(sg->account, "server",
+ "silc.silcnet.org");
+ port = purple_account_get_int(sg->account, "port", 706),
+
+ memset(&service, 0, sizeof(service));
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_SERVICE, NULL);
+ service.port = port;
+ g_snprintf(service.address, sizeof(service.address), "%s", server);
+ service.idle = idle;
+ silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_SERVICE,
+ &service, sizeof(service));
+}
+
+char *silcpurple_status_text(PurpleBuddy *b)
+{
+ SilcPurple sg = b->account->gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcClientID *client_id = b->proto_data;
+ SilcClientEntry client_entry;
+ SilcAttributePayload attr;
+ SilcAttributeMood mood = 0;
+
+ /* Get the client entry. */
+ client_entry = silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ return NULL;
+
+ /* If user is online, we show the mood status, if available.
+ If user is offline or away that status is indicated. */
+
+ if (client_entry->mode & SILC_UMODE_DETACHED)
+ return g_strdup(_("Detached"));
+ if (client_entry->mode & SILC_UMODE_GONE)
+ return g_strdup(_("Away"));
+ if (client_entry->mode & SILC_UMODE_INDISPOSED)
+ return g_strdup(_("Indisposed"));
+ if (client_entry->mode & SILC_UMODE_BUSY)
+ return g_strdup(_("Busy"));
+ if (client_entry->mode & SILC_UMODE_PAGE)
+ return g_strdup(_("Wake Me Up"));
+ if (client_entry->mode & SILC_UMODE_HYPER)
+ return g_strdup(_("Hyper Active"));
+ if (client_entry->mode & SILC_UMODE_ROBOT)
+ return g_strdup(_("Robot"));
+
+ attr = silcpurple_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD);
+ if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
+ /* The mood is a bit mask, so we could show multiple moods,
+ but let's show only one for now. */
+ if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
+ return g_strdup(_("Happy"));
+ if (mood & SILC_ATTRIBUTE_MOOD_SAD)
+ return g_strdup(_("Sad"));
+ if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
+ return g_strdup(_("Angry"));
+ if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
+ return g_strdup(_("Jealous"));
+ if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
+ return g_strdup(_("Ashamed"));
+ if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
+ return g_strdup(_("Invincible"));
+ if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
+ return g_strdup(_("In Love"));
+ if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
+ return g_strdup(_("Sleepy"));
+ if (mood & SILC_ATTRIBUTE_MOOD_BORED)
+ return g_strdup(_("Bored"));
+ if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
+ return g_strdup(_("Excited"));
+ if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
+ return g_strdup(_("Anxious"));
+ }
+
+ return NULL;
+}
+
+void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
+{
+ SilcPurple sg = b->account->gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcClientID *client_id = b->proto_data;
+ SilcClientEntry client_entry;
+ char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
+ char tmp[256];
+
+ /* Get the client entry. */
+ client_entry = silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ return;
+
+ if (client_entry->nickname)
+ purple_notify_user_info_add_pair(user_info, _("Nickname"),
+ client_entry->nickname);
+ if (client_entry->username && client_entry->hostname) {
+ g_snprintf(tmp, sizeof(tmp), "%s@%s", client_entry->username, client_entry->hostname);
+ purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
+ }
+ if (client_entry->mode) {
+ memset(tmp, 0, sizeof(tmp));
+ silcpurple_get_umode_string(client_entry->mode,
+ tmp, sizeof(tmp) - strlen(tmp));
+ purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
+ }
+
+ silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
+
+ if (statusstr) {
+ purple_notify_user_info_add_pair(user_info, _("Message"), statusstr);
+ g_free(statusstr);
+ }
+
+ if (full) {
+ if (moodstr) {
+ purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
+ g_free(moodstr);
+ }
+
+ if (contactstr) {
+ purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
+ g_free(contactstr);
+ }
+
+ if (langstr) {
+ purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
+ g_free(langstr);
+ }
+
+ if (devicestr) {
+ purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
+ g_free(devicestr);
+ }
+
+ if (tzstr) {
+ purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
+ g_free(tzstr);
+ }
+
+ if (geostr) {
+ purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
+ g_free(geostr);
+ }
+ }
+}
+
+static void
+silcpurple_buddy_kill(PurpleBlistNode *node, gpointer data)
+{
+ PurpleBuddy *b;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+ b = (PurpleBuddy *) node;
+ gc = purple_account_get_connection(b->account);
+ sg = gc->proto_data;
+
+ /* Call KILL */
+ silc_client_command_call(sg->client, sg->conn, NULL, "KILL",
+ b->name, "Killed by operator", NULL);
+}
+
+typedef struct {
+ SilcPurple sg;
+ SilcClientEntry client_entry;
+} *SilcPurpleBuddyWb;
+
+static void
+silcpurple_buddy_wb(PurpleBlistNode *node, gpointer data)
+{
+ SilcPurpleBuddyWb wb = data;
+ silcpurple_wb_init(wb->sg, wb->client_entry);
+ silc_free(wb);
+}
+
+GList *silcpurple_buddy_menu(PurpleBuddy *buddy)
+{
+ PurpleConnection *gc = purple_account_get_connection(buddy->account);
+ SilcPurple sg = gc->proto_data;
+ SilcClientConnection conn = sg->conn;
+ const char *pkfile = NULL;
+ SilcClientEntry client_entry = NULL;
+ PurpleMenuAction *act;
+ GList *m = NULL;
+ SilcPurpleBuddyWb wb;
+
+ pkfile = purple_blist_node_get_string((PurpleBlistNode *) buddy, "public-key");
+ client_entry = silc_client_get_client_by_id(sg->client,
+ sg->conn,
+ buddy->proto_data);
+
+ if (client_entry && client_entry->send_key) {
+ act = purple_menu_action_new(_("Reset IM Key"),
+ PURPLE_CALLBACK(silcpurple_buddy_resetkey),
+ NULL, NULL);
+ m = g_list_append(m, act);
+
+ } else {
+ act = purple_menu_action_new(_("IM with Key Exchange"),
+ PURPLE_CALLBACK(silcpurple_buddy_keyagr),
+ NULL, NULL);
+ m = g_list_append(m, act);
+
+ act = purple_menu_action_new(_("IM with Password"),
+ PURPLE_CALLBACK(silcpurple_buddy_privkey_menu),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ }
+
+ if (pkfile) {
+ act = purple_menu_action_new(_("Show Public Key"),
+ PURPLE_CALLBACK(silcpurple_buddy_showkey),
+ NULL, NULL);
+ m = g_list_append(m, act);
+
+ } else {
+ act = purple_menu_action_new(_("Get Public Key..."),
+ PURPLE_CALLBACK(silcpurple_buddy_getkey_menu),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ }
+
+ if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) {
+ act = purple_menu_action_new(_("Kill User"),
+ PURPLE_CALLBACK(silcpurple_buddy_kill),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ }
+
+ if (client_entry) {
+ wb = silc_calloc(1, sizeof(*wb));
+ wb->sg = sg;
+ wb->client_entry = client_entry;
+ act = purple_menu_action_new(_("Draw On Whiteboard"),
+ PURPLE_CALLBACK(silcpurple_buddy_wb),
+ (void *)wb, NULL);
+ m = g_list_append(m, act);
+ }
+ return m;
+}
+
+#ifdef SILC_ATTRIBUTE_USER_ICON
+void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcMime mime;
+ char type[32];
+ unsigned char *icon;
+ const char *t;
+ SilcAttributeObjMime obj;
+
+ /* Remove */
+ if (!img) {
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_USER_ICON, NULL);
+ return;
+ }
+
+ /* Add */
+ mime = silc_mime_alloc();
+ if (!mime)
+ return;
+
+ t = purple_imgstore_get_extension(img);
+ if (!t || !strcmp(t, "icon")) {
+ silc_mime_free(mime);
+ return;
+ }
+ if (!strcmp(t, "jpg"))
+ t = "jpeg";
+ g_snprintf(type, sizeof(type), "image/%s", t);
+ silc_mime_add_field(mime, "Content-Type", type);
+ silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
+
+ obj.mime = icon = silc_mime_encode(mime, &obj.mime_len);
+ if (obj.mime)
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj));
+
+ silc_free(icon);
+ silc_mime_free(mime);
+}
+#endif
diff --git a/libpurple/protocols/silc10/chat.c b/libpurple/protocols/silc10/chat.c
new file mode 100644
index 0000000000..343a153999
--- /dev/null
+++ b/libpurple/protocols/silc10/chat.c
@@ -0,0 +1,1456 @@
+/*
+
+ silcpurple_chat.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2004 Pekka Riikonen
+
+ 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; version 2 of the License.
+
+ 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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcpurple.h"
+#include "wb.h"
+
+/***************************** Channel Routines ******************************/
+
+GList *silcpurple_chat_info(PurpleConnection *gc)
+{
+ GList *ci = NULL;
+ struct proto_chat_entry *pce;
+
+ pce = g_new0(struct proto_chat_entry, 1);
+ pce->label = _("_Channel:");
+ pce->identifier = "channel";
+ pce->required = TRUE;
+ ci = g_list_append(ci, pce);
+
+ pce = g_new0(struct proto_chat_entry, 1);
+ pce->label = _("_Passphrase:");
+ pce->identifier = "passphrase";
+ pce->secret = TRUE;
+ ci = g_list_append(ci, pce);
+
+ return ci;
+}
+
+GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
+{
+ GHashTable *defaults;
+
+ defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+
+ if (chat_name != NULL)
+ g_hash_table_insert(defaults, "channel", g_strdup(chat_name));
+
+ return defaults;
+}
+
+static void
+silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components);
+
+static void
+silcpurple_chat_getinfo_res(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry *channels,
+ SilcUInt32 channels_count,
+ void *context)
+{
+ GHashTable *components = context;
+ PurpleConnection *gc = client->application;
+ const char *chname;
+ char tmp[256];
+
+ chname = g_hash_table_lookup(components, "channel");
+ if (!chname)
+ return;
+
+ if (!channels) {
+ g_snprintf(tmp, sizeof(tmp),
+ _("Channel %s does not exist in the network"), chname);
+ purple_notify_error(gc, _("Channel Information"),
+ _("Cannot get channel information"), tmp);
+ return;
+ }
+
+ silcpurple_chat_getinfo(gc, components);
+}
+
+
+static void
+silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components)
+{
+ SilcPurple sg = gc->proto_data;
+ const char *chname;
+ char *buf, tmp[256], *tmp2;
+ GString *s;
+ SilcChannelEntry channel;
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+
+ if (!components)
+ return;
+
+ chname = g_hash_table_lookup(components, "channel");
+ if (!chname)
+ return;
+ channel = silc_client_get_channel(sg->client, sg->conn,
+ (char *)chname);
+ if (!channel) {
+ silc_client_get_channel_resolve(sg->client, sg->conn,
+ (char *)chname,
+ silcpurple_chat_getinfo_res,
+ components);
+ return;
+ }
+
+ s = g_string_new("");
+ tmp2 = g_markup_escape_text(channel->channel_name, -1);
+ g_string_append_printf(s, _("<b>Channel Name:</b> %s"), tmp2);
+ g_free(tmp2);
+ if (channel->user_list && silc_hash_table_count(channel->user_list))
+ g_string_append_printf(s, _("<br><b>User Count:</b> %d"),
+ (int)silc_hash_table_count(channel->user_list));
+
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ tmp2 = g_markup_escape_text(chu->client->nickname, -1);
+ g_string_append_printf(s, _("<br><b>Channel Founder:</b> %s"),
+ tmp2);
+ g_free(tmp2);
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ if (channel->channel_key)
+ g_string_append_printf(s, _("<br><b>Channel Cipher:</b> %s"),
+ silc_cipher_get_name(channel->channel_key));
+ if (channel->hmac)
+ /* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */
+ g_string_append_printf(s, _("<br><b>Channel HMAC:</b> %s"),
+ silc_hmac_get_name(channel->hmac));
+
+ if (channel->topic) {
+ tmp2 = g_markup_escape_text(channel->topic, -1);
+ g_string_append_printf(s, _("<br><b>Channel Topic:</b><br>%s"), tmp2);
+ g_free(tmp2);
+ }
+
+ if (channel->mode) {
+ g_string_append_printf(s, _("<br><b>Channel Modes:</b> "));
+ silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
+ g_string_append(s, tmp);
+ }
+
+ if (channel->founder_key) {
+ char *fingerprint, *babbleprint;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+ pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len);
+ fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+ babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
+
+ g_string_append_printf(s, _("<br><b>Founder Key Fingerprint:</b><br>%s"), fingerprint);
+ g_string_append_printf(s, _("<br><b>Founder Key Babbleprint:</b><br>%s"), babbleprint);
+
+ silc_free(fingerprint);
+ silc_free(babbleprint);
+ silc_free(pk);
+ }
+
+ buf = g_string_free(s, FALSE);
+ purple_notify_formatted(gc, NULL, _("Channel Information"), NULL, buf, NULL, NULL);
+ g_free(buf);
+}
+
+
+static void
+silcpurple_chat_getinfo_menu(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat = (PurpleChat *)node;
+ silcpurple_chat_getinfo(chat->account->gc, chat->components);
+}
+
+
+#if 0 /* XXX For now these are not implemented. We need better
+ listview dialog from Purple for these. */
+/************************** Channel Invite List ******************************/
+
+static void
+silcpurple_chat_invitelist(PurpleBlistNode *node, gpointer data);
+{
+
+}
+
+
+/**************************** Channel Ban List *******************************/
+
+static void
+silcpurple_chat_banlist(PurpleBlistNode *node, gpointer data);
+{
+
+}
+#endif
+
+
+/************************* Channel Authentication ****************************/
+
+typedef struct {
+ SilcPurple sg;
+ SilcChannelEntry channel;
+ PurpleChat *c;
+ SilcBuffer pubkeys;
+} *SilcPurpleChauth;
+
+static void
+silcpurple_chat_chpk_add(void *user_data, const char *name)
+{
+ SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
+ SilcPurple sg = sgc->sg;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcPublicKey public_key;
+ SilcBuffer chpks, pk, chidp;
+ unsigned char mode[4];
+ SilcUInt32 m;
+
+ /* Load the public key */
+ if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
+ !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
+ silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
+ silc_buffer_free(sgc->pubkeys);
+ silc_free(sgc);
+ purple_notify_error(client->application,
+ _("Add Channel Public Key"),
+ _("Could not load public key"), NULL);
+ return;
+ }
+
+ pk = silc_pkcs_public_key_payload_encode(public_key);
+ chpks = silc_buffer_alloc_size(2);
+ SILC_PUT16_MSB(1, chpks->head);
+ chpks = silc_argument_payload_encode_one(chpks, pk->data,
+ pk->len, 0x00);
+ silc_buffer_free(pk);
+
+ m = sgc->channel->mode;
+ m |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
+
+ /* Send CMODE */
+ SILC_PUT32_MSB(m, mode);
+ chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL);
+ silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
+ ++conn->cmd_ident, 3,
+ 1, chidp->data, chidp->len,
+ 2, mode, sizeof(mode),
+ 9, chpks->data, chpks->len);
+ silc_buffer_free(chpks);
+ silc_buffer_free(chidp);
+ silc_buffer_free(sgc->pubkeys);
+ silc_free(sgc);
+}
+
+static void
+silcpurple_chat_chpk_cancel(void *user_data, const char *name)
+{
+ SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
+ silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
+ silc_buffer_free(sgc->pubkeys);
+ silc_free(sgc);
+}
+
+static void
+silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields)
+{
+ SilcPurple sg = sgc->sg;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ PurpleRequestField *f;
+ const GList *list;
+ SilcPublicKey public_key;
+ SilcBuffer chpks, pk, chidp;
+ SilcUInt16 c = 0, ct;
+ unsigned char mode[4];
+ SilcUInt32 m;
+
+ f = purple_request_fields_get_field(fields, "list");
+ if (!purple_request_field_list_get_selected(f)) {
+ /* Add new public key */
+ purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE,
+ G_CALLBACK(silcpurple_chat_chpk_add),
+ G_CALLBACK(silcpurple_chat_chpk_cancel),
+ purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+ return;
+ }
+
+ list = purple_request_field_list_get_items(f);
+ chpks = silc_buffer_alloc_size(2);
+
+ for (ct = 0; list; list = list->next, ct++) {
+ public_key = purple_request_field_list_get_data(f, list->data);
+ if (purple_request_field_list_is_selected(f, list->data)) {
+ /* Delete this public key */
+ pk = silc_pkcs_public_key_payload_encode(public_key);
+ chpks = silc_argument_payload_encode_one(chpks, pk->data,
+ pk->len, 0x01);
+ silc_buffer_free(pk);
+ c++;
+ }
+ silc_pkcs_public_key_free(public_key);
+ }
+ if (!c) {
+ silc_buffer_free(chpks);
+ return;
+ }
+ SILC_PUT16_MSB(c, chpks->head);
+
+ m = sgc->channel->mode;
+ if (ct == c)
+ m &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
+
+ /* Send CMODE */
+ SILC_PUT32_MSB(m, mode);
+ chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL);
+ silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
+ ++conn->cmd_ident, 3,
+ 1, chidp->data, chidp->len,
+ 2, mode, sizeof(mode),
+ 9, chpks->data, chpks->len);
+ silc_buffer_free(chpks);
+ silc_buffer_free(chidp);
+ silc_buffer_free(sgc->pubkeys);
+ silc_free(sgc);
+}
+
+static void
+silcpurple_chat_chauth_ok(SilcPurpleChauth sgc, PurpleRequestFields *fields)
+{
+ SilcPurple sg = sgc->sg;
+ PurpleRequestField *f;
+ const char *curpass, *val;
+ int set;
+
+ f = purple_request_fields_get_field(fields, "passphrase");
+ val = purple_request_field_string_get_value(f);
+ curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase");
+
+ if (!val && curpass)
+ set = 0;
+ else if (val && !curpass)
+ set = 1;
+ else if (val && curpass && strcmp(val, curpass))
+ set = 1;
+ else
+ set = -1;
+
+ if (set == 1) {
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
+ sgc->channel->channel_name, "+a", val, NULL);
+ purple_blist_node_set_string((PurpleBlistNode *)sgc->c, "passphrase", val);
+ } else if (set == 0) {
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
+ sgc->channel->channel_name, "-a", NULL);
+ purple_blist_node_remove_setting((PurpleBlistNode *)sgc->c, "passphrase");
+ }
+
+ silc_buffer_free(sgc->pubkeys);
+ silc_free(sgc);
+}
+
+void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
+ SilcBuffer channel_pubkeys)
+{
+ SilcUInt16 argc;
+ SilcArgumentPayload chpks;
+ unsigned char *pk;
+ SilcUInt32 pk_len, type;
+ char *fingerprint, *babbleprint;
+ SilcPublicKey pubkey;
+ SilcPublicKeyIdentifier ident;
+ char tmp2[1024], t[512];
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *g;
+ PurpleRequestField *f;
+ SilcPurpleChauth sgc;
+ const char *curpass = NULL;
+
+ sgc = silc_calloc(1, sizeof(*sgc));
+ if (!sgc)
+ return;
+ sgc->sg = sg;
+ sgc->channel = channel;
+
+ fields = purple_request_fields_new();
+
+ if (sgc->c)
+ curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase");
+
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_string_new("passphrase", _("Channel Passphrase"),
+ curpass, FALSE);
+ purple_request_field_string_set_masked(f, TRUE);
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_label_new("l1", _("Channel Public Keys List"));
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+
+ g_snprintf(t, sizeof(t),
+ _("Channel authentication is used to secure the channel from "
+ "unauthorized access. The authentication may be based on "
+ "passphrase and digital signatures. If passphrase is set, it "
+ "is required to be able to join. If channel public keys are set "
+ "then only users whose public keys are listed are able to join."));
+
+ if (!channel_pubkeys) {
+ f = purple_request_field_list_new("list", NULL);
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields(sg->gc, _("Channel Authentication"),
+ _("Channel Authentication"), t, fields,
+ _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
+ _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
+ purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+ return;
+ }
+ sgc->pubkeys = silc_buffer_copy(channel_pubkeys);
+
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_list_new("list", NULL);
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+
+ SILC_GET16_MSB(argc, channel_pubkeys->data);
+ chpks = silc_argument_payload_parse(channel_pubkeys->data + 2,
+ channel_pubkeys->len - 2, argc);
+ if (!chpks)
+ return;
+
+ pk = silc_argument_get_first_arg(chpks, &type, &pk_len);
+ while (pk) {
+ fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
+ babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
+ silc_pkcs_public_key_payload_decode(pk, pk_len, &pubkey);
+ ident = silc_pkcs_decode_identifier(pubkey->identifier);
+
+ g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s",
+ ident->realname ? ident->realname : ident->username ?
+ ident->username : "", fingerprint, babbleprint);
+ purple_request_field_list_add(f, tmp2, pubkey);
+
+ silc_free(fingerprint);
+ silc_free(babbleprint);
+ silc_pkcs_free_identifier(ident);
+ pk = silc_argument_get_next_arg(chpks, &type, &pk_len);
+ }
+
+ purple_request_field_list_set_multi_select(f, FALSE);
+ purple_request_fields(sg->gc, _("Channel Authentication"),
+ _("Channel Authentication"), t, fields,
+ _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
+ _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
+ purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+
+ silc_argument_payload_free(chpks);
+}
+
+static void
+silcpurple_chat_chauth(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
+ g_hash_table_lookup(chat->components, "channel"),
+ "+C", NULL);
+}
+
+
+/************************** Channel Private Groups **************************/
+
+/* Private groups are "virtual" channels. They are groups inside a channel.
+ This is implemented by using channel private keys. By knowing a channel
+ private key user becomes part of that group and is able to talk on that
+ group. Other users, on the same channel, won't be able to see the
+ messages of that group. It is possible to have multiple groups inside
+ a channel - and thus having multiple private keys on the channel. */
+
+typedef struct {
+ SilcPurple sg;
+ PurpleChat *c;
+ const char *channel;
+} *SilcPurpleCharPrv;
+
+static void
+silcpurple_chat_prv_add(SilcPurpleCharPrv p, PurpleRequestFields *fields)
+{
+ SilcPurple sg = p->sg;
+ char tmp[512];
+ PurpleRequestField *f;
+ const char *name, *passphrase, *alias;
+ GHashTable *comp;
+ PurpleGroup *g;
+ PurpleChat *cn;
+
+ f = purple_request_fields_get_field(fields, "name");
+ name = purple_request_field_string_get_value(f);
+ if (!name) {
+ silc_free(p);
+ return;
+ }
+ f = purple_request_fields_get_field(fields, "passphrase");
+ passphrase = purple_request_field_string_get_value(f);
+ f = purple_request_fields_get_field(fields, "alias");
+ alias = purple_request_field_string_get_value(f);
+
+ /* Add private group to buddy list */
+ g_snprintf(tmp, sizeof(tmp), "%s [Private Group]", name);
+ comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ g_hash_table_replace(comp, g_strdup("channel"), g_strdup(tmp));
+ g_hash_table_replace(comp, g_strdup("passphrase"), g_strdup(passphrase));
+
+ cn = purple_chat_new(sg->account, alias, comp);
+ g = (PurpleGroup *)p->c->node.parent;
+ purple_blist_add_chat(cn, g, (PurpleBlistNode *)p->c);
+
+ /* Associate to a real channel */
+ purple_blist_node_set_string((PurpleBlistNode *)cn, "parentch", p->channel);
+
+ /* Join the group */
+ silcpurple_chat_join(sg->gc, comp);
+
+ silc_free(p);
+}
+
+static void
+silcpurple_chat_prv_cancel(SilcPurpleCharPrv p, PurpleRequestFields *fields)
+{
+ silc_free(p);
+}
+
+static void
+silcpurple_chat_prv(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ SilcPurpleCharPrv p;
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *g;
+ PurpleRequestField *f;
+ char tmp[512];
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ p = silc_calloc(1, sizeof(*p));
+ if (!p)
+ return;
+ p->sg = sg;
+
+ p->channel = g_hash_table_lookup(chat->components, "channel");
+ p->c = purple_blist_find_chat(sg->account, p->channel);
+
+ fields = purple_request_fields_new();
+
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_string_new("name", _("Group Name"),
+ NULL, FALSE);
+ purple_request_field_group_add_field(g, f);
+
+ f = purple_request_field_string_new("passphrase", _("Passphrase"),
+ NULL, FALSE);
+ purple_request_field_string_set_masked(f, TRUE);
+ purple_request_field_group_add_field(g, f);
+
+ f = purple_request_field_string_new("alias", _("Alias"),
+ NULL, FALSE);
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+
+ g_snprintf(tmp, sizeof(tmp),
+ _("Please enter the %s channel private group name and passphrase."),
+ p->channel);
+ purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields,
+ _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
+ _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
+ purple_connection_get_account(gc), NULL, NULL, p);
+}
+
+
+/****************************** Channel Modes ********************************/
+
+static void
+silcpurple_chat_permanent_reset(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
+ g_hash_table_lookup(chat->components, "channel"),
+ "-f", NULL);
+}
+
+static void
+silcpurple_chat_permanent(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+ const char *channel;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ if (!sg->conn)
+ return;
+
+ /* XXX we should have ability to define which founder
+ key to use. Now we use the user's own public key
+ (default key). */
+
+ /* Call CMODE */
+ channel = g_hash_table_lookup(chat->components, "channel");
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel,
+ "+f", NULL);
+}
+
+typedef struct {
+ SilcPurple sg;
+ const char *channel;
+} *SilcPurpleChatInput;
+
+static void
+silcpurple_chat_ulimit_cb(SilcPurpleChatInput s, const char *limit)
+{
+ SilcChannelEntry channel;
+ int ulimit = 0;
+
+ channel = silc_client_get_channel(s->sg->client, s->sg->conn,
+ (char *)s->channel);
+ if (!channel)
+ return;
+ if (limit)
+ ulimit = atoi(limit);
+
+ if (!limit || !(*limit) || *limit == '0') {
+ if (limit && ulimit == channel->user_limit) {
+ silc_free(s);
+ return;
+ }
+ silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
+ s->channel, "-l", NULL);
+
+ silc_free(s);
+ return;
+ }
+
+ if (ulimit == channel->user_limit) {
+ silc_free(s);
+ return;
+ }
+
+ /* Call CMODE */
+ silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
+ s->channel, "+l", limit, NULL);
+
+ silc_free(s);
+}
+
+static void
+silcpurple_chat_ulimit(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ SilcPurpleChatInput s;
+ SilcChannelEntry channel;
+ const char *ch;
+ char tmp[32];
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ if (!sg->conn)
+ return;
+
+ ch = g_strdup(g_hash_table_lookup(chat->components, "channel"));
+ channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch);
+ if (!channel)
+ return;
+
+ s = silc_calloc(1, sizeof(*s));
+ if (!s)
+ return;
+ s->channel = ch;
+ s->sg = sg;
+ g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit);
+ purple_request_input(gc, _("User Limit"), NULL,
+ _("Set user limit on channel. Set to zero to reset user limit."),
+ tmp, FALSE, FALSE, NULL,
+ _("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb),
+ _("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb),
+ purple_connection_get_account(gc), NULL, NULL, s);
+}
+
+static void
+silcpurple_chat_resettopic(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
+ g_hash_table_lookup(chat->components, "channel"),
+ "-t", NULL);
+}
+
+static void
+silcpurple_chat_settopic(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
+ g_hash_table_lookup(chat->components, "channel"),
+ "+t", NULL);
+}
+
+static void
+silcpurple_chat_resetprivate(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
+ g_hash_table_lookup(chat->components, "channel"),
+ "-p", NULL);
+}
+
+static void
+silcpurple_chat_setprivate(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
+ g_hash_table_lookup(chat->components, "channel"),
+ "+p", NULL);
+}
+
+static void
+silcpurple_chat_resetsecret(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
+ g_hash_table_lookup(chat->components, "channel"),
+ "-s", NULL);
+}
+
+static void
+silcpurple_chat_setsecret(PurpleBlistNode *node, gpointer data)
+{
+ PurpleChat *chat;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
+
+ chat = (PurpleChat *) node;
+ gc = purple_account_get_connection(chat->account);
+ sg = gc->proto_data;
+
+ silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
+ g_hash_table_lookup(chat->components, "channel"),
+ "+s", NULL);
+}
+
+typedef struct {
+ SilcPurple sg;
+ SilcChannelEntry channel;
+} *SilcPurpleChatWb;
+
+static void
+silcpurple_chat_wb(PurpleBlistNode *node, gpointer data)
+{
+ SilcPurpleChatWb wb = data;
+ silcpurple_wb_init_ch(wb->sg, wb->channel);
+ silc_free(wb);
+}
+
+GList *silcpurple_chat_menu(PurpleChat *chat)
+{
+ GHashTable *components = chat->components;
+ PurpleConnection *gc = purple_account_get_connection(chat->account);
+ SilcPurple sg = gc->proto_data;
+ SilcClientConnection conn = sg->conn;
+ const char *chname = NULL;
+ SilcChannelEntry channel = NULL;
+ SilcChannelUser chu = NULL;
+ SilcUInt32 mode = 0;
+
+ GList *m = NULL;
+ PurpleMenuAction *act;
+
+ if (components)
+ chname = g_hash_table_lookup(components, "channel");
+ if (chname)
+ channel = silc_client_get_channel(sg->client, sg->conn,
+ (char *)chname);
+ if (channel) {
+ chu = silc_client_on_channel(channel, conn->local_entry);
+ if (chu)
+ mode = chu->mode;
+ }
+
+ if (strstr(chname, "[Private Group]"))
+ return NULL;
+
+ act = purple_menu_action_new(_("Get Info"),
+ PURPLE_CALLBACK(silcpurple_chat_getinfo_menu),
+ NULL, NULL);
+ m = g_list_append(m, act);
+
+#if 0 /* XXX For now these are not implemented. We need better
+ listview dialog from Purple for these. */
+ if (mode & SILC_CHANNEL_UMODE_CHANOP) {
+ act = purple_menu_action_new(_("Invite List"),
+ PURPLE_CALLBACK(silcpurple_chat_invitelist),
+ NULL, NULL);
+ m = g_list_append(m, act);
+
+ act = purple_menu_action_new(_("Ban List"),
+ PURPLE_CALLBACK(silcpurple_chat_banlist),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ }
+#endif
+
+ if (chu) {
+ act = purple_menu_action_new(_("Add Private Group"),
+ PURPLE_CALLBACK(silcpurple_chat_prv),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ }
+
+ if (mode & SILC_CHANNEL_UMODE_CHANFO) {
+ act = purple_menu_action_new(_("Channel Authentication"),
+ PURPLE_CALLBACK(silcpurple_chat_chauth),
+ NULL, NULL);
+ m = g_list_append(m, act);
+
+ if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ act = purple_menu_action_new(_("Reset Permanent"),
+ PURPLE_CALLBACK(silcpurple_chat_permanent_reset),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ } else {
+ act = purple_menu_action_new(_("Set Permanent"),
+ PURPLE_CALLBACK(silcpurple_chat_permanent),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ }
+ }
+
+ if (mode & SILC_CHANNEL_UMODE_CHANOP) {
+ act = purple_menu_action_new(_("Set User Limit"),
+ PURPLE_CALLBACK(silcpurple_chat_ulimit),
+ NULL, NULL);
+ m = g_list_append(m, act);
+
+ if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
+ act = purple_menu_action_new(_("Reset Topic Restriction"),
+ PURPLE_CALLBACK(silcpurple_chat_resettopic),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ } else {
+ act = purple_menu_action_new(_("Set Topic Restriction"),
+ PURPLE_CALLBACK(silcpurple_chat_settopic),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ }
+
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) {
+ act = purple_menu_action_new(_("Reset Private Channel"),
+ PURPLE_CALLBACK(silcpurple_chat_resetprivate),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ } else {
+ act = purple_menu_action_new(_("Set Private Channel"),
+ PURPLE_CALLBACK(silcpurple_chat_setprivate),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ }
+
+ if (channel->mode & SILC_CHANNEL_MODE_SECRET) {
+ act = purple_menu_action_new(_("Reset Secret Channel"),
+ PURPLE_CALLBACK(silcpurple_chat_resetsecret),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ } else {
+ act = purple_menu_action_new(_("Set Secret Channel"),
+ PURPLE_CALLBACK(silcpurple_chat_setsecret),
+ NULL, NULL);
+ m = g_list_append(m, act);
+ }
+ }
+
+ if (channel) {
+ SilcPurpleChatWb wb;
+ wb = silc_calloc(1, sizeof(*wb));
+ wb->sg = sg;
+ wb->channel = channel;
+ act = purple_menu_action_new(_("Draw On Whiteboard"),
+ PURPLE_CALLBACK(silcpurple_chat_wb),
+ (void *)wb, NULL);
+ m = g_list_append(m, act);
+ }
+
+ return m;
+}
+
+
+/******************************* Joining Etc. ********************************/
+
+void silcpurple_chat_join_done(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ SilcUInt32 clients_count,
+ void *context)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ SilcChannelEntry channel = context;
+ PurpleConversation *convo;
+ SilcUInt32 retry = SILC_PTR_TO_32(channel->context);
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+ GList *users = NULL, *flags = NULL;
+ char tmp[256];
+
+ if (!clients && retry < 1) {
+ /* Resolving users failed, try again. */
+ channel->context = SILC_32_TO_PTR(retry + 1);
+ silc_client_get_clients_by_channel(client, conn, channel,
+ silcpurple_chat_join_done, channel);
+ return;
+ }
+
+ /* Add channel to Purple */
+ channel->context = SILC_32_TO_PTR(++sg->channel_ids);
+ serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name);
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (!convo)
+ return;
+
+ /* Add all users to channel */
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE;
+ if (!chu->client->nickname)
+ continue;
+ chu->context = SILC_32_TO_PTR(sg->channel_ids);
+
+ if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
+ f |= PURPLE_CBFLAGS_FOUNDER;
+ if (chu->mode & SILC_CHANNEL_UMODE_CHANOP)
+ f |= PURPLE_CBFLAGS_OP;
+ users = g_list_append(users, g_strdup(chu->client->nickname));
+ flags = g_list_append(flags, GINT_TO_POINTER(f));
+
+ if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (chu->client == conn->local_entry)
+ g_snprintf(tmp, sizeof(tmp),
+ _("You are channel founder on <I>%s</I>"),
+ channel->channel_name);
+ else
+ g_snprintf(tmp, sizeof(tmp),
+ _("Channel founder on <I>%s</I> is <I>%s</I>"),
+ channel->channel_name, chu->client->nickname);
+
+ purple_conversation_write(convo, NULL, tmp,
+ PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE);
+ g_list_free(users);
+ g_list_free(flags);
+
+ /* Set topic */
+ if (channel->topic)
+ purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic);
+
+ /* Set nick */
+ purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname);
+}
+
+char *silcpurple_get_chat_name(GHashTable *data)
+{
+ return g_strdup(g_hash_table_lookup(data, "channel"));
+}
+
+void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ const char *channel, *passphrase, *parentch;
+
+ if (!conn)
+ return;
+
+ channel = g_hash_table_lookup(data, "channel");
+ passphrase = g_hash_table_lookup(data, "passphrase");
+
+ /* Check if we are joining a private group. Handle it
+ purely locally as it's not a real channel */
+ if (strstr(channel, "[Private Group]")) {
+ SilcChannelEntry channel_entry;
+ SilcChannelPrivateKey key;
+ PurpleChat *c;
+ SilcPurplePrvgrp grp;
+
+ c = purple_blist_find_chat(sg->account, channel);
+ parentch = purple_blist_node_get_string((PurpleBlistNode *)c, "parentch");
+ if (!parentch)
+ return;
+
+ channel_entry = silc_client_get_channel(sg->client, sg->conn,
+ (char *)parentch);
+ if (!channel_entry ||
+ !silc_client_on_channel(channel_entry, sg->conn->local_entry)) {
+ char tmp[512];
+ g_snprintf(tmp, sizeof(tmp),
+ _("You have to join the %s channel before you are "
+ "able to join the private group"), parentch);
+ purple_notify_error(gc, _("Join Private Group"),
+ _("Cannot join private group"), tmp);
+ return;
+ }
+
+ /* Add channel private key */
+ if (!silc_client_add_channel_private_key(client, conn,
+ channel_entry, channel,
+ NULL, NULL,
+ (unsigned char *)passphrase,
+ strlen(passphrase), &key))
+ return;
+
+ /* Join the group */
+ grp = silc_calloc(1, sizeof(*grp));
+ if (!grp)
+ return;
+ grp->id = ++sg->channel_ids + SILCPURPLE_PRVGRP;
+ grp->chid = SILC_PTR_TO_32(channel_entry->context);
+ grp->parentch = parentch;
+ grp->channel = channel;
+ grp->key = key;
+ sg->grps = g_list_append(sg->grps, grp);
+ serv_got_joined_chat(gc, grp->id, channel);
+ return;
+ }
+
+ /* XXX We should have other properties here as well:
+ 1. whether to try to authenticate to the channel
+ 1a. with default key,
+ 1b. with specific key.
+ 2. whether to try to authenticate to become founder.
+ 2a. with default key,
+ 2b. with specific key.
+
+ Since now such variety is not possible in the join dialog
+ we always use -founder and -auth options, which try to
+ do both 1 and 2 with default keys. */
+
+ /* Call JOIN */
+ if ((passphrase != NULL) && (*passphrase != '\0'))
+ silc_client_command_call(client, conn, NULL, "JOIN",
+ channel, passphrase, "-auth", "-founder", NULL);
+ else
+ silc_client_command_call(client, conn, NULL, "JOIN",
+ channel, "-auth", "-founder", NULL);
+}
+
+void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
+ const char *name)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+ gboolean found = FALSE;
+
+ if (!conn)
+ return;
+
+ /* See if we are inviting on a private group. Invite
+ to the actual channel */
+ if (id > SILCPURPLE_PRVGRP) {
+ GList *l;
+ SilcPurplePrvgrp prv;
+
+ for (l = sg->grps; l; l = l->next)
+ if (((SilcPurplePrvgrp)l->data)->id == id)
+ break;
+ if (!l)
+ return;
+ prv = l->data;
+ id = prv->chid;
+ }
+
+ /* Find channel by id */
+ silc_hash_table_list(conn->local_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ if (SILC_PTR_TO_32(chu->channel->context) == id ) {
+ found = TRUE;
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+ if (!found)
+ return;
+
+ /* Call INVITE */
+ silc_client_command_call(client, conn, NULL, "INVITE",
+ chu->channel->channel_name,
+ name, NULL);
+}
+
+void silcpurple_chat_leave(PurpleConnection *gc, int id)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+ gboolean found = FALSE;
+ GList *l;
+ SilcPurplePrvgrp prv;
+
+ if (!conn)
+ return;
+
+ /* See if we are leaving a private group */
+ if (id > SILCPURPLE_PRVGRP) {
+ SilcChannelEntry channel;
+
+ for (l = sg->grps; l; l = l->next)
+ if (((SilcPurplePrvgrp)l->data)->id == id)
+ break;
+ if (!l)
+ return;
+ prv = l->data;
+ channel = silc_client_get_channel(sg->client, sg->conn,
+ (char *)prv->parentch);
+ if (!channel)
+ return;
+ silc_client_del_channel_private_key(client, conn,
+ channel, prv->key);
+ silc_free(prv);
+ sg->grps = g_list_remove(sg->grps, prv);
+ serv_got_chat_left(gc, id);
+ return;
+ }
+
+ /* Find channel by id */
+ silc_hash_table_list(conn->local_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ if (SILC_PTR_TO_32(chu->channel->context) == id ) {
+ found = TRUE;
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+ if (!found)
+ return;
+
+ /* Call LEAVE */
+ silc_client_command_call(client, conn, NULL, "LEAVE",
+ chu->channel->channel_name, NULL);
+
+ serv_got_chat_left(gc, id);
+
+ /* Leave from private groups on this channel as well */
+ for (l = sg->grps; l; l = l->next)
+ if (((SilcPurplePrvgrp)l->data)->chid == id) {
+ prv = l->data;
+ silc_client_del_channel_private_key(client, conn,
+ chu->channel,
+ prv->key);
+ serv_got_chat_left(gc, prv->id);
+ silc_free(prv);
+ sg->grps = g_list_remove(sg->grps, prv);
+ if (!sg->grps)
+ break;
+ }
+}
+
+int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags msgflags)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+ SilcChannelEntry channel = NULL;
+ SilcChannelPrivateKey key = NULL;
+ SilcUInt32 flags;
+ int ret;
+ char *msg2, *tmp;
+ gboolean found = FALSE;
+ gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
+
+ if (!msg || !conn)
+ return 0;
+
+ flags = SILC_MESSAGE_FLAG_UTF8;
+
+ tmp = msg2 = purple_unescape_html(msg);
+
+ if (!g_ascii_strncasecmp(msg2, "/me ", 4))
+ {
+ msg2 += 4;
+ if (!*msg2) {
+ g_free(tmp);
+ return 0;
+ }
+ flags |= SILC_MESSAGE_FLAG_ACTION;
+ } else if (strlen(msg) > 1 && msg[0] == '/') {
+ if (!silc_client_command_call(client, conn, msg + 1))
+ purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
+ _("Unknown command"));
+ g_free(tmp);
+ return 0;
+ }
+
+
+ if (sign)
+ flags |= SILC_MESSAGE_FLAG_SIGNED;
+
+ /* Get the channel private key if we are sending on
+ private group */
+ if (id > SILCPURPLE_PRVGRP) {
+ GList *l;
+ SilcPurplePrvgrp prv;
+
+ for (l = sg->grps; l; l = l->next)
+ if (((SilcPurplePrvgrp)l->data)->id == id)
+ break;
+ if (!l) {
+ g_free(tmp);
+ return 0;
+ }
+ prv = l->data;
+ channel = silc_client_get_channel(sg->client, sg->conn,
+ (char *)prv->parentch);
+ if (!channel) {
+ g_free(tmp);
+ return 0;
+ }
+ key = prv->key;
+ }
+
+ if (!channel) {
+ /* Find channel by id */
+ silc_hash_table_list(conn->local_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ if (SILC_PTR_TO_32(chu->channel->context) == id ) {
+ found = TRUE;
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+ if (!found) {
+ g_free(tmp);
+ return 0;
+ }
+ channel = chu->channel;
+ }
+
+ /* Send channel message */
+ ret = silc_client_send_channel_message(client, conn, channel, key,
+ flags, (unsigned char *)msg2,
+ strlen(msg2), TRUE);
+ if (ret) {
+ serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, msg,
+ time(NULL));
+ }
+ g_free(tmp);
+
+ return ret;
+}
+
+void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+ gboolean found = FALSE;
+
+ if (!conn)
+ return;
+
+ /* See if setting topic on private group. Set it
+ on the actual channel */
+ if (id > SILCPURPLE_PRVGRP) {
+ GList *l;
+ SilcPurplePrvgrp prv;
+
+ for (l = sg->grps; l; l = l->next)
+ if (((SilcPurplePrvgrp)l->data)->id == id)
+ break;
+ if (!l)
+ return;
+ prv = l->data;
+ id = prv->chid;
+ }
+
+ /* Find channel by id */
+ silc_hash_table_list(conn->local_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ if (SILC_PTR_TO_32(chu->channel->context) == id ) {
+ found = TRUE;
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+ if (!found)
+ return;
+
+ /* Call TOPIC */
+ silc_client_command_call(client, conn, NULL, "TOPIC",
+ chu->channel->channel_name, topic, NULL);
+}
+
+PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ GList *fields = NULL;
+ PurpleRoomlistField *f;
+
+ if (!conn)
+ return NULL;
+
+ if (sg->roomlist)
+ purple_roomlist_unref(sg->roomlist);
+
+ sg->roomlist_canceled = FALSE;
+
+ sg->roomlist = purple_roomlist_new(purple_connection_get_account(gc));
+ f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "channel", TRUE);
+ fields = g_list_append(fields, f);
+ f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT,
+ _("Users"), "users", FALSE);
+ fields = g_list_append(fields, f);
+ f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
+ _("Topic"), "topic", FALSE);
+ fields = g_list_append(fields, f);
+ purple_roomlist_set_fields(sg->roomlist, fields);
+
+ /* Call LIST */
+ silc_client_command_call(client, conn, "LIST");
+
+ purple_roomlist_set_in_progress(sg->roomlist, TRUE);
+
+ return sg->roomlist;
+}
+
+void silcpurple_roomlist_cancel(PurpleRoomlist *list)
+{
+ PurpleConnection *gc = purple_account_get_connection(list->account);
+ SilcPurple sg;
+
+ if (!gc)
+ return;
+ sg = gc->proto_data;
+
+ purple_roomlist_set_in_progress(list, FALSE);
+ if (sg->roomlist == list) {
+ purple_roomlist_unref(sg->roomlist);
+ sg->roomlist = NULL;
+ sg->roomlist_canceled = TRUE;
+ }
+}
diff --git a/libpurple/protocols/silc10/ft.c b/libpurple/protocols/silc10/ft.c
new file mode 100644
index 0000000000..39e74495b7
--- /dev/null
+++ b/libpurple/protocols/silc10/ft.c
@@ -0,0 +1,412 @@
+/*
+
+ silcpurple_ft.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2004 Pekka Riikonen
+
+ 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; version 2 of the License.
+
+ 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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcpurple.h"
+
+/****************************** File Transfer ********************************/
+
+/* This implements the secure file transfer protocol (SFTP) using the SILC
+ SFTP library implementation. The API we use from the SILC Toolkit is the
+ SILC Client file transfer API, as it provides a simple file transfer we
+ need in this case. We could use the SILC SFTP API directly, but it would
+ be an overkill since we'd effectively re-implement the file transfer what
+ the SILC Client's file transfer API already provides.
+
+ From Purple we do NOT use the FT API to do the transfer as it is very limiting.
+ In fact it does not suite to file transfers like SFTP at all. For example,
+ it assumes that read operations are synchronous what they are not in SFTP.
+ It also assumes that the file transfer socket is to be handled by the Purple
+ eventloop, and this naturally is something we don't want to do in case of
+ SILC Toolkit. The FT API suites well to purely stream based file transfers
+ like HTTP GET and similar.
+
+ For this reason, we directly access the Purple GKT FT API and hack the FT
+ API to merely provide the user interface experience and all the magic
+ is done in the SILC Toolkit. Ie. we update the statistics information in
+ the FT API for user interface, and that's it. A bit dirty but until the
+ FT API gets better this is the way to go. Good thing that FT API allowed
+ us to do this. */
+
+typedef struct {
+ SilcPurple sg;
+ SilcClientEntry client_entry;
+ SilcUInt32 session_id;
+ char *hostname;
+ SilcUInt16 port;
+ PurpleXfer *xfer;
+
+ SilcClientFileName completion;
+ void *completion_context;
+} *SilcPurpleXfer;
+
+static void
+silcpurple_ftp_monitor(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientMonitorStatus status,
+ SilcClientFileError error,
+ SilcUInt64 offset,
+ SilcUInt64 filesize,
+ SilcClientEntry client_entry,
+ SilcUInt32 session_id,
+ const char *filepath,
+ void *context)
+{
+ SilcPurpleXfer xfer = context;
+ PurpleConnection *gc = xfer->sg->gc;
+ char tmp[256];
+
+ if (status == SILC_CLIENT_FILE_MONITOR_CLOSED) {
+ purple_xfer_unref(xfer->xfer);
+ silc_free(xfer);
+ return;
+ }
+
+ if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT)
+ return;
+
+ if (status == SILC_CLIENT_FILE_MONITOR_ERROR) {
+ if (error == SILC_CLIENT_FILE_NO_SUCH_FILE) {
+ g_snprintf(tmp, sizeof(tmp), "No such file %s",
+ filepath ? filepath : "[N/A]");
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Error during file transfer"), tmp);
+ } else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED) {
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Error during file transfer"),
+ _("Permission denied"));
+ } else if (error == SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED) {
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Error during file transfer"),
+ _("Key agreement failed"));
+ } else if (error == SILC_CLIENT_FILE_UNKNOWN_SESSION) {
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Error during file transfer"),
+ _("File transfer session does not exist"));
+ } else {
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Error during file transfer"), NULL);
+ }
+ silc_client_file_close(client, conn, session_id);
+ purple_xfer_unref(xfer->xfer);
+ silc_free(xfer);
+ return;
+ }
+
+ /* Update file transfer UI */
+ if (!offset && filesize)
+ purple_xfer_set_size(xfer->xfer, filesize);
+ if (offset && filesize) {
+ xfer->xfer->bytes_sent = offset;
+ xfer->xfer->bytes_remaining = filesize - offset;
+ }
+ purple_xfer_update_progress(xfer->xfer);
+
+ if (status == SILC_CLIENT_FILE_MONITOR_SEND ||
+ status == SILC_CLIENT_FILE_MONITOR_RECEIVE) {
+ if (offset == filesize) {
+ /* Download finished */
+ purple_xfer_set_completed(xfer->xfer, TRUE);
+ silc_client_file_close(client, conn, session_id);
+ }
+ }
+}
+
+static void
+silcpurple_ftp_cancel(PurpleXfer *x)
+{
+ SilcPurpleXfer xfer = x->data;
+ xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL;
+ purple_xfer_update_progress(xfer->xfer);
+ silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
+}
+
+static void
+silcpurple_ftp_ask_name_cancel(PurpleXfer *x)
+{
+ SilcPurpleXfer xfer = x->data;
+
+ /* Cancel the transmission */
+ xfer->completion(NULL, xfer->completion_context);
+ silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
+}
+
+static void
+silcpurple_ftp_ask_name_ok(PurpleXfer *x)
+{
+ SilcPurpleXfer xfer = x->data;
+ const char *name;
+
+ name = purple_xfer_get_local_filename(x);
+ g_unlink(name);
+ xfer->completion(name, xfer->completion_context);
+}
+
+static void
+silcpurple_ftp_ask_name(SilcClient client,
+ SilcClientConnection conn,
+ SilcUInt32 session_id,
+ const char *remote_filename,
+ SilcClientFileName completion,
+ void *completion_context,
+ void *context)
+{
+ SilcPurpleXfer xfer = context;
+
+ xfer->completion = completion;
+ xfer->completion_context = completion_context;
+
+ purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_ask_name_ok);
+ purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_ask_name_cancel);
+
+ /* Request to save the file */
+ purple_xfer_set_filename(xfer->xfer, remote_filename);
+ purple_xfer_request(xfer->xfer);
+}
+
+static void
+silcpurple_ftp_request_result(PurpleXfer *x)
+{
+ SilcPurpleXfer xfer = x->data;
+ SilcClientFileError status;
+ PurpleConnection *gc = xfer->sg->gc;
+
+ if (purple_xfer_get_status(x) != PURPLE_XFER_STATUS_ACCEPTED)
+ return;
+
+ /* Start the file transfer */
+ status = silc_client_file_receive(xfer->sg->client, xfer->sg->conn,
+ silcpurple_ftp_monitor, xfer,
+ NULL, xfer->session_id,
+ silcpurple_ftp_ask_name, xfer);
+ switch (status) {
+ case SILC_CLIENT_FILE_OK:
+ return;
+ break;
+
+ case SILC_CLIENT_FILE_UNKNOWN_SESSION:
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("No file transfer session active"), NULL);
+ break;
+
+ case SILC_CLIENT_FILE_ALREADY_STARTED:
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("File transfer already started"), NULL);
+ break;
+
+ case SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED:
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Could not perform key agreement for file transfer"),
+ NULL);
+ break;
+
+ default:
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Could not start the file transfer"), NULL);
+ break;
+ }
+
+ /* Error */
+ purple_xfer_unref(xfer->xfer);
+ g_free(xfer->hostname);
+ silc_free(xfer);
+}
+
+static void
+silcpurple_ftp_request_denied(PurpleXfer *x)
+{
+
+}
+
+void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry client_entry, SilcUInt32 session_id,
+ const char *hostname, SilcUInt16 port)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ SilcPurpleXfer xfer;
+
+ xfer = silc_calloc(1, sizeof(*xfer));
+ if (!xfer) {
+ silc_client_file_close(sg->client, sg->conn, session_id);
+ return;
+ }
+
+ xfer->sg = sg;
+ xfer->client_entry = client_entry;
+ xfer->session_id = session_id;
+ xfer->hostname = g_strdup(hostname);
+ xfer->port = port;
+ xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_RECEIVE,
+ xfer->client_entry->nickname);
+ if (!xfer->xfer) {
+ silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
+ g_free(xfer->hostname);
+ silc_free(xfer);
+ return;
+ }
+ purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_request_result);
+ purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied);
+ purple_xfer_set_cancel_recv_fnc(xfer->xfer, silcpurple_ftp_cancel);
+ xfer->xfer->remote_ip = g_strdup(hostname);
+ xfer->xfer->remote_port = port;
+ xfer->xfer->data = xfer;
+
+ /* File transfer request */
+ purple_xfer_request(xfer->xfer);
+}
+
+static void
+silcpurple_ftp_send_cancel(PurpleXfer *x)
+{
+ SilcPurpleXfer xfer = x->data;
+ silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
+ purple_xfer_unref(xfer->xfer);
+ g_free(xfer->hostname);
+ silc_free(xfer);
+}
+
+static void
+silcpurple_ftp_send(PurpleXfer *x)
+{
+ SilcPurpleXfer xfer = x->data;
+ const char *name;
+ char *local_ip = NULL, *remote_ip = NULL;
+ gboolean local = TRUE;
+
+ name = purple_xfer_get_local_filename(x);
+
+ /* Do the same magic what we do with key agreement (see silcpurple_buddy.c)
+ to see if we are behind NAT. */
+ if (silc_net_check_local_by_sock(xfer->sg->conn->sock->sock,
+ NULL, &local_ip)) {
+ /* Check if the IP is private */
+ if (silcpurple_ip_is_private(local_ip)) {
+ local = FALSE;
+ /* Local IP is private, resolve the remote server IP to see whether
+ we are talking to Internet or just on LAN. */
+ if (silc_net_check_host_by_sock(xfer->sg->conn->sock->sock, NULL,
+ &remote_ip))
+ if (silcpurple_ip_is_private(remote_ip))
+ /* We assume we are in LAN. Let's provide the connection point. */
+ local = TRUE;
+ }
+ }
+
+ if (local && !local_ip)
+ local_ip = silc_net_localip();
+
+ /* Send the file */
+ silc_client_file_send(xfer->sg->client, xfer->sg->conn,
+ silcpurple_ftp_monitor, xfer,
+ local_ip, 0, !local, xfer->client_entry,
+ name, &xfer->session_id);
+
+ silc_free(local_ip);
+ silc_free(remote_ip);
+}
+
+static void
+silcpurple_ftp_send_file_resolved(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ SilcUInt32 clients_count,
+ void *context)
+{
+ PurpleConnection *gc = client->application;
+ char tmp[256];
+
+ if (!clients) {
+ g_snprintf(tmp, sizeof(tmp),
+ _("User %s is not present in the network"),
+ (const char *)context);
+ purple_notify_error(gc, _("Secure File Transfer"),
+ _("Cannot send file"), tmp);
+ silc_free(context);
+ return;
+ }
+
+ silcpurple_ftp_send_file(client->application, (const char *)context, NULL);
+ silc_free(context);
+}
+
+PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcClientEntry *clients;
+ SilcUInt32 clients_count;
+ SilcPurpleXfer xfer;
+ char *nickname;
+
+ g_return_val_if_fail(name != NULL, NULL);
+
+ if (!silc_parse_userfqdn(name, &nickname, NULL))
+ return NULL;
+
+ /* Find client entry */
+ clients = silc_client_get_clients_local(client, conn, nickname, name,
+ &clients_count);
+ if (!clients) {
+ silc_client_get_clients(client, conn, nickname, NULL,
+ silcpurple_ftp_send_file_resolved,
+ strdup(name));
+ silc_free(nickname);
+ return NULL;
+ }
+
+ xfer = silc_calloc(1, sizeof(*xfer));
+
+ g_return_val_if_fail(xfer != NULL, NULL);
+
+ xfer->sg = sg;
+ xfer->client_entry = clients[0];
+ xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_SEND,
+ xfer->client_entry->nickname);
+ if (!xfer->xfer) {
+ silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
+ g_free(xfer->hostname);
+ silc_free(xfer);
+ return NULL;
+ }
+ purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_send);
+ purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied);
+ purple_xfer_set_cancel_send_fnc(xfer->xfer, silcpurple_ftp_send_cancel);
+ xfer->xfer->data = xfer;
+
+ silc_free(clients);
+ silc_free(nickname);
+
+ return xfer->xfer;
+}
+
+void silcpurple_ftp_send_file(PurpleConnection *gc, const char *name, const char *file)
+{
+ PurpleXfer *xfer = silcpurple_ftp_new_xfer(gc, name);
+
+ g_return_if_fail(xfer != NULL);
+
+ /* Choose file to send */
+ if (file)
+ purple_xfer_request_accepted(xfer, file);
+ else
+ purple_xfer_request(xfer);
+}
diff --git a/libpurple/protocols/silc10/ops.c b/libpurple/protocols/silc10/ops.c
new file mode 100644
index 0000000000..c53f65898a
--- /dev/null
+++ b/libpurple/protocols/silc10/ops.c
@@ -0,0 +1,2057 @@
+/*
+
+ silcpurple_ops.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2004 Pekka Riikonen
+
+ 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; version 2 of the License.
+
+ 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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcpurple.h"
+#include "imgstore.h"
+#include "wb.h"
+
+static void
+silc_channel_message(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry sender, SilcChannelEntry channel,
+ SilcMessagePayload payload, SilcChannelPrivateKey key,
+ SilcMessageFlags flags, const unsigned char *message,
+ SilcUInt32 message_len);
+static void
+silc_private_message(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry sender, SilcMessagePayload payload,
+ SilcMessageFlags flags, const unsigned char *message,
+ SilcUInt32 message_len);
+
+/* Message sent to the application by library. `conn' associates the
+ message to a specific connection. `conn', however, may be NULL.
+ The `type' indicates the type of the message sent by the library.
+ The application can for example filter the message according the
+ type. */
+
+static void
+silc_say(SilcClient client, SilcClientConnection conn,
+ SilcClientMessageType type, char *msg, ...)
+{
+ /* Nothing */
+}
+
+#ifdef HAVE_SILCMIME_H
+/* Processes incoming MIME message. Can be private message or channel
+ message. */
+
+static void
+silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry sender, SilcChannelEntry channel,
+ SilcMessagePayload payload, SilcChannelPrivateKey key,
+ SilcMessageFlags flags, SilcMime mime,
+ gboolean recursive)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ const char *type;
+ const unsigned char *data;
+ SilcUInt32 data_len;
+ PurpleMessageFlags cflags = 0;
+ PurpleConversation *convo = NULL;
+
+ if (!mime)
+ return;
+
+ /* Check for fragmented MIME message */
+ if (silc_mime_is_partial(mime)) {
+ if (!sg->mimeass)
+ sg->mimeass = silc_mime_assembler_alloc();
+
+ /* Defragment */
+ mime = silc_mime_assemble(sg->mimeass, mime);
+ if (!mime)
+ /* More fragments to come */
+ return;
+
+ /* Process the complete message */
+ silcpurple_mime_message(client, conn, sender, channel,
+ payload, key, flags, mime, FALSE);
+ return;
+ }
+
+ /* Check for multipart message */
+ if (silc_mime_is_multipart(mime)) {
+ SilcMime p;
+ const char *mtype;
+ SilcDList parts = silc_mime_get_multiparts(mime, &mtype);
+
+ /* Only "mixed" type supported */
+ if (strcmp(mtype, "mixed"))
+ goto out;
+
+ silc_dlist_start(parts);
+ while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
+ /* Recursively process parts */
+ silcpurple_mime_message(client, conn, sender, channel,
+ payload, key, flags, p, TRUE);
+ }
+ goto out;
+ }
+
+ /* Get content type and MIME data */
+ type = silc_mime_get_field(mime, "Content-Type");
+ if (!type)
+ goto out;
+ data = silc_mime_get_data(mime, &data_len);
+ if (!data)
+ goto out;
+
+ /* Process according to content type */
+
+ /* Plain text */
+ if (strstr(type, "text/plain")) {
+ /* Default is UTF-8, don't check for other charsets */
+ if (!strstr(type, "utf-8"))
+ goto out;
+
+ if (channel)
+ silc_channel_message(client, conn, sender, channel,
+ payload, key,
+ SILC_MESSAGE_FLAG_UTF8, data,
+ data_len);
+ else
+ silc_private_message(client, conn, sender, payload,
+ SILC_MESSAGE_FLAG_UTF8, data,
+ data_len);
+ goto out;
+ }
+
+ /* Image */
+ if (strstr(type, "image/png") ||
+ strstr(type, "image/jpeg") ||
+ strstr(type, "image/gif") ||
+ strstr(type, "image/tiff")) {
+ char tmp[32];
+ int imgid;
+
+ /* Get channel convo (if message is for channel) */
+ if (key && channel) {
+ GList *l;
+ SilcPurplePrvgrp prv;
+
+ for (l = sg->grps; l; l = l->next)
+ if (((SilcPurplePrvgrp)l->data)->key == key) {
+ prv = l->data;
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ prv->channel, sg->account);
+ break;
+ }
+ }
+ if (channel && !convo)
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (channel && !convo)
+ goto out;
+
+ imgid = purple_imgstore_add_with_id(g_memdup(data, data_len), data_len, "");
+ if (imgid) {
+ cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
+ g_snprintf(tmp, sizeof(tmp), "<IMG ID=\"%d\">", imgid);
+
+ if (channel)
+ serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
+ sender->nickname ?
+ sender->nickname :
+ "<unknown>", cflags,
+ tmp, time(NULL));
+ else
+ serv_got_im(gc, sender->nickname ?
+ sender->nickname : "<unknown>",
+ tmp, cflags, time(NULL));
+
+ purple_imgstore_unref_by_id(imgid);
+ cflags = 0;
+ }
+ goto out;
+ }
+
+ /* Whiteboard message */
+ if (strstr(type, "application/x-wb") &&
+ !purple_account_get_bool(sg->account, "block-wb", FALSE)) {
+ if (channel)
+ silcpurple_wb_receive_ch(client, conn, sender, channel,
+ payload, flags, data, data_len);
+ else
+ silcpurple_wb_receive(client, conn, sender, payload,
+ flags, data, data_len);
+ goto out;
+ }
+
+ out:
+ if (!recursive)
+ silc_mime_free(mime);
+}
+#endif /* HAVE_SILCMIME_H */
+
+/* Message for a channel. The `sender' is the sender of the message
+ The `channel' is the channel. The `message' is the message. Note
+ that `message' maybe NULL. The `flags' indicates message flags
+ and it is used to determine how the message can be interpreted
+ (like it may tell the message is multimedia message). */
+
+static void
+silc_channel_message(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry sender, SilcChannelEntry channel,
+ SilcMessagePayload payload, SilcChannelPrivateKey key,
+ SilcMessageFlags flags, const unsigned char *message,
+ SilcUInt32 message_len)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ PurpleConversation *convo = NULL;
+ char *msg, *tmp;
+
+ if (!message)
+ return;
+
+ if (key) {
+ GList *l;
+ SilcPurplePrvgrp prv;
+
+ for (l = sg->grps; l; l = l->next)
+ if (((SilcPurplePrvgrp)l->data)->key == key) {
+ prv = l->data;
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ prv->channel, sg->account);
+ break;
+ }
+ }
+ if (!convo)
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (!convo)
+ return;
+
+ if (flags & SILC_MESSAGE_FLAG_SIGNED &&
+ purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
+ /* XXX */
+ }
+
+ if (flags & SILC_MESSAGE_FLAG_DATA) {
+ /* Process MIME message */
+#ifdef HAVE_SILCMIME_H
+ SilcMime mime;
+ mime = silc_mime_decode(message, message_len);
+ silcpurple_mime_message(client, conn, sender, channel, payload,
+ key, flags, mime, FALSE);
+#else
+ char type[128], enc[128];
+ unsigned char *data;
+ SilcUInt32 data_len;
+
+ memset(type, 0, sizeof(type));
+ memset(enc, 0, sizeof(enc));
+
+ if (!silc_mime_parse(message, message_len, NULL, 0,
+ type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
+ &data_len))
+ return;
+
+ if (!strcmp(type, "application/x-wb") &&
+ !strcmp(enc, "binary") &&
+ !purple_account_get_bool(sg->account, "block-wb", FALSE))
+ silcpurple_wb_receive_ch(client, conn, sender, channel,
+ payload, flags, data, data_len);
+#endif
+ return;
+ }
+
+ if (flags & SILC_MESSAGE_FLAG_ACTION) {
+ msg = g_strdup_printf("/me %s",
+ (const char *)message);
+ if (!msg)
+ return;
+
+ tmp = g_markup_escape_text(msg, -1);
+ /* Send to Purple */
+ serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
+ sender->nickname ?
+ sender->nickname : "<unknown>", 0,
+ tmp, time(NULL));
+ g_free(tmp);
+ g_free(msg);
+ return;
+ }
+
+ if (flags & SILC_MESSAGE_FLAG_NOTICE) {
+ msg = g_strdup_printf("(notice) <I>%s</I> %s",
+ sender->nickname ?
+ sender->nickname : "<unknown>",
+ (const char *)message);
+ if (!msg)
+ return;
+
+ /* Send to Purple */
+ purple_conversation_write(convo, NULL, (const char *)msg,
+ PURPLE_MESSAGE_SYSTEM, time(NULL));
+ g_free(msg);
+ return;
+ }
+
+ if (flags & SILC_MESSAGE_FLAG_UTF8) {
+ tmp = g_markup_escape_text((const char *)message, -1);
+ /* Send to Purple */
+ serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
+ sender->nickname ?
+ sender->nickname : "<unknown>", 0,
+ tmp, time(NULL));
+ g_free(tmp);
+ }
+}
+
+
+/* Private message to the client. The `sender' is the sender of the
+ message. The message is `message'and maybe NULL. The `flags'
+ indicates message flags and it is used to determine how the message
+ can be interpreted (like it may tell the message is multimedia
+ message). */
+
+static void
+silc_private_message(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry sender, SilcMessagePayload payload,
+ SilcMessageFlags flags, const unsigned char *message,
+ SilcUInt32 message_len)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ PurpleConversation *convo = NULL;
+ char *msg, *tmp;
+
+ if (!message)
+ return;
+
+ if (sender->nickname)
+ /* XXX - Should this be PURPLE_CONV_TYPE_IM? */
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
+ sender->nickname, sg->account);
+
+ if (flags & SILC_MESSAGE_FLAG_SIGNED &&
+ purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
+ /* XXX */
+ }
+
+ if (flags & SILC_MESSAGE_FLAG_DATA) {
+#ifdef HAVE_SILCMIME_H
+ /* Process MIME message */
+ SilcMime mime;
+ mime = silc_mime_decode(message, message_len);
+ silcpurple_mime_message(client, conn, sender, NULL, payload,
+ NULL, flags, mime, FALSE);
+#else
+ char type[128], enc[128];
+ unsigned char *data;
+ SilcUInt32 data_len;
+
+ memset(type, 0, sizeof(type));
+ memset(enc, 0, sizeof(enc));
+
+ if (!silc_mime_parse(message, message_len, NULL, 0,
+ type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
+ &data_len))
+ return;
+
+ if (!strcmp(type, "application/x-wb") &&
+ !strcmp(enc, "binary") &&
+ !purple_account_get_bool(sg->account, "block-wb", FALSE))
+ silcpurple_wb_receive(client, conn, sender, payload,
+ flags, data, data_len);
+#endif
+ return;
+ }
+
+ if (flags & SILC_MESSAGE_FLAG_ACTION && convo) {
+ msg = g_strdup_printf("/me %s",
+ (const char *)message);
+ if (!msg)
+ return;
+
+ tmp = g_markup_escape_text(msg, -1);
+ /* Send to Purple */
+ serv_got_im(gc, sender->nickname ?
+ sender->nickname : "<unknown>",
+ tmp, 0, time(NULL));
+ g_free(msg);
+ g_free(tmp);
+ return;
+ }
+
+ if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) {
+ msg = g_strdup_printf("(notice) <I>%s</I> %s",
+ sender->nickname ?
+ sender->nickname : "<unknown>",
+ (const char *)message);
+ if (!msg)
+ return;
+
+ /* Send to Purple */
+ purple_conversation_write(convo, NULL, (const char *)msg,
+ PURPLE_MESSAGE_SYSTEM, time(NULL));
+ g_free(msg);
+ return;
+ }
+
+ if (flags & SILC_MESSAGE_FLAG_UTF8) {
+ tmp = g_markup_escape_text((const char *)message, -1);
+ /* Send to Purple */
+ serv_got_im(gc, sender->nickname ?
+ sender->nickname : "<unknown>",
+ tmp, 0, time(NULL));
+ g_free(tmp);
+ }
+}
+
+
+/* Notify message to the client. The notify arguments are sent in the
+ same order as servers sends them. The arguments are same as received
+ from the server except for ID's. If ID is received application receives
+ the corresponding entry to the ID. For example, if Client ID is received
+ application receives SilcClientEntry. Also, if the notify type is
+ for channel the channel entry is sent to application (even if server
+ does not send it because client library gets the channel entry from
+ the Channel ID in the packet's header). */
+
+static void
+silc_notify(SilcClient client, SilcClientConnection conn,
+ SilcNotifyType type, ...)
+{
+ va_list va;
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ PurpleConversation *convo;
+ SilcClientEntry client_entry, client_entry2;
+ SilcChannelEntry channel;
+ SilcServerEntry server_entry;
+ SilcIdType idtype;
+ void *entry;
+ SilcUInt32 mode;
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+ char buf[512], buf2[512], *tmp, *name;
+ SilcNotifyType notify;
+ PurpleBuddy *b;
+ int i;
+
+ va_start(va, type);
+ memset(buf, 0, sizeof(buf));
+
+ switch (type) {
+
+ case SILC_NOTIFY_TYPE_NONE:
+ break;
+
+ case SILC_NOTIFY_TYPE_INVITE:
+ {
+ GHashTable *components;
+ va_arg(va, SilcChannelEntry);
+ name = va_arg(va, char *);
+ client_entry = va_arg(va, SilcClientEntry);
+
+ components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ g_hash_table_insert(components, strdup("channel"), strdup(name));
+ serv_got_chat_invite(gc, name, client_entry->nickname, NULL, components);
+ }
+ break;
+
+ case SILC_NOTIFY_TYPE_JOIN:
+ client_entry = va_arg(va, SilcClientEntry);
+ channel = va_arg(va, SilcChannelEntry);
+
+ /* If we joined channel, do nothing */
+ if (client_entry == conn->local_entry)
+ break;
+
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (!convo)
+ break;
+
+ /* Join user to channel */
+ g_snprintf(buf, sizeof(buf), "%s@%s",
+ client_entry->username, client_entry->hostname);
+ purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo),
+ g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE);
+
+ break;
+
+ case SILC_NOTIFY_TYPE_LEAVE:
+ client_entry = va_arg(va, SilcClientEntry);
+ channel = va_arg(va, SilcChannelEntry);
+
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (!convo)
+ break;
+
+ /* Remove user from channel */
+ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ client_entry->nickname, NULL);
+
+ break;
+
+ case SILC_NOTIFY_TYPE_SIGNOFF:
+ client_entry = va_arg(va, SilcClientEntry);
+ tmp = va_arg(va, char *);
+
+ if (!client_entry->nickname)
+ break;
+
+ /* Remove from all channels */
+ silc_hash_table_list(client_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chu->channel->channel_name, sg->account);
+ if (!convo)
+ continue;
+ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ client_entry->nickname,
+ tmp);
+ }
+ silc_hash_table_list_reset(&htl);
+
+ break;
+
+ case SILC_NOTIFY_TYPE_TOPIC_SET:
+ {
+ char *esc, *tmp2;
+ idtype = va_arg(va, int);
+ entry = va_arg(va, void *);
+ tmp = va_arg(va, char *);
+ channel = va_arg(va, SilcChannelEntry);
+
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (!convo)
+ break;
+
+ if (!tmp)
+ break;
+
+ esc = g_markup_escape_text(tmp, -1);
+ tmp2 = purple_markup_linkify(esc);
+ g_free(esc);
+
+ if (idtype == SILC_ID_CLIENT) {
+ client_entry = (SilcClientEntry)entry;
+ g_snprintf(buf, sizeof(buf),
+ _("%s has changed the topic of <I>%s</I> to: %s"),
+ client_entry->nickname, channel->channel_name, tmp2);
+ purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
+ client_entry->nickname, tmp);
+ } else if (idtype == SILC_ID_SERVER) {
+ server_entry = (SilcServerEntry)entry;
+ g_snprintf(buf, sizeof(buf),
+ _("%s has changed the topic of <I>%s</I> to: %s"),
+ server_entry->server_name, channel->channel_name, tmp2);
+ purple_conv_chat_write(PURPLE_CONV_CHAT(convo), server_entry->server_name,
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
+ server_entry->server_name, tmp);
+ } else if (idtype == SILC_ID_CHANNEL) {
+ channel = (SilcChannelEntry)entry;
+ g_snprintf(buf, sizeof(buf),
+ _("%s has changed the topic of <I>%s</I> to: %s"),
+ channel->channel_name, channel->channel_name, tmp2);
+ purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
+ channel->channel_name, tmp);
+ } else {
+ purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, tmp);
+ }
+
+ g_free(tmp2);
+
+ break;
+
+ }
+ case SILC_NOTIFY_TYPE_NICK_CHANGE:
+ client_entry = va_arg(va, SilcClientEntry);
+ client_entry2 = va_arg(va, SilcClientEntry);
+
+ if (!strcmp(client_entry->nickname, client_entry2->nickname))
+ break;
+
+ /* Change nick on all channels */
+ silc_hash_table_list(client_entry2->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chu->channel->channel_name, sg->account);
+ if (!convo)
+ continue;
+ if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname))
+ purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
+ client_entry->nickname,
+ client_entry2->nickname);
+ }
+ silc_hash_table_list_reset(&htl);
+
+ break;
+
+ case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+ idtype = va_arg(va, int);
+ entry = va_arg(va, void *);
+ mode = va_arg(va, SilcUInt32);
+ (void)va_arg(va, char *);
+ (void)va_arg(va, char *);
+ (void)va_arg(va, char *);
+ (void)va_arg(va, SilcPublicKey);
+ (void)va_arg(va, SilcBuffer);
+ channel = va_arg(va, SilcChannelEntry);
+
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (!convo)
+ break;
+
+ if (idtype == SILC_ID_CLIENT)
+ name = ((SilcClientEntry)entry)->nickname;
+ else if (idtype == SILC_ID_SERVER)
+ name = ((SilcServerEntry)entry)->server_name;
+ else
+ name = ((SilcChannelEntry)entry)->channel_name;
+ if (!name)
+ break;
+
+ if (mode) {
+ silcpurple_get_chmode_string(mode, buf2, sizeof(buf2));
+ g_snprintf(buf, sizeof(buf),
+ _("<I>%s</I> set channel <I>%s</I> modes to: %s"), name,
+ channel->channel_name, buf2);
+ } else {
+ g_snprintf(buf, sizeof(buf),
+ _("<I>%s</I> removed all channel <I>%s</I> modes"), name,
+ channel->channel_name);
+ }
+ purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ break;
+
+ case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+ {
+ PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
+ idtype = va_arg(va, int);
+ entry = va_arg(va, void *);
+ mode = va_arg(va, SilcUInt32);
+ client_entry2 = va_arg(va, SilcClientEntry);
+ channel = va_arg(va, SilcChannelEntry);
+
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (!convo)
+ break;
+
+ if (idtype == SILC_ID_CLIENT)
+ name = ((SilcClientEntry)entry)->nickname;
+ else if (idtype == SILC_ID_SERVER)
+ name = ((SilcServerEntry)entry)->server_name;
+ else
+ name = ((SilcChannelEntry)entry)->channel_name;
+ if (!name)
+ break;
+
+ if (mode) {
+ silcpurple_get_chumode_string(mode, buf2, sizeof(buf2));
+ g_snprintf(buf, sizeof(buf),
+ _("<I>%s</I> set <I>%s's</I> modes to: %s"), name,
+ client_entry2->nickname, buf2);
+ if (mode & SILC_CHANNEL_UMODE_CHANFO)
+ flags |= PURPLE_CBFLAGS_FOUNDER;
+ if (mode & SILC_CHANNEL_UMODE_CHANOP)
+ flags |= PURPLE_CBFLAGS_OP;
+ } else {
+ g_snprintf(buf, sizeof(buf),
+ _("<I>%s</I> removed all <I>%s's</I> modes"), name,
+ client_entry2->nickname);
+ }
+ purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags);
+ break;
+ }
+
+ case SILC_NOTIFY_TYPE_MOTD:
+ tmp = va_arg(va, char *);
+ silc_free(sg->motd);
+ sg->motd = silc_memdup(tmp, strlen(tmp));
+ break;
+
+ case SILC_NOTIFY_TYPE_KICKED:
+ client_entry = va_arg(va, SilcClientEntry);
+ tmp = va_arg(va, char *);
+ client_entry2 = va_arg(va, SilcClientEntry);
+ channel = va_arg(va, SilcChannelEntry);
+
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (!convo)
+ break;
+
+ if (client_entry == conn->local_entry) {
+ /* Remove us from channel */
+ g_snprintf(buf, sizeof(buf),
+ _("You have been kicked off <I>%s</I> by <I>%s</I> (%s)"),
+ channel->channel_name, client_entry2->nickname,
+ tmp ? tmp : "");
+ purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
+ } else {
+ /* Remove user from channel */
+ g_snprintf(buf, sizeof(buf), _("Kicked by %s (%s)"),
+ client_entry2->nickname, tmp ? tmp : "");
+ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ client_entry->nickname,
+ buf);
+ }
+
+ break;
+
+ case SILC_NOTIFY_TYPE_KILLED:
+ client_entry = va_arg(va, SilcClientEntry);
+ tmp = va_arg(va, char *);
+ idtype = va_arg(va, int);
+ entry = va_arg(va, SilcClientEntry);
+
+ if (!client_entry->nickname)
+ break;
+
+ if (client_entry == conn->local_entry) {
+ if (idtype == SILC_ID_CLIENT) {
+ client_entry2 = (SilcClientEntry)entry;
+ g_snprintf(buf, sizeof(buf),
+ _("You have been killed by %s (%s)"),
+ client_entry2->nickname, tmp ? tmp : "");
+ } else if (idtype == SILC_ID_SERVER) {
+ server_entry = (SilcServerEntry)entry;
+ g_snprintf(buf, sizeof(buf),
+ _("You have been killed by %s (%s)"),
+ server_entry->server_name, tmp ? tmp : "");
+ } else if (idtype == SILC_ID_CHANNEL) {
+ channel = (SilcChannelEntry)entry;
+ g_snprintf(buf, sizeof(buf),
+ _("You have been killed by %s (%s)"),
+ channel->channel_name, tmp ? tmp : "");
+ }
+
+ /* Remove us from all channels */
+ silc_hash_table_list(client_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chu->channel->channel_name, sg->account);
+ if (!convo)
+ continue;
+ purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
+ buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
+ }
+ silc_hash_table_list_reset(&htl);
+
+ } else {
+ if (idtype == SILC_ID_CLIENT) {
+ client_entry2 = (SilcClientEntry)entry;
+ g_snprintf(buf, sizeof(buf),
+ _("Killed by %s (%s)"),
+ client_entry2->nickname, tmp ? tmp : "");
+ } else if (idtype == SILC_ID_SERVER) {
+ server_entry = (SilcServerEntry)entry;
+ g_snprintf(buf, sizeof(buf),
+ _("Killed by %s (%s)"),
+ server_entry->server_name, tmp ? tmp : "");
+ } else if (idtype == SILC_ID_CHANNEL) {
+ channel = (SilcChannelEntry)entry;
+ g_snprintf(buf, sizeof(buf),
+ _("Killed by %s (%s)"),
+ channel->channel_name, tmp ? tmp : "");
+ }
+
+ /* Remove user from all channels */
+ silc_hash_table_list(client_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chu->channel->channel_name, sg->account);
+ if (!convo)
+ continue;
+ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ client_entry->nickname, tmp);
+ }
+ silc_hash_table_list_reset(&htl);
+ }
+
+ break;
+
+ case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+ break;
+
+ case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+ {
+ int i;
+ SilcClientEntry *clients;
+ SilcUInt32 clients_count;
+
+ (void)va_arg(va, void *);
+ clients = va_arg(va, SilcClientEntry *);
+ clients_count = va_arg(va, SilcUInt32);
+
+ for (i = 0; i < clients_count; i++) {
+ if (!clients[i]->nickname)
+ break;
+
+ /* Remove from all channels */
+ silc_hash_table_list(clients[i]->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ convo =
+ purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chu->channel->channel_name, sg->account);
+ if (!convo)
+ continue;
+ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+ clients[i]->nickname,
+ _("Server signoff"));
+ }
+ silc_hash_table_list_reset(&htl);
+ }
+ }
+ break;
+
+ case SILC_NOTIFY_TYPE_ERROR:
+ {
+ SilcStatus error = va_arg(va, int);
+ purple_notify_error(gc, "Error Notify",
+ silc_get_status_message(error),
+ NULL);
+ }
+ break;
+
+ case SILC_NOTIFY_TYPE_WATCH:
+ {
+ SilcPublicKey public_key;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+ char *fingerprint;
+
+ client_entry = va_arg(va, SilcClientEntry);
+ (void)va_arg(va, char *);
+ mode = va_arg(va, SilcUInt32);
+ notify = va_arg(va, int);
+ public_key = va_arg(va, SilcPublicKey);
+
+ b = NULL;
+ if (public_key) {
+ PurpleBlistNode *gnode, *cnode, *bnode;
+ const char *f;
+
+ pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+ if (!pk)
+ break;
+ fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+ for (i = 0; i < strlen(fingerprint); i++)
+ if (fingerprint[i] == ' ')
+ fingerprint[i] = '_';
+ g_snprintf(buf, sizeof(buf) - 1,
+ "%s" G_DIR_SEPARATOR_S "clientkeys"
+ G_DIR_SEPARATOR_S "clientkey_%s.pub",
+ silcpurple_silcdir(), fingerprint);
+ silc_free(fingerprint);
+ silc_free(pk);
+
+ /* Find buddy by associated public key */
+ for (gnode = purple_get_blist()->root; gnode;
+ gnode = gnode->next) {
+ if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ continue;
+ for (cnode = gnode->child; cnode; cnode = cnode->next) {
+ if( !PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+ continue;
+ for (bnode = cnode->child; bnode;
+ bnode = bnode->next) {
+ if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ continue;
+ b = (PurpleBuddy *)bnode;
+ if (b->account != gc->account)
+ continue;
+ f = purple_blist_node_get_string(bnode, "public-key");
+ if (f && !strcmp(f, buf))
+ goto cont;
+ b = NULL;
+ }
+ }
+ }
+ }
+ cont:
+ if (!b) {
+ /* Find buddy by nickname */
+ b = purple_find_buddy(sg->account, client_entry->nickname);
+ if (!b) {
+ purple_debug_warning("silc", "WATCH for %s, unknown buddy",
+ client_entry->nickname);
+ break;
+ }
+ }
+
+ silc_free(b->proto_data);
+ b->proto_data = silc_memdup(client_entry->id,
+ sizeof(*client_entry->id));
+ if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
+ break;
+ } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
+ /* See if client was away and is now present */
+ if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
+ SILC_UMODE_BUSY | SILC_UMODE_PAGE |
+ SILC_UMODE_DETACHED)) &&
+ (client_entry->mode & SILC_UMODE_GONE ||
+ client_entry->mode & SILC_UMODE_INDISPOSED ||
+ client_entry->mode & SILC_UMODE_BUSY ||
+ client_entry->mode & SILC_UMODE_PAGE ||
+ client_entry->mode & SILC_UMODE_DETACHED)) {
+ client_entry->mode = mode;
+ purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
+ }
+ else if ((mode & SILC_UMODE_GONE) ||
+ (mode & SILC_UMODE_INDISPOSED) ||
+ (mode & SILC_UMODE_BUSY) ||
+ (mode & SILC_UMODE_PAGE) ||
+ (mode & SILC_UMODE_DETACHED)) {
+ client_entry->mode = mode;
+ purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
+ }
+ } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
+ notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
+ notify == SILC_NOTIFY_TYPE_KILLED) {
+ client_entry->mode = mode;
+ purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
+ } else if (notify == SILC_NOTIFY_TYPE_NONE) {
+ client_entry->mode = mode;
+ purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
+ }
+ }
+ break;
+
+ default:
+ purple_debug_info("silc", "Unhandled notification: %d\n", type);
+ break;
+ }
+
+ va_end(va);
+}
+
+
+/* Command handler. This function is called always in the command function.
+ If error occurs it will be called as well. `conn' is the associated
+ client connection. `cmd_context' is the command context that was
+ originally sent to the command. `success' is FALSE if error occurred
+ during command. `command' is the command being processed. It must be
+ noted that this is not reply from server. This is merely called just
+ after application has called the command. Just to tell application
+ that the command really was processed. */
+
+static void
+silc_command(SilcClient client, SilcClientConnection conn,
+ SilcClientCommandContext cmd_context, bool success,
+ SilcCommand command, SilcStatus status)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+
+ switch (command) {
+
+ case SILC_COMMAND_CMODE:
+ if (cmd_context->argc == 3 &&
+ !strcmp((char *)cmd_context->argv[2], "+C"))
+ sg->chpk = TRUE;
+ else
+ sg->chpk = FALSE;
+ break;
+
+ default:
+ break;
+ }
+}
+
+#if 0
+static void
+silcpurple_whois_more(SilcClientEntry client_entry, gint id)
+{
+ SilcAttributePayload attr;
+ SilcAttribute attribute;
+ char *buf;
+ GString *s;
+ SilcVCardStruct vcard;
+ int i;
+
+ if (id != 0)
+ return;
+
+ memset(&vcard, 0, sizeof(vcard));
+
+ s = g_string_new("");
+
+ silc_dlist_start(client_entry->attrs);
+ while ((attr = silc_dlist_get(client_entry->attrs)) != SILC_LIST_END) {
+ attribute = silc_attribute_get_attribute(attr);
+ switch (attribute) {
+
+ case SILC_ATTRIBUTE_USER_INFO:
+ if (!silc_attribute_get_object(attr, (void *)&vcard,
+ sizeof(vcard)))
+ continue;
+ g_string_append_printf(s, "%s:\n\n", _("Personal Information"));
+ if (vcard.full_name)
+ g_string_append_printf(s, "%s:\t\t%s\n",
+ _("Full Name"),
+ vcard.full_name);
+ if (vcard.first_name)
+ g_string_append_printf(s, "%s:\t%s\n",
+ _("First Name"),
+ vcard.first_name);
+ if (vcard.middle_names)
+ g_string_append_printf(s, "%s:\t%s\n",
+ _("Middle Name"),
+ vcard.middle_names);
+ if (vcard.family_name)
+ g_string_append_printf(s, "%s:\t%s\n",
+ _("Family Name"),
+ vcard.family_name);
+ if (vcard.nickname)
+ g_string_append_printf(s, "%s:\t\t%s\n",
+ _("Nickname"),
+ vcard.nickname);
+ if (vcard.bday)
+ g_string_append_printf(s, "%s:\t\t%s\n",
+ _("Birth Day"),
+ vcard.bday);
+ if (vcard.title)
+ g_string_append_printf(s, "%s:\t\t%s\n",
+ _("Job Title"),
+ vcard.title);
+ if (vcard.role)
+ g_string_append_printf(s, "%s:\t\t%s\n",
+ _("Job Role"),
+ vcard.role);
+ if (vcard.org_name)
+ g_string_append_printf(s, "%s:\t%s\n",
+ _("Organization"),
+ vcard.org_name);
+ if (vcard.org_unit)
+ g_string_append_printf(s, "%s:\t\t%s\n",
+ _("Unit"),
+ vcard.org_unit);
+ if (vcard.url)
+ g_string_append_printf(s, "%s:\t%s\n",
+ _("Homepage"),
+ vcard.url);
+ if (vcard.label)
+ g_string_append_printf(s, "%s:\t%s\n",
+ _("Address"),
+ vcard.label);
+ for (i = 0; i < vcard.num_tels; i++) {
+ if (vcard.tels[i].telnum)
+ g_string_append_printf(s, "%s:\t\t\t%s\n",
+ _("Phone"),
+ vcard.tels[i].telnum);
+ }
+ for (i = 0; i < vcard.num_emails; i++) {
+ if (vcard.emails[i].address)
+ g_string_append_printf(s, "%s:\t\t%s\n",
+ _("E-Mail"),
+ vcard.emails[i].address);
+ }
+ if (vcard.note)
+ g_string_append_printf(s, "\n%s:\t\t%s\n",
+ _("Note"),
+ vcard.note);
+ break;
+ }
+ }
+
+ buf = g_string_free(s, FALSE);
+ purple_notify_info(NULL, _("User Information"), _("User Information"),
+ buf);
+ g_free(buf);
+}
+#endif
+
+/* Command reply handler. This function is called always in the command reply
+ function. If error occurs it will be called as well. Normal scenario
+ is that it will be called after the received command data has been parsed
+ and processed. The function is used to pass the received command data to
+ the application.
+
+ `conn' is the associated client connection. `cmd_payload' is the command
+ payload data received from server and it can be ignored. It is provided
+ if the application would like to re-parse the received command data,
+ however, it must be noted that the data is parsed already by the library
+ thus the payload can be ignored. `success' is FALSE if error occurred.
+ In this case arguments are not sent to the application. The `status' is
+ the command reply status server returned. The `command' is the command
+ reply being processed. The function has variable argument list and each
+ command defines the number and type of arguments it passes to the
+ application (on error they are not sent). */
+
+static void
+silc_command_reply(SilcClient client, SilcClientConnection conn,
+ SilcCommandPayload cmd_payload, bool success,
+ SilcCommand command, SilcStatus status, ...)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ PurpleConversation *convo;
+ va_list vp;
+
+ va_start(vp, status);
+
+ switch (command) {
+ case SILC_COMMAND_JOIN:
+ {
+ SilcChannelEntry channel_entry;
+
+ if (!success) {
+ purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"),
+ silc_get_status_message(status));
+ return;
+ }
+
+ (void)va_arg(vp, char *);
+ channel_entry = va_arg(vp, SilcChannelEntry);
+
+ /* Resolve users on channel */
+ silc_client_get_clients_by_channel(client, conn, channel_entry,
+ silcpurple_chat_join_done,
+ channel_entry);
+ }
+ break;
+
+ case SILC_COMMAND_LEAVE:
+ break;
+
+ case SILC_COMMAND_USERS:
+ break;
+
+ case SILC_COMMAND_WHOIS:
+ {
+ SilcUInt32 idle, mode;
+ SilcBuffer channels, user_modes;
+ SilcClientEntry client_entry;
+ char tmp[1024], *tmp2;
+ char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
+ PurpleNotifyUserInfo *user_info;
+
+ if (!success) {
+ purple_notify_error(gc, _("User Information"),
+ _("Cannot get user information"),
+ silc_get_status_message(status));
+ break;
+ }
+
+ client_entry = va_arg(vp, SilcClientEntry);
+ if (!client_entry->nickname)
+ break;
+ (void)va_arg(vp, char *);
+ (void)va_arg(vp, char *);
+ (void)va_arg(vp, char *);
+ channels = va_arg(vp, SilcBuffer);
+ mode = va_arg(vp, SilcUInt32);
+ idle = va_arg(vp, SilcUInt32);
+ (void)va_arg(vp, unsigned char *);
+ user_modes = va_arg(vp, SilcBuffer);
+
+ user_info = purple_notify_user_info_new();
+ tmp2 = g_markup_escape_text(client_entry->nickname, -1);
+ purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp2);
+ g_free(tmp2);
+ if (client_entry->realname) {
+ tmp2 = g_markup_escape_text(client_entry->realname, -1);
+ purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2);
+ g_free(tmp2);
+ }
+ if (client_entry->username) {
+ tmp2 = g_markup_escape_text(client_entry->username, -1);
+ if (client_entry->hostname) {
+ gchar *tmp3;
+ tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname);
+ purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
+ g_free(tmp3);
+ } else
+ purple_notify_user_info_add_pair(user_info, _("Username"), tmp2);
+ g_free(tmp2);
+ }
+
+ if (client_entry->mode) {
+ memset(tmp, 0, sizeof(tmp));
+ silcpurple_get_umode_string(client_entry->mode,
+ tmp, sizeof(tmp) - strlen(tmp));
+ purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
+ }
+
+ silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
+ if (moodstr) {
+ purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
+ g_free(moodstr);
+ }
+
+ if (statusstr) {
+ tmp2 = g_markup_escape_text(statusstr, -1);
+ purple_notify_user_info_add_pair(user_info, _("Status Text"), tmp2);
+ g_free(statusstr);
+ g_free(tmp2);
+ }
+
+ if (contactstr) {
+ purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
+ g_free(contactstr);
+ }
+
+ if (langstr) {
+ purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
+ g_free(langstr);
+ }
+
+ if (devicestr) {
+ purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
+ g_free(devicestr);
+ }
+
+ if (tzstr) {
+ purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
+ g_free(tzstr);
+ }
+
+ if (geostr) {
+ purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
+ g_free(geostr);
+ }
+
+ if (client_entry->server)
+ purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
+
+ if (channels && user_modes) {
+ SilcUInt32 *umodes;
+ SilcDList list =
+ silc_channel_payload_parse_list(channels->data,
+ channels->len);
+ if (list && silc_get_mode_list(user_modes,
+ silc_dlist_count(list),
+ &umodes)) {
+ SilcChannelPayload entry;
+ int i = 0;
+
+ memset(tmp, 0, sizeof(tmp));
+ silc_dlist_start(list);
+ while ((entry = silc_dlist_get(list))
+ != SILC_LIST_END) {
+ SilcUInt32 name_len;
+ char *m = silc_client_chumode_char(umodes[i++]);
+ char *name = (char *)silc_channel_get_name(entry, &name_len);
+ if (m)
+ silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m));
+ silc_strncat(tmp, sizeof(tmp) - 1, name, name_len);
+ silc_strncat(tmp, sizeof(tmp) - 1, " ", 1);
+ silc_free(m);
+
+ }
+ tmp2 = g_markup_escape_text(tmp, -1);
+ purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2);
+ g_free(tmp2);
+ silc_free(umodes);
+ }
+ }
+
+ if (client_entry->public_key) {
+ char *fingerprint, *babbleprint;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+ pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
+ fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+ babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
+ purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
+ purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
+ silc_free(fingerprint);
+ silc_free(babbleprint);
+ silc_free(pk);
+ }
+
+#if 0 /* XXX for now, let's not show attrs here */
+ if (client_entry->attrs)
+ purple_request_action(gc, _("User Information"),
+ _("User Information"),
+ buf, 1, client_entry, 2,
+ _("OK"), G_CALLBACK(silcpurple_whois_more),
+ _("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL);
+ else
+#endif
+ purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL);
+ purple_notify_user_info_destroy(user_info);
+ }
+ break;
+
+ case SILC_COMMAND_WHOWAS:
+ {
+ SilcClientEntry client_entry;
+ char *nickname, *realname, *username, *tmp;
+ PurpleNotifyUserInfo *user_info;
+
+ if (!success) {
+ purple_notify_error(gc, _("User Information"),
+ _("Cannot get user information"),
+ silc_get_status_message(status));
+ break;
+ }
+
+ client_entry = va_arg(vp, SilcClientEntry);
+ nickname = va_arg(vp, char *);
+ username = va_arg(vp, char *);
+ realname = va_arg(vp, char *);
+ if (!nickname)
+ break;
+
+ user_info = purple_notify_user_info_new();
+ tmp = g_markup_escape_text(nickname, -1);
+ purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
+ g_free(tmp);
+ if (realname) {
+ tmp = g_markup_escape_text(realname, -1);
+ purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp);
+ g_free(tmp);
+ }
+ if (username) {
+ tmp = g_markup_escape_text(username, -1);
+ if (client_entry && client_entry->hostname) {
+ gchar *tmp3;
+ tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname);
+ purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
+ g_free(tmp3);
+ } else
+ purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
+ g_free(tmp);
+ }
+ if (client_entry && client_entry->server)
+ purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
+
+
+ if (client_entry && client_entry->public_key) {
+ char *fingerprint, *babbleprint;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+ pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
+ fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+ babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
+ purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
+ purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
+ silc_free(fingerprint);
+ silc_free(babbleprint);
+ silc_free(pk);
+ }
+
+ purple_notify_userinfo(gc, nickname, user_info, NULL, NULL);
+ purple_notify_user_info_destroy(user_info);
+ }
+ break;
+
+ case SILC_COMMAND_DETACH:
+ if (!success) {
+ purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
+ silc_get_status_message(status));
+ return;
+ }
+ break;
+
+ case SILC_COMMAND_TOPIC:
+ {
+ SilcChannelEntry channel;
+
+ if (!success) {
+ purple_notify_error(gc, _("Topic"), _("Cannot set topic"),
+ silc_get_status_message(status));
+ return;
+ }
+
+ channel = va_arg(vp, SilcChannelEntry);
+
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ channel->channel_name, sg->account);
+ if (!convo) {
+ purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
+ channel->channel_name);
+ break;
+ }
+
+ /* Set topic */
+ if (channel->topic)
+ purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic);
+ }
+ break;
+
+ case SILC_COMMAND_NICK:
+ {
+ /* I don't think we should need to do this because the server should
+ * be sending a SILC_NOTIFY_TYPE_NICK_CHANGE when we change our own
+ * nick, but it isn't, so we deal with it here instead. Stu. */
+ SilcClientEntry local_entry;
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+ const char *oldnick;
+
+ if (!success) {
+ purple_notify_error(gc, _("Nick"), _("Failed to change nickname"),
+ silc_get_status_message(status));
+ return;
+ }
+
+ local_entry = va_arg(vp, SilcClientEntry);
+
+ /* Change nick on all channels */
+ silc_hash_table_list(local_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ chu->channel->channel_name, sg->account);
+ if (!convo)
+ continue;
+ oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo));
+ if (strcmp(oldnick, purple_normalize(purple_conversation_get_account(convo), local_entry->nickname))) {
+ purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
+ oldnick, local_entry->nickname);
+ purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), local_entry->nickname);
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ purple_connection_set_display_name(gc, local_entry->nickname);
+ }
+ break;
+
+ case SILC_COMMAND_LIST:
+ {
+ char *topic, *name;
+ int usercount;
+ PurpleRoomlistRoom *room;
+
+ if (sg->roomlist_canceled)
+ break;
+
+ if (!success) {
+ purple_notify_error(gc, _("Error"), _("Error retrieving room list"),
+ silc_get_status_message(status));
+ purple_roomlist_set_in_progress(sg->roomlist, FALSE);
+ purple_roomlist_unref(sg->roomlist);
+ sg->roomlist = NULL;
+ return;
+ }
+
+ (void)va_arg(vp, SilcChannelEntry);
+ name = va_arg(vp, char *);
+ if (!name) {
+ purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"),
+ silc_get_status_message(status));
+ purple_roomlist_set_in_progress(sg->roomlist, FALSE);
+ purple_roomlist_unref(sg->roomlist);
+ sg->roomlist = NULL;
+ return;
+ }
+ topic = va_arg(vp, char *);
+ usercount = va_arg(vp, int);
+
+ room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL);
+ purple_roomlist_room_add_field(sg->roomlist, room, name);
+ purple_roomlist_room_add_field(sg->roomlist, room,
+ SILC_32_TO_PTR(usercount));
+ purple_roomlist_room_add_field(sg->roomlist, room,
+ topic ? topic : "");
+ purple_roomlist_room_add(sg->roomlist, room);
+
+ if (status == SILC_STATUS_LIST_END ||
+ status == SILC_STATUS_OK) {
+ purple_roomlist_set_in_progress(sg->roomlist, FALSE);
+ purple_roomlist_unref(sg->roomlist);
+ sg->roomlist = NULL;
+ }
+ }
+ break;
+
+ case SILC_COMMAND_GETKEY:
+ {
+ SilcPublicKey public_key;
+
+ if (!success) {
+ purple_notify_error(gc, _("Get Public Key"),
+ _("Cannot fetch the public key"),
+ silc_get_status_message(status));
+ return;
+ }
+
+ (void)va_arg(vp, SilcUInt32);
+ (void)va_arg(vp, void *);
+ public_key = va_arg(vp, SilcPublicKey);
+
+ if (!public_key)
+ purple_notify_error(gc, _("Get Public Key"),
+ _("Cannot fetch the public key"),
+ _("No public key was received"));
+ }
+ break;
+
+ case SILC_COMMAND_INFO:
+ {
+
+ char *server_name;
+ char *server_info;
+ char tmp[256];
+
+ if (!success) {
+ purple_notify_error(gc, _("Server Information"),
+ _("Cannot get server information"),
+ silc_get_status_message(status));
+ return;
+ }
+
+ (void)va_arg(vp, SilcServerEntry);
+ server_name = va_arg(vp, char *);
+ server_info = va_arg(vp, char *);
+
+ if (server_name && server_info) {
+ g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s",
+ server_name, server_info);
+ purple_notify_info(gc, NULL, _("Server Information"), tmp);
+ }
+ }
+ break;
+
+ case SILC_COMMAND_STATS:
+ {
+ SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
+ my_router_ops, cell_clients, cell_channels, cell_servers,
+ clients, channels, servers, routers, server_ops, router_ops;
+ SilcUInt32 buffer_length;
+ SilcBufferStruct buf;
+
+ unsigned char *server_stats;
+ char *msg;
+
+ if (!success) {
+ purple_notify_error(gc, _("Server Statistics"),
+ _("Cannot get server statistics"),
+ silc_get_status_message(status));
+ return;
+ }
+
+ server_stats = va_arg(vp, unsigned char *);
+ buffer_length = va_arg(vp, SilcUInt32);
+ if (!server_stats || !buffer_length) {
+ purple_notify_error(gc, _("Server Statistics"),
+ _("No server statistics available"), NULL);
+ break;
+ }
+ silc_buffer_set(&buf, server_stats, buffer_length);
+ silc_buffer_unformat(&buf,
+ SILC_STR_UI_INT(&starttime),
+ SILC_STR_UI_INT(&uptime),
+ SILC_STR_UI_INT(&my_clients),
+ SILC_STR_UI_INT(&my_channels),
+ SILC_STR_UI_INT(&my_server_ops),
+ SILC_STR_UI_INT(&my_router_ops),
+ SILC_STR_UI_INT(&cell_clients),
+ SILC_STR_UI_INT(&cell_channels),
+ SILC_STR_UI_INT(&cell_servers),
+ SILC_STR_UI_INT(&clients),
+ SILC_STR_UI_INT(&channels),
+ SILC_STR_UI_INT(&servers),
+ SILC_STR_UI_INT(&routers),
+ SILC_STR_UI_INT(&server_ops),
+ SILC_STR_UI_INT(&router_ops),
+ SILC_STR_END);
+
+ msg = g_strdup_printf(_("Local server start time: %s\n"
+ "Local server uptime: %s\n"
+ "Local server clients: %d\n"
+ "Local server channels: %d\n"
+ "Local server operators: %d\n"
+ "Local router operators: %d\n"
+ "Local cell clients: %d\n"
+ "Local cell channels: %d\n"
+ "Local cell servers: %d\n"
+ "Total clients: %d\n"
+ "Total channels: %d\n"
+ "Total servers: %d\n"
+ "Total routers: %d\n"
+ "Total server operators: %d\n"
+ "Total router operators: %d\n"),
+ silc_get_time(starttime),
+ purple_str_seconds_to_string((int)uptime),
+ (int)my_clients, (int)my_channels, (int)my_server_ops, (int)my_router_ops,
+ (int)cell_clients, (int)cell_channels, (int)cell_servers,
+ (int)clients, (int)channels, (int)servers, (int)routers,
+ (int)server_ops, (int)router_ops);
+
+ purple_notify_info(gc, NULL,
+ _("Network Statistics"), msg);
+ g_free(msg);
+ }
+ break;
+
+ case SILC_COMMAND_PING:
+ {
+ if (!success) {
+ purple_notify_error(gc, _("Ping"), _("Ping failed"),
+ silc_get_status_message(status));
+ return;
+ }
+
+ purple_notify_info(gc, _("Ping"), _("Ping reply received from server"),
+ NULL);
+ }
+ break;
+
+ case SILC_COMMAND_KILL:
+ if (!success) {
+ purple_notify_error(gc, _("Kill User"),
+ _("Could not kill user"),
+ silc_get_status_message(status));
+ return;
+ }
+ break;
+
+ case SILC_COMMAND_CMODE:
+ {
+ SilcChannelEntry channel_entry;
+ SilcBuffer channel_pubkeys;
+
+ if (!success)
+ return;
+
+ channel_entry = va_arg(vp, SilcChannelEntry);
+ (void)va_arg(vp, SilcUInt32);
+ (void)va_arg(vp, SilcPublicKey);
+ channel_pubkeys = va_arg(vp, SilcBuffer);
+
+ if (sg->chpk)
+ silcpurple_chat_chauth_show(sg, channel_entry, channel_pubkeys);
+ }
+ break;
+
+ default:
+ if (success)
+ purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command);
+ else
+ purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command,
+ silc_get_status_message(status));
+ break;
+ }
+
+ va_end(vp);
+}
+
+
+/* Called to indicate that connection was either successfully established
+ or connecting failed. This is also the first time application receives
+ the SilcClientConnection object which it should save somewhere.
+ If the `success' is FALSE the application must always call the function
+ silc_client_close_connection. */
+
+static void
+silc_connected(SilcClient client, SilcClientConnection conn,
+ SilcClientConnectionStatus status)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg;
+
+ if (gc == NULL) {
+ silc_client_close_connection(client, conn);
+ return;
+ }
+ sg = gc->proto_data;
+
+ switch (status) {
+ case SILC_CLIENT_CONN_SUCCESS:
+ case SILC_CLIENT_CONN_SUCCESS_RESUME:
+ purple_connection_set_state(gc, PURPLE_CONNECTED);
+
+ /* Send the server our buddy list */
+ silcpurple_send_buddylist(gc);
+
+ g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+
+ /* Send any UMODEs configured for account */
+ if (purple_account_get_bool(sg->account, "block-ims", FALSE)) {
+ silc_client_command_call(sg->client, sg->conn, NULL,
+ "UMODE", "+P", NULL);
+ }
+
+ return;
+ break;
+ case SILC_CLIENT_CONN_ERROR:
+ purple_connection_error(gc, _("Error during connecting to SILC Server"));
+ g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+ break;
+
+ case SILC_CLIENT_CONN_ERROR_KE:
+ purple_connection_error(gc, _("Key Exchange failed"));
+ break;
+
+ case SILC_CLIENT_CONN_ERROR_AUTH:
+ purple_connection_error(gc, _("Authentication failed"));
+ break;
+
+ case SILC_CLIENT_CONN_ERROR_RESUME:
+ purple_connection_error(gc,
+ _("Resuming detached session failed. "
+ "Press Reconnect to create new connection."));
+ g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+ break;
+
+ case SILC_CLIENT_CONN_ERROR_TIMEOUT:
+ purple_connection_error(gc, _("Connection Timeout"));
+ break;
+ }
+
+ /* Error */
+ sg->conn = NULL;
+ silc_client_close_connection(client, conn);
+}
+
+
+/* Called to indicate that connection was disconnected to the server.
+ The `status' may tell the reason of the disconnection, and if the
+ `message' is non-NULL it may include the disconnection message
+ received from server. */
+
+static void
+silc_disconnected(SilcClient client, SilcClientConnection conn,
+ SilcStatus status, const char *message)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+
+ if (sg->resuming && !sg->detaching)
+ g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+
+ sg->conn = NULL;
+
+ /* Close the connection */
+ if (!sg->detaching)
+ purple_connection_error(gc, _("Disconnected by server"));
+ else
+ /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
+ purple_account_disconnect(purple_connection_get_account(gc));
+}
+
+
+typedef struct {
+ SilcGetAuthMeth completion;
+ void *context;
+} *SilcPurpleGetAuthMethod;
+
+/* Callback called when we've received the authentication method information
+ from the server after we've requested it. */
+
+static void silc_get_auth_method_callback(SilcClient client,
+ SilcClientConnection conn,
+ SilcAuthMethod auth_meth,
+ void *context)
+{
+ SilcPurpleGetAuthMethod internal = context;
+
+ switch (auth_meth) {
+ case SILC_AUTH_NONE:
+ /* No authentication required. */
+ (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
+ break;
+
+ case SILC_AUTH_PASSWORD:
+ /* By returning NULL here the library will ask the passphrase from us
+ by calling the silc_ask_passphrase. */
+ (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
+ break;
+
+ case SILC_AUTH_PUBLIC_KEY:
+ /* Do not get the authentication data now, the library will generate
+ it using our default key, if we do not provide it here. */
+ (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
+ break;
+ }
+
+ silc_free(internal);
+}
+
+/* Find authentication method and authentication data by hostname and
+ port. The hostname may be IP address as well. When the authentication
+ method has been resolved the `completion' callback with the found
+ authentication method and authentication data is called. The `conn'
+ may be NULL. */
+
+static void
+silc_get_auth_method(SilcClient client, SilcClientConnection conn,
+ char *hostname, SilcUInt16 port,
+ SilcGetAuthMeth completion, void *context)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ SilcPurpleGetAuthMethod internal;
+ const char *password;
+
+ /* Progress */
+ if (sg->resuming)
+ purple_connection_update_progress(gc, _("Resuming session"), 4, 5);
+ else
+ purple_connection_update_progress(gc, _("Authenticating connection"), 4, 5);
+
+ /* Check configuration if we have this connection configured. If we
+ have then return that data immediately, as it's faster way. */
+ if (purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) {
+ completion(TRUE, SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
+ return;
+ }
+ password = purple_connection_get_password(gc);
+ if (password && *password) {
+ completion(TRUE, SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context);
+ return;
+ }
+
+ /* Resolve the authentication method from server, as we may not know it. */
+ internal = silc_calloc(1, sizeof(*internal));
+ if (!internal)
+ return;
+ internal->completion = completion;
+ internal->context = context;
+ silc_client_request_authentication_method(client, conn,
+ silc_get_auth_method_callback,
+ internal);
+}
+
+
+/* Verifies received public key. The `conn_type' indicates which entity
+ (server, client etc.) has sent the public key. If user decides to trust
+ the application may save the key as trusted public key for later
+ use. The `completion' must be called after the public key has been
+ verified. */
+
+static void
+silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+ SilcSocketType conn_type, unsigned char *pk,
+ SilcUInt32 pk_len, SilcSKEPKType pk_type,
+ SilcVerifyPublicKey completion, void *context)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+
+ if (!sg->conn && (conn_type == SILC_SOCKET_TYPE_SERVER ||
+ conn_type == SILC_SOCKET_TYPE_ROUTER)) {
+ /* Progress */
+ if (sg->resuming)
+ purple_connection_update_progress(gc, _("Resuming session"), 3, 5);
+ else
+ purple_connection_update_progress(gc, _("Verifying server public key"),
+ 3, 5);
+ }
+
+ /* Verify public key */
+ silcpurple_verify_public_key(client, conn, NULL, conn_type, pk,
+ pk_len, pk_type, completion, context);
+}
+
+typedef struct {
+ SilcAskPassphrase completion;
+ void *context;
+} *SilcPurpleAskPassphrase;
+
+static void
+silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase)
+{
+ if (!passphrase || !(*passphrase))
+ internal->completion(NULL, 0, internal->context);
+ else
+ internal->completion((unsigned char *)passphrase,
+ strlen(passphrase), internal->context);
+ silc_free(internal);
+}
+
+/* Ask (interact, that is) a passphrase from user. The passphrase is
+ returned to the library by calling the `completion' callback with
+ the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
+ if not then the library will attempt to encode. */
+
+static void
+silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+ SilcAskPassphrase completion, void *context)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurpleAskPassphrase internal = silc_calloc(1, sizeof(*internal));
+
+ if (!internal)
+ return;
+ internal->completion = completion;
+ internal->context = context;
+ purple_request_input(gc, _("Passphrase"), NULL,
+ _("Passphrase required"), NULL, FALSE, TRUE, NULL,
+ _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
+ _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
+ purple_connection_get_account(gc), NULL, NULL, internal);
+}
+
+
+/* Notifies application that failure packet was received. This is called
+ if there is some protocol active in the client. The `protocol' is the
+ protocol context. The `failure' is opaque pointer to the failure
+ indication. Note, that the `failure' is protocol dependant and
+ application must explicitly cast it to correct type. Usually `failure'
+ is 32 bit failure type (see protocol specs for all protocol failure
+ types). */
+
+static void
+silc_failure(SilcClient client, SilcClientConnection conn,
+ SilcProtocol protocol, void *failure)
+{
+ PurpleConnection *gc = client->application;
+ char buf[128];
+
+ memset(buf, 0, sizeof(buf));
+
+ if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
+ SilcSKEStatus status = (SilcSKEStatus)SILC_PTR_TO_32(failure);
+
+ if (status == SILC_SKE_STATUS_BAD_VERSION)
+ g_snprintf(buf, sizeof(buf),
+ _("Failure: Version mismatch, upgrade your client"));
+ if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
+ g_snprintf(buf, sizeof(buf),
+ _("Failure: Remote does not trust/support your public key"));
+ if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
+ g_snprintf(buf, sizeof(buf),
+ _("Failure: Remote does not support proposed KE group"));
+ if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
+ g_snprintf(buf, sizeof(buf),
+ _("Failure: Remote does not support proposed cipher"));
+ if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
+ g_snprintf(buf, sizeof(buf),
+ _("Failure: Remote does not support proposed PKCS"));
+ if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
+ g_snprintf(buf, sizeof(buf),
+ _("Failure: Remote does not support proposed hash function"));
+ if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
+ g_snprintf(buf, sizeof(buf),
+ _("Failure: Remote does not support proposed HMAC"));
+ if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
+ g_snprintf(buf, sizeof(buf), _("Failure: Incorrect signature"));
+ if (status == SILC_SKE_STATUS_INVALID_COOKIE)
+ g_snprintf(buf, sizeof(buf), _("Failure: Invalid cookie"));
+
+ /* Show the error on the progress bar. A more generic error message
+ is going to be showed to user after this in the silc_connected. */
+ purple_connection_update_progress(gc, buf, 2, 5);
+ }
+
+ if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
+ SilcUInt32 err = SILC_PTR_TO_32(failure);
+
+ if (err == SILC_AUTH_FAILED)
+ g_snprintf(buf, sizeof(buf), _("Failure: Authentication failed"));
+
+ /* Show the error on the progress bar. A more generic error message
+ is going to be showed to user after this in the silc_connected. */
+ purple_connection_update_progress(gc, buf, 4, 5);
+ }
+}
+
+/* Asks whether the user would like to perform the key agreement protocol.
+ This is called after we have received an key agreement packet or an
+ reply to our key agreement packet. This returns TRUE if the user wants
+ the library to perform the key agreement protocol and FALSE if it is not
+ desired (application may start it later by calling the function
+ silc_client_perform_key_agreement). If TRUE is returned also the
+ `completion' and `context' arguments must be set by the application. */
+
+static bool
+silc_key_agreement(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry client_entry, const char *hostname,
+ SilcUInt16 port, SilcKeyAgreementCallback *completion,
+ void **context)
+{
+ silcpurple_buddy_keyagr_request(client, conn, client_entry, hostname, port);
+ *completion = NULL;
+ *context = NULL;
+ return FALSE;
+}
+
+
+/* Notifies application that file transfer protocol session is being
+ requested by the remote client indicated by the `client_entry' from
+ the `hostname' and `port'. The `session_id' is the file transfer
+ session and it can be used to either accept or reject the file
+ transfer request, by calling the silc_client_file_receive or
+ silc_client_file_close, respectively. */
+
+static void
+silc_ftp(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry client_entry, SilcUInt32 session_id,
+ const char *hostname, SilcUInt16 port)
+{
+ silcpurple_ftp_request(client, conn, client_entry, session_id,
+ hostname, port);
+}
+
+
+/* Delivers SILC session detachment data indicated by `detach_data' to the
+ application. If application has issued SILC_COMMAND_DETACH command
+ the client session in the SILC network is not quit. The client remains
+ in the network but is detached. The detachment data may be used later
+ to resume the session in the SILC Network. The appliation is
+ responsible of saving the `detach_data', to for example in a file.
+
+ The detachment data can be given as argument to the functions
+ silc_client_connect_to_server, or silc_client_add_connection when
+ creating connection to remote server, inside SilcClientConnectionParams
+ structure. If it is provided the client library will attempt to resume
+ the session in the network. After the connection is created
+ successfully, the application is responsible of setting the user
+ interface for user into the same state it was before detaching (showing
+ same channels, channel modes, etc). It can do this by fetching the
+ information (like joined channels) from the client library. */
+
+static void
+silc_detach(SilcClient client, SilcClientConnection conn,
+ const unsigned char *detach_data, SilcUInt32 detach_data_len)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ const char *file;
+
+ /* Save the detachment data to file. */
+ file = silcpurple_session_file(purple_account_get_username(sg->account));
+ g_unlink(file);
+ silc_file_writefile(file, (char *)detach_data, detach_data_len);
+}
+
+SilcClientOperations ops = {
+ silc_say,
+ silc_channel_message,
+ silc_private_message,
+ silc_notify,
+ silc_command,
+ silc_command_reply,
+ silc_connected,
+ silc_disconnected,
+ silc_get_auth_method,
+ silc_verify_public_key,
+ silc_ask_passphrase,
+ silc_failure,
+ silc_key_agreement,
+ silc_ftp,
+ silc_detach
+};
diff --git a/libpurple/protocols/silc10/pk.c b/libpurple/protocols/silc10/pk.c
new file mode 100644
index 0000000000..0bd399d6d5
--- /dev/null
+++ b/libpurple/protocols/silc10/pk.c
@@ -0,0 +1,274 @@
+/*
+
+ silcpurple_pk.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2004 Pekka Riikonen
+
+ 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; version 2 of the License.
+
+ 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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcpurple.h"
+
+/************************* Public Key Verification ***************************/
+
+typedef struct {
+ SilcClient client;
+ SilcClientConnection conn;
+ char *filename;
+ char *entity;
+ char *entity_name;
+ char *fingerprint;
+ char *babbleprint;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+ SilcSKEPKType pk_type;
+ SilcVerifyPublicKey completion;
+ void *context;
+ gboolean changed;
+} *PublicKeyVerify;
+
+static void silcpurple_verify_ask(const char *entity,
+ const char *fingerprint,
+ const char *babbleprint,
+ PublicKeyVerify verify);
+
+static void silcpurple_verify_cb(PublicKeyVerify verify, gint id)
+{
+ if (id != 2) {
+ if (verify->completion)
+ verify->completion(FALSE, verify->context);
+ } else {
+ if (verify->completion)
+ verify->completion(TRUE, verify->context);
+
+ /* Save the key for future checking */
+ silc_pkcs_save_public_key_data(verify->filename, verify->pk,
+ verify->pk_len, SILC_PKCS_FILE_PEM);
+ }
+
+ silc_free(verify->filename);
+ silc_free(verify->entity);
+ silc_free(verify->entity_name);
+ silc_free(verify->fingerprint);
+ silc_free(verify->babbleprint);
+ silc_free(verify->pk);
+ silc_free(verify);
+}
+
+static void silcpurple_verify_details_cb(PublicKeyVerify verify)
+{
+ /* What a hack. We have to display the accept dialog _again_
+ because Purple closes the dialog after you press the button. Purple
+ should have option for the dialogs whether the buttons close them
+ or not. */
+ silcpurple_verify_ask(verify->entity, verify->fingerprint,
+ verify->babbleprint, verify);
+}
+
+static void silcpurple_verify_details(PublicKeyVerify verify, gint id)
+{
+ SilcPublicKey public_key;
+ PurpleConnection *gc = verify->client->application;
+ SilcPurple sg = gc->proto_data;
+
+ silc_pkcs_public_key_decode(verify->pk, verify->pk_len,
+ &public_key);
+ silcpurple_show_public_key(sg, verify->entity_name, public_key,
+ G_CALLBACK(silcpurple_verify_details_cb),
+ verify);
+ silc_pkcs_public_key_free(public_key);
+}
+
+static void silcpurple_verify_ask(const char *entity,
+ const char *fingerprint,
+ const char *babbleprint,
+ PublicKeyVerify verify)
+{
+ PurpleConnection *gc = verify->client->application;
+ char tmp[256], tmp2[256];
+
+ if (verify->changed) {
+ g_snprintf(tmp, sizeof(tmp),
+ _("Received %s's public key. Your local copy does not match this "
+ "key. Would you still like to accept this public key?"),
+ entity);
+ } else {
+ g_snprintf(tmp, sizeof(tmp),
+ _("Received %s's public key. Would you like to accept this "
+ "public key?"), entity);
+ }
+ g_snprintf(tmp2, sizeof(tmp2),
+ _("Fingerprint and babbleprint for the %s key are:\n\n"
+ "%s\n%s\n"), entity, fingerprint, babbleprint);
+
+ purple_request_action(gc, _("Verify Public Key"), tmp, tmp2,
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_connection_get_account(gc), entity, NULL, verify, 3,
+ _("Yes"), G_CALLBACK(silcpurple_verify_cb),
+ _("No"), G_CALLBACK(silcpurple_verify_cb),
+ _("_View..."), G_CALLBACK(silcpurple_verify_details));
+}
+
+void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
+ const char *name, SilcSocketType conn_type,
+ unsigned char *pk, SilcUInt32 pk_len,
+ SilcSKEPKType pk_type,
+ SilcVerifyPublicKey completion, void *context)
+{
+ PurpleConnection *gc = client->application;
+ int i;
+ char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
+ char *fingerprint, *babbleprint;
+ struct passwd *pw;
+ struct stat st;
+ char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
+ conn_type == SILC_SOCKET_TYPE_ROUTER) ?
+ "server" : "client");
+ PublicKeyVerify verify;
+
+ if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+ purple_notify_error(gc, _("Verify Public Key"),
+ _("Unsupported public key type"), NULL);
+ if (completion)
+ completion(FALSE, context);
+ return;
+ }
+
+ pw = getpwuid(getuid());
+ if (!pw) {
+ if (completion)
+ completion(FALSE, context);
+ return;
+ }
+
+ memset(filename, 0, sizeof(filename));
+ memset(filename2, 0, sizeof(filename2));
+ memset(file, 0, sizeof(file));
+
+ if (conn_type == SILC_SOCKET_TYPE_SERVER ||
+ conn_type == SILC_SOCKET_TYPE_ROUTER) {
+ if (!name) {
+ g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
+ conn->sock->ip, conn->sock->port);
+ g_snprintf(filename, sizeof(filename) - 1,
+ "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
+ silcpurple_silcdir(), entity, file);
+
+ g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
+ conn->sock->hostname, conn->sock->port);
+ g_snprintf(filename2, sizeof(filename2) - 1,
+ "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
+ silcpurple_silcdir(), entity, file);
+
+ ipf = filename;
+ hostf = filename2;
+ } else {
+ g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
+ name, conn->sock->port);
+ g_snprintf(filename, sizeof(filename) - 1,
+ "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
+ silcpurple_silcdir(), entity, file);
+
+ ipf = filename;
+ }
+ } else {
+ /* Replace all whitespaces with `_'. */
+ fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+ for (i = 0; i < strlen(fingerprint); i++)
+ if (fingerprint[i] == ' ')
+ fingerprint[i] = '_';
+
+ g_snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
+ g_snprintf(filename, sizeof(filename) - 1,
+ "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
+ silcpurple_silcdir(), entity, file);
+ silc_free(fingerprint);
+
+ ipf = filename;
+ }
+
+ verify = silc_calloc(1, sizeof(*verify));
+ if (!verify)
+ return;
+ verify->client = client;
+ verify->conn = conn;
+ verify->filename = strdup(ipf);
+ verify->entity = strdup(entity);
+ verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
+ (name ? strdup(name) : strdup(conn->sock->hostname))
+ : NULL);
+ verify->pk = silc_memdup(pk, pk_len);
+ verify->pk_len = pk_len;
+ verify->pk_type = pk_type;
+ verify->completion = completion;
+ verify->context = context;
+ fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+ babbleprint = verify->babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
+
+ /* Check whether this key already exists */
+ if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) {
+ /* Key does not exist, ask user to verify the key and save it */
+ silcpurple_verify_ask(name ? name : entity,
+ fingerprint, babbleprint, verify);
+ return;
+ } else {
+ /* The key already exists, verify it. */
+ SilcPublicKey public_key;
+ unsigned char *encpk;
+ SilcUInt32 encpk_len;
+
+ /* Load the key file, try for both IP filename and hostname filename */
+ if (!silc_pkcs_load_public_key(ipf, &public_key,
+ SILC_PKCS_FILE_PEM) &&
+ !silc_pkcs_load_public_key(ipf, &public_key,
+ SILC_PKCS_FILE_BIN) &&
+ (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
+ SILC_PKCS_FILE_PEM) &&
+ !silc_pkcs_load_public_key(hostf, &public_key,
+ SILC_PKCS_FILE_BIN)))) {
+ silcpurple_verify_ask(name ? name : entity,
+ fingerprint, babbleprint, verify);
+ return;
+ }
+
+ /* Encode the key data */
+ encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
+ if (!encpk) {
+ silcpurple_verify_ask(name ? name : entity,
+ fingerprint, babbleprint, verify);
+ return;
+ }
+
+ /* Compare the keys */
+ if (memcmp(encpk, pk, encpk_len)) {
+ /* Ask user to verify the key and save it */
+ verify->changed = TRUE;
+ silcpurple_verify_ask(name ? name : entity,
+ fingerprint, babbleprint, verify);
+ return;
+ }
+
+ /* Local copy matched */
+ if (completion)
+ completion(TRUE, context);
+ silc_free(verify->filename);
+ silc_free(verify->entity);
+ silc_free(verify->entity_name);
+ silc_free(verify->pk);
+ silc_free(verify->fingerprint);
+ silc_free(verify->babbleprint);
+ silc_free(verify);
+ }
+}
diff --git a/libpurple/protocols/silc10/silc.c b/libpurple/protocols/silc10/silc.c
new file mode 100644
index 0000000000..45ca537114
--- /dev/null
+++ b/libpurple/protocols/silc10/silc.c
@@ -0,0 +1,1914 @@
+/*
+
+ silcpurple.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2004 - 2005 Pekka Riikonen
+
+ 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; version 2 of the License.
+
+ 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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcpurple.h"
+#include "version.h"
+#include "wb.h"
+
+extern SilcClientOperations ops;
+static PurplePlugin *silc_plugin = NULL;
+
+static const char *
+silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b)
+{
+ return (const char *)"silc";
+}
+
+static GList *
+silcpurple_away_states(PurpleAccount *account)
+{
+ PurpleStatusType *type;
+ GList *types = NULL;
+
+ type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, FALSE, TRUE, FALSE);
+ types = g_list_append(types, type);
+ type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), FALSE, TRUE, FALSE);
+ types = g_list_append(types, type);
+ type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, FALSE, TRUE, FALSE);
+ types = g_list_append(types, type);
+ type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), FALSE, TRUE, FALSE);
+ types = g_list_append(types, type);
+ type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), FALSE, TRUE, FALSE);
+ types = g_list_append(types, type);
+ type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), FALSE, TRUE, FALSE);
+ types = g_list_append(types, type);
+ type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, FALSE, TRUE, FALSE);
+ types = g_list_append(types, type);
+
+ return types;
+}
+
+static void
+silcpurple_set_status(PurpleAccount *account, PurpleStatus *status)
+{
+ PurpleConnection *gc = purple_account_get_connection(account);
+ SilcPurple sg = NULL;
+ SilcUInt32 mode;
+ SilcBuffer idp;
+ unsigned char mb[4];
+ const char *state;
+
+ if (gc != NULL)
+ sg = gc->proto_data;
+
+ if (status == NULL)
+ return;
+
+ state = purple_status_get_id(status);
+
+ if (state == NULL)
+ return;
+
+ if ((sg == NULL) || (sg->conn == NULL))
+ return;
+
+ mode = sg->conn->local_entry->mode;
+ mode &= ~(SILC_UMODE_GONE |
+ SILC_UMODE_HYPER |
+ SILC_UMODE_BUSY |
+ SILC_UMODE_INDISPOSED |
+ SILC_UMODE_PAGE);
+
+ if (!strcmp(state, "hyper"))
+ mode |= SILC_UMODE_HYPER;
+ else if (!strcmp(state, "away"))
+ mode |= SILC_UMODE_GONE;
+ else if (!strcmp(state, "busy"))
+ mode |= SILC_UMODE_BUSY;
+ else if (!strcmp(state, "indisposed"))
+ mode |= SILC_UMODE_INDISPOSED;
+ else if (!strcmp(state, "page"))
+ mode |= SILC_UMODE_PAGE;
+
+ /* Send UMODE */
+ idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT);
+ SILC_PUT32_MSB(mode, mb);
+ silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE,
+ ++sg->conn->cmd_ident, 2,
+ 1, idp->data, idp->len,
+ 2, mb, sizeof(mb));
+ silc_buffer_free(idp);
+}
+
+
+/*************************** Connection Routines *****************************/
+
+static void
+silcpurple_keepalive(PurpleConnection *gc)
+{
+ SilcPurple sg = gc->proto_data;
+ silc_client_send_packet(sg->client, sg->conn, SILC_PACKET_HEARTBEAT,
+ NULL, 0);
+}
+
+static gboolean
+silcpurple_scheduler(gpointer *context)
+{
+ SilcPurple sg = (SilcPurple)context;
+ silc_client_run_one(sg->client);
+ return TRUE;
+}
+
+static void
+silcpurple_nickname_parse(const char *nickname,
+ char **ret_nickname)
+{
+ silc_parse_userfqdn(nickname, ret_nickname, NULL);
+}
+
+static void
+silcpurple_login_connected(gpointer data, gint source, const gchar *error_message)
+{
+ PurpleConnection *gc = data;
+ SilcPurple sg;
+ SilcClient client;
+ SilcClientConnection conn;
+ PurpleAccount *account;
+ SilcClientConnectionParams params;
+ SilcUInt32 mask;
+ const char *dfile, *tmp;
+#ifdef SILC_ATTRIBUTE_USER_ICON
+ PurpleStoredImage *img;
+#endif
+#ifdef HAVE_SYS_UTSNAME_H
+ struct utsname u;
+#endif
+
+
+ g_return_if_fail(gc != NULL);
+
+ sg = gc->proto_data;
+
+ if (source < 0) {
+ purple_connection_error(gc, _("Connection failed"));
+ return;
+ }
+
+ client = sg->client;
+ account = sg->account;
+
+ /* Get session detachment data, if available */
+ memset(&params, 0, sizeof(params));
+ dfile = silcpurple_session_file(purple_account_get_username(sg->account));
+ params.detach_data = (unsigned char *)silc_file_readfile(dfile, &params.detach_data_len);
+ if (params.detach_data)
+ params.detach_data[params.detach_data_len] = 0;
+
+ /* Add connection to SILC client library */
+ conn = silc_client_add_connection(
+ sg->client, &params,
+ (char *)purple_account_get_string(account, "server",
+ "silc.silcnet.org"),
+ purple_account_get_int(account, "port", 706), sg);
+ if (!conn) {
+ purple_connection_error(gc, _("Cannot initialize SILC Client connection"));
+ gc->proto_data = NULL;
+ return;
+ }
+ sg->conn = conn;
+
+ /* Progress */
+ if (params.detach_data) {
+ purple_connection_update_progress(gc, _("Resuming session"), 2, 5);
+ sg->resuming = TRUE;
+ } else {
+ purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5);
+ }
+
+ /* Perform SILC Key Exchange. The "silc_connected" will be called
+ eventually. */
+ silc_client_start_key_exchange(sg->client, sg->conn, source);
+
+ /* Set default attributes */
+ mask = SILC_ATTRIBUTE_MOOD_NORMAL;
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_STATUS_MOOD,
+ SILC_32_TO_PTR(mask),
+ sizeof(SilcUInt32));
+ mask = SILC_ATTRIBUTE_CONTACT_CHAT;
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_PREFERRED_CONTACT,
+ SILC_32_TO_PTR(mask),
+ sizeof(SilcUInt32));
+#ifdef HAVE_SYS_UTSNAME_H
+ if (!uname(&u)) {
+ SilcAttributeObjDevice dev;
+ memset(&dev, 0, sizeof(dev));
+ dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
+ dev.version = u.release;
+ dev.model = u.sysname;
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_DEVICE_INFO,
+ (void *)&dev, sizeof(dev));
+ }
+#endif
+#ifdef _WIN32
+ tmp = _tzname[0];
+#else
+ tmp = tzname[0];
+#endif
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_TIMEZONE,
+ (void *)tmp, strlen(tmp));
+
+#ifdef SILC_ATTRIBUTE_USER_ICON
+ /* Set our buddy icon */
+ img = purple_buddy_icons_find_account_icon(account);
+ silcpurple_buddy_set_icon(gc, img);
+ purple_imgstore_unref(img);
+#endif
+
+ silc_free(params.detach_data);
+}
+
+static void
+silcpurple_login(PurpleAccount *account)
+{
+ SilcPurple sg;
+ SilcClient client;
+ SilcClientParams params;
+ PurpleConnection *gc;
+ char pkd[256], prd[256];
+ const char *cipher, *hmac;
+ char *realname;
+ int i;
+
+ gc = account->gc;
+ if (!gc)
+ return;
+ gc->proto_data = NULL;
+
+ memset(&params, 0, sizeof(params));
+ strcat(params.nickname_format, "%n@%h%a");
+ params.nickname_parse = silcpurple_nickname_parse;
+ params.ignore_requested_attributes = FALSE;
+
+ /* Allocate SILC client */
+ client = silc_client_alloc(&ops, &params, gc, NULL);
+ if (!client) {
+ purple_connection_error(gc, _("Out of memory"));
+ return;
+ }
+
+ /* Get username, real name and local hostname for SILC library */
+ if (purple_account_get_username(account)) {
+ const char *u = purple_account_get_username(account);
+ char **up = g_strsplit(u, "@", 2);
+ client->username = strdup(up[0]);
+ g_strfreev(up);
+ } else {
+ client->username = silc_get_username();
+ purple_account_set_username(account, client->username);
+ }
+ realname = silc_get_real_name();
+ if (purple_account_get_user_info(account)) {
+ client->realname = strdup(purple_account_get_user_info(account));
+ free(realname);
+ } else if ((silc_get_real_name() != NULL) && (*realname != '\0')) {
+ client->realname = realname;
+ purple_account_set_user_info(account, client->realname);
+ } else {
+ free(realname);
+ client->realname = strdup(_("John Noname"));
+ }
+ client->hostname = silc_net_localhost();
+
+ purple_connection_set_display_name(gc, client->username);
+
+ /* Register requested cipher and HMAC */
+ cipher = purple_account_get_string(account, "cipher", SILC_DEFAULT_CIPHER);
+ for (i = 0; silc_default_ciphers[i].name; i++)
+ if (!strcmp(silc_default_ciphers[i].name, cipher)) {
+ silc_cipher_register(&(silc_default_ciphers[i]));
+ break;
+ }
+ hmac = purple_account_get_string(account, "hmac", SILC_DEFAULT_HMAC);
+ for (i = 0; silc_default_hmacs[i].name; i++)
+ if (!strcmp(silc_default_hmacs[i].name, hmac)) {
+ silc_hmac_register(&(silc_default_hmacs[i]));
+ break;
+ }
+
+ /* Init SILC client */
+ if (!silc_client_init(client)) {
+ gc->wants_to_die = TRUE;
+ purple_connection_error(gc, _("Cannot initialize SILC protocol"));
+ return;
+ }
+
+ /* Check the ~/.silc dir and create it, and new key pair if necessary. */
+ if (!silcpurple_check_silc_dir(gc)) {
+ gc->wants_to_die = TRUE;
+ purple_connection_error(gc, _("Cannot find/access ~/.silc directory"));
+ return;
+ }
+
+ /* Progress */
+ purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5);
+
+ /* Load SILC key pair */
+ g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
+ g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
+ if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
+ (char *)purple_account_get_string(account, "private-key", prd),
+ (gc->password == NULL) ? "" : gc->password, &client->pkcs,
+ &client->public_key, &client->private_key)) {
+ g_snprintf(pkd, sizeof(pkd), _("Could not load SILC key pair: %s"), strerror(errno));
+ purple_connection_error(gc, pkd);
+ return;
+ }
+
+ sg = silc_calloc(1, sizeof(*sg));
+ if (!sg)
+ return;
+ memset(sg, 0, sizeof(*sg));
+ sg->client = client;
+ sg->gc = gc;
+ sg->account = account;
+ gc->proto_data = sg;
+
+ /* Connect to the SILC server */
+ if (purple_proxy_connect(gc, account,
+ purple_account_get_string(account, "server",
+ "silc.silcnet.org"),
+ purple_account_get_int(account, "port", 706),
+ silcpurple_login_connected, gc) == NULL)
+ {
+ purple_connection_error(gc, _("Unable to create connection"));
+ return;
+ }
+
+ /* Schedule SILC using Glib's event loop */
+ sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg);
+}
+
+static int
+silcpurple_close_final(gpointer *context)
+{
+ SilcPurple sg = (SilcPurple)context;
+ silc_client_stop(sg->client);
+ silc_client_free(sg->client);
+#ifdef HAVE_SILCMIME_H
+ if (sg->mimeass)
+ silc_mime_assembler_free(sg->mimeass);
+#endif
+ silc_free(sg);
+ return 0;
+}
+
+static void
+silcpurple_close(PurpleConnection *gc)
+{
+ SilcPurple sg = gc->proto_data;
+
+ g_return_if_fail(sg != NULL);
+
+ /* Send QUIT */
+ silc_client_command_call(sg->client, sg->conn, NULL,
+ "QUIT", "Download this: " PURPLE_WEBSITE, NULL);
+
+ if (sg->conn)
+ silc_client_close_connection(sg->client, sg->conn);
+
+ purple_timeout_remove(sg->scheduler);
+ purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg);
+}
+
+
+/****************************** Protocol Actions *****************************/
+
+static void
+silcpurple_attrs_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
+{
+ /* Nothing */
+}
+
+static void
+silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ PurpleRequestField *f;
+ char *tmp;
+ SilcUInt32 tmp_len, mask;
+ SilcAttributeObjService service;
+ SilcAttributeObjDevice dev;
+ SilcVCardStruct vcard;
+ const char *val;
+
+ sg = gc->proto_data;
+ if (!sg)
+ return;
+
+ memset(&service, 0, sizeof(service));
+ memset(&dev, 0, sizeof(dev));
+ memset(&vcard, 0, sizeof(vcard));
+
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_USER_INFO, NULL);
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_SERVICE, NULL);
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_STATUS_MOOD, NULL);
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_TIMEZONE, NULL);
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_GEOLOCATION, NULL);
+ silc_client_attribute_del(client, conn,
+ SILC_ATTRIBUTE_DEVICE_INFO, NULL);
+
+ /* Set mood */
+ mask = 0;
+ f = purple_request_fields_get_field(fields, "mood_normal");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_NORMAL;
+ f = purple_request_fields_get_field(fields, "mood_happy");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_HAPPY;
+ f = purple_request_fields_get_field(fields, "mood_sad");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_SAD;
+ f = purple_request_fields_get_field(fields, "mood_angry");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_ANGRY;
+ f = purple_request_fields_get_field(fields, "mood_jealous");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_JEALOUS;
+ f = purple_request_fields_get_field(fields, "mood_ashamed");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_ASHAMED;
+ f = purple_request_fields_get_field(fields, "mood_invincible");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE;
+ f = purple_request_fields_get_field(fields, "mood_inlove");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_INLOVE;
+ f = purple_request_fields_get_field(fields, "mood_sleepy");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_SLEEPY;
+ f = purple_request_fields_get_field(fields, "mood_bored");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_BORED;
+ f = purple_request_fields_get_field(fields, "mood_excited");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_EXCITED;
+ f = purple_request_fields_get_field(fields, "mood_anxious");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS;
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_STATUS_MOOD,
+ SILC_32_TO_PTR(mask),
+ sizeof(SilcUInt32));
+
+ /* Set preferred contact */
+ mask = 0;
+ f = purple_request_fields_get_field(fields, "contact_chat");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_CONTACT_CHAT;
+ f = purple_request_fields_get_field(fields, "contact_email");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_CONTACT_EMAIL;
+ f = purple_request_fields_get_field(fields, "contact_call");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_CONTACT_CALL;
+ f = purple_request_fields_get_field(fields, "contact_sms");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_CONTACT_SMS;
+ f = purple_request_fields_get_field(fields, "contact_mms");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_CONTACT_MMS;
+ f = purple_request_fields_get_field(fields, "contact_video");
+ if (f && purple_request_field_bool_get_value(f))
+ mask |= SILC_ATTRIBUTE_CONTACT_VIDEO;
+ if (mask)
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_PREFERRED_CONTACT,
+ SILC_32_TO_PTR(mask),
+ sizeof(SilcUInt32));
+
+ /* Set status text */
+ val = NULL;
+ f = purple_request_fields_get_field(fields, "status_text");
+ if (f)
+ val = purple_request_field_string_get_value(f);
+ if (val && *val)
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_STATUS_FREETEXT,
+ (void *)val, strlen(val));
+
+ /* Set vcard */
+ val = NULL;
+ f = purple_request_fields_get_field(fields, "vcard");
+ if (f)
+ val = purple_request_field_string_get_value(f);
+ if (val && *val) {
+ purple_account_set_string(sg->account, "vcard", val);
+ tmp = silc_file_readfile(val, &tmp_len);
+ if (tmp) {
+ tmp[tmp_len] = 0;
+ if (silc_vcard_decode((unsigned char *)tmp, tmp_len, &vcard))
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_USER_INFO,
+ (void *)&vcard,
+ sizeof(vcard));
+ }
+ silc_vcard_free(&vcard);
+ silc_free(tmp);
+ } else {
+ purple_account_set_string(sg->account, "vcard", "");
+ }
+
+#ifdef HAVE_SYS_UTSNAME_H
+ /* Set device info */
+ f = purple_request_fields_get_field(fields, "device");
+ if (f && purple_request_field_bool_get_value(f)) {
+ struct utsname u;
+ if (!uname(&u)) {
+ dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
+ dev.version = u.release;
+ dev.model = u.sysname;
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_DEVICE_INFO,
+ (void *)&dev, sizeof(dev));
+ }
+ }
+#endif
+
+ /* Set timezone */
+ val = NULL;
+ f = purple_request_fields_get_field(fields, "timezone");
+ if (f)
+ val = purple_request_field_string_get_value(f);
+ if (val && *val)
+ silc_client_attribute_add(client, conn,
+ SILC_ATTRIBUTE_TIMEZONE,
+ (void *)val, strlen(val));
+}
+
+static void
+silcpurple_attrs(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *g;
+ PurpleRequestField *f;
+ SilcHashTable attrs;
+ SilcAttributePayload attr;
+ gboolean mnormal = TRUE, mhappy = FALSE, msad = FALSE,
+ mangry = FALSE, mjealous = FALSE, mashamed = FALSE,
+ minvincible = FALSE, minlove = FALSE, msleepy = FALSE,
+ mbored = FALSE, mexcited = FALSE, manxious = FALSE;
+ gboolean cemail = FALSE, ccall = FALSE, csms = FALSE,
+ cmms = FALSE, cchat = TRUE, cvideo = FALSE;
+ gboolean device = TRUE;
+ char status[1024];
+
+ sg = gc->proto_data;
+ if (!sg)
+ return;
+
+ memset(status, 0, sizeof(status));
+
+ attrs = silc_client_attributes_get(client, conn);
+ if (attrs) {
+ if (silc_hash_table_find(attrs,
+ SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_MOOD),
+ NULL, (void *)&attr)) {
+ SilcUInt32 mood = 0;
+ silc_attribute_get_object(attr, &mood, sizeof(mood));
+ mnormal = !mood;
+ mhappy = (mood & SILC_ATTRIBUTE_MOOD_HAPPY);
+ msad = (mood & SILC_ATTRIBUTE_MOOD_SAD);
+ mangry = (mood & SILC_ATTRIBUTE_MOOD_ANGRY);
+ mjealous = (mood & SILC_ATTRIBUTE_MOOD_JEALOUS);
+ mashamed = (mood & SILC_ATTRIBUTE_MOOD_ASHAMED);
+ minvincible = (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE);
+ minlove = (mood & SILC_ATTRIBUTE_MOOD_INLOVE);
+ msleepy = (mood & SILC_ATTRIBUTE_MOOD_SLEEPY);
+ mbored = (mood & SILC_ATTRIBUTE_MOOD_BORED);
+ mexcited = (mood & SILC_ATTRIBUTE_MOOD_EXCITED);
+ manxious = (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS);
+ }
+
+ if (silc_hash_table_find(attrs,
+ SILC_32_TO_PTR(SILC_ATTRIBUTE_PREFERRED_CONTACT),
+ NULL, (void *)&attr)) {
+ SilcUInt32 contact = 0;
+ silc_attribute_get_object(attr, &contact, sizeof(contact));
+ cemail = (contact & SILC_ATTRIBUTE_CONTACT_EMAIL);
+ ccall = (contact & SILC_ATTRIBUTE_CONTACT_CALL);
+ csms = (contact & SILC_ATTRIBUTE_CONTACT_SMS);
+ cmms = (contact & SILC_ATTRIBUTE_CONTACT_MMS);
+ cchat = (contact & SILC_ATTRIBUTE_CONTACT_CHAT);
+ cvideo = (contact & SILC_ATTRIBUTE_CONTACT_VIDEO);
+ }
+
+ if (silc_hash_table_find(attrs,
+ SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_FREETEXT),
+ NULL, (void *)&attr))
+ silc_attribute_get_object(attr, &status, sizeof(status));
+
+ if (!silc_hash_table_find(attrs,
+ SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO),
+ NULL, (void *)&attr))
+ device = FALSE;
+ }
+
+ fields = purple_request_fields_new();
+
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_label_new("l3", _("Your Current Mood"));
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_normal", _("Normal"), mnormal);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_happy", _("Happy"), mhappy);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_sad", _("Sad"), msad);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_angry", _("Angry"), mangry);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_jealous", _("Jealous"), mjealous);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_ashamed", _("Ashamed"), mashamed);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_invincible", _("Invincible"), minvincible);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_inlove", _("In love"), minlove);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_sleepy", _("Sleepy"), msleepy);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_bored", _("Bored"), mbored);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_excited", _("Excited"), mexcited);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("mood_anxious", _("Anxious"), manxious);
+ purple_request_field_group_add_field(g, f);
+
+ f = purple_request_field_label_new("l4", _("\nYour Preferred Contact Methods"));
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("contact_chat", _("Chat"), cchat);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("contact_email", _("E-mail"), cemail);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("contact_call", _("Phone"), ccall);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("contact_sms", _("SMS"), csms);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("contact_mms", _("MMS"), cmms);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("contact_video", _("Video conferencing"), cvideo);
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_string_new("status_text", _("Your Current Status"),
+ status[0] ? status : NULL, TRUE);
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+
+ g = purple_request_field_group_new(NULL);
+#if 0
+ f = purple_request_field_label_new("l2", _("Online Services"));
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_bool_new("services",
+ _("Let others see what services you are using"),
+ TRUE);
+ purple_request_field_group_add_field(g, f);
+#endif
+#ifdef HAVE_SYS_UTSNAME_H
+ f = purple_request_field_bool_new("device",
+ _("Let others see what computer you are using"),
+ device);
+ purple_request_field_group_add_field(g, f);
+#endif
+ purple_request_fields_add_group(fields, g);
+
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_string_new("vcard", _("Your VCard File"),
+ purple_account_get_string(sg->account, "vcard", ""),
+ FALSE);
+ purple_request_field_group_add_field(g, f);
+#ifdef _WIN32
+ f = purple_request_field_string_new("timezone", _("Timezone"), _tzname[0], FALSE);
+#else
+ f = purple_request_field_string_new("timezone", _("Timezone"), tzname[0], FALSE);
+#endif
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+
+ purple_request_fields(gc, _("User Online Status Attributes"),
+ _("User Online Status Attributes"),
+ _("You can let other users see your online status information "
+ "and your personal information. Please fill the information "
+ "you would like other users to see about yourself."),
+ fields,
+ _("OK"), G_CALLBACK(silcpurple_attrs_cb),
+ _("Cancel"), G_CALLBACK(silcpurple_attrs_cancel),
+ gc->account, NULL, NULL, gc);
+}
+
+static void
+silcpurple_detach(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ SilcPurple sg;
+
+ if (!gc)
+ return;
+ sg = gc->proto_data;
+ if (!sg)
+ return;
+
+ /* Call DETACH */
+ silc_client_command_call(sg->client, sg->conn, "DETACH");
+ sg->detaching = TRUE;
+}
+
+static void
+silcpurple_view_motd(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ SilcPurple sg;
+ char *tmp;
+
+ if (!gc)
+ return;
+ sg = gc->proto_data;
+ if (!sg)
+ return;
+
+ if (!sg->motd) {
+ purple_notify_error(
+ gc, _("Message of the Day"), _("No Message of the Day available"),
+ _("There is no Message of the Day associated with this connection"));
+ return;
+ }
+
+ tmp = g_markup_escape_text(sg->motd, -1);
+ purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL,
+ tmp, NULL, NULL);
+ g_free(tmp);
+}
+
+static void
+silcpurple_create_keypair_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
+{
+ /* Nothing */
+}
+
+static void
+silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields)
+{
+ SilcPurple sg = gc->proto_data;
+ PurpleRequestField *f;
+ const char *val, *pkfile = NULL, *prfile = NULL;
+ const char *pass1 = NULL, *pass2 = NULL, *un = NULL, *hn = NULL;
+ const char *rn = NULL, *e = NULL, *o = NULL, *c = NULL;
+ char *identifier;
+ int keylen = SILCPURPLE_DEF_PKCS_LEN;
+ SilcPublicKey public_key;
+
+ sg = gc->proto_data;
+ if (!sg)
+ return;
+
+ val = NULL;
+ f = purple_request_fields_get_field(fields, "pass1");
+ if (f)
+ val = purple_request_field_string_get_value(f);
+ if (val && *val)
+ pass1 = val;
+ else
+ pass1 = "";
+ val = NULL;
+ f = purple_request_fields_get_field(fields, "pass2");
+ if (f)
+ val = purple_request_field_string_get_value(f);
+ if (val && *val)
+ pass2 = val;
+ else
+ pass2 = "";
+
+ if (strcmp(pass1, pass2)) {
+ purple_notify_error(
+ gc, _("Create New SILC Key Pair"), _("Passphrases do not match"), NULL);
+ return;
+ }
+
+ val = NULL;
+ f = purple_request_fields_get_field(fields, "key");
+ if (f)
+ val = purple_request_field_string_get_value(f);
+ if (val && *val)
+ keylen = atoi(val);
+ f = purple_request_fields_get_field(fields, "pkfile");
+ if (f)
+ pkfile = purple_request_field_string_get_value(f);
+ f = purple_request_fields_get_field(fields, "prfile");
+ if (f)
+ prfile = purple_request_field_string_get_value(f);
+
+ f = purple_request_fields_get_field(fields, "un");
+ if (f)
+ un = purple_request_field_string_get_value(f);
+ f = purple_request_fields_get_field(fields, "hn");
+ if (f)
+ hn = purple_request_field_string_get_value(f);
+ f = purple_request_fields_get_field(fields, "rn");
+ if (f)
+ rn = purple_request_field_string_get_value(f);
+ f = purple_request_fields_get_field(fields, "e");
+ if (f)
+ e = purple_request_field_string_get_value(f);
+ f = purple_request_fields_get_field(fields, "o");
+ if (f)
+ o = purple_request_field_string_get_value(f);
+ f = purple_request_fields_get_field(fields, "c");
+ if (f)
+ c = purple_request_field_string_get_value(f);
+
+ identifier = silc_pkcs_encode_identifier((char *)un, (char *)hn,
+ (char *)rn, (char *)e, (char *)o, (char *)c);
+
+ /* Create the key pair */
+ if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile,
+ identifier, pass1, NULL, &public_key, NULL,
+ FALSE)) {
+ purple_notify_error(
+ gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL);
+ return;
+ }
+
+ silcpurple_show_public_key(sg, NULL, public_key, NULL, NULL);
+
+ silc_pkcs_public_key_free(public_key);
+ silc_free(identifier);
+}
+
+static void
+silcpurple_create_keypair(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ SilcPurple sg = gc->proto_data;
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *g;
+ PurpleRequestField *f;
+ const char *username, *realname;
+ char *hostname, **u;
+ char tmp[256], pkd[256], pkd2[256], prd[256], prd2[256];
+
+ username = purple_account_get_username(sg->account);
+ u = g_strsplit(username, "@", 2);
+ username = u[0];
+ realname = purple_account_get_user_info(sg->account);
+ hostname = silc_net_localhost();
+ g_snprintf(tmp, sizeof(tmp), "%s@%s", username, hostname);
+
+ g_snprintf(pkd2, sizeof(pkd2), "%s" G_DIR_SEPARATOR_S"public_key.pub", silcpurple_silcdir());
+ g_snprintf(prd2, sizeof(prd2), "%s" G_DIR_SEPARATOR_S"private_key.prv", silcpurple_silcdir());
+ g_snprintf(pkd, sizeof(pkd) - 1, "%s",
+ purple_account_get_string(gc->account, "public-key", pkd2));
+ g_snprintf(prd, sizeof(prd) - 1, "%s",
+ purple_account_get_string(gc->account, "private-key", prd2));
+
+ fields = purple_request_fields_new();
+
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_string_new("key", _("Key length"), "2048", FALSE);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_string_new("pkfile", _("Public key file"), pkd, FALSE);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_string_new("prfile", _("Private key file"), prd, FALSE);
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_string_new("un", _("Username"), username ? username : "", FALSE);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_string_new("hn", _("Hostname"), hostname ? hostname : "", FALSE);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_string_new("rn", _("Real name"), realname ? realname : "", FALSE);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_string_new("e", _("E-mail"), tmp, FALSE);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_string_new("o", _("Organization"), "", FALSE);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_string_new("c", _("Country"), "", FALSE);
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_string_new("pass1", _("Passphrase"), "", FALSE);
+ purple_request_field_string_set_masked(f, TRUE);
+ purple_request_field_group_add_field(g, f);
+ f = purple_request_field_string_new("pass2", _("Passphrase (retype)"), "", FALSE);
+ purple_request_field_string_set_masked(f, TRUE);
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+
+ purple_request_fields(gc, _("Create New SILC Key Pair"),
+ _("Create New SILC Key Pair"), NULL, fields,
+ _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
+ _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
+ gc->account, NULL, NULL, gc);
+
+ g_strfreev(u);
+ silc_free(hostname);
+}
+
+static void
+silcpurple_change_pass(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ purple_account_request_change_password(purple_connection_get_account(gc));
+}
+
+static void
+silcpurple_change_passwd(PurpleConnection *gc, const char *old, const char *new)
+{
+ char prd[256];
+ g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir());
+ silc_change_private_key_passphrase(purple_account_get_string(gc->account,
+ "private-key",
+ prd), old, new);
+}
+
+static void
+silcpurple_show_set_info(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ purple_account_request_change_user_info(purple_connection_get_account(gc));
+}
+
+static void
+silcpurple_set_info(PurpleConnection *gc, const char *text)
+{
+}
+
+static GList *
+silcpurple_actions(PurplePlugin *plugin, gpointer context)
+{
+ GList *list = NULL;
+ PurplePluginAction *act;
+
+ act = purple_plugin_action_new(_("Online Status"),
+ silcpurple_attrs);
+ list = g_list_append(list, act);
+
+ act = purple_plugin_action_new(_("Detach From Server"),
+ silcpurple_detach);
+ list = g_list_append(list, act);
+
+ act = purple_plugin_action_new(_("View Message of the Day"),
+ silcpurple_view_motd);
+ list = g_list_append(list, act);
+
+ act = purple_plugin_action_new(_("Create SILC Key Pair..."),
+ silcpurple_create_keypair);
+ list = g_list_append(list, act);
+
+ act = purple_plugin_action_new(_("Change Password..."),
+ silcpurple_change_pass);
+ list = g_list_append(list, act);
+
+ act = purple_plugin_action_new(_("Set User Info..."),
+ silcpurple_show_set_info);
+ list = g_list_append(list, act);
+
+ return list;
+}
+
+
+/******************************* IM Routines *********************************/
+
+typedef struct {
+ char *nick;
+ char *message;
+ SilcUInt32 message_len;
+ SilcMessageFlags flags;
+ PurpleMessageFlags gflags;
+} *SilcPurpleIM;
+
+static void
+silcpurple_send_im_resolved(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ SilcUInt32 clients_count,
+ void *context)
+{
+ PurpleConnection *gc = client->application;
+ SilcPurple sg = gc->proto_data;
+ SilcPurpleIM im = context;
+ PurpleConversation *convo;
+ char tmp[256], *nickname = NULL;
+ SilcClientEntry client_entry;
+#ifdef HAVE_SILCMIME_H
+ SilcDList list;
+#endif
+
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick,
+ sg->account);
+ if (!convo)
+ return;
+
+ if (!clients)
+ goto err;
+
+ if (clients_count > 1) {
+ silc_parse_userfqdn(im->nick, &nickname, NULL);
+
+ /* Find the correct one. The im->nick might be a formatted nick
+ so this will find the correct one. */
+ clients = silc_client_get_clients_local(client, conn,
+ nickname, im->nick,
+ &clients_count);
+ if (!clients)
+ goto err;
+ client_entry = clients[0];
+ silc_free(clients);
+ } else {
+ client_entry = clients[0];
+ }
+
+#ifdef HAVE_SILCMIME_H
+ /* Check for images */
+ if (im->gflags & PURPLE_MESSAGE_IMAGES) {
+ list = silcpurple_image_message(im->message, (SilcUInt32 *)&im->flags);
+ if (list) {
+ /* Send one or more MIME message. If more than one, they
+ are MIME fragments due to over large message */
+ SilcBuffer buf;
+
+ silc_dlist_start(list);
+ while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
+ silc_client_send_private_message(client, conn,
+ client_entry, im->flags,
+ buf->data, buf->len,
+ TRUE);
+ silc_mime_partial_free(list);
+ purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
+ im->message, 0, time(NULL));
+ goto out;
+ }
+ }
+#endif
+
+ /* Send the message */
+ silc_client_send_private_message(client, conn, client_entry, im->flags,
+ (unsigned char *)im->message, im->message_len, TRUE);
+ purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
+ im->message, 0, time(NULL));
+ goto out;
+
+ err:
+ g_snprintf(tmp, sizeof(tmp),
+ _("User <I>%s</I> is not present in the network"), im->nick);
+ purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+ out:
+ g_free(im->nick);
+ g_free(im->message);
+ silc_free(im);
+ silc_free(nickname);
+}
+
+static int
+silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
+ PurpleMessageFlags flags)
+{
+ SilcPurple sg = gc->proto_data;
+ SilcClient client = sg->client;
+ SilcClientConnection conn = sg->conn;
+ SilcClientEntry *clients;
+ SilcUInt32 clients_count, mflags;
+ char *nickname, *msg, *tmp;
+ int ret = 0;
+ gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
+#ifdef HAVE_SILCMIME_H
+ SilcDList list;
+#endif
+
+ if (!who || !message)
+ return 0;
+
+ mflags = SILC_MESSAGE_FLAG_UTF8;
+
+ tmp = msg = purple_unescape_html(message);
+
+ if (!g_ascii_strncasecmp(msg, "/me ", 4)) {
+ msg += 4;
+ if (!*msg) {
+ g_free(tmp);
+ return 0;
+ }
+ mflags |= SILC_MESSAGE_FLAG_ACTION;
+ } else if (strlen(msg) > 1 && msg[0] == '/') {
+ if (!silc_client_command_call(client, conn, msg + 1))
+ purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
+ _("Unknown command"));
+ g_free(tmp);
+ return 0;
+ }
+
+
+ if (!silc_parse_userfqdn(who, &nickname, NULL)) {
+ g_free(tmp);
+ return 0;
+ }
+
+ if (sign)
+ mflags |= SILC_MESSAGE_FLAG_SIGNED;
+
+ /* Find client entry */
+ clients = silc_client_get_clients_local(client, conn, nickname, who,
+ &clients_count);
+ if (!clients) {
+ /* Resolve unknown user */
+ SilcPurpleIM im = silc_calloc(1, sizeof(*im));
+ if (!im) {
+ g_free(tmp);
+ return 0;
+ }
+ im->nick = g_strdup(who);
+ im->message = g_strdup(message);
+ im->message_len = strlen(im->message);
+ im->flags = mflags;
+ im->gflags = flags;
+ silc_client_get_clients(client, conn, nickname, NULL,
+ silcpurple_send_im_resolved, im);
+ silc_free(nickname);
+ g_free(tmp);
+ return 0;
+ }
+
+#ifdef HAVE_SILCMIME_H
+ /* Check for images */
+ if (flags & PURPLE_MESSAGE_IMAGES) {
+ list = silcpurple_image_message(message, &mflags);
+ if (list) {
+ /* Send one or more MIME message. If more than one, they
+ are MIME fragments due to over large message */
+ SilcBuffer buf;
+
+ silc_dlist_start(list);
+ while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
+ ret =
+ silc_client_send_private_message(client, conn,
+ clients[0], mflags,
+ buf->data, buf->len,
+ TRUE);
+ silc_mime_partial_free(list);
+ g_free(tmp);
+ silc_free(nickname);
+ silc_free(clients);
+ return ret;
+ }
+ }
+#endif
+
+ /* Send private message directly */
+ ret = silc_client_send_private_message(client, conn, clients[0],
+ mflags,
+ (unsigned char *)msg,
+ strlen(msg), TRUE);
+
+ g_free(tmp);
+ silc_free(nickname);
+ silc_free(clients);
+ return ret;
+}
+
+
+static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) {
+ /* split this single menu building function back into the two
+ original: one for buddies and one for chats */
+
+ if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ return silcpurple_chat_menu((PurpleChat *) node);
+ } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ return silcpurple_buddy_menu((PurpleBuddy *) node);
+ } else {
+ g_return_val_if_reached(NULL);
+ }
+}
+
+/********************************* Commands **********************************/
+
+static PurpleCmdRet silcpurple_cmd_chat_part(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ PurpleConnection *gc;
+ PurpleConversation *convo = conv;
+ int id = 0;
+
+ gc = purple_conversation_get_gc(conv);
+
+ if (gc == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ if(args && args[0])
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0],
+ gc->account);
+
+ if (convo != NULL)
+ id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo));
+
+ if (id == 0)
+ return PURPLE_CMD_RET_FAILED;
+
+ silcpurple_chat_leave(gc, id);
+
+ return PURPLE_CMD_RET_OK;
+
+}
+
+static PurpleCmdRet silcpurple_cmd_chat_topic(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ PurpleConnection *gc;
+ int id = 0;
+ char *buf, *tmp, *tmp2;
+ const char *topic;
+
+ gc = purple_conversation_get_gc(conv);
+ id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
+
+ if (gc == NULL || id == 0)
+ return PURPLE_CMD_RET_FAILED;
+
+ if (!args || !args[0]) {
+ topic = purple_conv_chat_get_topic (PURPLE_CONV_CHAT(conv));
+ if (topic) {
+ tmp = g_markup_escape_text(topic, -1);
+ tmp2 = purple_markup_linkify(tmp);
+ buf = g_strdup_printf(_("current topic is: %s"), tmp2);
+ g_free(tmp);
+ g_free(tmp2);
+ } else
+ buf = g_strdup(_("No topic is set"));
+ purple_conv_chat_write(PURPLE_CONV_CHAT(conv), gc->account->username, buf,
+ PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ g_free(buf);
+
+ }
+
+ if (args && args[0] && (strlen(args[0]) > 255)) {
+ *error = g_strdup(_("Topic too long"));
+ return PURPLE_CMD_RET_FAILED;
+ }
+
+ silcpurple_chat_set_topic(gc, id, args ? args[0] : NULL);
+
+ return PURPLE_CMD_RET_OK;
+}
+
+static PurpleCmdRet silcpurple_cmd_chat_join(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ GHashTable *comp;
+
+ if(!args || !args[0])
+ return PURPLE_CMD_RET_FAILED;
+
+ comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+
+ g_hash_table_replace(comp, "channel", args[0]);
+ if(args[1])
+ g_hash_table_replace(comp, "passphrase", args[1]);
+
+ silcpurple_chat_join(purple_conversation_get_gc(conv), comp);
+
+ g_hash_table_destroy(comp);
+ return PURPLE_CMD_RET_OK;
+}
+
+static PurpleCmdRet silcpurple_cmd_chat_list(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ PurpleConnection *gc;
+ gc = purple_conversation_get_gc(conv);
+ purple_roomlist_show_with_account(purple_connection_get_account(gc));
+ return PURPLE_CMD_RET_OK;
+}
+
+static PurpleCmdRet silcpurple_cmd_whois(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ PurpleConnection *gc;
+
+ gc = purple_conversation_get_gc(conv);
+
+ if (gc == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ silcpurple_get_info(gc, args[0]);
+
+ return PURPLE_CMD_RET_OK;
+}
+
+static PurpleCmdRet silcpurple_cmd_msg(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ int ret;
+ PurpleConnection *gc;
+
+ gc = purple_conversation_get_gc(conv);
+
+ if (gc == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND);
+
+ if (ret)
+ return PURPLE_CMD_RET_OK;
+ else
+ return PURPLE_CMD_RET_FAILED;
+}
+
+static PurpleCmdRet silcpurple_cmd_query(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ int ret = 1;
+ PurpleConversation *convo;
+ PurpleConnection *gc;
+ PurpleAccount *account;
+
+ if (!args || !args[0]) {
+ *error = g_strdup(_("You must specify a nick"));
+ return PURPLE_CMD_RET_FAILED;
+ }
+
+ gc = purple_conversation_get_gc(conv);
+
+ if (gc == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ account = purple_connection_get_account(gc);
+
+ convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, args[0]);
+
+ if (args[1]) {
+ ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND);
+ purple_conv_im_write(PURPLE_CONV_IM(convo), purple_connection_get_display_name(gc),
+ args[1], PURPLE_MESSAGE_SEND, time(NULL));
+ }
+
+ if (ret)
+ return PURPLE_CMD_RET_OK;
+ else
+ return PURPLE_CMD_RET_FAILED;
+}
+
+static PurpleCmdRet silcpurple_cmd_motd(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ PurpleConnection *gc;
+ SilcPurple sg;
+ char *tmp;
+
+ gc = purple_conversation_get_gc(conv);
+
+ if (gc == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ sg = gc->proto_data;
+
+ if (sg == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ if (!sg->motd) {
+ *error = g_strdup(_("There is no Message of the Day associated with this connection"));
+ return PURPLE_CMD_RET_FAILED;
+ }
+
+ tmp = g_markup_escape_text(sg->motd, -1);
+ purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL,
+ tmp, NULL, NULL);
+ g_free(tmp);
+
+ return PURPLE_CMD_RET_OK;
+}
+
+static PurpleCmdRet silcpurple_cmd_detach(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ gc = purple_conversation_get_gc(conv);
+
+ if (gc == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ sg = gc->proto_data;
+
+ if (sg == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ silc_client_command_call(sg->client, sg->conn, "DETACH");
+ sg->detaching = TRUE;
+
+ return PURPLE_CMD_RET_OK;
+}
+
+static PurpleCmdRet silcpurple_cmd_cmode(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ PurpleConnection *gc;
+ SilcPurple sg;
+ SilcChannelEntry channel;
+ char *silccmd, *silcargs, *msg, tmp[256];
+ const char *chname;
+
+ gc = purple_conversation_get_gc(conv);
+
+ if (gc == NULL || !args || gc->proto_data == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ sg = gc->proto_data;
+
+ if (args[0])
+ chname = args[0];
+ else
+ chname = purple_conversation_get_name(conv);
+
+ if (!args[1]) {
+ channel = silc_client_get_channel(sg->client, sg->conn,
+ (char *)chname);
+ if (!channel) {
+ *error = g_strdup_printf(_("channel %s not found"), chname);
+ return PURPLE_CMD_RET_FAILED;
+ }
+ if (channel->mode) {
+ silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
+ msg = g_strdup_printf(_("channel modes for %s: %s"), chname, tmp);
+ } else {
+ msg = g_strdup_printf(_("no channel modes are set on %s"), chname);
+ }
+ purple_conv_chat_write(PURPLE_CONV_CHAT(conv), "",
+ msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
+ g_free(msg);
+ return PURPLE_CMD_RET_OK;
+ }
+
+ silcargs = g_strjoinv(" ", args);
+ silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL);
+ g_free(silcargs);
+ if (!silc_client_command_call(sg->client, sg->conn, silccmd)) {
+ g_free(silccmd);
+ *error = g_strdup_printf(_("Failed to set cmodes for %s"), args[0]);
+ return PURPLE_CMD_RET_FAILED;
+ }
+ g_free(silccmd);
+
+ return PURPLE_CMD_RET_OK;
+}
+
+static PurpleCmdRet silcpurple_cmd_generic(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ PurpleConnection *gc;
+ SilcPurple sg;
+ char *silccmd, *silcargs;
+
+ gc = purple_conversation_get_gc(conv);
+
+ if (gc == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ sg = gc->proto_data;
+
+ if (sg == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ silcargs = g_strjoinv(" ", args);
+ silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL);
+ g_free(silcargs);
+ if (!silc_client_command_call(sg->client, sg->conn, silccmd)) {
+ g_free(silccmd);
+ *error = g_strdup_printf(_("Unknown command: %s, (may be a client bug)"), cmd);
+ return PURPLE_CMD_RET_FAILED;
+ }
+ g_free(silccmd);
+
+ return PURPLE_CMD_RET_OK;
+}
+
+static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ gc = purple_conversation_get_gc(conv);
+
+ if (gc == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ sg = gc->proto_data;
+
+ if (sg == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ silc_client_command_call(sg->client, sg->conn, NULL,
+ "QUIT", (args && args[0]) ? args[0] : "Download this: " PURPLE_WEBSITE, NULL);
+
+ return PURPLE_CMD_RET_OK;
+}
+
+static PurpleCmdRet silcpurple_cmd_call(PurpleConversation *conv,
+ const char *cmd, char **args, char **error, void *data)
+{
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ gc = purple_conversation_get_gc(conv);
+
+ if (gc == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ sg = gc->proto_data;
+
+ if (sg == NULL)
+ return PURPLE_CMD_RET_FAILED;
+
+ if (!silc_client_command_call(sg->client, sg->conn, args[0])) {
+ *error = g_strdup_printf(_("Unknown command: %s"), args[0]);
+ return PURPLE_CMD_RET_FAILED;
+ }
+
+ return PURPLE_CMD_RET_OK;
+}
+
+
+/************************** Plugin Initialization ****************************/
+
+static void
+silcpurple_register_commands(void)
+{
+ purple_cmd_register("part", "w", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
+ PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
+ "prpl-silc", silcpurple_cmd_chat_part, _("part [channel]: Leave the chat"), NULL);
+ purple_cmd_register("leave", "w", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
+ PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
+ "prpl-silc", silcpurple_cmd_chat_part, _("leave [channel]: Leave the chat"), NULL);
+ purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc",
+ silcpurple_cmd_chat_topic, _("topic [&lt;new topic&gt;]: View or change the topic"), NULL);
+ purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
+ PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
+ "prpl-silc", silcpurple_cmd_chat_join,
+ _("join &lt;channel&gt; [&lt;password&gt;]: Join a chat on this network"), NULL);
+ purple_cmd_register("list", "", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc",
+ silcpurple_cmd_chat_list, _("list: List channels on this network"), NULL);
+ purple_cmd_register("whois", "w", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc",
+ silcpurple_cmd_whois, _("whois &lt;nick&gt;: View nick's information"), NULL);
+ purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc", silcpurple_cmd_msg,
+ _("msg &lt;nick&gt; &lt;message&gt;: Send a private message to a user"), NULL);
+ purple_cmd_register("query", "ws", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_query,
+ _("query &lt;nick&gt; [&lt;message&gt;]: Send a private message to a user"), NULL);
+ purple_cmd_register("motd", "", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_motd,
+ _("motd: View the server's Message Of The Day"), NULL);
+ purple_cmd_register("detach", "", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc", silcpurple_cmd_detach,
+ _("detach: Detach this session"), NULL);
+ purple_cmd_register("quit", "s", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_quit,
+ _("quit [message]: Disconnect from the server, with an optional message"), NULL);
+ purple_cmd_register("call", "s", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc", silcpurple_cmd_call,
+ _("call &lt;command&gt;: Call any silc client command"), NULL);
+ /* These below just get passed through for the silc client library to deal
+ * with */
+ purple_cmd_register("kill", "ws", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
+ _("kill &lt;nick&gt; [-pubkey|&lt;reason&gt;]: Kill nick"), NULL);
+ purple_cmd_register("nick", "w", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc", silcpurple_cmd_generic,
+ _("nick &lt;newnick&gt;: Change your nickname"), NULL);
+ purple_cmd_register("whowas", "ww", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
+ _("whowas &lt;nick&gt;: View nick's information"), NULL);
+ purple_cmd_register("cmode", "wws", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_cmode,
+ _("cmode &lt;channel&gt; [+|-&lt;modes&gt;] [arguments]: Change or display channel modes"), NULL);
+ purple_cmd_register("cumode", "wws", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
+ _("cumode &lt;channel&gt; +|-&lt;modes&gt; &lt;nick&gt;: Change nick's modes on channel"), NULL);
+ purple_cmd_register("umode", "w", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc", silcpurple_cmd_generic,
+ _("umode &lt;usermodes&gt;: Set your modes in the network"), NULL);
+ purple_cmd_register("oper", "s", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc", silcpurple_cmd_generic,
+ _("oper &lt;nick&gt; [-pubkey]: Get server operator privileges"), NULL);
+ purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
+ _("invite &lt;channel&gt; [-|+]&lt;nick&gt;: invite nick or add/remove from channel invite list"), NULL);
+ purple_cmd_register("kick", "wws", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
+ _("kick &lt;channel&gt; &lt;nick&gt; [comment]: Kick client from channel"), NULL);
+ purple_cmd_register("info", "w", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
+ _("info [server]: View server administrative details"), NULL);
+ purple_cmd_register("ban", "ww", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
+ _("ban [&lt;channel&gt; +|-&lt;nick&gt;]: Ban client from channel"), NULL);
+ purple_cmd_register("getkey", "w", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc", silcpurple_cmd_generic,
+ _("getkey &lt;nick|server&gt;: Retrieve client's or server's public key"), NULL);
+ purple_cmd_register("stats", "", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc", silcpurple_cmd_generic,
+ _("stats: View server and network statistics"), NULL);
+ purple_cmd_register("ping", "", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc", silcpurple_cmd_generic,
+ _("ping: Send PING to the connected server"), NULL);
+#if 0 /* Purple doesn't handle these yet */
+ purple_cmd_register("users", "w", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
+ "prpl-silc", silcpurple_cmd_users,
+ _("users &lt;channel&gt;: List users in channel"));
+ purple_cmd_register("names", "ww", PURPLE_CMD_P_PRPL,
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
+ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_names,
+ _("names [-count|-ops|-halfops|-voices|-normal] &lt;channel(s)&gt;: List specific users in channel(s)"));
+#endif
+}
+
+static PurpleWhiteboardPrplOps silcpurple_wb_ops =
+{
+ silcpurple_wb_start,
+ silcpurple_wb_end,
+ silcpurple_wb_get_dimensions,
+ silcpurple_wb_set_dimensions,
+ silcpurple_wb_get_brush,
+ silcpurple_wb_set_brush,
+ silcpurple_wb_send,
+ silcpurple_wb_clear,
+
+ /* padding */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static PurplePluginProtocolInfo prpl_info =
+{
+#ifdef HAVE_SILCMIME_H
+ OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
+ OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE,
+#else
+ OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
+ OPT_PROTO_PASSWORD_OPTIONAL,
+#endif
+ NULL, /* user_splits */
+ NULL, /* protocol_options */
+#ifdef SILC_ATTRIBUTE_USER_ICON
+ {"jpeg,gif,png,bmp", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
+#else
+ NO_BUDDY_ICONS,
+#endif
+ silcpurple_list_icon, /* list_icon */
+ NULL, /* list_emblems */
+ silcpurple_status_text, /* status_text */
+ silcpurple_tooltip_text, /* tooltip_text */
+ silcpurple_away_states, /* away_states */
+ silcpurple_blist_node_menu, /* blist_node_menu */
+ silcpurple_chat_info, /* chat_info */
+ silcpurple_chat_info_defaults,/* chat_info_defaults */
+ silcpurple_login, /* login */
+ silcpurple_close, /* close */
+ silcpurple_send_im, /* send_im */
+ silcpurple_set_info, /* set_info */
+ NULL, /* send_typing */
+ silcpurple_get_info, /* get_info */
+ silcpurple_set_status, /* set_status */
+ silcpurple_idle_set, /* set_idle */
+ silcpurple_change_passwd, /* change_passwd */
+ silcpurple_add_buddy, /* add_buddy */
+ NULL, /* add_buddies */
+ silcpurple_remove_buddy, /* remove_buddy */
+ NULL, /* remove_buddies */
+ NULL, /* add_permit */
+ NULL, /* add_deny */
+ NULL, /* rem_permit */
+ NULL, /* rem_deny */
+ NULL, /* set_permit_deny */
+ silcpurple_chat_join, /* join_chat */
+ NULL, /* reject_chat */
+ silcpurple_get_chat_name, /* get_chat_name */
+ silcpurple_chat_invite, /* chat_invite */
+ silcpurple_chat_leave, /* chat_leave */
+ NULL, /* chat_whisper */
+ silcpurple_chat_send, /* chat_send */
+ silcpurple_keepalive, /* keepalive */
+ NULL, /* register_user */
+ NULL, /* get_cb_info */
+ NULL, /* get_cb_away */
+ NULL, /* alias_buddy */
+ NULL, /* group_buddy */
+ NULL, /* rename_group */
+ NULL, /* buddy_free */
+ NULL, /* convo_closed */
+ NULL, /* normalize */
+#ifdef SILC_ATTRIBUTE_USER_ICON
+ silcpurple_buddy_set_icon, /* set_buddy_icon */
+#else
+ NULL,
+#endif
+ NULL, /* remove_group */
+ NULL, /* get_cb_real_name */
+ silcpurple_chat_set_topic, /* set_chat_topic */
+ NULL, /* find_blist_chat */
+ silcpurple_roomlist_get_list, /* roomlist_get_list */
+ silcpurple_roomlist_cancel, /* roomlist_cancel */
+ NULL, /* roomlist_expand_category */
+ NULL, /* can_receive_file */
+ silcpurple_ftp_send_file, /* send_file */
+ silcpurple_ftp_new_xfer, /* new_xfer */
+ NULL, /* offline_message */
+ &silcpurple_wb_ops, /* whiteboard_prpl_ops */
+ NULL, /* send_raw */
+ NULL, /* roomlist_room_serialize */
+
+ /* padding */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static PurplePluginInfo info =
+{
+ PURPLE_PLUGIN_MAGIC,
+ PURPLE_MAJOR_VERSION,
+ PURPLE_MINOR_VERSION,
+ PURPLE_PLUGIN_PROTOCOL, /**< type */
+ NULL, /**< ui_requirement */
+ 0, /**< flags */
+ NULL, /**< dependencies */
+ PURPLE_PRIORITY_DEFAULT, /**< priority */
+
+ "prpl-silc", /**< id */
+ "SILC", /**< name */
+ "1.0", /**< version */
+ /** summary */
+ N_("SILC Protocol Plugin"),
+ /** description */
+ N_("Secure Internet Live Conferencing (SILC) Protocol"),
+ "Pekka Riikonen", /**< author */
+ "http://silcnet.org/", /**< homepage */
+
+ NULL, /**< load */
+ NULL, /**< unload */
+ NULL, /**< destroy */
+
+ NULL, /**< ui_info */
+ &prpl_info, /**< extra_info */
+ NULL, /**< prefs_info */
+ silcpurple_actions,
+
+ /* padding */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+ PurpleAccountOption *option;
+ PurpleAccountUserSplit *split;
+ char tmp[256];
+ int i;
+ PurpleKeyValuePair *kvp;
+ GList *list = NULL;
+
+ silc_plugin = plugin;
+
+ split = purple_account_user_split_new(_("Network"), "silcnet.org", '@');
+ prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
+
+ /* Account options */
+ option = purple_account_option_string_new(_("Connect server"),
+ "server",
+ "silc.silcnet.org");
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+ option = purple_account_option_int_new(_("Port"), "port", 706);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+ g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
+ option = purple_account_option_string_new(_("Public Key file"),
+ "public-key", tmp);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+ g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
+ option = purple_account_option_string_new(_("Private Key file"),
+ "private-key", tmp);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+ for (i = 0; silc_default_ciphers[i].name; i++) {
+ kvp = g_new0(PurpleKeyValuePair, 1);
+ kvp->key = g_strdup(silc_default_ciphers[i].name);
+ kvp->value = g_strdup(silc_default_ciphers[i].name);
+ list = g_list_append(list, kvp);
+ }
+ option = purple_account_option_list_new(_("Cipher"), "cipher", list);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+ list = NULL;
+ for (i = 0; silc_default_hmacs[i].name; i++) {
+ kvp = g_new0(PurpleKeyValuePair, 1);
+ kvp->key = g_strdup(silc_default_hmacs[i].name);
+ kvp->value = g_strdup(silc_default_hmacs[i].name);
+ list = g_list_append(list, kvp);
+ }
+ option = purple_account_option_list_new(_("HMAC"), "hmac", list);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+ option = purple_account_option_bool_new(_("Public key authentication"),
+ "pubkey-auth", FALSE);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+ option = purple_account_option_bool_new(_("Block IMs without Key Exchange"),
+ "block-ims", FALSE);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+ option = purple_account_option_bool_new(_("Block messages to whiteboard"),
+ "block-wb", FALSE);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+ option = purple_account_option_bool_new(_("Automatically open whiteboard"),
+ "open-wb", FALSE);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+ option = purple_account_option_bool_new(_("Digitally sign and verify all messages"),
+ "sign-verify", FALSE);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+ purple_prefs_remove("/plugins/prpl/silc");
+
+ silcpurple_register_commands();
+
+#ifdef _WIN32
+ silc_net_win32_init();
+#endif
+}
+
+PURPLE_INIT_PLUGIN(silc, init_plugin, info);
diff --git a/libpurple/protocols/silc10/silcpurple.h b/libpurple/protocols/silc10/silcpurple.h
new file mode 100644
index 0000000000..86ba056614
--- /dev/null
+++ b/libpurple/protocols/silc10/silcpurple.h
@@ -0,0 +1,173 @@
+/*
+
+ silcpurple.h
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2004 Pekka Riikonen
+
+ 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; version 2 of the License.
+
+ 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.
+
+*/
+
+#ifndef SILCPURPLE_H
+#define SILCPURPLE_H
+
+/* Purple includes */
+#include "internal.h"
+#include "account.h"
+#include "accountopt.h"
+#include "cmds.h"
+#include "conversation.h"
+#include "debug.h"
+#include "ft.h"
+#include "notify.h"
+#include "prpl.h"
+#include "request.h"
+#include "roomlist.h"
+#include "server.h"
+#include "util.h"
+
+/* Default public and private key file names */
+#define SILCPURPLE_PUBLIC_KEY_NAME "public_key.pub"
+#define SILCPURPLE_PRIVATE_KEY_NAME "private_key.prv"
+
+/* Default settings for creating key pair */
+#define SILCPURPLE_DEF_PKCS "rsa"
+#define SILCPURPLE_DEF_PKCS_LEN 2048
+
+#define SILCPURPLE_PRVGRP 0x001fffff
+
+/* Status IDs */
+#define SILCPURPLE_STATUS_ID_OFFLINE "offline"
+#define SILCPURPLE_STATUS_ID_AVAILABLE "available"
+#define SILCPURPLE_STATUS_ID_HYPER "hyper"
+#define SILCPURPLE_STATUS_ID_AWAY "away"
+#define SILCPURPLE_STATUS_ID_BUSY "busy"
+#define SILCPURPLE_STATUS_ID_INDISPOSED "indisposed"
+#define SILCPURPLE_STATUS_ID_PAGE "page"
+
+typedef struct {
+ unsigned long id;
+ const char *channel;
+ unsigned long chid;
+ const char *parentch;
+ SilcChannelPrivateKey key;
+} *SilcPurplePrvgrp;
+
+/* The SILC Purple plugin context */
+typedef struct SilcPurpleStruct {
+ SilcClient client;
+ SilcClientConnection conn;
+
+ guint scheduler;
+ PurpleConnection *gc;
+ PurpleAccount *account;
+ unsigned long channel_ids;
+ GList *grps;
+
+ char *motd;
+ PurpleRoomlist *roomlist;
+#ifdef HAVE_SILCMIME_H
+ SilcMimeAssembler mimeass;
+#endif
+ unsigned int detaching : 1;
+ unsigned int resuming : 1;
+ unsigned int roomlist_canceled : 1;
+ unsigned int chpk : 1;
+} *SilcPurple;
+
+
+gboolean silcpurple_check_silc_dir(PurpleConnection *gc);
+void silcpurple_chat_join_done(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ SilcUInt32 clients_count,
+ void *context);
+const char *silcpurple_silcdir(void);
+const char *silcpurple_session_file(const char *account);
+void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
+ const char *name, SilcSocketType conn_type,
+ unsigned char *pk, SilcUInt32 pk_len,
+ SilcSKEPKType pk_type,
+ SilcVerifyPublicKey completion, void *context);
+GList *silcpurple_buddy_menu(PurpleBuddy *buddy);
+void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
+void silcpurple_send_buddylist(PurpleConnection *gc);
+void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
+void silcpurple_buddy_keyagr_request(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ const char *hostname, SilcUInt16 port);
+void silcpurple_idle_set(PurpleConnection *gc, int idle);
+void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full);
+char *silcpurple_status_text(PurpleBuddy *b);
+gboolean silcpurple_ip_is_private(const char *ip);
+void silcpurple_ftp_send_file(PurpleConnection *gc, const char *name, const char *file);
+PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name);
+void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry client_entry, SilcUInt32 session_id,
+ const char *hostname, SilcUInt16 port);
+void silcpurple_show_public_key(SilcPurple sg,
+ const char *name, SilcPublicKey public_key,
+ GCallback callback, void *context);
+void silcpurple_get_info(PurpleConnection *gc, const char *who);
+SilcAttributePayload
+silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute);
+void silcpurple_get_umode_string(SilcUInt32 mode, char *buf,
+ SilcUInt32 buf_size);
+void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf,
+ SilcUInt32 buf_size);
+void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf,
+ SilcUInt32 buf_size);
+GList *silcpurple_chat_info(PurpleConnection *gc);
+GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name);
+GList *silcpurple_chat_menu(PurpleChat *);
+void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data);
+char *silcpurple_get_chat_name(GHashTable *data);
+void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
+ const char *name);
+void silcpurple_chat_leave(PurpleConnection *gc, int id);
+int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags flags);
+void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic);
+PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc);
+void silcpurple_roomlist_cancel(PurpleRoomlist *list);
+void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
+ SilcBuffer channel_pubkeys);
+void silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
+ char **contactstr, char **langstr, char **devicestr,
+ char **tzstr, char **geostr);
+#ifdef SILC_ATTRIBUTE_USER_ICON
+void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img);
+#endif
+#ifdef HAVE_SILCMIME_H
+char *silcpurple_file2mime(const char *filename);
+SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags);
+#endif
+
+#ifdef _WIN32
+typedef int uid_t;
+
+struct passwd {
+ char *pw_name; /* user name */
+ char *pw_passwd; /* user password */
+ int pw_uid; /* user id */
+ int pw_gid; /* group id */
+ char *pw_gecos; /* real name */
+ char *pw_dir; /* home directory */
+ char *pw_shell; /* shell program */
+};
+
+struct passwd *getpwuid(int uid);
+int getuid(void);
+int geteuid(void);
+#endif
+
+#endif /* SILCPURPLE_H */
diff --git a/libpurple/protocols/silc10/util.c b/libpurple/protocols/silc10/util.c
new file mode 100644
index 0000000000..5310424ee0
--- /dev/null
+++ b/libpurple/protocols/silc10/util.c
@@ -0,0 +1,771 @@
+/*
+
+ silcpurple_util.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2004 - 2005 Pekka Riikonen
+
+ 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; version 2 of the License.
+
+ 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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcpurple.h"
+#include "imgstore.h"
+
+/**************************** Utility Routines *******************************/
+
+static char str[256], str2[256];
+
+const char *silcpurple_silcdir(void)
+{
+ const char *hd = purple_home_dir();
+ memset(str, 0, sizeof(str));
+ g_snprintf(str, sizeof(str) - 1, "%s" G_DIR_SEPARATOR_S ".silc", hd ? hd : "/tmp");
+ return (const char *)str;
+}
+
+const char *silcpurple_session_file(const char *account)
+{
+ memset(str2, 0, sizeof(str2));
+ g_snprintf(str2, sizeof(str2) - 1, "%s" G_DIR_SEPARATOR_S "%s_session",
+ silcpurple_silcdir(), account);
+ return (const char *)str2;
+}
+
+gboolean silcpurple_ip_is_private(const char *ip)
+{
+ if (silc_net_is_ip4(ip)) {
+ if (!strncmp(ip, "10.", 3)) {
+ return TRUE;
+ } else if (!strncmp(ip, "172.", 4) && strlen(ip) > 6) {
+ char tmp[3];
+ int s;
+ memset(tmp, 0, sizeof(tmp));
+ strncpy(tmp, ip + 4, 2);
+ s = atoi(tmp);
+ if (s >= 16 && s <= 31)
+ return TRUE;
+ } else if (!strncmp(ip, "192.168.", 8)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* This checks stats for various SILC files and directories. First it
+ checks if ~/.silc directory exist and is owned by the correct user. If
+ it doesn't exist, it will create the directory. After that it checks if
+ user's Public and Private key files exists and creates them if needed. */
+
+gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
+{
+ char filename[256], file_public_key[256], file_private_key[256];
+ char servfilename[256], clientfilename[256], friendsfilename[256];
+ char pkd[256], prd[256];
+ struct stat st;
+ struct passwd *pw;
+ int fd;
+
+ pw = getpwuid(getuid());
+ if (!pw) {
+ purple_debug_error("silc", "silc: %s\n", strerror(errno));
+ return FALSE;
+ }
+
+ g_snprintf(filename, sizeof(filename) - 1, "%s", silcpurple_silcdir());
+ g_snprintf(servfilename, sizeof(servfilename) - 1, "%s" G_DIR_SEPARATOR_S "serverkeys",
+ silcpurple_silcdir());
+ g_snprintf(clientfilename, sizeof(clientfilename) - 1, "%s" G_DIR_SEPARATOR_S "clientkeys",
+ silcpurple_silcdir());
+ g_snprintf(friendsfilename, sizeof(friendsfilename) - 1, "%s" G_DIR_SEPARATOR_S "friends",
+ silcpurple_silcdir());
+
+ /*
+ * Check ~/.silc directory
+ */
+ if ((g_stat(filename, &st)) == -1) {
+ /* If dir doesn't exist */
+ if (errno == ENOENT) {
+ if (pw->pw_uid == geteuid()) {
+ if ((g_mkdir(filename, 0755)) == -1) {
+ purple_debug_error("silc", "Couldn't create '%s' directory\n", filename);
+ return FALSE;
+ }
+ } else {
+ purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
+ filename);
+ return FALSE;
+ }
+ } else {
+ purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n", filename, strerror(errno));
+ return FALSE;
+ }
+ } else {
+#ifndef _WIN32
+ /* Check the owner of the dir */
+ if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
+ purple_debug_error("silc", "You don't seem to own '%s' directory\n",
+ filename);
+ return FALSE;
+ }
+#endif
+ }
+
+ /*
+ * Check ~./silc/serverkeys directory
+ */
+ if ((g_stat(servfilename, &st)) == -1) {
+ /* If dir doesn't exist */
+ if (errno == ENOENT) {
+ if (pw->pw_uid == geteuid()) {
+ if ((g_mkdir(servfilename, 0755)) == -1) {
+ purple_debug_error("silc", "Couldn't create '%s' directory\n", servfilename);
+ return FALSE;
+ }
+ } else {
+ purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
+ servfilename);
+ return FALSE;
+ }
+ } else {
+ purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n",
+ servfilename, strerror(errno));
+ return FALSE;
+ }
+ }
+
+ /*
+ * Check ~./silc/clientkeys directory
+ */
+ if ((g_stat(clientfilename, &st)) == -1) {
+ /* If dir doesn't exist */
+ if (errno == ENOENT) {
+ if (pw->pw_uid == geteuid()) {
+ if ((g_mkdir(clientfilename, 0755)) == -1) {
+ purple_debug_error("silc", "Couldn't create '%s' directory\n", clientfilename);
+ return FALSE;
+ }
+ } else {
+ purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
+ clientfilename);
+ return FALSE;
+ }
+ } else {
+ purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n",
+ clientfilename, strerror(errno));
+ return FALSE;
+ }
+ }
+
+ /*
+ * Check ~./silc/friends directory
+ */
+ if ((g_stat(friendsfilename, &st)) == -1) {
+ /* If dir doesn't exist */
+ if (errno == ENOENT) {
+ if (pw->pw_uid == geteuid()) {
+ if ((g_mkdir(friendsfilename, 0755)) == -1) {
+ purple_debug_error("silc", "Couldn't create '%s' directory\n", friendsfilename);
+ return FALSE;
+ }
+ } else {
+ purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
+ friendsfilename);
+ return FALSE;
+ }
+ } else {
+ purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n",
+ friendsfilename, strerror(errno));
+ return FALSE;
+ }
+ }
+
+ /*
+ * Check Public and Private keys
+ */
+ g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
+ g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
+ g_snprintf(file_public_key, sizeof(file_public_key) - 1, "%s",
+ purple_account_get_string(gc->account, "public-key", pkd));
+ g_snprintf(file_private_key, sizeof(file_public_key) - 1, "%s",
+ purple_account_get_string(gc->account, "private-key", prd));
+
+ if ((g_stat(file_public_key, &st)) == -1) {
+ /* If file doesn't exist */
+ if (errno == ENOENT) {
+ purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
+ if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
+ SILCPURPLE_DEF_PKCS_LEN,
+ file_public_key, file_private_key, NULL,
+ (gc->password == NULL) ? "" : gc->password,
+ NULL, NULL, NULL, FALSE)) {
+ purple_debug_error("silc", "Couldn't create key pair\n");
+ return FALSE;
+ }
+
+ if ((g_stat(file_public_key, &st)) == -1) {
+ purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
+ file_public_key, strerror(errno));
+ return FALSE;
+ }
+ } else {
+ purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
+ file_public_key, strerror(errno));
+ return FALSE;
+ }
+ }
+
+#ifndef _WIN32
+ /* Check the owner of the public key */
+ if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
+ purple_debug_error("silc", "You don't seem to own your public key!?\n");
+ return FALSE;
+ }
+#endif
+
+ if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
+ if ((fstat(fd, &st)) == -1) {
+ purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
+ file_private_key, strerror(errno));
+ close(fd);
+ return FALSE;
+ }
+ } else if ((g_stat(file_private_key, &st)) == -1) {
+ /* If file doesn't exist */
+ if (errno == ENOENT) {
+ purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
+ if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
+ SILCPURPLE_DEF_PKCS_LEN,
+ file_public_key, file_private_key, NULL,
+ (gc->password == NULL) ? "" : gc->password,
+ NULL, NULL, NULL, FALSE)) {
+ purple_debug_error("silc", "Couldn't create key pair\n");
+ return FALSE;
+ }
+
+ if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
+ if ((fstat(fd, &st)) == -1) {
+ purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
+ file_private_key, strerror(errno));
+ close(fd);
+ return FALSE;
+ }
+ }
+ /* This shouldn't really happen because silc_create_key_pair()
+ * will set the permissions */
+ else if ((g_stat(file_private_key, &st)) == -1) {
+ purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
+ file_private_key, strerror(errno));
+ return FALSE;
+ }
+ } else {
+ purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
+ file_private_key, strerror(errno));
+ return FALSE;
+ }
+ }
+
+#ifndef _WIN32
+ /* Check the owner of the private key */
+ if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
+ purple_debug_error("silc", "You don't seem to own your private key!?\n");
+ if (fd != -1)
+ close(fd);
+ return FALSE;
+ }
+
+ /* Check the permissions for the private key */
+ if ((st.st_mode & 0777) != 0600) {
+ purple_debug_warning("silc", "Wrong permissions in your private key file `%s'!\n"
+ "Trying to change them ...\n", file_private_key);
+ if ((fd == -1) || (fchmod(fd, S_IRUSR | S_IWUSR)) == -1) {
+ purple_debug_error("silc",
+ "Failed to change permissions for private key file!\n"
+ "Permissions for your private key file must be 0600.\n");
+ if (fd != -1)
+ close(fd);
+ return FALSE;
+ }
+ purple_debug_warning("silc", "Done.\n\n");
+ }
+#endif
+
+ if (fd != -1)
+ close(fd);
+
+ return TRUE;
+}
+
+#ifdef _WIN32
+struct passwd *getpwuid(uid_t uid) {
+ struct passwd *pwd = calloc(1, sizeof(struct passwd));
+ return pwd;
+}
+
+uid_t getuid() {
+ return 0;
+}
+
+uid_t geteuid() {
+ return 0;
+}
+#endif
+
+void silcpurple_show_public_key(SilcPurple sg,
+ const char *name, SilcPublicKey public_key,
+ GCallback callback, void *context)
+{
+ SilcPublicKeyIdentifier ident;
+ SilcPKCS pkcs;
+ char *fingerprint, *babbleprint;
+ unsigned char *pk;
+ SilcUInt32 pk_len, key_len = 0;
+ GString *s;
+ char *buf;
+
+ ident = silc_pkcs_decode_identifier(public_key->identifier);
+ if (!ident)
+ return;
+
+ pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+ fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+ babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
+
+ if (silc_pkcs_alloc((unsigned char *)public_key->name, &pkcs)) {
+ key_len = silc_pkcs_public_key_set(pkcs, public_key);
+ silc_pkcs_free(pkcs);
+ }
+
+ s = g_string_new("");
+ if (ident->realname)
+ /* Hint for translators: Please check the tabulator width here and in
+ the next strings (short strings: 2 tabs, longer strings 1 tab,
+ sum: 3 tabs or 24 characters) */
+ g_string_append_printf(s, _("Real Name: \t%s\n"), ident->realname);
+ if (ident->username)
+ g_string_append_printf(s, _("User Name: \t%s\n"), ident->username);
+ if (ident->email)
+ g_string_append_printf(s, _("E-Mail: \t\t%s\n"), ident->email);
+ if (ident->host)
+ g_string_append_printf(s, _("Host Name: \t%s\n"), ident->host);
+ if (ident->org)
+ g_string_append_printf(s, _("Organization: \t%s\n"), ident->org);
+ if (ident->country)
+ g_string_append_printf(s, _("Country: \t%s\n"), ident->country);
+ g_string_append_printf(s, _("Algorithm: \t%s\n"), public_key->name);
+ g_string_append_printf(s, _("Key Length: \t%d bits\n"), (int)key_len);
+ g_string_append_printf(s, "\n");
+ g_string_append_printf(s, _("Public Key Fingerprint:\n%s\n\n"), fingerprint);
+ g_string_append_printf(s, _("Public Key Babbleprint:\n%s"), babbleprint);
+
+ buf = g_string_free(s, FALSE);
+
+ purple_request_action(sg->gc, _("Public Key Information"),
+ _("Public Key Information"),
+ buf, 0, purple_connection_get_account(sg->gc),
+ NULL, NULL, context, 1, _("Close"), callback);
+
+ g_free(buf);
+ silc_free(fingerprint);
+ silc_free(babbleprint);
+ silc_free(pk);
+ silc_pkcs_free_identifier(ident);
+}
+
+SilcAttributePayload
+silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute)
+{
+ SilcAttributePayload attr = NULL;
+
+ if (!attrs)
+ return NULL;
+
+ silc_dlist_start(attrs);
+ while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END)
+ if (attribute == silc_attribute_get_attribute(attr))
+ break;
+
+ return attr;
+}
+
+void silcpurple_get_umode_string(SilcUInt32 mode, char *buf,
+ SilcUInt32 buf_size)
+{
+ memset(buf, 0, buf_size);
+ if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
+ (mode & SILC_UMODE_ROUTER_OPERATOR)) {
+ strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
+ "[server operator] " :
+ (mode & SILC_UMODE_ROUTER_OPERATOR) ?
+ "[SILC operator] " : "[unknown mode] ");
+ }
+ if (mode & SILC_UMODE_GONE)
+ strcat(buf, "[away] ");
+ if (mode & SILC_UMODE_INDISPOSED)
+ strcat(buf, "[indisposed] ");
+ if (mode & SILC_UMODE_BUSY)
+ strcat(buf, "[busy] ");
+ if (mode & SILC_UMODE_PAGE)
+ strcat(buf, "[wake me up] ");
+ if (mode & SILC_UMODE_HYPER)
+ strcat(buf, "[hyperactive] ");
+ if (mode & SILC_UMODE_ROBOT)
+ strcat(buf, "[robot] ");
+ if (mode & SILC_UMODE_ANONYMOUS)
+ strcat(buf, "[anonymous] ");
+ if (mode & SILC_UMODE_BLOCK_PRIVMSG)
+ strcat(buf, "[blocks private messages] ");
+ if (mode & SILC_UMODE_DETACHED)
+ strcat(buf, "[detached] ");
+ if (mode & SILC_UMODE_REJECT_WATCHING)
+ strcat(buf, "[rejects watching] ");
+ if (mode & SILC_UMODE_BLOCK_INVITE)
+ strcat(buf, "[blocks invites] ");
+}
+
+void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf,
+ SilcUInt32 buf_size)
+{
+ memset(buf, 0, buf_size);
+ if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
+ strcat(buf, "[permanent] ");
+ if (mode & SILC_CHANNEL_MODE_PRIVATE)
+ strcat(buf, "[private] ");
+ if (mode & SILC_CHANNEL_MODE_SECRET)
+ strcat(buf, "[secret] ");
+ if (mode & SILC_CHANNEL_MODE_PRIVKEY)
+ strcat(buf, "[private key] ");
+ if (mode & SILC_CHANNEL_MODE_INVITE)
+ strcat(buf, "[invite only] ");
+ if (mode & SILC_CHANNEL_MODE_TOPIC)
+ strcat(buf, "[topic restricted] ");
+ if (mode & SILC_CHANNEL_MODE_ULIMIT)
+ strcat(buf, "[user count limit] ");
+ if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
+ strcat(buf, "[passphrase auth] ");
+ if (mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
+ strcat(buf, "[public key auth] ");
+ if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
+ strcat(buf, "[users silenced] ");
+ if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
+ strcat(buf, "[operators silenced] ");
+}
+
+void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf,
+ SilcUInt32 buf_size)
+{
+ memset(buf, 0, buf_size);
+ if (mode & SILC_CHANNEL_UMODE_CHANFO)
+ strcat(buf, "[founder] ");
+ if (mode & SILC_CHANNEL_UMODE_CHANOP)
+ strcat(buf, "[operator] ");
+ if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
+ strcat(buf, "[blocks messages] ");
+ if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
+ strcat(buf, "[blocks user messages] ");
+ if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
+ strcat(buf, "[blocks robot messages] ");
+ if (mode & SILC_CHANNEL_UMODE_QUIET)
+ strcat(buf, "[quieted] ");
+}
+
+void
+silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
+ char **contactstr, char **langstr, char **devicestr,
+ char **tzstr, char **geostr)
+{
+ SilcAttributePayload attr;
+ SilcAttributeMood mood = 0;
+ SilcAttributeContact contact;
+ SilcAttributeObjDevice device;
+ SilcAttributeObjGeo geo;
+
+ char tmp[1024];
+ GString *s;
+
+ *moodstr = NULL;
+ *statusstr = NULL;
+ *contactstr = NULL;
+ *langstr = NULL;
+ *devicestr = NULL;
+ *tzstr = NULL;
+ *geostr = NULL;
+
+ if (!attrs)
+ return;
+
+ s = g_string_new("");
+ attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_MOOD);
+ if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
+ if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
+ g_string_append_printf(s, "[%s] ", _("Happy"));
+ if (mood & SILC_ATTRIBUTE_MOOD_SAD)
+ g_string_append_printf(s, "[%s] ", _("Sad"));
+ if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
+ g_string_append_printf(s, "[%s] ", _("Angry"));
+ if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
+ g_string_append_printf(s, "[%s] ", _("Jealous"));
+ if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
+ g_string_append_printf(s, "[%s] ", _("Ashamed"));
+ if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
+ g_string_append_printf(s, "[%s] ", _("Invincible"));
+ if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
+ g_string_append_printf(s, "[%s] ", _("In Love"));
+ if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
+ g_string_append_printf(s, "[%s] ", _("Sleepy"));
+ if (mood & SILC_ATTRIBUTE_MOOD_BORED)
+ g_string_append_printf(s, "[%s] ", _("Bored"));
+ if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
+ g_string_append_printf(s, "[%s] ", _("Excited"));
+ if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
+ g_string_append_printf(s, "[%s] ", _("Anxious"));
+ }
+ if (strlen(s->str)) {
+ *moodstr = s->str;
+ g_string_free(s, FALSE);
+ } else
+ g_string_free(s, TRUE);
+
+ attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_FREETEXT);
+ memset(tmp, 0, sizeof(tmp));
+ if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
+ *statusstr = g_strdup(tmp);
+
+ s = g_string_new("");
+ attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_CONTACT);
+ if (attr && silc_attribute_get_object(attr, &contact, sizeof(contact))) {
+ if (contact & SILC_ATTRIBUTE_CONTACT_CHAT)
+ g_string_append_printf(s, "[%s] ", _("Chat"));
+ if (contact & SILC_ATTRIBUTE_CONTACT_EMAIL)
+ g_string_append_printf(s, "[%s] ", _("E-Mail"));
+ if (contact & SILC_ATTRIBUTE_CONTACT_CALL)
+ g_string_append_printf(s, "[%s] ", _("Phone"));
+ if (contact & SILC_ATTRIBUTE_CONTACT_PAGE)
+ g_string_append_printf(s, "[%s] ", _("Paging"));
+ if (contact & SILC_ATTRIBUTE_CONTACT_SMS)
+ g_string_append_printf(s, "[%s] ", _("SMS"));
+ if (contact & SILC_ATTRIBUTE_CONTACT_MMS)
+ g_string_append_printf(s, "[%s] ", _("MMS"));
+ if (contact & SILC_ATTRIBUTE_CONTACT_VIDEO)
+ g_string_append_printf(s, "[%s] ", _("Video Conferencing"));
+ }
+ if (strlen(s->str)) {
+ *contactstr = s->str;
+ g_string_free(s, FALSE);
+ } else
+ g_string_free(s, TRUE);
+
+ attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_LANGUAGE);
+ memset(tmp, 0, sizeof(tmp));
+ if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
+ *langstr = g_strdup(tmp);
+
+ s = g_string_new("");
+ attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_DEVICE_INFO);
+ memset(&device, 0, sizeof(device));
+ if (attr && silc_attribute_get_object(attr, &device, sizeof(device))) {
+ if (device.type == SILC_ATTRIBUTE_DEVICE_COMPUTER)
+ g_string_append_printf(s, "%s: ", _("Computer"));
+ if (device.type == SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE)
+ g_string_append_printf(s, "%s: ", _("Mobile Phone"));
+ if (device.type == SILC_ATTRIBUTE_DEVICE_PDA)
+ g_string_append_printf(s, "%s: ", _("PDA"));
+ if (device.type == SILC_ATTRIBUTE_DEVICE_TERMINAL)
+ g_string_append_printf(s, "%s: ", _("Terminal"));
+ g_string_append_printf(s, "%s %s %s %s",
+ device.manufacturer ? device.manufacturer : "",
+ device.version ? device.version : "",
+ device.model ? device.model : "",
+ device.language ? device.language : "");
+ }
+ if (strlen(s->str)) {
+ *devicestr = s->str;
+ g_string_free(s, FALSE);
+ } else
+ g_string_free(s, TRUE);
+
+ attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_TIMEZONE);
+ memset(tmp, 0, sizeof(tmp));
+ if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
+ *tzstr = g_strdup(tmp);
+
+ attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_GEOLOCATION);
+ memset(&geo, 0, sizeof(geo));
+ if (attr && silc_attribute_get_object(attr, &geo, sizeof(geo)))
+ *geostr = g_strdup_printf("%s %s %s (%s)",
+ geo.longitude ? geo.longitude : "",
+ geo.latitude ? geo.latitude : "",
+ geo.altitude ? geo.altitude : "",
+ geo.accuracy ? geo.accuracy : "");
+}
+
+#ifdef HAVE_SILCMIME_H
+/* Returns MIME type of filetype */
+
+char *silcpurple_file2mime(const char *filename)
+{
+ const char *ct;
+
+ ct = strrchr(filename, '.');
+ if (!ct)
+ return NULL;
+ else if (!g_ascii_strcasecmp(".png", ct))
+ return strdup("image/png");
+ else if (!g_ascii_strcasecmp(".jpg", ct))
+ return strdup("image/jpeg");
+ else if (!g_ascii_strcasecmp(".jpeg", ct))
+ return strdup("image/jpeg");
+ else if (!g_ascii_strcasecmp(".gif", ct))
+ return strdup("image/gif");
+ else if (!g_ascii_strcasecmp(".tiff", ct))
+ return strdup("image/tiff");
+
+ return NULL;
+}
+
+/* Checks if message has images, and assembles MIME message if it has.
+ If only one image is present, creates simple MIME image message. If
+ there are multiple images and/or text with images multipart MIME
+ message is created. */
+
+SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags)
+{
+ SilcMime mime = NULL, p;
+ SilcDList list, parts = NULL;
+ const char *start, *end, *last;
+ GData *attribs;
+ char *type;
+ gboolean images = FALSE;
+
+ last = msg;
+ while (last && *last && purple_markup_find_tag("img", last, &start,
+ &end, &attribs)) {
+ PurpleStoredImage *image = NULL;
+ const char *id;
+
+ /* Check if there is text before image */
+ if (start - last) {
+ char *text, *tmp;
+ p = silc_mime_alloc();
+
+ /* Add content type */
+ silc_mime_add_field(p, "Content-Type",
+ "text/plain; charset=utf-8");
+
+ tmp = g_strndup(last, start - last);
+ text = purple_unescape_html(tmp);
+ g_free(tmp);
+ /* Add text */
+ silc_mime_add_data(p, text, strlen(text));
+ g_free(text);
+
+ if (!parts)
+ parts = silc_dlist_init();
+ silc_dlist_add(parts, p);
+ }
+
+ id = g_datalist_get_data(&attribs, "id");
+ if (id && (image = purple_imgstore_find_by_id(atoi(id)))) {
+ unsigned long imglen = purple_imgstore_get_size(image);
+ gconstpointer img = purple_imgstore_get_data(image);
+
+ p = silc_mime_alloc();
+
+ /* Add content type */
+ type = silcpurple_file2mime(purple_imgstore_get_filename(image));
+ if (!type) {
+ g_datalist_clear(&attribs);
+ last = end + 1;
+ continue;
+ }
+ silc_mime_add_field(p, "Content-Type", type);
+ silc_free(type);
+
+ /* Add content transfer encoding */
+ silc_mime_add_field(p, "Content-Transfer-Encoding", "binary");
+
+ /* Add image data */
+ silc_mime_add_data(p, img, imglen);
+
+ if (!parts)
+ parts = silc_dlist_init();
+ silc_dlist_add(parts, p);
+ images = TRUE;
+ }
+
+ g_datalist_clear(&attribs);
+
+ /* Continue after tag */
+ last = end + 1;
+ }
+
+ /* Check for text after the image(s) */
+ if (images && last && *last) {
+ char *tmp = purple_unescape_html(last);
+ p = silc_mime_alloc();
+
+ /* Add content type */
+ silc_mime_add_field(p, "Content-Type",
+ "text/plain; charset=utf-8");
+
+ /* Add text */
+ silc_mime_add_data(p, tmp, strlen(tmp));
+ g_free(tmp);
+
+ if (!parts)
+ parts = silc_dlist_init();
+ silc_dlist_add(parts, p);
+ }
+
+ /* If there weren't any images, don't return anything. */
+ if (!images) {
+ if (parts)
+ silc_dlist_uninit(parts);
+ return NULL;
+ }
+
+ if (silc_dlist_count(parts) > 1) {
+ /* Multipart MIME message */
+ char b[32];
+ mime = silc_mime_alloc();
+ silc_mime_add_field(mime, "MIME-Version", "1.0");
+ g_snprintf(b, sizeof(b), "b%4X%4X",
+ (unsigned int)time(NULL),
+ silc_dlist_count(parts));
+ silc_mime_set_multipart(mime, "mixed", b);
+ silc_dlist_start(parts);
+ while ((p = silc_dlist_get(parts)) != SILC_LIST_END)
+ silc_mime_add_multipart(mime, p);
+ } else {
+ /* Simple MIME message */
+ silc_dlist_start(parts);
+ mime = silc_dlist_get(parts);
+ silc_mime_add_field(mime, "MIME-Version", "1.0");
+ }
+
+ *mflags &= ~SILC_MESSAGE_FLAG_UTF8;
+ *mflags |= SILC_MESSAGE_FLAG_DATA;
+
+ /* Encode message. Fragment if it is too large */
+ list = silc_mime_encode_partial(mime, 0xfc00);
+
+ silc_dlist_uninit(parts);
+
+ /* Added multiparts gets freed here */
+ silc_mime_free(mime);
+
+ return list;
+}
+
+#endif /* HAVE_SILCMIME_H */
diff --git a/libpurple/protocols/silc10/wb.c b/libpurple/protocols/silc10/wb.c
new file mode 100644
index 0000000000..1c3b49cd45
--- /dev/null
+++ b/libpurple/protocols/silc10/wb.c
@@ -0,0 +1,517 @@
+/*
+
+ wb.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2005 Pekka Riikonen
+
+ 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; version 2 of the License.
+
+ 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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcpurple.h"
+#include "wb.h"
+
+/*
+ SILC Whiteboard packet:
+
+ 1 byte command
+ 2 bytes width
+ 2 bytes height
+ 4 bytes brush color
+ 2 bytes brush size
+ n bytes data
+
+ Data:
+
+ 4 bytes x
+ 4 bytes y
+
+ Commands:
+
+ 0x01 draw
+ 0x02 clear
+
+ MIME:
+
+ MIME-Version: 1.0
+ Content-Type: application/x-wb
+ Content-Transfer-Encoding: binary
+
+*/
+
+#define SILCPURPLE_WB_MIME "MIME-Version: 1.0\r\nContent-Type: application/x-wb\r\nContent-Transfer-Encoding: binary\r\n\r\n"
+#define SILCPURPLE_WB_HEADER strlen(SILCPURPLE_WB_MIME) + 11
+
+#define SILCPURPLE_WB_WIDTH 500
+#define SILCPURPLE_WB_HEIGHT 400
+#define SILCPURPLE_WB_WIDTH_MAX 1024
+#define SILCPURPLE_WB_HEIGHT_MAX 1024
+
+/* Commands */
+typedef enum {
+ SILCPURPLE_WB_DRAW = 0x01,
+ SILCPURPLE_WB_CLEAR = 0x02,
+} SilcPurpleWbCommand;
+
+/* Brush size */
+typedef enum {
+ SILCPURPLE_WB_BRUSH_SMALL = 2,
+ SILCPURPLE_WB_BRUSH_MEDIUM = 5,
+ SILCPURPLE_WB_BRUSH_LARGE = 10,
+} SilcPurpleWbBrushSize;
+
+/* Brush color (XXX Purple should provide default colors) */
+typedef enum {
+ SILCPURPLE_WB_COLOR_BLACK = 0,
+ SILCPURPLE_WB_COLOR_RED = 13369344,
+ SILCPURPLE_WB_COLOR_GREEN = 52224,
+ SILCPURPLE_WB_COLOR_BLUE = 204,
+ SILCPURPLE_WB_COLOR_YELLOW = 15658496,
+ SILCPURPLE_WB_COLOR_ORANGE = 16737792,
+ SILCPURPLE_WB_COLOR_CYAN = 52428,
+ SILCPURPLE_WB_COLOR_VIOLET = 5381277,
+ SILCPURPLE_WB_COLOR_PURPLE = 13369548,
+ SILCPURPLE_WB_COLOR_TAN = 12093547,
+ SILCPURPLE_WB_COLOR_BROWN = 5256485,
+ SILCPURPLE_WB_COLOR_GREY = 11184810,
+ SILCPURPLE_WB_COLOR_WHITE = 16777215,
+} SilcPurpleWbColor;
+
+typedef struct {
+ int type; /* 0 = buddy, 1 = channel */
+ union {
+ SilcClientEntry client;
+ SilcChannelEntry channel;
+ } u;
+ int width;
+ int height;
+ int brush_size;
+ int brush_color;
+} *SilcPurpleWb;
+
+/* Initialize whiteboard */
+
+PurpleWhiteboard *silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry)
+{
+ SilcClientConnection conn;
+ PurpleWhiteboard *wb;
+ SilcPurpleWb wbs;
+
+ conn = sg->conn;
+ wb = purple_whiteboard_get_session(sg->account, client_entry->nickname);
+ if (!wb)
+ wb = purple_whiteboard_create(sg->account, client_entry->nickname, 0);
+ if (!wb)
+ return NULL;
+
+ if (!wb->proto_data) {
+ wbs = silc_calloc(1, sizeof(*wbs));
+ if (!wbs)
+ return NULL;
+ wbs->type = 0;
+ wbs->u.client = client_entry;
+ wbs->width = SILCPURPLE_WB_WIDTH;
+ wbs->height = SILCPURPLE_WB_HEIGHT;
+ wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL;
+ wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK;
+ wb->proto_data = wbs;
+
+ /* Start the whiteboard */
+ purple_whiteboard_start(wb);
+ purple_whiteboard_clear(wb);
+ }
+
+ return wb;
+}
+
+PurpleWhiteboard *silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel)
+{
+ PurpleWhiteboard *wb;
+ SilcPurpleWb wbs;
+
+ wb = purple_whiteboard_get_session(sg->account, channel->channel_name);
+ if (!wb)
+ wb = purple_whiteboard_create(sg->account, channel->channel_name, 0);
+ if (!wb)
+ return NULL;
+
+ if (!wb->proto_data) {
+ wbs = silc_calloc(1, sizeof(*wbs));
+ if (!wbs)
+ return NULL;
+ wbs->type = 1;
+ wbs->u.channel = channel;
+ wbs->width = SILCPURPLE_WB_WIDTH;
+ wbs->height = SILCPURPLE_WB_HEIGHT;
+ wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL;
+ wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK;
+ wb->proto_data = wbs;
+
+ /* Start the whiteboard */
+ purple_whiteboard_start(wb);
+ purple_whiteboard_clear(wb);
+ }
+
+ return wb;
+}
+
+static void
+silcpurple_wb_parse(SilcPurpleWb wbs, PurpleWhiteboard *wb,
+ unsigned char *message, SilcUInt32 message_len)
+{
+ SilcUInt8 command;
+ SilcUInt16 width, height, brush_size;
+ SilcUInt32 brush_color, x, y, dx, dy;
+ SilcBufferStruct buf;
+ int ret;
+
+ /* Parse the packet */
+ silc_buffer_set(&buf, message, message_len);
+ ret = silc_buffer_unformat(&buf,
+ SILC_STR_UI_CHAR(&command),
+ SILC_STR_UI_SHORT(&width),
+ SILC_STR_UI_SHORT(&height),
+ SILC_STR_UI_INT(&brush_color),
+ SILC_STR_UI_SHORT(&brush_size),
+ SILC_STR_END);
+ if (ret < 0)
+ return;
+ silc_buffer_pull(&buf, ret);
+
+ /* Update whiteboard if its dimensions changed */
+ if (width != wbs->width || height != wbs->height)
+ silcpurple_wb_set_dimensions(wb, height, width);
+
+ if (command == SILCPURPLE_WB_DRAW) {
+ /* Parse data and draw it */
+ ret = silc_buffer_unformat(&buf,
+ SILC_STR_UI_INT(&dx),
+ SILC_STR_UI_INT(&dy),
+ SILC_STR_END);
+ if (ret < 0)
+ return;
+ silc_buffer_pull(&buf, 8);
+ x = dx;
+ y = dy;
+ while (buf.len > 0) {
+ ret = silc_buffer_unformat(&buf,
+ SILC_STR_UI_INT(&dx),
+ SILC_STR_UI_INT(&dy),
+ SILC_STR_END);
+ if (ret < 0)
+ return;
+ silc_buffer_pull(&buf, 8);
+
+ purple_whiteboard_draw_line(wb, x, y, x + dx, y + dy,
+ brush_color, brush_size);
+ x += dx;
+ y += dy;
+ }
+ }
+
+ if (command == SILCPURPLE_WB_CLEAR)
+ purple_whiteboard_clear(wb);
+}
+
+typedef struct {
+ unsigned char *message;
+ SilcUInt32 message_len;
+ SilcPurple sg;
+ SilcClientEntry sender;
+ SilcChannelEntry channel;
+} *SilcPurpleWbRequest;
+
+static void
+silcpurple_wb_request_cb(SilcPurpleWbRequest req, gint id)
+{
+ PurpleWhiteboard *wb;
+
+ if (id != 1)
+ goto out;
+
+ if (!req->channel)
+ wb = silcpurple_wb_init(req->sg, req->sender);
+ else
+ wb = silcpurple_wb_init_ch(req->sg, req->channel);
+
+ silcpurple_wb_parse(wb->proto_data, wb, req->message, req->message_len);
+
+ out:
+ silc_free(req->message);
+ silc_free(req);
+}
+
+static void
+silcpurple_wb_request(SilcClient client, const unsigned char *message,
+ SilcUInt32 message_len, SilcClientEntry sender,
+ SilcChannelEntry channel)
+{
+ char tmp[128];
+ SilcPurpleWbRequest req;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ gc = client->application;
+ sg = gc->proto_data;
+
+ /* Open whiteboard automatically if requested */
+ if (purple_account_get_bool(sg->account, "open-wb", FALSE)) {
+ PurpleWhiteboard *wb;
+
+ if (!channel)
+ wb = silcpurple_wb_init(sg, sender);
+ else
+ wb = silcpurple_wb_init_ch(sg, channel);
+
+ silcpurple_wb_parse(wb->proto_data, wb, (unsigned char *)message,
+ message_len);
+ return;
+ }
+
+ if (!channel) {
+ g_snprintf(tmp, sizeof(tmp),
+ _("%s sent message to whiteboard. Would you like "
+ "to open the whiteboard?"), sender->nickname);
+ } else {
+ g_snprintf(tmp, sizeof(tmp),
+ _("%s sent message to whiteboard on %s channel. "
+ "Would you like to open the whiteboard?"),
+ sender->nickname, channel->channel_name);
+ }
+
+ req = silc_calloc(1, sizeof(*req));
+ if (!req)
+ return;
+ req->message = silc_memdup(message, message_len);
+ req->message_len = message_len;
+ req->sender = sender;
+ req->channel = channel;
+ req->sg = sg;
+
+ purple_request_action(gc, _("Whiteboard"), tmp, NULL, 1,
+ sg->account, sender->nickname, NULL, req, 2,
+ _("Yes"), G_CALLBACK(silcpurple_wb_request_cb),
+ _("No"), G_CALLBACK(silcpurple_wb_request_cb));
+}
+
+/* Process incoming whiteboard message */
+
+void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry sender, SilcMessagePayload payload,
+ SilcMessageFlags flags, const unsigned char *message,
+ SilcUInt32 message_len)
+{
+ SilcPurple sg;
+ PurpleConnection *gc;
+ PurpleWhiteboard *wb;
+ SilcPurpleWb wbs;
+
+ gc = client->application;
+ sg = gc->proto_data;
+
+ wb = purple_whiteboard_get_session(sg->account, sender->nickname);
+ if (!wb) {
+ /* Ask user if they want to open the whiteboard */
+ silcpurple_wb_request(client, message, message_len,
+ sender, NULL);
+ return;
+ }
+
+ wbs = wb->proto_data;
+ silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len);
+}
+
+/* Process incoming whiteboard message on channel */
+
+void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry sender, SilcChannelEntry channel,
+ SilcMessagePayload payload,
+ SilcMessageFlags flags,
+ const unsigned char *message,
+ SilcUInt32 message_len)
+{
+ SilcPurple sg;
+ PurpleConnection *gc;
+ PurpleWhiteboard *wb;
+ SilcPurpleWb wbs;
+
+ gc = client->application;
+ sg = gc->proto_data;
+
+ wb = purple_whiteboard_get_session(sg->account, channel->channel_name);
+ if (!wb) {
+ /* Ask user if they want to open the whiteboard */
+ silcpurple_wb_request(client, message, message_len,
+ sender, channel);
+ return;
+ }
+
+ wbs = wb->proto_data;
+ silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len);
+}
+
+/* Send whiteboard message */
+
+void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list)
+{
+ SilcPurpleWb wbs = wb->proto_data;
+ SilcBuffer packet;
+ GList *list;
+ int len;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ g_return_if_fail(draw_list);
+ gc = purple_account_get_connection(wb->account);
+ g_return_if_fail(gc);
+ sg = gc->proto_data;
+ g_return_if_fail(sg);
+
+ len = SILCPURPLE_WB_HEADER;
+ for (list = draw_list; list; list = list->next)
+ len += 4;
+
+ packet = silc_buffer_alloc_size(len);
+ if (!packet)
+ return;
+
+ /* Assmeble packet */
+ silc_buffer_format(packet,
+ SILC_STR_UI32_STRING(SILCPURPLE_WB_MIME),
+ SILC_STR_UI_CHAR(SILCPURPLE_WB_DRAW),
+ SILC_STR_UI_SHORT(wbs->width),
+ SILC_STR_UI_SHORT(wbs->height),
+ SILC_STR_UI_INT(wbs->brush_color),
+ SILC_STR_UI_SHORT(wbs->brush_size),
+ SILC_STR_END);
+ silc_buffer_pull(packet, SILCPURPLE_WB_HEADER);
+ for (list = draw_list; list; list = list->next) {
+ silc_buffer_format(packet,
+ SILC_STR_UI_INT(GPOINTER_TO_INT(list->data)),
+ SILC_STR_END);
+ silc_buffer_pull(packet, 4);
+ }
+
+ /* Send the message */
+ if (wbs->type == 0) {
+ /* Private message */
+ silc_client_send_private_message(sg->client, sg->conn,
+ wbs->u.client,
+ SILC_MESSAGE_FLAG_DATA,
+ packet->head, len, TRUE);
+ } else if (wbs->type == 1) {
+ /* Channel message. Channel private keys are not supported. */
+ silc_client_send_channel_message(sg->client, sg->conn,
+ wbs->u.channel, NULL,
+ SILC_MESSAGE_FLAG_DATA,
+ packet->head, len, TRUE);
+ }
+
+ silc_buffer_free(packet);
+}
+
+/* Purple Whiteboard operations */
+
+void silcpurple_wb_start(PurpleWhiteboard *wb)
+{
+ /* Nothing here. Everything is in initialization */
+}
+
+void silcpurple_wb_end(PurpleWhiteboard *wb)
+{
+ silc_free(wb->proto_data);
+ wb->proto_data = NULL;
+}
+
+void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height)
+{
+ SilcPurpleWb wbs = wb->proto_data;
+ *width = wbs->width;
+ *height = wbs->height;
+}
+
+void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height)
+{
+ SilcPurpleWb wbs = wb->proto_data;
+ wbs->width = width > SILCPURPLE_WB_WIDTH_MAX ? SILCPURPLE_WB_WIDTH_MAX :
+ width;
+ wbs->height = height > SILCPURPLE_WB_HEIGHT_MAX ? SILCPURPLE_WB_HEIGHT_MAX :
+ height;
+
+ /* Update whiteboard */
+ purple_whiteboard_set_dimensions(wb, wbs->width, wbs->height);
+}
+
+void silcpurple_wb_get_brush(const PurpleWhiteboard *wb, int *size, int *color)
+{
+ SilcPurpleWb wbs = wb->proto_data;
+ *size = wbs->brush_size;
+ *color = wbs->brush_color;
+}
+
+void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color)
+{
+ SilcPurpleWb wbs = wb->proto_data;
+ wbs->brush_size = size;
+ wbs->brush_color = color;
+
+ /* Update whiteboard */
+ purple_whiteboard_set_brush(wb, size, color);
+}
+
+void silcpurple_wb_clear(PurpleWhiteboard *wb)
+{
+ SilcPurpleWb wbs = wb->proto_data;
+ SilcBuffer packet;
+ int len;
+ PurpleConnection *gc;
+ SilcPurple sg;
+
+ gc = purple_account_get_connection(wb->account);
+ g_return_if_fail(gc);
+ sg = gc->proto_data;
+ g_return_if_fail(sg);
+
+ len = SILCPURPLE_WB_HEADER;
+ packet = silc_buffer_alloc_size(len);
+ if (!packet)
+ return;
+
+ /* Assmeble packet */
+ silc_buffer_format(packet,
+ SILC_STR_UI32_STRING(SILCPURPLE_WB_MIME),
+ SILC_STR_UI_CHAR(SILCPURPLE_WB_CLEAR),
+ SILC_STR_UI_SHORT(wbs->width),
+ SILC_STR_UI_SHORT(wbs->height),
+ SILC_STR_UI_INT(wbs->brush_color),
+ SILC_STR_UI_SHORT(wbs->brush_size),
+ SILC_STR_END);
+
+ /* Send the message */
+ if (wbs->type == 0) {
+ /* Private message */
+ silc_client_send_private_message(sg->client, sg->conn,
+ wbs->u.client,
+ SILC_MESSAGE_FLAG_DATA,
+ packet->head, len, TRUE);
+ } else if (wbs->type == 1) {
+ /* Channel message */
+ silc_client_send_channel_message(sg->client, sg->conn,
+ wbs->u.channel, NULL,
+ SILC_MESSAGE_FLAG_DATA,
+ packet->head, len, TRUE);
+ }
+
+ silc_buffer_free(packet);
+}
diff --git a/libpurple/protocols/silc10/wb.h b/libpurple/protocols/silc10/wb.h
new file mode 100644
index 0000000000..5ad0bf6fdd
--- /dev/null
+++ b/libpurple/protocols/silc10/wb.h
@@ -0,0 +1,49 @@
+/*
+
+ silcpurple.h
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2005 Pekka Riikonen
+
+ 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; version 2 of the License.
+
+ 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.
+
+*/
+
+#ifndef SILCPURPLE_WB_H
+#define SILCPURPLE_WB_H
+
+#include "silcpurple.h"
+#include "whiteboard.h"
+
+PurpleWhiteboard *
+silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry);
+PurpleWhiteboard *
+silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel);
+void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry sender, SilcMessagePayload payload,
+ SilcMessageFlags flags, const unsigned char *message,
+ SilcUInt32 message_len);
+void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry sender, SilcChannelEntry channel,
+ SilcMessagePayload payload,
+ SilcMessageFlags flags,
+ const unsigned char *message,
+ SilcUInt32 message_len);
+void silcpurple_wb_start(PurpleWhiteboard *wb);
+void silcpurple_wb_end(PurpleWhiteboard *wb);
+void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height);
+void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height);
+void silcpurple_wb_get_brush(const PurpleWhiteboard *wb, int *size, int *color);
+void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color);
+void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list);
+void silcpurple_wb_clear(PurpleWhiteboard *wb);
+
+#endif /* SILCPURPLE_WB_H */
diff --git a/libpurple/protocols/simple/simple.c b/libpurple/protocols/simple/simple.c
index 20e029c8c5..c667a1490d 100644
--- a/libpurple/protocols/simple/simple.c
+++ b/libpurple/protocols/simple/simple.c
@@ -1243,7 +1243,7 @@ static void process_incoming_subscribe(struct simple_account_data *sip, struct s
foundxpidf = TRUE;
if(tmp2) {
*tmp2 = ',';
- tmp = tmp2;
+ tmp = tmp2 + 1;
while(*tmp == ' ') tmp++;
} else
tmp = 0;
diff --git a/libpurple/win32/libc_interface.c b/libpurple/win32/libc_interface.c
index cd7b9e4fa7..cbcd42081e 100644
--- a/libpurple/win32/libc_interface.c
+++ b/libpurple/win32/libc_interface.c
@@ -1,6 +1,6 @@
/*
* purple
- *
+ *
* Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -74,7 +74,7 @@ int wpurple_connect(int socket, struct sockaddr *addr, u_long length) {
int ret;
ret = connect( socket, addr, length );
-
+
if( ret == SOCKET_ERROR ) {
errno = WSAGetLastError();
if( errno == WSAEWOULDBLOCK )
@@ -129,6 +129,8 @@ int wpurple_sendto(int socket, const void *buf, size_t len, int flags, const str
if ((ret = sendto(socket, buf, len, flags, to, tolen)
) == SOCKET_ERROR) {
errno = WSAGetLastError();
+ if(errno == WSAEWOULDBLOCK || errno == WSAEINPROGRESS)
+ errno = EAGAIN;
return -1;
}
return ret;
@@ -302,7 +304,7 @@ int wpurple_read(int fd, void *buf, unsigned int size) {
if(wpurple_is_socket(fd)) {
if((ret = recv(fd, buf, size, 0)) == SOCKET_ERROR) {
errno = WSAGetLastError();
- if(errno == WSAEWOULDBLOCK)
+ if(errno == WSAEWOULDBLOCK || errno == WSAEINPROGRESS)
errno = EAGAIN;
return -1;
}
@@ -330,7 +332,7 @@ int wpurple_send(int fd, const void *buf, unsigned int size, int flags) {
if (ret == SOCKET_ERROR) {
errno = WSAGetLastError();
- if(errno == WSAEWOULDBLOCK)
+ if(errno == WSAEWOULDBLOCK || errno == WSAEINPROGRESS)
errno = EAGAIN;
return -1;
}
@@ -350,7 +352,7 @@ int wpurple_recv(int fd, void *buf, size_t len, int flags) {
if((ret = recv(fd, buf, len, flags)) == SOCKET_ERROR) {
errno = WSAGetLastError();
- if(errno == WSAEWOULDBLOCK)
+ if(errno == WSAEWOULDBLOCK || errno == WSAEINPROGRESS)
errno = EAGAIN;
return -1;
} else {
@@ -392,7 +394,7 @@ int wpurple_gettimeofday(struct timeval *p, struct timezone *z) {
z->tz_minuteswest = _timezone/60;
z->tz_dsttime = _daylight;
}
-
+
if (p != 0) {
_ftime(&timebuffer);
p->tv_sec = timebuffer.time; /* seconds since 1-1-1970 */
@@ -1044,7 +1046,7 @@ wpurple_get_timezone_abbreviation(const struct tm *tm)
* Returns: zero if the pathname refers to an existing file system
* object that has all the tested permissions, or -1 otherwise or on
* error.
- *
+ *
* Since: 2.8
*/
int
@@ -1056,7 +1058,7 @@ wpurple_g_access (const gchar *filename,
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
-
+
if (wfilename == NULL)
{
errno = EINVAL;
@@ -1072,7 +1074,7 @@ wpurple_g_access (const gchar *filename,
return retval;
}
else
- {
+ {
gchar *cp_filename = g_locale_from_utf8 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
diff --git a/pidgin/gtkaccount.c b/pidgin/gtkaccount.c
index c63799e406..0ed8148cee 100644
--- a/pidgin/gtkaccount.c
+++ b/pidgin/gtkaccount.c
@@ -461,7 +461,7 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
PurpleAccountUserSplit *split = l->data;
char *buf;
- buf = g_strdup_printf("%s:", purple_account_user_split_get_text(split));
+ buf = g_strdup_printf("_%s:", purple_account_user_split_get_text(split));
entry = gtk_entry_new();
@@ -528,7 +528,7 @@ add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent)
/* Alias */
dialog->alias_entry = gtk_entry_new();
- add_pref_box(dialog, vbox, _("Local _alias:"), dialog->alias_entry);
+ add_pref_box(dialog, vbox, _("_Local alias:"), dialog->alias_entry);
/* Remember Password */
dialog->remember_pass_check =
@@ -705,8 +705,8 @@ add_protocol_options(AccountPrefsDialog *dialog, GtkWidget *parent)
PurpleKeyValuePair *kvp;
GList *l;
char buf[1024];
- char *title;
- const char *str_value;
+ char *title, *tmp;
+ const char *str_value, *protocol;
gboolean bool_value;
if (dialog->protocol_frame != NULL) {
@@ -762,8 +762,9 @@ add_protocol_options(AccountPrefsDialog *dialog, GtkWidget *parent)
purple_account_option_get_default_bool(option));
}
- check = gtk_check_button_new_with_label(
- purple_account_option_get_text(option));
+ tmp = g_strconcat("_", purple_account_option_get_text(option), NULL);
+ check = gtk_check_button_new_with_mnemonic(tmp);
+ g_free(tmp);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check),
bool_value);
@@ -795,7 +796,7 @@ add_protocol_options(AccountPrefsDialog *dialog, GtkWidget *parent)
entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), buf);
- title = g_strdup_printf("%s:",
+ title = g_strdup_printf("_%s:",
purple_account_option_get_text(option));
add_pref_box(dialog, vbox, title, entry);
@@ -835,11 +836,11 @@ add_protocol_options(AccountPrefsDialog *dialog, GtkWidget *parent)
if (str_value == NULL && g_object_get_data(G_OBJECT(item), "fake") &&
!strcmp(_("Connect server"), purple_account_option_get_text(option)))
str_value = "talk.google.com";
-
+
if (str_value != NULL)
gtk_entry_set_text(GTK_ENTRY(entry), str_value);
- title = g_strdup_printf("%s:",
+ title = g_strdup_printf("_%s:",
purple_account_option_get_text(option));
add_pref_box(dialog, vbox, title, entry);
@@ -900,7 +901,7 @@ add_protocol_options(AccountPrefsDialog *dialog, GtkWidget *parent)
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),
renderer, "text", 0, NULL);
- title = g_strdup_printf("%s:",
+ title = g_strdup_printf("_%s:",
purple_account_option_get_text(option));
add_pref_box(dialog, vbox, title, combo);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e50199730f..f7510a47b9 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -132,6 +132,14 @@ libpurple/protocols/silc/pk.c
libpurple/protocols/silc/silc.c
libpurple/protocols/silc/util.c
libpurple/protocols/silc/wb.c
+libpurple/protocols/silc10/buddy.c
+libpurple/protocols/silc10/chat.c
+libpurple/protocols/silc10/ft.c
+libpurple/protocols/silc10/ops.c
+libpurple/protocols/silc10/pk.c
+libpurple/protocols/silc10/silc.c
+libpurple/protocols/silc10/util.c
+libpurple/protocols/silc10/wb.c
libpurple/protocols/simple/simple.c
libpurple/protocols/toc/toc.c
libpurple/protocols/yahoo/yahoo.c