| |
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 "silcgaim.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 SILCGAIM_WB_MIME "MIME-Version: 1.0\r\nContent-Type: application/x-wb\r\nContent-Transfer-Encoding: binary\r\n\r\n" |
| |
54 #define SILCGAIM_WB_HEADER strlen(SILCGAIM_WB_MIME) + 11 |
| |
55 |
| |
56 #define SILCGAIM_WB_WIDTH 500 |
| |
57 #define SILCGAIM_WB_HEIGHT 400 |
| |
58 #define SILCGAIM_WB_WIDTH_MAX 1024 |
| |
59 #define SILCGAIM_WB_HEIGHT_MAX 1024 |
| |
60 |
| |
61 /* Commands */ |
| |
62 typedef enum { |
| |
63 SILCGAIM_WB_DRAW = 0x01, |
| |
64 SILCGAIM_WB_CLEAR = 0x02, |
| |
65 } SilcGaimWbCommand; |
| |
66 |
| |
67 /* Brush size */ |
| |
68 typedef enum { |
| |
69 SILCGAIM_WB_BRUSH_SMALL = 2, |
| |
70 SILCGAIM_WB_BRUSH_MEDIUM = 5, |
| |
71 SILCGAIM_WB_BRUSH_LARGE = 10, |
| |
72 } SilcGaimWbBrushSize; |
| |
73 |
| |
74 /* Brush color (XXX Gaim should provide default colors) */ |
| |
75 typedef enum { |
| |
76 SILCGAIM_WB_COLOR_BLACK = 0, |
| |
77 SILCGAIM_WB_COLOR_RED = 13369344, |
| |
78 SILCGAIM_WB_COLOR_GREEN = 52224, |
| |
79 SILCGAIM_WB_COLOR_BLUE = 204, |
| |
80 SILCGAIM_WB_COLOR_YELLOW = 15658496, |
| |
81 SILCGAIM_WB_COLOR_ORANGE = 16737792, |
| |
82 SILCGAIM_WB_COLOR_CYAN = 52428, |
| |
83 SILCGAIM_WB_COLOR_VIOLET = 5381277, |
| |
84 SILCGAIM_WB_COLOR_PURPLE = 13369548, |
| |
85 SILCGAIM_WB_COLOR_TAN = 12093547, |
| |
86 SILCGAIM_WB_COLOR_BROWN = 5256485, |
| |
87 SILCGAIM_WB_COLOR_GREY = 11184810, |
| |
88 SILCGAIM_WB_COLOR_WHITE = 16777215, |
| |
89 } SilcGaimWbColor; |
| |
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 } *SilcGaimWb; |
| |
102 |
| |
103 /* Initialize whiteboard */ |
| |
104 |
| |
105 GaimWhiteboard *silcgaim_wb_init(SilcGaim sg, SilcClientEntry client_entry) |
| |
106 { |
| |
107 SilcClientConnection conn; |
| |
108 GaimWhiteboard *wb; |
| |
109 SilcGaimWb wbs; |
| |
110 |
| |
111 conn = sg->conn; |
| |
112 wb = gaim_whiteboard_get_session(sg->account, client_entry->nickname); |
| |
113 if (!wb) |
| |
114 wb = gaim_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 = SILCGAIM_WB_WIDTH; |
| |
125 wbs->height = SILCGAIM_WB_HEIGHT; |
| |
126 wbs->brush_size = SILCGAIM_WB_BRUSH_SMALL; |
| |
127 wbs->brush_color = SILCGAIM_WB_COLOR_BLACK; |
| |
128 wb->proto_data = wbs; |
| |
129 |
| |
130 /* Start the whiteboard */ |
| |
131 gaim_whiteboard_start(wb); |
| |
132 gaim_whiteboard_clear(wb); |
| |
133 } |
| |
134 |
| |
135 return wb; |
| |
136 } |
| |
137 |
| |
138 GaimWhiteboard *silcgaim_wb_init_ch(SilcGaim sg, SilcChannelEntry channel) |
| |
139 { |
| |
140 GaimWhiteboard *wb; |
| |
141 SilcGaimWb wbs; |
| |
142 |
| |
143 wb = gaim_whiteboard_get_session(sg->account, channel->channel_name); |
| |
144 if (!wb) |
| |
145 wb = gaim_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 = SILCGAIM_WB_WIDTH; |
| |
156 wbs->height = SILCGAIM_WB_HEIGHT; |
| |
157 wbs->brush_size = SILCGAIM_WB_BRUSH_SMALL; |
| |
158 wbs->brush_color = SILCGAIM_WB_COLOR_BLACK; |
| |
159 wb->proto_data = wbs; |
| |
160 |
| |
161 /* Start the whiteboard */ |
| |
162 gaim_whiteboard_start(wb); |
| |
163 gaim_whiteboard_clear(wb); |
| |
164 } |
| |
165 |
| |
166 return wb; |
| |
167 } |
| |
168 |
| |
169 static void |
| |
170 silcgaim_wb_parse(SilcGaimWb wbs, GaimWhiteboard *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 silcgaim_wb_set_dimensions(wb, height, width); |
| |
195 |
| |
196 if (command == SILCGAIM_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 gaim_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 == SILCGAIM_WB_CLEAR) |
| |
224 gaim_whiteboard_clear(wb); |
| |
225 } |
| |
226 |
| |
227 typedef struct { |
| |
228 unsigned char *message; |
| |
229 SilcUInt32 message_len; |
| |
230 SilcGaim sg; |
| |
231 SilcClientEntry sender; |
| |
232 SilcChannelEntry channel; |
| |
233 } *SilcGaimWbRequest; |
| |
234 |
| |
235 static void |
| |
236 silcgaim_wb_request_cb(SilcGaimWbRequest req, gint id) |
| |
237 { |
| |
238 GaimWhiteboard *wb; |
| |
239 |
| |
240 if (id != 1) |
| |
241 goto out; |
| |
242 |
| |
243 if (!req->channel) |
| |
244 wb = silcgaim_wb_init(req->sg, req->sender); |
| |
245 else |
| |
246 wb = silcgaim_wb_init_ch(req->sg, req->channel); |
| |
247 |
| |
248 silcgaim_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 silcgaim_wb_request(SilcClient client, const unsigned char *message, |
| |
257 SilcUInt32 message_len, SilcClientEntry sender, |
| |
258 SilcChannelEntry channel) |
| |
259 { |
| |
260 char tmp[128]; |
| |
261 SilcGaimWbRequest req; |
| |
262 GaimConnection *gc; |
| |
263 SilcGaim sg; |
| |
264 |
| |
265 gc = client->application; |
| |
266 sg = gc->proto_data; |
| |
267 |
| |
268 /* Open whiteboard automatically if requested */ |
| |
269 if (gaim_account_get_bool(sg->account, "open-wb", FALSE)) { |
| |
270 GaimWhiteboard *wb; |
| |
271 |
| |
272 if (!channel) |
| |
273 wb = silcgaim_wb_init(sg, sender); |
| |
274 else |
| |
275 wb = silcgaim_wb_init_ch(sg, channel); |
| |
276 |
| |
277 silcgaim_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 gaim_request_action(gc, _("Whiteboard"), tmp, NULL, 1, req, 2, |
| |
303 _("Yes"), G_CALLBACK(silcgaim_wb_request_cb), |
| |
304 _("No"), G_CALLBACK(silcgaim_wb_request_cb)); |
| |
305 } |
| |
306 |
| |
307 /* Process incoming whiteboard message */ |
| |
308 |
| |
309 void silcgaim_wb_receive(SilcClient client, SilcClientConnection conn, |
| |
310 SilcClientEntry sender, SilcMessagePayload payload, |
| |
311 SilcMessageFlags flags, const unsigned char *message, |
| |
312 SilcUInt32 message_len) |
| |
313 { |
| |
314 SilcGaim sg; |
| |
315 GaimConnection *gc; |
| |
316 GaimWhiteboard *wb; |
| |
317 SilcGaimWb wbs; |
| |
318 |
| |
319 gc = client->application; |
| |
320 sg = gc->proto_data; |
| |
321 |
| |
322 wb = gaim_whiteboard_get_session(sg->account, sender->nickname); |
| |
323 if (!wb) { |
| |
324 /* Ask user if they want to open the whiteboard */ |
| |
325 silcgaim_wb_request(client, message, message_len, |
| |
326 sender, NULL); |
| |
327 return; |
| |
328 } |
| |
329 |
| |
330 wbs = wb->proto_data; |
| |
331 silcgaim_wb_parse(wbs, wb, (unsigned char *)message, message_len); |
| |
332 } |
| |
333 |
| |
334 /* Process incoming whiteboard message on channel */ |
| |
335 |
| |
336 void silcgaim_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 SilcGaim sg; |
| |
344 GaimConnection *gc; |
| |
345 GaimWhiteboard *wb; |
| |
346 SilcGaimWb wbs; |
| |
347 |
| |
348 gc = client->application; |
| |
349 sg = gc->proto_data; |
| |
350 |
| |
351 wb = gaim_whiteboard_get_session(sg->account, channel->channel_name); |
| |
352 if (!wb) { |
| |
353 /* Ask user if they want to open the whiteboard */ |
| |
354 silcgaim_wb_request(client, message, message_len, |
| |
355 sender, channel); |
| |
356 return; |
| |
357 } |
| |
358 |
| |
359 wbs = wb->proto_data; |
| |
360 silcgaim_wb_parse(wbs, wb, (unsigned char *)message, message_len); |
| |
361 } |
| |
362 |
| |
363 /* Send whiteboard message */ |
| |
364 |
| |
365 void silcgaim_wb_send(GaimWhiteboard *wb, GList *draw_list) |
| |
366 { |
| |
367 SilcGaimWb wbs = wb->proto_data; |
| |
368 SilcBuffer packet; |
| |
369 GList *list; |
| |
370 int len; |
| |
371 GaimConnection *gc; |
| |
372 SilcGaim sg; |
| |
373 |
| |
374 g_return_if_fail(draw_list); |
| |
375 gc = gaim_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 = SILCGAIM_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(SILCGAIM_WB_MIME), |
| |
391 SILC_STR_UI_CHAR(SILCGAIM_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, SILCGAIM_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 /* Gaim Whiteboard operations */ |
| |
424 |
| |
425 void silcgaim_wb_start(GaimWhiteboard *wb) |
| |
426 { |
| |
427 /* Nothing here. Everything is in initialization */ |
| |
428 } |
| |
429 |
| |
430 void silcgaim_wb_end(GaimWhiteboard *wb) |
| |
431 { |
| |
432 silc_free(wb->proto_data); |
| |
433 wb->proto_data = NULL; |
| |
434 } |
| |
435 |
| |
436 void silcgaim_wb_get_dimensions(GaimWhiteboard *wb, int *width, int *height) |
| |
437 { |
| |
438 SilcGaimWb wbs = wb->proto_data; |
| |
439 *width = wbs->width; |
| |
440 *height = wbs->height; |
| |
441 } |
| |
442 |
| |
443 void silcgaim_wb_set_dimensions(GaimWhiteboard *wb, int width, int height) |
| |
444 { |
| |
445 SilcGaimWb wbs = wb->proto_data; |
| |
446 wbs->width = width > SILCGAIM_WB_WIDTH_MAX ? SILCGAIM_WB_WIDTH_MAX : |
| |
447 width; |
| |
448 wbs->height = height > SILCGAIM_WB_HEIGHT_MAX ? SILCGAIM_WB_HEIGHT_MAX : |
| |
449 height; |
| |
450 |
| |
451 /* Update whiteboard */ |
| |
452 gaim_whiteboard_set_dimensions(wb, wbs->width, wbs->height); |
| |
453 } |
| |
454 |
| |
455 void silcgaim_wb_get_brush(GaimWhiteboard *wb, int *size, int *color) |
| |
456 { |
| |
457 SilcGaimWb wbs = wb->proto_data; |
| |
458 *size = wbs->brush_size; |
| |
459 *color = wbs->brush_color; |
| |
460 } |
| |
461 |
| |
462 void silcgaim_wb_set_brush(GaimWhiteboard *wb, int size, int color) |
| |
463 { |
| |
464 SilcGaimWb wbs = wb->proto_data; |
| |
465 wbs->brush_size = size; |
| |
466 wbs->brush_color = color; |
| |
467 |
| |
468 /* Update whiteboard */ |
| |
469 gaim_whiteboard_set_brush(wb, size, color); |
| |
470 } |
| |
471 |
| |
472 void silcgaim_wb_clear(GaimWhiteboard *wb) |
| |
473 { |
| |
474 SilcGaimWb wbs = wb->proto_data; |
| |
475 SilcBuffer packet; |
| |
476 int len; |
| |
477 GaimConnection *gc; |
| |
478 SilcGaim sg; |
| |
479 |
| |
480 gc = gaim_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 = SILCGAIM_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(SILCGAIM_WB_MIME), |
| |
493 SILC_STR_UI_CHAR(SILCGAIM_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 } |