pidgin/pidgin

closing merged branch
port-changes-from-branch-2.x.y-to-default
2020-02-03, Gary Kramlich
2f836435c33c
closing merged branch
/*
wb.c
Author: Pekka Riikonen <priikone@silcnet.org>
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
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 "internal.h"
PURPLE_BEGIN_IGNORE_CAST_ALIGN
#include "silc.h"
PURPLE_END_IGNORE_CAST_ALIGN
#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)
{
PurpleWhiteboard *wb;
SilcPurpleWb wbs;
wb = purple_whiteboard_get_session(sg->account, client_entry->nickname);
if (!wb)
wb = purple_whiteboard_new(sg->account, client_entry->nickname, 0);
if (!wb)
return NULL;
if (!purple_whiteboard_get_protocol_data(wb)) {
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;
purple_whiteboard_set_protocol_data(wb, 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_new(sg->account, channel->channel_name, 0);
if (!wb)
return NULL;
if (!purple_whiteboard_get_protocol_data(wb)) {
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;
purple_whiteboard_set_protocol_data(wb, wbs);
/* Start the whiteboard */
purple_whiteboard_start(wb);
purple_whiteboard_clear(wb);
}
return wb;
}
static void
silcpurple_wb_parse(PurpleWhiteboard *wb,
unsigned char *message, SilcUInt32 message_len)
{
SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
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, width, height);
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 (silc_buffer_len(&buf) > 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, 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[256];
SilcPurpleWbRequest req;
PurpleConnection *gc;
SilcPurple sg;
gc = client->application;
sg = purple_connection_get_protocol_data(gc);
/* 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,
(unsigned char *)message,
message_len);
return;
}
/* Close any previous unaccepted requests */
purple_request_close_with_handle(sender);
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,
purple_request_cpar_from_connection(gc), 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;
gc = client->application;
sg = purple_connection_get_protocol_data(gc);
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;
}
silcpurple_wb_parse(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;
gc = client->application;
sg = purple_connection_get_protocol_data(gc);
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;
}
silcpurple_wb_parse(wb, (unsigned char *)message, message_len);
}
/* Send whiteboard message */
void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list)
{
SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
SilcBuffer packet;
GList *list;
int len;
PurpleConnection *gc;
SilcPurple sg;
g_return_if_fail(draw_list);
gc = purple_account_get_connection(purple_whiteboard_get_account(wb));
g_return_if_fail(gc);
sg = purple_connection_get_protocol_data(gc);
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, 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, NULL,
packet->head, len);
}
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)
{
SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
silc_free(wbs);
purple_whiteboard_set_protocol_data(wb, NULL);
}
void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height)
{
SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
*width = wbs->width;
*height = wbs->height;
}
void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height)
{
SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
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 = purple_whiteboard_get_protocol_data(wb);
*size = wbs->brush_size;
*color = wbs->brush_color;
}
void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color)
{
SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
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 = purple_whiteboard_get_protocol_data(wb);
SilcBuffer packet;
int len;
PurpleConnection *gc;
SilcPurple sg;
gc = purple_account_get_connection(purple_whiteboard_get_account(wb));
g_return_if_fail(gc);
sg = purple_connection_get_protocol_data(gc);
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, 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, NULL,
packet->head, len);
}
silc_buffer_free(packet);
}