| |
1 /* |
| |
2 |
| |
3 wb.c |
| |
4 |
| |
5 Author: Pekka Riikonen <priikone@silcnet.org> |
| |
6 |
| |
7 Copyright (C) 2005 Pekka Riikonen |
| |
8 |
| |
9 This program is free software; you can redistribute it and/or modify |
| |
10 it under the terms of the GNU General Public License as published by |
| |
11 the Free Software Foundation; version 2 of the License. |
| |
12 |
| |
13 This program is distributed in the hope that it will be useful, |
| |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
16 GNU General Public License for more details. |
| |
17 |
| |
18 */ |
| |
19 |
| |
20 #include "silcincludes.h" |
| |
21 #include "silcclient.h" |
| |
22 #include "silcpurple.h" |
| |
23 #include "wb.h" |
| |
24 |
| |
25 /* |
| |
26 SILC Whiteboard packet: |
| |
27 |
| |
28 1 byte command |
| |
29 2 bytes width |
| |
30 2 bytes height |
| |
31 4 bytes brush color |
| |
32 2 bytes brush size |
| |
33 n bytes data |
| |
34 |
| |
35 Data: |
| |
36 |
| |
37 4 bytes x |
| |
38 4 bytes y |
| |
39 |
| |
40 Commands: |
| |
41 |
| |
42 0x01 draw |
| |
43 0x02 clear |
| |
44 |
| |
45 MIME: |
| |
46 |
| |
47 MIME-Version: 1.0 |
| |
48 Content-Type: application/x-wb |
| |
49 Content-Transfer-Encoding: binary |
| |
50 |
| |
51 */ |
| |
52 |
| |
53 #define SILCPURPLE_WB_MIME "MIME-Version: 1.0\r\nContent-Type: application/x-wb\r\nContent-Transfer-Encoding: binary\r\n\r\n" |
| |
54 #define SILCPURPLE_WB_HEADER strlen(SILCPURPLE_WB_MIME) + 11 |
| |
55 |
| |
56 #define SILCPURPLE_WB_WIDTH 500 |
| |
57 #define SILCPURPLE_WB_HEIGHT 400 |
| |
58 #define SILCPURPLE_WB_WIDTH_MAX 1024 |
| |
59 #define SILCPURPLE_WB_HEIGHT_MAX 1024 |
| |
60 |
| |
61 /* Commands */ |
| |
62 typedef enum { |
| |
63 SILCPURPLE_WB_DRAW = 0x01, |
| |
64 SILCPURPLE_WB_CLEAR = 0x02, |
| |
65 } SilcPurpleWbCommand; |
| |
66 |
| |
67 /* Brush size */ |
| |
68 typedef enum { |
| |
69 SILCPURPLE_WB_BRUSH_SMALL = 2, |
| |
70 SILCPURPLE_WB_BRUSH_MEDIUM = 5, |
| |
71 SILCPURPLE_WB_BRUSH_LARGE = 10, |
| |
72 } SilcPurpleWbBrushSize; |
| |
73 |
| |
74 /* Brush color (XXX Purple should provide default colors) */ |
| |
75 typedef enum { |
| |
76 SILCPURPLE_WB_COLOR_BLACK = 0, |
| |
77 SILCPURPLE_WB_COLOR_RED = 13369344, |
| |
78 SILCPURPLE_WB_COLOR_GREEN = 52224, |
| |
79 SILCPURPLE_WB_COLOR_BLUE = 204, |
| |
80 SILCPURPLE_WB_COLOR_YELLOW = 15658496, |
| |
81 SILCPURPLE_WB_COLOR_ORANGE = 16737792, |
| |
82 SILCPURPLE_WB_COLOR_CYAN = 52428, |
| |
83 SILCPURPLE_WB_COLOR_VIOLET = 5381277, |
| |
84 SILCPURPLE_WB_COLOR_PURPLE = 13369548, |
| |
85 SILCPURPLE_WB_COLOR_TAN = 12093547, |
| |
86 SILCPURPLE_WB_COLOR_BROWN = 5256485, |
| |
87 SILCPURPLE_WB_COLOR_GREY = 11184810, |
| |
88 SILCPURPLE_WB_COLOR_WHITE = 16777215, |
| |
89 } SilcPurpleWbColor; |
| |
90 |
| |
91 typedef struct { |
| |
92 int type; /* 0 = buddy, 1 = channel */ |
| |
93 union { |
| |
94 SilcClientEntry client; |
| |
95 SilcChannelEntry channel; |
| |
96 } u; |
| |
97 int width; |
| |
98 int height; |
| |
99 int brush_size; |
| |
100 int brush_color; |
| |
101 } *SilcPurpleWb; |
| |
102 |
| |
103 /* Initialize whiteboard */ |
| |
104 |
| |
105 PurpleWhiteboard *silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry) |
| |
106 { |
| |
107 SilcClientConnection conn; |
| |
108 PurpleWhiteboard *wb; |
| |
109 SilcPurpleWb wbs; |
| |
110 |
| |
111 conn = sg->conn; |
| |
112 wb = purple_whiteboard_get_session(sg->account, client_entry->nickname); |
| |
113 if (!wb) |
| |
114 wb = purple_whiteboard_create(sg->account, client_entry->nickname, 0); |
| |
115 if (!wb) |
| |
116 return NULL; |
| |
117 |
| |
118 if (!wb->proto_data) { |
| |
119 wbs = silc_calloc(1, sizeof(*wbs)); |
| |
120 if (!wbs) |
| |
121 return NULL; |
| |
122 wbs->type = 0; |
| |
123 wbs->u.client = client_entry; |
| |
124 wbs->width = SILCPURPLE_WB_WIDTH; |
| |
125 wbs->height = SILCPURPLE_WB_HEIGHT; |
| |
126 wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL; |
| |
127 wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK; |
| |
128 wb->proto_data = wbs; |
| |
129 |
| |
130 /* Start the whiteboard */ |
| |
131 purple_whiteboard_start(wb); |
| |
132 purple_whiteboard_clear(wb); |
| |
133 } |
| |
134 |
| |
135 return wb; |
| |
136 } |
| |
137 |
| |
138 PurpleWhiteboard *silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel) |
| |
139 { |
| |
140 PurpleWhiteboard *wb; |
| |
141 SilcPurpleWb wbs; |
| |
142 |
| |
143 wb = purple_whiteboard_get_session(sg->account, channel->channel_name); |
| |
144 if (!wb) |
| |
145 wb = purple_whiteboard_create(sg->account, channel->channel_name, 0); |
| |
146 if (!wb) |
| |
147 return NULL; |
| |
148 |
| |
149 if (!wb->proto_data) { |
| |
150 wbs = silc_calloc(1, sizeof(*wbs)); |
| |
151 if (!wbs) |
| |
152 return NULL; |
| |
153 wbs->type = 1; |
| |
154 wbs->u.channel = channel; |
| |
155 wbs->width = SILCPURPLE_WB_WIDTH; |
| |
156 wbs->height = SILCPURPLE_WB_HEIGHT; |
| |
157 wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL; |
| |
158 wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK; |
| |
159 wb->proto_data = wbs; |
| |
160 |
| |
161 /* Start the whiteboard */ |
| |
162 purple_whiteboard_start(wb); |
| |
163 purple_whiteboard_clear(wb); |
| |
164 } |
| |
165 |
| |
166 return wb; |
| |
167 } |
| |
168 |
| |
169 static void |
| |
170 silcpurple_wb_parse(SilcPurpleWb wbs, PurpleWhiteboard *wb, |
| |
171 unsigned char *message, SilcUInt32 message_len) |
| |
172 { |
| |
173 SilcUInt8 command; |
| |
174 SilcUInt16 width, height, brush_size; |
| |
175 SilcUInt32 brush_color, x, y, dx, dy; |
| |
176 SilcBufferStruct buf; |
| |
177 int ret; |
| |
178 |
| |
179 /* Parse the packet */ |
| |
180 silc_buffer_set(&buf, message, message_len); |
| |
181 ret = silc_buffer_unformat(&buf, |
| |
182 SILC_STR_UI_CHAR(&command), |
| |
183 SILC_STR_UI_SHORT(&width), |
| |
184 SILC_STR_UI_SHORT(&height), |
| |
185 SILC_STR_UI_INT(&brush_color), |
| |
186 SILC_STR_UI_SHORT(&brush_size), |
| |
187 SILC_STR_END); |
| |
188 if (ret < 0) |
| |
189 return; |
| |
190 silc_buffer_pull(&buf, ret); |
| |
191 |
| |
192 /* Update whiteboard if its dimensions changed */ |
| |
193 if (width != wbs->width || height != wbs->height) |
| |
194 silcpurple_wb_set_dimensions(wb, height, width); |
| |
195 |
| |
196 if (command == SILCPURPLE_WB_DRAW) { |
| |
197 /* Parse data and draw it */ |
| |
198 ret = silc_buffer_unformat(&buf, |
| |
199 SILC_STR_UI_INT(&dx), |
| |
200 SILC_STR_UI_INT(&dy), |
| |
201 SILC_STR_END); |
| |
202 if (ret < 0) |
| |
203 return; |
| |
204 silc_buffer_pull(&buf, 8); |
| |
205 x = dx; |
| |
206 y = dy; |
| |
207 while (buf.len > 0) { |
| |
208 ret = silc_buffer_unformat(&buf, |
| |
209 SILC_STR_UI_INT(&dx), |
| |
210 SILC_STR_UI_INT(&dy), |
| |
211 SILC_STR_END); |
| |
212 if (ret < 0) |
| |
213 return; |
| |
214 silc_buffer_pull(&buf, 8); |
| |
215 |
| |
216 purple_whiteboard_draw_line(wb, x, y, x + dx, y + dy, |
| |
217 brush_color, brush_size); |
| |
218 x += dx; |
| |
219 y += dy; |
| |
220 } |
| |
221 } |
| |
222 |
| |
223 if (command == SILCPURPLE_WB_CLEAR) |
| |
224 purple_whiteboard_clear(wb); |
| |
225 } |
| |
226 |
| |
227 typedef struct { |
| |
228 unsigned char *message; |
| |
229 SilcUInt32 message_len; |
| |
230 SilcPurple sg; |
| |
231 SilcClientEntry sender; |
| |
232 SilcChannelEntry channel; |
| |
233 } *SilcPurpleWbRequest; |
| |
234 |
| |
235 static void |
| |
236 silcpurple_wb_request_cb(SilcPurpleWbRequest req, gint id) |
| |
237 { |
| |
238 PurpleWhiteboard *wb; |
| |
239 |
| |
240 if (id != 1) |
| |
241 goto out; |
| |
242 |
| |
243 if (!req->channel) |
| |
244 wb = silcpurple_wb_init(req->sg, req->sender); |
| |
245 else |
| |
246 wb = silcpurple_wb_init_ch(req->sg, req->channel); |
| |
247 |
| |
248 silcpurple_wb_parse(wb->proto_data, wb, req->message, req->message_len); |
| |
249 |
| |
250 out: |
| |
251 silc_free(req->message); |
| |
252 silc_free(req); |
| |
253 } |
| |
254 |
| |
255 static void |
| |
256 silcpurple_wb_request(SilcClient client, const unsigned char *message, |
| |
257 SilcUInt32 message_len, SilcClientEntry sender, |
| |
258 SilcChannelEntry channel) |
| |
259 { |
| |
260 char tmp[128]; |
| |
261 SilcPurpleWbRequest req; |
| |
262 PurpleConnection *gc; |
| |
263 SilcPurple sg; |
| |
264 |
| |
265 gc = client->application; |
| |
266 sg = gc->proto_data; |
| |
267 |
| |
268 /* Open whiteboard automatically if requested */ |
| |
269 if (purple_account_get_bool(sg->account, "open-wb", FALSE)) { |
| |
270 PurpleWhiteboard *wb; |
| |
271 |
| |
272 if (!channel) |
| |
273 wb = silcpurple_wb_init(sg, sender); |
| |
274 else |
| |
275 wb = silcpurple_wb_init_ch(sg, channel); |
| |
276 |
| |
277 silcpurple_wb_parse(wb->proto_data, wb, (unsigned char *)message, |
| |
278 message_len); |
| |
279 return; |
| |
280 } |
| |
281 |
| |
282 if (!channel) { |
| |
283 g_snprintf(tmp, sizeof(tmp), |
| |
284 _("%s sent message to whiteboard. Would you like " |
| |
285 "to open the whiteboard?"), sender->nickname); |
| |
286 } else { |
| |
287 g_snprintf(tmp, sizeof(tmp), |
| |
288 _("%s sent message to whiteboard on %s channel. " |
| |
289 "Would you like to open the whiteboard?"), |
| |
290 sender->nickname, channel->channel_name); |
| |
291 } |
| |
292 |
| |
293 req = silc_calloc(1, sizeof(*req)); |
| |
294 if (!req) |
| |
295 return; |
| |
296 req->message = silc_memdup(message, message_len); |
| |
297 req->message_len = message_len; |
| |
298 req->sender = sender; |
| |
299 req->channel = channel; |
| |
300 req->sg = sg; |
| |
301 |
| |
302 purple_request_action(gc, _("Whiteboard"), tmp, NULL, 1, req, 2, |
| |
303 _("Yes"), G_CALLBACK(silcpurple_wb_request_cb), |
| |
304 _("No"), G_CALLBACK(silcpurple_wb_request_cb)); |
| |
305 } |
| |
306 |
| |
307 /* Process incoming whiteboard message */ |
| |
308 |
| |
309 void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn, |
| |
310 SilcClientEntry sender, SilcMessagePayload payload, |
| |
311 SilcMessageFlags flags, const unsigned char *message, |
| |
312 SilcUInt32 message_len) |
| |
313 { |
| |
314 SilcPurple sg; |
| |
315 PurpleConnection *gc; |
| |
316 PurpleWhiteboard *wb; |
| |
317 SilcPurpleWb wbs; |
| |
318 |
| |
319 gc = client->application; |
| |
320 sg = gc->proto_data; |
| |
321 |
| |
322 wb = purple_whiteboard_get_session(sg->account, sender->nickname); |
| |
323 if (!wb) { |
| |
324 /* Ask user if they want to open the whiteboard */ |
| |
325 silcpurple_wb_request(client, message, message_len, |
| |
326 sender, NULL); |
| |
327 return; |
| |
328 } |
| |
329 |
| |
330 wbs = wb->proto_data; |
| |
331 silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len); |
| |
332 } |
| |
333 |
| |
334 /* Process incoming whiteboard message on channel */ |
| |
335 |
| |
336 void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn, |
| |
337 SilcClientEntry sender, SilcChannelEntry channel, |
| |
338 SilcMessagePayload payload, |
| |
339 SilcMessageFlags flags, |
| |
340 const unsigned char *message, |
| |
341 SilcUInt32 message_len) |
| |
342 { |
| |
343 SilcPurple sg; |
| |
344 PurpleConnection *gc; |
| |
345 PurpleWhiteboard *wb; |
| |
346 SilcPurpleWb wbs; |
| |
347 |
| |
348 gc = client->application; |
| |
349 sg = gc->proto_data; |
| |
350 |
| |
351 wb = purple_whiteboard_get_session(sg->account, channel->channel_name); |
| |
352 if (!wb) { |
| |
353 /* Ask user if they want to open the whiteboard */ |
| |
354 silcpurple_wb_request(client, message, message_len, |
| |
355 sender, channel); |
| |
356 return; |
| |
357 } |
| |
358 |
| |
359 wbs = wb->proto_data; |
| |
360 silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len); |
| |
361 } |
| |
362 |
| |
363 /* Send whiteboard message */ |
| |
364 |
| |
365 void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list) |
| |
366 { |
| |
367 SilcPurpleWb wbs = wb->proto_data; |
| |
368 SilcBuffer packet; |
| |
369 GList *list; |
| |
370 int len; |
| |
371 PurpleConnection *gc; |
| |
372 SilcPurple sg; |
| |
373 |
| |
374 g_return_if_fail(draw_list); |
| |
375 gc = purple_account_get_connection(wb->account); |
| |
376 g_return_if_fail(gc); |
| |
377 sg = gc->proto_data; |
| |
378 g_return_if_fail(sg); |
| |
379 |
| |
380 len = SILCPURPLE_WB_HEADER; |
| |
381 for (list = draw_list; list; list = list->next) |
| |
382 len += 4; |
| |
383 |
| |
384 packet = silc_buffer_alloc_size(len); |
| |
385 if (!packet) |
| |
386 return; |
| |
387 |
| |
388 /* Assmeble packet */ |
| |
389 silc_buffer_format(packet, |
| |
390 SILC_STR_UI32_STRING(SILCPURPLE_WB_MIME), |
| |
391 SILC_STR_UI_CHAR(SILCPURPLE_WB_DRAW), |
| |
392 SILC_STR_UI_SHORT(wbs->width), |
| |
393 SILC_STR_UI_SHORT(wbs->height), |
| |
394 SILC_STR_UI_INT(wbs->brush_color), |
| |
395 SILC_STR_UI_SHORT(wbs->brush_size), |
| |
396 SILC_STR_END); |
| |
397 silc_buffer_pull(packet, SILCPURPLE_WB_HEADER); |
| |
398 for (list = draw_list; list; list = list->next) { |
| |
399 silc_buffer_format(packet, |
| |
400 SILC_STR_UI_INT(GPOINTER_TO_INT(list->data)), |
| |
401 SILC_STR_END); |
| |
402 silc_buffer_pull(packet, 4); |
| |
403 } |
| |
404 |
| |
405 /* Send the message */ |
| |
406 if (wbs->type == 0) { |
| |
407 /* Private message */ |
| |
408 silc_client_send_private_message(sg->client, sg->conn, |
| |
409 wbs->u.client, |
| |
410 SILC_MESSAGE_FLAG_DATA, |
| |
411 packet->head, len, TRUE); |
| |
412 } else if (wbs->type == 1) { |
| |
413 /* Channel message. Channel private keys are not supported. */ |
| |
414 silc_client_send_channel_message(sg->client, sg->conn, |
| |
415 wbs->u.channel, NULL, |
| |
416 SILC_MESSAGE_FLAG_DATA, |
| |
417 packet->head, len, TRUE); |
| |
418 } |
| |
419 |
| |
420 silc_buffer_free(packet); |
| |
421 } |
| |
422 |
| |
423 /* Purple Whiteboard operations */ |
| |
424 |
| |
425 void silcpurple_wb_start(PurpleWhiteboard *wb) |
| |
426 { |
| |
427 /* Nothing here. Everything is in initialization */ |
| |
428 } |
| |
429 |
| |
430 void silcpurple_wb_end(PurpleWhiteboard *wb) |
| |
431 { |
| |
432 silc_free(wb->proto_data); |
| |
433 wb->proto_data = NULL; |
| |
434 } |
| |
435 |
| |
436 void silcpurple_wb_get_dimensions(PurpleWhiteboard *wb, int *width, int *height) |
| |
437 { |
| |
438 SilcPurpleWb wbs = wb->proto_data; |
| |
439 *width = wbs->width; |
| |
440 *height = wbs->height; |
| |
441 } |
| |
442 |
| |
443 void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height) |
| |
444 { |
| |
445 SilcPurpleWb wbs = wb->proto_data; |
| |
446 wbs->width = width > SILCPURPLE_WB_WIDTH_MAX ? SILCPURPLE_WB_WIDTH_MAX : |
| |
447 width; |
| |
448 wbs->height = height > SILCPURPLE_WB_HEIGHT_MAX ? SILCPURPLE_WB_HEIGHT_MAX : |
| |
449 height; |
| |
450 |
| |
451 /* Update whiteboard */ |
| |
452 purple_whiteboard_set_dimensions(wb, wbs->width, wbs->height); |
| |
453 } |
| |
454 |
| |
455 void silcpurple_wb_get_brush(PurpleWhiteboard *wb, int *size, int *color) |
| |
456 { |
| |
457 SilcPurpleWb wbs = wb->proto_data; |
| |
458 *size = wbs->brush_size; |
| |
459 *color = wbs->brush_color; |
| |
460 } |
| |
461 |
| |
462 void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color) |
| |
463 { |
| |
464 SilcPurpleWb wbs = wb->proto_data; |
| |
465 wbs->brush_size = size; |
| |
466 wbs->brush_color = color; |
| |
467 |
| |
468 /* Update whiteboard */ |
| |
469 purple_whiteboard_set_brush(wb, size, color); |
| |
470 } |
| |
471 |
| |
472 void silcpurple_wb_clear(PurpleWhiteboard *wb) |
| |
473 { |
| |
474 SilcPurpleWb wbs = wb->proto_data; |
| |
475 SilcBuffer packet; |
| |
476 int len; |
| |
477 PurpleConnection *gc; |
| |
478 SilcPurple sg; |
| |
479 |
| |
480 gc = purple_account_get_connection(wb->account); |
| |
481 g_return_if_fail(gc); |
| |
482 sg = gc->proto_data; |
| |
483 g_return_if_fail(sg); |
| |
484 |
| |
485 len = SILCPURPLE_WB_HEADER; |
| |
486 packet = silc_buffer_alloc_size(len); |
| |
487 if (!packet) |
| |
488 return; |
| |
489 |
| |
490 /* Assmeble packet */ |
| |
491 silc_buffer_format(packet, |
| |
492 SILC_STR_UI32_STRING(SILCPURPLE_WB_MIME), |
| |
493 SILC_STR_UI_CHAR(SILCPURPLE_WB_CLEAR), |
| |
494 SILC_STR_UI_SHORT(wbs->width), |
| |
495 SILC_STR_UI_SHORT(wbs->height), |
| |
496 SILC_STR_UI_INT(wbs->brush_color), |
| |
497 SILC_STR_UI_SHORT(wbs->brush_size), |
| |
498 SILC_STR_END); |
| |
499 |
| |
500 /* Send the message */ |
| |
501 if (wbs->type == 0) { |
| |
502 /* Private message */ |
| |
503 silc_client_send_private_message(sg->client, sg->conn, |
| |
504 wbs->u.client, |
| |
505 SILC_MESSAGE_FLAG_DATA, |
| |
506 packet->head, len, TRUE); |
| |
507 } else if (wbs->type == 1) { |
| |
508 /* Channel message */ |
| |
509 silc_client_send_channel_message(sg->client, sg->conn, |
| |
510 wbs->u.channel, NULL, |
| |
511 SILC_MESSAGE_FLAG_DATA, |
| |
512 packet->head, len, TRUE); |
| |
513 } |
| |
514 |
| |
515 silc_buffer_free(packet); |
| |
516 } |