changeset 12058:d5daff460913

[gaim-migrate @ 14353] SILC whiteboard support from Pekka Riikonen. committer: Tailor Script <tailor@pidgin.im>
author Ethan Blanton <elb@pidgin.im>
date Sat, 12 Nov 2005 23:04:44 +0000
parents 68c18dd8a316
children 7bef0fa7d4aa
files ChangeLog src/protocols/silc/buddy.c src/protocols/silc/chat.c src/protocols/silc/ops.c src/protocols/silc/silc.c src/protocols/silc/wb.c src/protocols/silc/wb.h
diffstat 7 files changed, 646 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Nov 12 23:04:12 2005 +0000
+++ b/ChangeLog	Sat Nov 12 23:04:44 2005 +0000
@@ -99,6 +99,7 @@
 	* Updated Gadu-Gadu protocol support (Bartosz Oler)
 	* SIP/SIMPLE support (Thomas Butter)
 	* Sametime protocol support
+	* SILC whiteboard support (Pekka Riikonen)
 
 	Other Noteworthy Changes:
 	* UPNP and NAT traversal support (Adam J. Warrington)
--- a/src/protocols/silc/buddy.c	Sat Nov 12 23:04:12 2005 +0000
+++ b/src/protocols/silc/buddy.c	Sat Nov 12 23:04:44 2005 +0000
@@ -20,6 +20,7 @@
 #include "silcincludes.h"
 #include "silcclient.h"
 #include "silcgaim.h"
+#include "wb.h"
 
 /***************************** Key Agreement *********************************/
 
@@ -1547,9 +1548,21 @@
 				 b->name, "Killed by operator", NULL);
 }
 
+typedef struct {
+	SilcGaim sg;
+	SilcClientEntry client_entry;
+} *SilcGaimBuddyWb;
+
+static void
+silcgaim_buddy_wb(GaimBlistNode *node, gpointer data)
+{
+	SilcGaimBuddyWb wb = data;
+	silcgaim_wb_init(wb->sg, wb->client_entry);
+	silc_free(wb);
+}
+
 GList *silcgaim_buddy_menu(GaimBuddy *buddy)
 {
-
 	GaimConnection *gc = gaim_account_get_connection(buddy->account);
 	SilcGaim sg = gc->proto_data;
 	SilcClientConnection conn = sg->conn;
@@ -1557,6 +1570,7 @@
 	SilcClientEntry client_entry = NULL;
 	GaimBlistNodeAction *act;
 	GList *m = NULL;
+	SilcGaimBuddyWb wb;
 
 	pkfile = gaim_blist_node_get_string((GaimBlistNode *) buddy, "public-key");
 	client_entry = silc_client_get_client_by_id(sg->client,
@@ -1597,5 +1611,12 @@
 		m = g_list_append(m, act);
 	}
 
+	wb = silc_calloc(1, sizeof(*wb));
+	wb->sg = sg;
+	wb->client_entry = client_entry;
+	act = gaim_blist_node_action_new(_("Draw On Whiteboard"),
+	                                 silcgaim_buddy_wb, (void *)wb, NULL);
+	m = g_list_append(m, act);
+
 	return m;
 }
--- a/src/protocols/silc/chat.c	Sat Nov 12 23:04:12 2005 +0000
+++ b/src/protocols/silc/chat.c	Sat Nov 12 23:04:44 2005 +0000
@@ -20,6 +20,7 @@
 #include "silcincludes.h"
 #include "silcclient.h"
 #include "silcgaim.h"
+#include "wb.h"
 
 /***************************** Channel Routines ******************************/
 
@@ -832,6 +833,19 @@
 				 "+s", NULL);
 }
 
+typedef struct {
+	SilcGaim sg;
+	SilcChannelEntry channel;
+} *SilcGaimChatWb;
+
+static void
+silcgaim_chat_wb(GaimBlistNode *node, gpointer data)
+{
+	SilcGaimChatWb wb = data;
+	silcgaim_wb_init_ch(wb->sg, wb->channel);
+	silc_free(wb);
+}
+
 GList *silcgaim_chat_menu(GaimChat *chat)
 {
 	GHashTable *components = chat->components;
@@ -949,6 +963,16 @@
 		}
 	}
 
