1 /*
2
3 silcpurple_chat.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 2004 - 2007 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 "internal.h"
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "silcpurple.h"
24 #include "wb.h"
25
26 /***************************** Channel Routines ******************************/
27
silcpurple_chat_info(PurpleConnection * gc)28 GList *silcpurple_chat_info(PurpleConnection *gc)
29 {
30 GList *ci = NULL;
31 struct proto_chat_entry *pce;
32
33 pce = g_new0(struct proto_chat_entry, 1);
34 pce->label = _("_Channel:");
35 pce->identifier = "channel";
36 pce->required = TRUE;
37 ci = g_list_append(ci, pce);
38
39 pce = g_new0(struct proto_chat_entry, 1);
40 pce->label = _("_Passphrase:");
41 pce->identifier = "passphrase";
42 pce->secret = TRUE;
43 ci = g_list_append(ci, pce);
44
45 return ci;
46 }
47
silcpurple_chat_info_defaults(PurpleConnection * gc,const char * chat_name)48 GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
49 {
50 GHashTable *defaults;
51
52 defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
53
54 if (chat_name != NULL)
55 g_hash_table_insert(defaults, "channel", g_strdup(chat_name));
56
57 return defaults;
58 }
59
60 static void
61 silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components);
62
63 static void
silcpurple_chat_getinfo_res(SilcClient client,SilcClientConnection conn,SilcStatus status,SilcDList channels,void * context)64 silcpurple_chat_getinfo_res(SilcClient client,
65 SilcClientConnection conn,
66 SilcStatus status,
67 SilcDList channels,
68 void *context)
69 {
70 GHashTable *components = context;
71 PurpleConnection *gc = client->application;
72 const char *chname;
73 char tmp[256];
74
75 chname = g_hash_table_lookup(components, "channel");
76 if (!chname)
77 return;
78
79 if (!channels) {
80 g_snprintf(tmp, sizeof(tmp),
81 _("Channel %s does not exist in the network"), chname);
82 purple_notify_error(gc, _("Channel Information"),
83 _("Cannot get channel information"), tmp);
84 return;
85 }
86
87 silcpurple_chat_getinfo(gc, components);
88 }
89
90
91 static void
silcpurple_chat_getinfo(PurpleConnection * gc,GHashTable * components)92 silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components)
93 {
94 SilcPurple sg = gc->proto_data;
95 const char *chname;
96 char tmp[256], *tmp2;
97 GString *s;
98 SilcChannelEntry channel;
99 SilcHashTableList htl;
100 SilcChannelUser chu;
101
102 if (!components)
103 return;
104
105 chname = g_hash_table_lookup(components, "channel");
106 if (!chname)
107 return;
108 channel = silc_client_get_channel(sg->client, sg->conn,
109 (char *)chname);
110 if (!channel) {
111 silc_client_get_channel_resolve(sg->client, sg->conn,
112 (char *)chname,
113 silcpurple_chat_getinfo_res,
114 components);
115 return;
116 }
117
118 s = g_string_new("");
119 tmp2 = g_markup_escape_text(channel->channel_name, -1);
120 g_string_append_printf(s, _("<b>Channel Name:</b> %s"), tmp2);
121 g_free(tmp2);
122 if (channel->user_list && silc_hash_table_count(channel->user_list))
123 g_string_append_printf(s, _("<br><b>User Count:</b> %d"),
124 (int)silc_hash_table_count(channel->user_list));
125
126 silc_hash_table_list(channel->user_list, &htl);
127 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
128 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
129 tmp2 = g_markup_escape_text(chu->client->nickname, -1);
130 g_string_append_printf(s, _("<br><b>Channel Founder:</b> %s"),
131 tmp2);
132 g_free(tmp2);
133 break;
134 }
135 }
136 silc_hash_table_list_reset(&htl);
137
138 if (channel->cipher)
139 g_string_append_printf(s, _("<br><b>Channel Cipher:</b> %s"),
140 channel->cipher);
141
142 if (channel->hmac)
143 /* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */
144 g_string_append_printf(s, _("<br><b>Channel HMAC:</b> %s"),
145 channel->hmac);
146
147 if (channel->topic) {
148 tmp2 = g_markup_escape_text(channel->topic, -1);
149 g_string_append_printf(s, _("<br><b>Channel Topic:</b><br>%s"), tmp2);
150 g_free(tmp2);
151 }
152
153 if (channel->mode) {
154 g_string_append(s, _("<br><b>Channel Modes:</b> "));
155 silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
156 g_string_append(s, tmp);
157 }
158
159 if (channel->founder_key) {
160 char *fingerprint, *babbleprint;
161 unsigned char *pk;
162 SilcUInt32 pk_len;
163 pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len);
164 if (pk) {
165 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
166 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
167
168 g_string_append_printf(s, _("<br><b>Founder Key Fingerprint:</b><br>%s"), fingerprint);
169 g_string_append_printf(s, _("<br><b>Founder Key Babbleprint:</b><br>%s"), babbleprint);
170
171 silc_free(fingerprint);
172 silc_free(babbleprint);
173 silc_free(pk);
174 }
175 }
176
177 purple_notify_formatted(gc, NULL, _("Channel Information"), NULL, s->str, NULL, NULL);
178 g_string_free(s, TRUE);
179 }
180
181
182 static void
silcpurple_chat_getinfo_menu(PurpleBlistNode * node,gpointer data)183 silcpurple_chat_getinfo_menu(PurpleBlistNode *node, gpointer data)
184 {
185 PurpleChat *chat = (PurpleChat *)node;
186 PurpleAccount *account = purple_chat_get_account(chat);
187 silcpurple_chat_getinfo(purple_account_get_connection(account),
188 purple_chat_get_components(chat));
189 }
190
191
192 #if 0 /* XXX For now these are not implemented. We need better
193 listview dialog from Purple for these. */
194 /************************** Channel Invite List ******************************/
195
196 static void
197 silcpurple_chat_invitelist(PurpleBlistNode *node, gpointer data);
198 {
199
200 }
201
202
203 /**************************** Channel Ban List *******************************/
204
205 static void
206 silcpurple_chat_banlist(PurpleBlistNode *node, gpointer data);
207 {
208
209 }
210 #endif
211
212
213 /************************* Channel Authentication ****************************/
214
215 typedef struct {
216 SilcPurple sg;
217 SilcChannelEntry channel;
218 PurpleChat *c;
219 SilcDList pubkeys;
220 } *SilcPurpleChauth;
221
222 static void
silcpurple_chat_chpk_add(void * user_data,const char * name)223 silcpurple_chat_chpk_add(void *user_data, const char *name)
224 {
225 SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
226 SilcPurple sg = sgc->sg;
227 SilcClient client = sg->client;
228 SilcClientConnection conn = sg->conn;
229 SilcPublicKey public_key;
230 SilcBuffer chpks, pk, chidp;
231 unsigned char mode[4];
232 SilcUInt32 m;
233
234 /* Load the public key */
235 if (!silc_pkcs_load_public_key(name, &public_key)) {
236 silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
237 silc_dlist_uninit(sgc->pubkeys);
238 silc_free(sgc);
239 purple_notify_error(client->application,
240 _("Add Channel Public Key"),
241 _("Could not load public key"), NULL);
242 return;
243 }
244
245 pk = silc_public_key_payload_encode(public_key);
246 chpks = silc_buffer_alloc_size(2);
247 SILC_PUT16_MSB(1, chpks->head);
248 chpks = silc_argument_payload_encode_one(chpks, pk->data,
249 silc_buffer_len(pk), 0x00);
250 silc_buffer_free(pk);
251
252 m = sgc->channel->mode;
253 m |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
254
255 /* Send CMODE */
256 SILC_PUT32_MSB(m, mode);
257 chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL);
258 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
259 silcpurple_command_reply, NULL, 3,
260 1, chidp->data, silc_buffer_len(chidp),
261 2, mode, sizeof(mode),
262 9, chpks->data, silc_buffer_len(chpks));
263 silc_buffer_free(chpks);
264 silc_buffer_free(chidp);
265 if (sgc->pubkeys) {
266 silc_dlist_start(sgc->pubkeys);
267 while ((public_key = silc_dlist_get(sgc->pubkeys)))
268 silc_pkcs_public_key_free(public_key);
269 silc_dlist_uninit(sgc->pubkeys);
270 }
271 silc_free(sgc);
272 }
273
274 static void
silcpurple_chat_chpk_cancel(void * user_data,const char * name)275 silcpurple_chat_chpk_cancel(void *user_data, const char *name)
276 {
277 SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
278 SilcPublicKey public_key;
279
280 silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
281
282 if (sgc->pubkeys) {
283 silc_dlist_start(sgc->pubkeys);
284 while ((public_key = silc_dlist_get(sgc->pubkeys)))
285 silc_pkcs_public_key_free(public_key);
286 silc_dlist_uninit(sgc->pubkeys);
287 }
288 silc_free(sgc);
289 }
290
291 static void
silcpurple_chat_chpk_cb(SilcPurpleChauth sgc,PurpleRequestFields * fields)292 silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields)
293 {
294 SilcPurple sg = sgc->sg;
295 SilcClient client = sg->client;
296 SilcClientConnection conn = sg->conn;
297 PurpleRequestField *f;
298 GList *list;
299 SilcPublicKey public_key;
300 SilcBuffer chpks, pk, chidp;
301 SilcUInt16 c = 0, ct;
302 unsigned char mode[4];
303 SilcUInt32 m;
304
305 f = purple_request_fields_get_field(fields, "list");
306 if (!purple_request_field_list_get_selected(f)) {
307 /* Add new public key */
308 purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE,
309 G_CALLBACK(silcpurple_chat_chpk_add),
310 G_CALLBACK(silcpurple_chat_chpk_cancel),
311 purple_connection_get_account(sg->gc), NULL, NULL, sgc);
312 return;
313 }
314
315 list = purple_request_field_list_get_items(f);
316 chpks = silc_buffer_alloc_size(2);
317
318 for (ct = 0; list; list = list->next, ct++) {
319 public_key = purple_request_field_list_get_data(f, list->data);
320 if (purple_request_field_list_is_selected(f, list->data)) {
321 /* Delete this public key */
322 pk = silc_public_key_payload_encode(public_key);
323 chpks = silc_argument_payload_encode_one(chpks, pk->data,
324 silc_buffer_len(pk), 0x01);
325 silc_buffer_free(pk);
326 c++;
327 }
328 }
329 if (!c) {
330 silc_buffer_free(chpks);
331 return;
332 }
333 SILC_PUT16_MSB(c, chpks->head);
334
335 m = sgc->channel->mode;
336 if (ct == c)
337 m &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
338
339 /* Send CMODE */
340 SILC_PUT32_MSB(m, mode);
341 chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL);
342 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
343 silcpurple_command_reply, NULL, 3,
344 1, chidp->data, silc_buffer_len(chidp),
345 2, mode, sizeof(mode),
346 9, chpks->data, silc_buffer_len(chpks));
347 silc_buffer_free(chpks);
348 silc_buffer_free(chidp);
349 if (sgc->pubkeys) {
350 silc_dlist_start(sgc->pubkeys);
351 while ((public_key = silc_dlist_get(sgc->pubkeys)))
352 silc_pkcs_public_key_free(public_key);
353 silc_dlist_uninit(sgc->pubkeys);
354 }
355 silc_free(sgc);
356 }
357
358 static void
silcpurple_chat_chauth_ok(SilcPurpleChauth sgc,PurpleRequestFields * fields)359 silcpurple_chat_chauth_ok(SilcPurpleChauth sgc, PurpleRequestFields *fields)
360 {
361 SilcPurple sg = sgc->sg;
362 PurpleRequestField *f;
363 SilcPublicKey public_key;
364 const char *curpass, *val;
365 int set;
366
367 f = purple_request_fields_get_field(fields, "passphrase");
368 val = purple_request_field_string_get_value(f);
369 curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase");
370
371 if (!val && curpass)
372 set = 0;
373 else if (val && !curpass)
374 set = 1;
375 else if (val && curpass && !purple_strequal(val, curpass))
376 set = 1;
377 else
378 set = -1;
379
380 if (set == 1) {
381 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
382 sgc->channel->channel_name, "+a", val, NULL);
383 purple_blist_node_set_string((PurpleBlistNode *)sgc->c, "passphrase", val);
384 } else if (set == 0) {
385 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
386 sgc->channel->channel_name, "-a", NULL);
387 purple_blist_node_remove_setting((PurpleBlistNode *)sgc->c, "passphrase");
388 }
389
390 if (sgc->pubkeys) {
391 silc_dlist_start(sgc->pubkeys);
392 while ((public_key = silc_dlist_get(sgc->pubkeys)))
393 silc_pkcs_public_key_free(public_key);
394 silc_dlist_uninit(sgc->pubkeys);
395 }
396 silc_free(sgc);
397 }
398
silcpurple_chat_chauth_show(SilcPurple sg,SilcChannelEntry channel,SilcDList channel_pubkeys)399 void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
400 SilcDList channel_pubkeys)
401 {
402 SilcPublicKey public_key;
403 SilcSILCPublicKey silc_pubkey;
404 unsigned char *pk;
405 SilcUInt32 pk_len;
406 char *fingerprint, *babbleprint;
407 SilcPublicKeyIdentifier ident;
408 char tmp2[1024], t[512];
409 PurpleRequestFields *fields;
410 PurpleRequestFieldGroup *g;
411 PurpleRequestField *f;
412 SilcPurpleChauth sgc;
413 const char *curpass = NULL;
414
415 sgc = silc_calloc(1, sizeof(*sgc));
416 if (!sgc)
417 return;
418 sgc->sg = sg;
419 sgc->channel = channel;
420
421 fields = purple_request_fields_new();
422
423 if (sgc->c)
424 curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase");
425
426 g = purple_request_field_group_new(NULL);
427 f = purple_request_field_string_new("passphrase", _("Channel Passphrase"),
428 curpass, FALSE);
429 purple_request_field_string_set_masked(f, TRUE);
430 purple_request_field_group_add_field(g, f);
431 purple_request_fields_add_group(fields, g);
432
433 g = purple_request_field_group_new(NULL);
434 f = purple_request_field_label_new("l1", _("Channel Public Keys List"));
435 purple_request_field_group_add_field(g, f);
436 purple_request_fields_add_group(fields, g);
437
438 g_snprintf(t, sizeof(t),
439 _("Channel authentication is used to secure the channel from "
440 "unauthorized access. The authentication may be based on "
441 "passphrase and digital signatures. If passphrase is set, it "
442 "is required to be able to join. If channel public keys are set "
443 "then only users whose public keys are listed are able to join."));
444
445 if (!channel_pubkeys || !silc_dlist_count(channel_pubkeys)) {
446 f = purple_request_field_list_new("list", NULL);
447 purple_request_field_group_add_field(g, f);
448 purple_request_fields(sg->gc, _("Channel Authentication"),
449 _("Channel Authentication"), t, fields,
450 _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
451 _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
452 purple_connection_get_account(sg->gc), NULL, NULL, sgc);
453 if (channel_pubkeys)
454 silc_dlist_uninit(channel_pubkeys);
455 return;
456 }
457 sgc->pubkeys = channel_pubkeys;
458
459 g = purple_request_field_group_new(NULL);
460 f = purple_request_field_list_new("list", NULL);
461 purple_request_field_group_add_field(g, f);
462 purple_request_fields_add_group(fields, g);
463
464 silc_dlist_start(channel_pubkeys);
465 while ((public_key = silc_dlist_get(channel_pubkeys))) {
466 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
467 if (!pk)
468 continue;
469 fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
470 babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
471
472 silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
473 ident = &silc_pubkey->identifier;
474
475 g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s",
476 ident->realname ? ident->realname : ident->username ?
477 ident->username : "", fingerprint, babbleprint);
478 purple_request_field_list_add_icon(f, tmp2, NULL, public_key);
479
480 silc_free(fingerprint);
481 silc_free(babbleprint);
482 }
483
484 purple_request_field_list_set_multi_select(f, FALSE);
485 purple_request_fields(sg->gc, _("Channel Authentication"),
486 _("Channel Authentication"), t, fields,
487 _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
488 _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
489 purple_connection_get_account(sg->gc), NULL, NULL, sgc);
490 }
491
492 static void
silcpurple_chat_chauth(PurpleBlistNode * node,gpointer data)493 silcpurple_chat_chauth(PurpleBlistNode *node, gpointer data)
494 {
495 PurpleChat *chat;
496 PurpleConnection *gc;
497 SilcPurple sg;
498
499 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
500
501 chat = (PurpleChat *) node;
502 gc = purple_account_get_connection(purple_chat_get_account(chat));
503 sg = gc->proto_data;
504
505 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
506 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
507 "+C", NULL);
508 }
509
510
511 /************************** Channel Private Groups **************************/
512
513 /* Private groups are "virtual" channels. They are groups inside a channel.
514 This is implemented by using channel private keys. By knowing a channel
515 private key user becomes part of that group and is able to talk on that
516 group. Other users, on the same channel, won't be able to see the
517 messages of that group. It is possible to have multiple groups inside
518 a channel - and thus having multiple private keys on the channel. */
519
520 typedef struct {
521 SilcPurple sg;
522 PurpleChat *c;
523 const char *channel;
524 } *SilcPurpleCharPrv;
525
526 static void
silcpurple_chat_prv_add(SilcPurpleCharPrv p,PurpleRequestFields * fields)527 silcpurple_chat_prv_add(SilcPurpleCharPrv p, PurpleRequestFields *fields)
528 {
529 SilcPurple sg = p->sg;
530 char tmp[512];
531 PurpleRequestField *f;
532 const char *name, *passphrase, *alias;
533 GHashTable *comp;
534 PurpleGroup *g;
535 PurpleChat *cn;
536
537 f = purple_request_fields_get_field(fields, "name");
538 name = purple_request_field_string_get_value(f);
539 if (!name) {
540 silc_free(p);
541 return;
542 }
543 f = purple_request_fields_get_field(fields, "passphrase");
544 passphrase = purple_request_field_string_get_value(f);
545 f = purple_request_fields_get_field(fields, "alias");
546 alias = purple_request_field_string_get_value(f);
547
548 /* Add private group to buddy list */
549 g_snprintf(tmp, sizeof(tmp), "%s [Private Group]", name);
550 comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
551 g_hash_table_replace(comp, "channel", g_strdup(tmp));
552 g_hash_table_replace(comp, "passphrase", g_strdup(passphrase));
553
554 cn = purple_chat_new(sg->account, alias, comp);
555 g = purple_chat_get_group(p->c);
556 purple_blist_add_chat(cn, g, (PurpleBlistNode *)p->c);
557
558 /* Associate to a real channel */
559 purple_blist_node_set_string((PurpleBlistNode *)cn, "parentch", p->channel);
560
561 /* Join the group */
562 silcpurple_chat_join(sg->gc, comp);
563
564 silc_free(p);
565 }
566
567 static void
silcpurple_chat_prv_cancel(SilcPurpleCharPrv p,PurpleRequestFields * fields)568 silcpurple_chat_prv_cancel(SilcPurpleCharPrv p, PurpleRequestFields *fields)
569 {
570 silc_free(p);
571 }
572
573 static void
silcpurple_chat_prv(PurpleBlistNode * node,gpointer data)574 silcpurple_chat_prv(PurpleBlistNode *node, gpointer data)
575 {
576 PurpleChat *chat;
577 PurpleConnection *gc;
578 SilcPurple sg;
579
580 SilcPurpleCharPrv p;
581 PurpleRequestFields *fields;
582 PurpleRequestFieldGroup *g;
583 PurpleRequestField *f;
584 char tmp[512];
585
586 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
587
588 chat = (PurpleChat *) node;
589 gc = purple_account_get_connection(purple_chat_get_account(chat));
590 sg = gc->proto_data;
591
592 p = silc_calloc(1, sizeof(*p));
593 if (!p)
594 return;
595 p->sg = sg;
596
597 p->channel = g_hash_table_lookup(purple_chat_get_components(chat), "channel");
598 p->c = purple_blist_find_chat(sg->account, p->channel);
599
600 fields = purple_request_fields_new();
601
602 g = purple_request_field_group_new(NULL);
603 f = purple_request_field_string_new("name", _("Group Name"),
604 NULL, FALSE);
605 purple_request_field_group_add_field(g, f);
606
607 f = purple_request_field_string_new("passphrase", _("Passphrase"),
608 NULL, FALSE);
609 purple_request_field_string_set_masked(f, TRUE);
610 purple_request_field_group_add_field(g, f);
611
612 f = purple_request_field_string_new("alias", _("Alias"),
613 NULL, FALSE);
614 purple_request_field_group_add_field(g, f);
615 purple_request_fields_add_group(fields, g);
616
617 g_snprintf(tmp, sizeof(tmp),
618 _("Please enter the %s channel private group name and passphrase."),
619 p->channel);
620 purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields,
621 _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
622 _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
623 purple_connection_get_account(gc), NULL, NULL, p);
624 }
625
626
627 /****************************** Channel Modes ********************************/
628
629 static void
silcpurple_chat_permanent_reset(PurpleBlistNode * node,gpointer data)630 silcpurple_chat_permanent_reset(PurpleBlistNode *node, gpointer data)
631 {
632 PurpleChat *chat;
633 PurpleConnection *gc;
634 SilcPurple sg;
635
636 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
637
638 chat = (PurpleChat *) node;
639 gc = purple_account_get_connection(purple_chat_get_account(chat));
640 sg = gc->proto_data;
641
642 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
643 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
644 "-f", NULL);
645 }
646
647 static void
silcpurple_chat_permanent(PurpleBlistNode * node,gpointer data)648 silcpurple_chat_permanent(PurpleBlistNode *node, gpointer data)
649 {
650 PurpleChat *chat;
651 PurpleConnection *gc;
652 SilcPurple sg;
653 const char *channel;
654
655 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
656
657 chat = (PurpleChat *) node;
658 gc = purple_account_get_connection(purple_chat_get_account(chat));
659 sg = gc->proto_data;
660
661 if (!sg->conn)
662 return;
663
664 /* XXX we should have ability to define which founder
665 key to use. Now we use the user's own public key
666 (default key). */
667
668 /* Call CMODE */
669 channel = g_hash_table_lookup(purple_chat_get_components(chat), "channel");
670 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel,
671 "+f", NULL);
672 }
673
674 typedef struct {
675 SilcPurple sg;
676 char *channel;
677 } *SilcPurpleChatInput;
678
679 static void
silcpurple_chat_ulimit_cb(SilcPurpleChatInput s,const char * limit)680 silcpurple_chat_ulimit_cb(SilcPurpleChatInput s, const char *limit)
681 {
682 SilcChannelEntry channel;
683 guint ulimit = 0;
684
685 channel = silc_client_get_channel(s->sg->client, s->sg->conn,
686 (char *)s->channel);
687 if (!channel)
688 return;
689 if (limit)
690 ulimit = strtoul(limit, NULL, 10);
691
692 if (!limit || !(*limit) || *limit == '0') {
693 if (limit && ulimit == channel->user_limit) {
694 g_free(s->channel);
695 silc_free(s);
696 return;
697 }
698 silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
699 s->channel, "-l", NULL);
700
701 g_free(s->channel);
702 silc_free(s);
703 return;
704 }
705
706 if (ulimit == channel->user_limit) {
707 g_free(s->channel);
708 silc_free(s);
709 return;
710 }
711
712 /* Call CMODE */
713 silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
714 s->channel, "+l", limit, NULL);
715
716 g_free(s->channel);
717 silc_free(s);
718 }
719
720 static void
silcpurple_chat_ulimit(PurpleBlistNode * node,gpointer data)721 silcpurple_chat_ulimit(PurpleBlistNode *node, gpointer data)
722 {
723 PurpleChat *chat;
724 PurpleConnection *gc;
725 SilcPurple sg;
726
727 SilcPurpleChatInput s;
728 SilcChannelEntry channel;
729 char *ch;
730 char tmp[32];
731
732 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
733
734 chat = (PurpleChat *) node;
735 gc = purple_account_get_connection(purple_chat_get_account(chat));
736 sg = gc->proto_data;
737
738 if (!sg->conn)
739 return;
740
741 ch = g_strdup(g_hash_table_lookup(purple_chat_get_components(chat), "channel"));
742 channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch);
743 if (!channel)
744 return;
745
746 s = silc_calloc(1, sizeof(*s));
747 if (!s)
748 return;
749 s->channel = ch;
750 s->sg = sg;
751 g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit);
752 purple_request_input(gc, _("User Limit"), NULL,
753 _("Set user limit on channel. Set to zero to reset user limit."),
754 tmp, FALSE, FALSE, NULL,
755 _("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb),
756 _("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb),
757 purple_connection_get_account(gc), NULL, NULL, s);
758 }
759
760 static void
silcpurple_chat_resettopic(PurpleBlistNode * node,gpointer data)761 silcpurple_chat_resettopic(PurpleBlistNode *node, gpointer data)
762 {
763 PurpleChat *chat;
764 PurpleConnection *gc;
765 SilcPurple sg;
766
767 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
768
769 chat = (PurpleChat *) node;
770 gc = purple_account_get_connection(purple_chat_get_account(chat));
771 sg = gc->proto_data;
772
773 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
774 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
775 "-t", NULL);
776 }
777
778 static void
silcpurple_chat_settopic(PurpleBlistNode * node,gpointer data)779 silcpurple_chat_settopic(PurpleBlistNode *node, gpointer data)
780 {
781 PurpleChat *chat;
782 PurpleConnection *gc;
783 SilcPurple sg;
784
785 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
786
787 chat = (PurpleChat *) node;
788 gc = purple_account_get_connection(purple_chat_get_account(chat));
789 sg = gc->proto_data;
790
791 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
792 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
793 "+t", NULL);
794 }
795
796 static void
silcpurple_chat_resetprivate(PurpleBlistNode * node,gpointer data)797 silcpurple_chat_resetprivate(PurpleBlistNode *node, gpointer data)
798 {
799 PurpleChat *chat;
800 PurpleConnection *gc;
801 SilcPurple sg;
802
803 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
804
805 chat = (PurpleChat *) node;
806 gc = purple_account_get_connection(purple_chat_get_account(chat));
807 sg = gc->proto_data;
808
809 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
810 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
811 "-p", NULL);
812 }
813
814 static void
silcpurple_chat_setprivate(PurpleBlistNode * node,gpointer data)815 silcpurple_chat_setprivate(PurpleBlistNode *node, gpointer data)
816 {
817 PurpleChat *chat;
818 PurpleConnection *gc;
819 SilcPurple sg;
820
821 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
822
823 chat = (PurpleChat *) node;
824 gc = purple_account_get_connection(purple_chat_get_account(chat));
825 sg = gc->proto_data;
826
827 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
828 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
829 "+p", NULL);
830 }
831
832 static void
silcpurple_chat_resetsecret(PurpleBlistNode * node,gpointer data)833 silcpurple_chat_resetsecret(PurpleBlistNode *node, gpointer data)
834 {
835 PurpleChat *chat;
836 PurpleConnection *gc;
837 SilcPurple sg;
838
839 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
840
841 chat = (PurpleChat *) node;
842 gc = purple_account_get_connection(purple_chat_get_account(chat));
843 sg = gc->proto_data;
844
845 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
846 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
847 "-s", NULL);
848 }
849
850 static void
silcpurple_chat_setsecret(PurpleBlistNode * node,gpointer data)851 silcpurple_chat_setsecret(PurpleBlistNode *node, gpointer data)
852 {
853 PurpleChat *chat;
854 PurpleConnection *gc;
855 SilcPurple sg;
856
857 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
858
859 chat = (PurpleChat *) node;
860 gc = purple_account_get_connection(purple_chat_get_account(chat));
861 sg = gc->proto_data;
862
863 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
864 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
865 "+s", NULL);
866 }
867
868 typedef struct {
869 SilcPurple sg;
870 SilcChannelEntry channel;
871 } *SilcPurpleChatWb;
872
873 static void
silcpurple_chat_wb(PurpleBlistNode * node,gpointer data)874 silcpurple_chat_wb(PurpleBlistNode *node, gpointer data)
875 {
876 SilcPurpleChatWb wb = data;
877 silcpurple_wb_init_ch(wb->sg, wb->channel);
878 silc_free(wb);
879 }
880
silcpurple_chat_menu(PurpleChat * chat)881 GList *silcpurple_chat_menu(PurpleChat *chat)
882 {
883 GHashTable *components = purple_chat_get_components(chat);
884 PurpleConnection *gc = purple_account_get_connection(purple_chat_get_account(chat));
885 SilcPurple sg = gc->proto_data;
886 SilcClientConnection conn = sg->conn;
887 const char *chname = NULL;
888 SilcChannelEntry channel = NULL;
889 SilcChannelUser chu = NULL;
890 SilcUInt32 mode = 0;
891
892 GList *m = NULL;
893 PurpleMenuAction *act;
894
895 if (components)
896 chname = g_hash_table_lookup(components, "channel");
897 if (!chname)
898 return NULL;
899 channel = silc_client_get_channel(sg->client, sg->conn,
900 (char *)chname);
901 if (channel) {
902 chu = silc_client_on_channel(channel, conn->local_entry);
903 if (chu)
904 mode = chu->mode;
905 }
906
907 if (strstr(chname, "[Private Group]"))
908 return NULL;
909
910 act = purple_menu_action_new(_("Get Info"),
911 PURPLE_CALLBACK(silcpurple_chat_getinfo_menu),
912 NULL, NULL);
913 m = g_list_append(m, act);
914
915 #if 0 /* XXX For now these are not implemented. We need better
916 listview dialog from Purple for these. */
917 if (mode & SILC_CHANNEL_UMODE_CHANOP) {
918 act = purple_menu_action_new(_("Invite List"),
919 PURPLE_CALLBACK(silcpurple_chat_invitelist),
920 NULL, NULL);
921 m = g_list_append(m, act);
922
923 act = purple_menu_action_new(_("Ban List"),
924 PURPLE_CALLBACK(silcpurple_chat_banlist),
925 NULL, NULL);
926 m = g_list_append(m, act);
927 }
928 #endif
929
930 if (chu) {
931 act = purple_menu_action_new(_("Add Private Group"),
932 PURPLE_CALLBACK(silcpurple_chat_prv),
933 NULL, NULL);
934 m = g_list_append(m, act);
935 }
936
937 if (chu && mode & SILC_CHANNEL_UMODE_CHANFO) {
938 act = purple_menu_action_new(_("Channel Authentication"),
939 PURPLE_CALLBACK(silcpurple_chat_chauth),
940 NULL, NULL);
941 m = g_list_append(m, act);
942
943 if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
944 act = purple_menu_action_new(_("Reset Permanent"),
945 PURPLE_CALLBACK(silcpurple_chat_permanent_reset),
946 NULL, NULL);
947 m = g_list_append(m, act);
948 } else {
949 act = purple_menu_action_new(_("Set Permanent"),
950 PURPLE_CALLBACK(silcpurple_chat_permanent),
951 NULL, NULL);
952 m = g_list_append(m, act);
953 }
954 }
955
956 if (chu && mode & SILC_CHANNEL_UMODE_CHANOP) {
957 act = purple_menu_action_new(_("Set User Limit"),
958 PURPLE_CALLBACK(silcpurple_chat_ulimit),
959 NULL, NULL);
960 m = g_list_append(m, act);
961
962 if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
963 act = purple_menu_action_new(_("Reset Topic Restriction"),
964 PURPLE_CALLBACK(silcpurple_chat_resettopic),
965 NULL, NULL);
966 m = g_list_append(m, act);
967 } else {
968 act = purple_menu_action_new(_("Set Topic Restriction"),
969 PURPLE_CALLBACK(silcpurple_chat_settopic),
970 NULL, NULL);
971 m = g_list_append(m, act);
972 }
973
974 if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) {
975 act = purple_menu_action_new(_("Reset Private Channel"),
976 PURPLE_CALLBACK(silcpurple_chat_resetprivate),
977 NULL, NULL);
978 m = g_list_append(m, act);
979 } else {
980 act = purple_menu_action_new(_("Set Private Channel"),
981 PURPLE_CALLBACK(silcpurple_chat_setprivate),
982 NULL, NULL);
983 m = g_list_append(m, act);
984 }
985
986 if (channel->mode & SILC_CHANNEL_MODE_SECRET) {
987 act = purple_menu_action_new(_("Reset Secret Channel"),
988 PURPLE_CALLBACK(silcpurple_chat_resetsecret),
989 NULL, NULL);
990 m = g_list_append(m, act);
991 } else {
992 act = purple_menu_action_new(_("Set Secret Channel"),
993 PURPLE_CALLBACK(silcpurple_chat_setsecret),
994 NULL, NULL);
995 m = g_list_append(m, act);
996 }
997 }
998
999 if (chu && channel) {
1000 SilcPurpleChatWb wb;
1001 wb = silc_calloc(1, sizeof(*wb));
1002 wb->sg = sg;
1003 wb->channel = channel;
1004 act = purple_menu_action_new(_("Draw On Whiteboard"),
1005 PURPLE_CALLBACK(silcpurple_chat_wb),
1006 (void *)wb, NULL);
1007 m = g_list_append(m, act);
1008 }
1009
1010 return m;
1011 }
1012
1013
1014 /******************************* Joining Etc. ********************************/
1015
silcpurple_get_chat_name(GHashTable * data)1016 char *silcpurple_get_chat_name(GHashTable *data)
1017 {
1018 return g_strdup(g_hash_table_lookup(data, "channel"));
1019 }
1020
silcpurple_chat_join(PurpleConnection * gc,GHashTable * data)1021 void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
1022 {
1023 SilcPurple sg = gc->proto_data;
1024 SilcClient client = sg->client;
1025 SilcClientConnection conn = sg->conn;
1026 const char *channel, *passphrase, *parentch;
1027
1028 if (!conn)
1029 return;
1030
1031 channel = g_hash_table_lookup(data, "channel");
1032 passphrase = g_hash_table_lookup(data, "passphrase");
1033
1034 /* Check if we are joining a private group. Handle it
1035 purely locally as it's not a real channel */
1036 if (strstr(channel, "[Private Group]")) {
1037 SilcChannelEntry channel_entry;
1038 SilcChannelPrivateKey key;
1039 PurpleChat *c;
1040 SilcPurplePrvgrp grp;
1041
1042 c = purple_blist_find_chat(sg->account, channel);
1043 parentch = purple_blist_node_get_string((PurpleBlistNode *)c, "parentch");
1044 if (!parentch)
1045 return;
1046
1047 channel_entry = silc_client_get_channel(sg->client, sg->conn,
1048 (char *)parentch);
1049 if (!channel_entry ||
1050 !silc_client_on_channel(channel_entry, sg->conn->local_entry)) {
1051 char tmp[512];
1052 g_snprintf(tmp, sizeof(tmp),
1053 _("You have to join the %s channel before you are "
1054 "able to join the private group"), parentch);
1055 purple_notify_error(gc, _("Join Private Group"),
1056 _("Cannot join private group"), tmp);
1057 return;
1058 }
1059
1060 /* Add channel private key */
1061 if (!silc_client_add_channel_private_key(client, conn,
1062 channel_entry, channel,
1063 NULL, NULL,
1064 (unsigned char *)passphrase,
1065 strlen(passphrase), &key))
1066 return;
1067
1068 /* Join the group */
1069 grp = silc_calloc(1, sizeof(*grp));
1070 if (!grp)
1071 return;
1072 grp->id = ++sg->channel_ids + SILCPURPLE_PRVGRP;
1073 grp->chid = SILC_PTR_TO_32(channel_entry->context);
1074 grp->parentch = parentch;
1075 grp->channel = channel;
1076 grp->key = key;
1077 sg->grps = g_list_append(sg->grps, grp);
1078 serv_got_joined_chat(gc, grp->id, channel);
1079 return;
1080 }
1081
1082 /* XXX We should have other properties here as well:
1083 1. whether to try to authenticate to the channel
1084 1a. with default key,
1085 1b. with specific key.
1086 2. whether to try to authenticate to become founder.
1087 2a. with default key,
1088 2b. with specific key.
1089
1090 Since now such variety is not possible in the join dialog
1091 we always use -founder and -auth options, which try to
1092 do both 1 and 2 with default keys. */
1093
1094 /* Call JOIN */
1095 if ((passphrase != NULL) && (*passphrase != '\0'))
1096 silc_client_command_call(client, conn, NULL, "JOIN",
1097 channel, passphrase, "-auth", "-founder", NULL);
1098 else
1099 silc_client_command_call(client, conn, NULL, "JOIN",
1100 channel, "-auth", "-founder", NULL);
1101 }
1102
silcpurple_chat_invite(PurpleConnection * gc,int id,const char * msg,const char * name)1103 void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
1104 const char *name)
1105 {
1106 SilcPurple sg = gc->proto_data;
1107 SilcClient client = sg->client;
1108 SilcClientConnection conn = sg->conn;
1109 SilcHashTableList htl;
1110 SilcChannelUser chu;
1111 gboolean found = FALSE;
1112
1113 if (!conn)
1114 return;
1115
1116 /* See if we are inviting on a private group. Invite
1117 to the actual channel */
1118 if (id > SILCPURPLE_PRVGRP) {
1119 GList *l;
1120 SilcPurplePrvgrp prv;
1121
1122 for (l = sg->grps; l; l = l->next)
1123 if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1124 break;
1125 if (!l)
1126 return;
1127 prv = l->data;
1128 id = prv->chid;
1129 }
1130
1131 /* Find channel by id */
1132 silc_hash_table_list(conn->local_entry->channels, &htl);
1133 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1134 if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1135 found = TRUE;
1136 break;
1137 }
1138 }
1139 silc_hash_table_list_reset(&htl);
1140 if (!found)
1141 return;
1142
1143 /* Call INVITE */
1144 silc_client_command_call(client, conn, NULL, "INVITE",
1145 chu->channel->channel_name,
1146 name, NULL);
1147 }
1148
silcpurple_chat_leave(PurpleConnection * gc,int id)1149 void silcpurple_chat_leave(PurpleConnection *gc, int id)
1150 {
1151 SilcPurple sg = gc->proto_data;
1152 SilcClient client = sg->client;
1153 SilcClientConnection conn = sg->conn;
1154 SilcHashTableList htl;
1155 SilcChannelUser chu;
1156 gboolean found = FALSE;
1157 GList *l;
1158 SilcPurplePrvgrp prv;
1159
1160 if (!conn)
1161 return;
1162
1163 /* See if we are leaving a private group */
1164 if (id > SILCPURPLE_PRVGRP) {
1165 SilcChannelEntry channel;
1166
1167 for (l = sg->grps; l; l = l->next)
1168 if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1169 break;
1170 if (!l)
1171 return;
1172 prv = l->data;
1173 channel = silc_client_get_channel(sg->client, sg->conn,
1174 (char *)prv->parentch);
1175 if (!channel)
1176 return;
1177 silc_client_del_channel_private_key(client, conn,
1178 channel, prv->key);
1179 silc_free(prv);
1180 sg->grps = g_list_remove(sg->grps, prv);
1181 serv_got_chat_left(gc, id);
1182 return;
1183 }
1184
1185 /* Find channel by id */
1186 silc_hash_table_list(conn->local_entry->channels, &htl);
1187 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1188 if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1189 found = TRUE;
1190 break;
1191 }
1192 }
1193 silc_hash_table_list_reset(&htl);
1194 if (!found)
1195 return;
1196
1197 /* Call LEAVE */
1198 silc_client_command_call(client, conn, NULL, "LEAVE",
1199 chu->channel->channel_name, NULL);
1200
1201 serv_got_chat_left(gc, id);
1202
1203 /* Leave from private groups on this channel as well */
1204 for (l = sg->grps; l; l = l->next)
1205 if (((SilcPurplePrvgrp)l->data)->chid == (gulong)id) {
1206 prv = l->data;
1207 silc_client_del_channel_private_key(client, conn,
1208 chu->channel,
1209 prv->key);
1210 serv_got_chat_left(gc, prv->id);
1211 silc_free(prv);
1212 sg->grps = g_list_remove(sg->grps, prv);
1213 if (!sg->grps)
1214 break;
1215 }
1216 }
1217
silcpurple_chat_send(PurpleConnection * gc,int id,const char * msg,PurpleMessageFlags msgflags)1218 int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
1219 PurpleMessageFlags msgflags)
1220 {
1221 SilcPurple sg = gc->proto_data;
1222 SilcClient client = sg->client;
1223 SilcClientConnection conn = sg->conn;
1224 SilcHashTableList htl;
1225 SilcChannelUser chu;
1226 SilcChannelEntry channel = NULL;
1227 SilcChannelPrivateKey key = NULL;
1228 SilcMessageFlags flags;
1229 int ret = 0;
1230 char *msg2, *tmp;
1231 gboolean found = FALSE;
1232 gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
1233 SilcDList list;
1234
1235 if (!msg || !conn)
1236 return 0;
1237
1238 flags = SILC_MESSAGE_FLAG_UTF8;
1239
1240 tmp = msg2 = purple_unescape_html(msg);
1241
1242 if (!g_ascii_strncasecmp(msg2, "/me ", 4))
1243 {
1244 msg2 += 4;
1245 if (!*msg2) {
1246 g_free(tmp);
1247 return 0;
1248 }
1249 flags |= SILC_MESSAGE_FLAG_ACTION;
1250 } else if (strlen(msg) > 1 && msg[0] == '/') {
1251 if (!silc_client_command_call(client, conn, msg + 1))
1252 purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
1253 _("Unknown command"));
1254 g_free(tmp);
1255 return 0;
1256 }
1257
1258
1259 if (sign)
1260 flags |= SILC_MESSAGE_FLAG_SIGNED;
1261
1262 /* Get the channel private key if we are sending on
1263 private group */
1264 if (id > SILCPURPLE_PRVGRP) {
1265 GList *l;
1266 SilcPurplePrvgrp prv;
1267
1268 for (l = sg->grps; l; l = l->next)
1269 if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1270 break;
1271 if (!l) {
1272 g_free(tmp);
1273 return 0;
1274 }
1275 prv = l->data;
1276 channel = silc_client_get_channel(sg->client, sg->conn,
1277 (char *)prv->parentch);
1278 if (!channel) {
1279 g_free(tmp);
1280 return 0;
1281 }
1282 key = prv->key;
1283 }
1284
1285 if (!channel) {
1286 /* Find channel by id */
1287 silc_hash_table_list(conn->local_entry->channels, &htl);
1288 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1289 if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1290 found = TRUE;
1291 break;
1292 }
1293 }
1294 silc_hash_table_list_reset(&htl);
1295 if (!found) {
1296 g_free(tmp);
1297 return 0;
1298 }
1299 channel = chu->channel;
1300 }
1301
1302 /* Check for images */
1303 if (msgflags & PURPLE_MESSAGE_IMAGES) {
1304 list = silcpurple_image_message(msg, &flags);
1305 if (list) {
1306 /* Send one or more MIME message. If more than one, they
1307 are MIME fragments due to over large message */
1308 SilcBuffer buf;
1309
1310 silc_dlist_start(list);
1311 while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
1312 ret =
1313 silc_client_send_channel_message(client, conn,
1314 channel, key,
1315 flags, sg->sha1hash,
1316 buf->data,
1317 silc_buffer_len(buf));
1318 silc_mime_partial_free(list);
1319 g_free(tmp);
1320
1321 if (ret)
1322 serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, time(NULL));
1323 return ret;
1324 }
1325 }
1326
1327 /* Send channel message */
1328 ret = silc_client_send_channel_message(client, conn, channel, key,
1329 flags, sg->sha1hash,
1330 (unsigned char *)msg2,
1331 strlen(msg2));
1332 if (ret) {
1333 serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg,
1334 time(NULL));
1335 }
1336 g_free(tmp);
1337
1338 return ret;
1339 }
1340
silcpurple_chat_set_topic(PurpleConnection * gc,int id,const char * topic)1341 void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
1342 {
1343 SilcPurple sg = gc->proto_data;
1344 SilcClient client = sg->client;
1345 SilcClientConnection conn = sg->conn;
1346 SilcHashTableList htl;
1347 SilcChannelUser chu;
1348 gboolean found = FALSE;
1349
1350 if (!conn)
1351 return;
1352
1353 /* See if setting topic on private group. Set it
1354 on the actual channel */
1355 if (id > SILCPURPLE_PRVGRP) {
1356 GList *l;
1357 SilcPurplePrvgrp prv;
1358
1359 for (l = sg->grps; l; l = l->next)
1360 if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1361 break;
1362 if (!l)
1363 return;
1364 prv = l->data;
1365 id = prv->chid;
1366 }
1367
1368 /* Find channel by id */
1369 silc_hash_table_list(conn->local_entry->channels, &htl);
1370 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1371 if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1372 found = TRUE;
1373 break;
1374 }
1375 }
1376 silc_hash_table_list_reset(&htl);
1377 if (!found)
1378 return;
1379
1380 /* Call TOPIC */
1381 silc_client_command_call(client, conn, NULL, "TOPIC",
1382 chu->channel->channel_name, topic, NULL);
1383 }
1384
silcpurple_roomlist_get_list(PurpleConnection * gc)1385 PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
1386 {
1387 SilcPurple sg = gc->proto_data;
1388 SilcClient client = sg->client;
1389 SilcClientConnection conn = sg->conn;
1390 GList *fields = NULL;
1391 PurpleRoomlistField *f;
1392
1393 if (!conn)
1394 return NULL;
1395
1396 if (sg->roomlist)
1397 purple_roomlist_unref(sg->roomlist);
1398
1399 sg->roomlist_cancelled = FALSE;
1400
1401 sg->roomlist = purple_roomlist_new(purple_connection_get_account(gc));
1402 f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "channel", TRUE);
1403 fields = g_list_append(fields, f);
1404 f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT,
1405 _("Users"), "users", FALSE);
1406 fields = g_list_append(fields, f);
1407 f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
1408 _("Topic"), "topic", FALSE);
1409 fields = g_list_append(fields, f);
1410 purple_roomlist_set_fields(sg->roomlist, fields);
1411
1412 /* Call LIST */
1413 silc_client_command_call(client, conn, "LIST");
1414
1415 purple_roomlist_set_in_progress(sg->roomlist, TRUE);
1416
1417 return sg->roomlist;
1418 }
1419
silcpurple_roomlist_cancel(PurpleRoomlist * list)1420 void silcpurple_roomlist_cancel(PurpleRoomlist *list)
1421 {
1422 PurpleConnection *gc = purple_account_get_connection(list->account);
1423 SilcPurple sg;
1424
1425 if (!gc)
1426 return;
1427 sg = gc->proto_data;
1428
1429 purple_roomlist_set_in_progress(list, FALSE);
1430 if (sg->roomlist == list) {
1431 purple_roomlist_unref(sg->roomlist);
1432 sg->roomlist = NULL;
1433 sg->roomlist_cancelled = TRUE;
1434 }
1435 }
1436