comparison libpurple/protocols/silc10/chat.c @ 17567:ba1b50f114f6

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