+	if (channel) {
+		SilcGaimChatWb wb;
+	        wb = silc_calloc(1, sizeof(*wb));
+        	wb->sg = sg;
+        	wb->channel = channel;
+        	act = gaim_blist_node_action_new(_("Draw On Whiteboard"),
+                                         silcgaim_chat_wb, (void *)wb, NULL);
+	        m = g_list_append(m, act);
+	}
+
 	return m;
 }
 
--- a/src/protocols/silc/ops.c	Sat Nov 12 23:04:12 2005 +0000
+++ b/src/protocols/silc/ops.c	Sat Nov 12 23:04:44 2005 +0000
@@ -20,6 +20,7 @@
 #include "silcincludes.h"
 #include "silcclient.h"
 #include "silcgaim.h"
+#include "wb.h"
 
 /* Message sent to the application by library. `conn' associates the
    message to a specific connection.  `conn', however, may be NULL.
@@ -80,7 +81,23 @@
 	}
 
 	if (flags & SILC_MESSAGE_FLAG_DATA) {
-		/* XXX */
+		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"))
+			silcgaim_wb_receive_ch(client, conn, sender, channel,
+					       payload, flags, data, data_len);
+
 		return;
 	}
 
@@ -159,7 +176,23 @@
 	}
 
 	if (flags & SILC_MESSAGE_FLAG_DATA) {
-		/* XXX */
+		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"))
+			silcgaim_wb_receive(client, conn, sender, payload,
+					    flags, data, data_len);
+
 		return;
 	}
 
--- a/src/protocols/silc/silc.c	Sat Nov 12 23:04:12 2005 +0000
+++ b/src/protocols/silc/silc.c	Sat Nov 12 23:04:44 2005 +0000
@@ -21,6 +21,7 @@
 #include "silcclient.h"
 #include "silcgaim.h"
 #include "version.h"
+#include "wb.h"
 
 extern SilcClientOperations ops;
 static GaimPlugin *silc_plugin = NULL;
@@ -1484,6 +1485,18 @@
 	silcgaim_pref_frame,
 };
 
+static GaimWhiteboardPrplOps silcgaim_wb_ops =
+{
+	silcgaim_wb_start,
+	silcgaim_wb_end,
+	silcgaim_wb_get_dimensions,
+	silcgaim_wb_set_dimensions,
+	silcgaim_wb_get_brush,
+	silcgaim_wb_set_brush,
+	silcgaim_wb_send,
+	silcgaim_wb_clear,
+};
+
 static GaimPluginProtocolInfo prpl_info =
 {
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
@@ -1543,7 +1556,8 @@
 	silcgaim_roomlist_cancel,	/* roomlist_cancel */
 	NULL,						/* roomlist_expand_category */
 	NULL,						/* can_receive_file */
-	silcgaim_ftp_send_file		/* send_file */
+	silcgaim_ftp_send_file,		/* send_file */
+	&silcgaim_wb_ops,		/* whiteboard operations */
 };
 
 static GaimPluginInfo info =
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/silc/wb.c	Sat Nov 12 23:04:44 2005 +0000
@@ -0,0 +1,500 @@
+/*
+
+  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 "silcgaim.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 SILCGAIM_WB_MIME "MIME-Version: 1.0\r\nContent-Type: application/x-wb\r\nContent-Transfer-Encoding: binary\r\n\r\n"
+#define SILCGAIM_WB_HEADER strlen(SILCGAIM_WB_MIME) + 11
+
+#define SILCGAIM_WB_WIDTH 500
+#define SILCGAIM_WB_HEIGHT 400
+#define SILCGAIM_WB_WIDTH_MAX 1024
+#define SILCGAIM_WB_HEIGHT_MAX 1024
+
+/* Commands */
+typedef enum {
+	SILCGAIM_WB_DRAW 	= 0x01,
+	SILCGAIM_WB_CLEAR	= 0x02,
+} SilcGaimWbCommand;
+
+/* Brush size */
+typedef enum {
+	SILCGAIM_WB_BRUSH_SMALL = 2,
+	SILCGAIM_WB_BRUSH_MEDIUM = 5,
+	SILCGAIM_WB_BRUSH_LARGE = 10,
+} SilcGaimWbBrushSize;
+
+/* Brush color (XXX Gaim should provide default colors) */
+typedef enum {
+	SILCGAIM_WB_COLOR_BLACK		= 0,
+	SILCGAIM_WB_COLOR_RED		= 13369344,
+	SILCGAIM_WB_COLOR_GREEN		= 52224,
+	SILCGAIM_WB_COLOR_BLUE		= 204,
+	SILCGAIM_WB_COLOR_YELLOW 	= 15658496,
+	SILCGAIM_WB_COLOR_ORANGE	= 16737792,
+	SILCGAIM_WB_COLOR_CYAN		= 52428,
+	SILCGAIM_WB_COLOR_VIOLET	= 5381277,
+	SILCGAIM_WB_COLOR_PURPLE	= 13369548,
+	SILCGAIM_WB_COLOR_TAN		= 12093547,
+	SILCGAIM_WB_COLOR_BROWN		= 5256485,
+	SILCGAIM_WB_COLOR_GREY		= 11184810,
+	SILCGAIM_WB_COLOR_WHITE		= 16777215,
+} SilcGaimWbColor;
+
+typedef struct {
+	int type;		/* 0 = buddy, 1 = channel */
+	union {
+		SilcClientEntry client;
+		SilcChannelEntry channel;
+	} u;
+	int width;
+	int height;
+	int brush_size;
+	int brush_color;
+} *SilcGaimWb;
+
+/* Initialize whiteboard */
+
+GaimWhiteboard *silcgaim_wb_init(SilcGaim sg, SilcClientEntry client_entry)
+{
+        SilcClientConnection conn;
+	GaimWhiteboard *wb;
+	SilcGaimWb wbs;
+
+	conn = sg->conn;
+	wb = gaim_whiteboard_get_session(sg->account, client_entry->nickname);
+	if (!wb)
+		wb = gaim_whiteboard_create(sg->account, client_entry->nickname,
+					    0);
+	if (!wb)
+		return NULL;
+
+	wbs = silc_calloc(1, sizeof(*wbs));
+	if (!wbs)
+		return NULL;
+	wbs->type = 0;
+	wbs->u.client = client_entry;
+	wbs->width = SILCGAIM_WB_WIDTH;
+	wbs->height = SILCGAIM_WB_HEIGHT;
+	wbs->brush_size = SILCGAIM_WB_BRUSH_SMALL;
+	wbs->brush_color = SILCGAIM_WB_COLOR_BLACK;
+	wb->proto_data = wbs;
+
+	/* Start the whiteboard */
+	gaim_whiteboard_start(wb);
+	gaim_whiteboard_clear(wb);
+
+	return wb;
+}
+
+GaimWhiteboard *silcgaim_wb_init_ch(SilcGaim sg, SilcChannelEntry channel)
+{
+	GaimWhiteboard *wb;
+	SilcGaimWb wbs;
+
+	wb = gaim_whiteboard_get_session(sg->account, channel->channel_name);
+	if (!wb)
+		wb = gaim_whiteboard_create(sg->account, channel->channel_name,
+					    0);
+	if (!wb)
+		return NULL;
+
+	wbs = silc_calloc(1, sizeof(*wbs));
+	if (!wbs)
+		return NULL;
+	wbs->type = 1;
+	wbs->u.channel = channel;
+	wbs->width = SILCGAIM_WB_WIDTH;
+	wbs->height = SILCGAIM_WB_HEIGHT;
+	wbs->brush_size = SILCGAIM_WB_BRUSH_SMALL;
+	wbs->brush_color = SILCGAIM_WB_COLOR_BLACK;
+	wb->proto_data = wbs;
+
+	/* Start the whiteboard */
+	gaim_whiteboard_start(wb);
+	gaim_whiteboard_clear(wb);
+
+	return wb;
+}
+
+static void
+silcgaim_wb_parse(SilcGaimWb wbs, GaimWhiteboard *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)
+		silcgaim_wb_set_dimensions(wb, height, width);
+
+	if (command == SILCGAIM_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);
+
+			gaim_whiteboard_draw_line(wb, x, y, x + dx, y + dy,
+						  brush_color, brush_size);
+			x += dx;
+			y += dy;
+		}
+	}
+
+	if (command == SILCGAIM_WB_CLEAR)
+		gaim_whiteboard_clear(wb);
+}
+
+typedef struct {
+  unsigned char *message;
+  SilcUInt32 message_len;
+  SilcGaim sg;
+  SilcClientEntry sender;
+  SilcChannelEntry channel;
+} *SilcGaimWbRequest;
+
+static void
+silcgaim_wb_request_cb(SilcGaimWbRequest req, gint id)
+{
+	GaimWhiteboard *wb;
+
+        if (id != 1)
+                goto out;
+
+	if (!req->channel)
+		wb = silcgaim_wb_init(req->sg, req->sender);
+	else
+		wb = silcgaim_wb_init_ch(req->sg, req->channel);
+
+	silcgaim_wb_parse(wb->proto_data, wb, req->message, req->message_len);
+
+  out:
+	silc_free(req->message);
+	silc_free(req);
+}
+
+static void
+silcgaim_wb_request(SilcClient client, const unsigned char *message, 
+		    SilcUInt32 message_len, SilcClientEntry sender, 
+		    SilcChannelEntry channel)
+{
+	char tmp[128];
+	SilcGaimWbRequest req;
+	GaimConnection *gc;
+	SilcGaim sg;
+
+	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);
+	}
+
+	gc = client->application;
+        sg = gc->proto_data;
+
+	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;
+
+	gaim_request_action(gc, _("Whiteboard"), tmp, NULL, 1, req, 2,
+			    _("Yes"), G_CALLBACK(silcgaim_wb_request_cb),
+			    _("No"), G_CALLBACK(silcgaim_wb_request_cb));
+}
+
+/* Process incoming whiteboard message */
+
+void silcgaim_wb_receive(SilcClient client, SilcClientConnection conn,
+			 SilcClientEntry sender, SilcMessagePayload payload,
+			 SilcMessageFlags flags, const unsigned char *message,
+			 SilcUInt32 message_len)
+{
+	SilcGaim sg;
+        GaimConnection *gc;
+	GaimWhiteboard *wb;
+	SilcGaimWb wbs;
+
+	gc = client->application;
+        sg = gc->proto_data;
+
+	wb = gaim_whiteboard_get_session(sg->account, sender->nickname);
+	if (!wb) {
+		/* Ask user if they want to open the whiteboard */
+		silcgaim_wb_request(client, message, message_len,
+				    sender, NULL);
+		return;
+	}
+
+	wbs = wb->proto_data;
+	silcgaim_wb_parse(wbs, wb, (unsigned char *)message, message_len);
+}
+
+/* Process incoming whiteboard message on channel */
+
+void silcgaim_wb_receive_ch(SilcClient client, SilcClientConnection conn,
+			    SilcClientEntry sender, SilcChannelEntry channel,
+			    SilcMessagePayload payload,
+			    SilcMessageFlags flags,
+			    const unsigned char *message,
+			    SilcUInt32 message_len)
+{
+	SilcGaim sg;
+        GaimConnection *gc;
+	GaimWhiteboard *wb;
+	SilcGaimWb wbs;
+
+	gc = client->application;
+        sg = gc->proto_data;
+
+	wb = gaim_whiteboard_get_session(sg->account, channel->channel_name);
+	if (!wb) {
+		/* Ask user if they want to open the whiteboard */
+		silcgaim_wb_request(client, message, message_len,
+				    sender, channel);
+		return;
+	}
+
+	wbs = wb->proto_data;
+	silcgaim_wb_parse(wbs, wb, (unsigned char *)message, message_len);
+}
+
+/* Send whiteboard message */
+
+void silcgaim_wb_send(GaimWhiteboard *wb, GList *draw_list)
+{
+	SilcGaimWb wbs = wb->proto_data;
+	SilcBuffer packet;
+	GList *list;
+	int len;
+        GaimConnection *gc;
+        SilcGaim sg;
+
+	g_return_if_fail(draw_list);
+	gc = gaim_account_get_connection(wb->account);
+	g_return_if_fail(gc);
+ 	sg = gc->proto_data;
+	g_return_if_fail(sg);
+
+	len = SILCGAIM_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(SILCGAIM_WB_MIME),
+			   SILC_STR_UI_CHAR(SILCGAIM_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, SILCGAIM_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 */
+		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);
+}
+
+/* Gaim Whiteboard operations */
+
+void silcgaim_wb_start(GaimWhiteboard *wb)
+{
+	/* Nothing here.  Everything is in initialization */
+}
+
+void silcgaim_wb_end(GaimWhiteboard *wb)
+{
+	silc_free(wb->proto_data);
+	wb->proto_data = NULL;
+}
+
+void silcgaim_wb_get_dimensions(GaimWhiteboard *wb, int *width, int *height)
+{
+	SilcGaimWb wbs = wb->proto_data;
+	*width = wbs->width;
+	*height = wbs->height;
+}
+
+void silcgaim_wb_set_dimensions(GaimWhiteboard *wb, int width, int height)
+{
+	SilcGaimWb wbs = wb->proto_data;
+	wbs->width = width > SILCGAIM_WB_WIDTH_MAX ? SILCGAIM_WB_WIDTH_MAX :
+			width;
+	wbs->height = height > SILCGAIM_WB_HEIGHT_MAX ? SILCGAIM_WB_HEIGHT_MAX :
+			height;
+
+	/* Update whiteboard */
+	gaim_whiteboard_set_dimensions(wb, width, height);
+}
+
+void silcgaim_wb_get_brush(GaimWhiteboard *wb, int *size, int *color)
+{
+	SilcGaimWb wbs = wb->proto_data;
+	*size = wbs->brush_size;
+	*color = wbs->brush_color;
+}
+
+void silcgaim_wb_set_brush(GaimWhiteboard *wb, int size, int color)
+{
+	SilcGaimWb wbs = wb->proto_data;
+	wbs->brush_size = size;
+	wbs->brush_color = color;
+
+	/* Update whiteboard */
+	gaim_whiteboard_set_brush(wb, size, color);
+}
+
+void silcgaim_wb_clear(GaimWhiteboard *wb)
+{
+	SilcGaimWb wbs = wb->proto_data;
+	SilcBuffer packet;
+	int len;
+        GaimConnection *gc;
+        SilcGaim sg;
+
+	gc = gaim_account_get_connection(wb->account);
+	g_return_if_fail(gc);
+ 	sg = gc->proto_data;
+	g_return_if_fail(sg);
+
+	len = SILCGAIM_WB_HEADER;
+	packet = silc_buffer_alloc_size(len);
+	if (!packet)
+		return;
+
+	/* Assmeble packet */
+	silc_buffer_format(packet,
+			   SILC_STR_UI32_STRING(SILCGAIM_WB_MIME),
+			   SILC_STR_UI_CHAR(SILCGAIM_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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/silc/wb.h	Sat Nov 12 23:04:44 2005 +0000
@@ -0,0 +1,49 @@
+/*
+
+  silcgaim.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 SILCGAIM_WB_H
+#define SILCGAIM_WB_H
+
+#include "silcgaim.h"
+#include "whiteboard.h"
+
+GaimWhiteboard *
+silcgaim_wb_init(SilcGaim sg, SilcClientEntry client_entry);
+GaimWhiteboard *
+silcgaim_wb_init_ch(SilcGaim sg, SilcChannelEntry channel);
+void silcgaim_wb_receive(SilcClient client, SilcClientConnection conn,
+			 SilcClientEntry sender, SilcMessagePayload payload,
+			 SilcMessageFlags flags, const unsigned char *message,
+			 SilcUInt32 message_len);
+void silcgaim_wb_receive_ch(SilcClient client, SilcClientConnection conn,
+			    SilcClientEntry sender, SilcChannelEntry channel,
+			    SilcMessagePayload payload,
+			    SilcMessageFlags flags,
+			    const unsigned char *message,
+			    SilcUInt32 message_len);
+void silcgaim_wb_start(GaimWhiteboard *wb);
+void silcgaim_wb_end(GaimWhiteboard *wb);
+void silcgaim_wb_get_dimensions(GaimWhiteboard *wb, int *width, int *height);
+void silcgaim_wb_set_dimensions(GaimWhiteboard *wb, int width, int height);
+void silcgaim_wb_get_brush(GaimWhiteboard *wb, int *size, int *color);
+void silcgaim_wb_set_brush(GaimWhiteboard *wb, int size, int color);
+void silcgaim_wb_send(GaimWhiteboard *wb, GList *draw_list);
+void silcgaim_wb_clear(GaimWhiteboard *wb);
+
+#endif /* SILCGAIM_WB_H */