rename accountopt.[ch] to purpleaccountoption.[ch]
[pidgin-git.git] / libpurple / protocols / silc / chat.c
blob093bbda541c834263f6bef4013c9448252ee3997
1 /*
3 silcpurple_chat.c
5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2004 - 2007 Pekka Riikonen
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.
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.
20 #include "internal.h"
21 PURPLE_BEGIN_IGNORE_CAST_ALIGN
22 #include "silc.h"
23 PURPLE_END_IGNORE_CAST_ALIGN
24 #include "silcclient.h"
25 #include "silcpurple.h"
26 #include "wb.h"
28 /***************************** Channel Routines ******************************/
30 GList *silcpurple_chat_info(PurpleConnection *gc)
32 GList *ci = NULL;
33 PurpleProtocolChatEntry *pce;
35 pce = g_new0(PurpleProtocolChatEntry, 1);
36 pce->label = _("_Channel:");
37 pce->identifier = "channel";
38 pce->required = TRUE;
39 ci = g_list_append(ci, pce);
41 pce = g_new0(PurpleProtocolChatEntry, 1);
42 pce->label = _("_Passphrase:");
43 pce->identifier = "passphrase";
44 pce->secret = TRUE;
45 ci = g_list_append(ci, pce);
47 return ci;
50 GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
52 GHashTable *defaults;
54 defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
56 if (chat_name != NULL)
57 g_hash_table_insert(defaults, "channel", g_strdup(chat_name));
59 return defaults;
62 static void
63 silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components);
65 static void
66 silcpurple_chat_getinfo_res(SilcClient client,
67 SilcClientConnection conn,
68 SilcStatus status,
69 SilcDList channels,
70 void *context)
72 GHashTable *components = context;
73 PurpleConnection *gc = client->application;
74 const char *chname;
75 char tmp[256];
77 chname = g_hash_table_lookup(components, "channel");
78 if (!chname)
79 return;
81 if (!channels) {
82 g_snprintf(tmp, sizeof(tmp),
83 _("Channel %s does not exist in the network"), chname);
84 purple_notify_error(gc, _("Channel Information"),
85 _("Cannot get channel information"), tmp,
86 purple_request_cpar_from_connection(gc));
87 return;
90 silcpurple_chat_getinfo(gc, components);
94 static void
95 silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components)
97 SilcPurple sg = purple_connection_get_protocol_data(gc);
98 const char *chname;
99 char tmp[256], *tmp2;
100 GString *s;
101 SilcChannelEntry channel;
102 SilcHashTableList htl;
103 SilcChannelUser chu;
105 if (!components)
106 return;
108 chname = g_hash_table_lookup(components, "channel");
109 if (!chname)
110 return;
111 channel = silc_client_get_channel(sg->client, sg->conn,
112 (char *)chname);
113 if (!channel) {
114 silc_client_get_channel_resolve(sg->client, sg->conn,
115 (char *)chname,
116 silcpurple_chat_getinfo_res,
117 components);
118 return;
121 s = g_string_new("");
122 tmp2 = g_markup_escape_text(channel->channel_name, -1);
123 g_string_append_printf(s, _("<b>Channel Name:</b> %s"), tmp2);
124 g_free(tmp2);
125 if (channel->user_list && silc_hash_table_count(channel->user_list))
126 g_string_append_printf(s, _("<br><b>User Count:</b> %d"),
127 (int)silc_hash_table_count(channel->user_list));
129 silc_hash_table_list(channel->user_list, &htl);
130 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
131 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
132 tmp2 = g_markup_escape_text(chu->client->nickname, -1);
133 g_string_append_printf(s, _("<br><b>Channel Founder:</b> %s"),
134 tmp2);
135 g_free(tmp2);
136 break;
139 silc_hash_table_list_reset(&htl);
141 if (channel->cipher)
142 g_string_append_printf(s, _("<br><b>Channel Cipher:</b> %s"),
143 channel->cipher);
145 if (channel->hmac)
146 /* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */
147 g_string_append_printf(s, _("<br><b>Channel HMAC:</b> %s"),
148 channel->hmac);
150 if (channel->topic) {
151 tmp2 = g_markup_escape_text(channel->topic, -1);
152 g_string_append_printf(s, _("<br><b>Channel Topic:</b><br>%s"), tmp2);
153 g_free(tmp2);
156 if (channel->mode) {
157 g_string_append(s, _("<br><b>Channel Modes:</b> "));
158 silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
159 g_string_append(s, tmp);
162 if (channel->founder_key) {
163 char *fingerprint, *babbleprint;
164 unsigned char *pk;
165 SilcUInt32 pk_len;
166 pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len);
167 if (pk) {
168 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
169 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
171 g_string_append_printf(s, _("<br><b>Founder Key Fingerprint:</b><br>%s"), fingerprint);
172 g_string_append_printf(s, _("<br><b>Founder Key Babbleprint:</b><br>%s"), babbleprint);
174 silc_free(fingerprint);
175 silc_free(babbleprint);
176 silc_free(pk);
180 purple_notify_formatted(gc, NULL, _("Channel Information"), NULL, s->str, NULL, NULL);
181 g_string_free(s, TRUE);
185 static void
186 silcpurple_chat_getinfo_menu(PurpleBlistNode *node, gpointer data)
188 PurpleChat *chat = (PurpleChat *)node;
189 PurpleAccount *account = purple_chat_get_account(chat);
190 silcpurple_chat_getinfo(purple_account_get_connection(account),
191 purple_chat_get_components(chat));
195 /************************* Channel Authentication ****************************/
197 typedef struct {
198 SilcPurple sg;
199 SilcChannelEntry channel;
200 PurpleChat *c;
201 SilcDList pubkeys;
202 } *SilcPurpleChauth;
204 static void
205 silcpurple_chat_chpk_add(void *user_data, const char *name)
207 SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
208 SilcPurple sg = sgc->sg;
209 SilcClient client = sg->client;
210 SilcClientConnection conn = sg->conn;
211 SilcPublicKey public_key;
212 SilcBuffer chpks, pk, chidp;
213 unsigned char mode[4];
214 SilcUInt32 m;
216 /* Load the public key */
217 if (!silc_pkcs_load_public_key(name, &public_key)) {
218 silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
219 silc_dlist_uninit(sgc->pubkeys);
220 silc_free(sgc);
221 purple_notify_error(client->application,
222 _("Add Channel Public Key"),
223 _("Could not load public key"), NULL, NULL);
224 return;
227 pk = silc_public_key_payload_encode(public_key);
228 chpks = silc_buffer_alloc_size(2);
229 SILC_PUT16_MSB(1, chpks->head);
230 chpks = silc_argument_payload_encode_one(chpks, pk->data,
231 silc_buffer_len(pk), 0x00);
232 silc_buffer_free(pk);
234 m = sgc->channel->mode;
235 m |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
237 /* Send CMODE */
238 SILC_PUT32_MSB(m, mode);
239 chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL);
240 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
241 silcpurple_command_reply, NULL, 3,
242 1, chidp->data, silc_buffer_len(chidp),
243 2, mode, sizeof(mode),
244 9, chpks->data, silc_buffer_len(chpks));
245 silc_buffer_free(chpks);
246 silc_buffer_free(chidp);
247 if (sgc->pubkeys) {
248 silc_dlist_start(sgc->pubkeys);
249 while ((public_key = silc_dlist_get(sgc->pubkeys)))
250 silc_pkcs_public_key_free(public_key);
251 silc_dlist_uninit(sgc->pubkeys);
253 silc_free(sgc);
256 static void
257 silcpurple_chat_chpk_cancel(void *user_data, const char *name)
259 SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
260 SilcPublicKey public_key;
262 silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
264 if (sgc->pubkeys) {
265 silc_dlist_start(sgc->pubkeys);
266 while ((public_key = silc_dlist_get(sgc->pubkeys)))
267 silc_pkcs_public_key_free(public_key);
268 silc_dlist_uninit(sgc->pubkeys);
270 silc_free(sgc);
273 static void
274 silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields)
276 SilcPurple sg = sgc->sg;
277 SilcClient client = sg->client;
278 SilcClientConnection conn = sg->conn;
279 PurpleRequestField *f;
280 GList *list;
281 SilcPublicKey public_key;
282 SilcBuffer chpks, pk, chidp;
283 SilcUInt16 c = 0, ct;
284 unsigned char mode[4];
285 SilcUInt32 m;
287 f = purple_request_fields_get_field(fields, "list");
288 if (!purple_request_field_list_get_selected(f)) {
289 /* Add new public key */
290 purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE,
291 G_CALLBACK(silcpurple_chat_chpk_add),
292 G_CALLBACK(silcpurple_chat_chpk_cancel),
293 purple_request_cpar_from_connection(sg->gc), sgc);
294 return;
297 list = purple_request_field_list_get_items(f);
298 chpks = silc_buffer_alloc_size(2);
300 for (ct = 0; list; list = list->next, ct++) {
301 public_key = purple_request_field_list_get_data(f, list->data);
302 if (purple_request_field_list_is_selected(f, list->data)) {
303 /* Delete this public key */
304 pk = silc_public_key_payload_encode(public_key);
305 chpks = silc_argument_payload_encode_one(chpks, pk->data,
306 silc_buffer_len(pk), 0x01);
307 silc_buffer_free(pk);
308 c++;
311 if (!c) {
312 silc_buffer_free(chpks);
313 return;
315 SILC_PUT16_MSB(c, chpks->head);
317 m = sgc->channel->mode;
318 if (ct == c)
319 m &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
321 /* Send CMODE */
322 SILC_PUT32_MSB(m, mode);
323 chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL);
324 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
325 silcpurple_command_reply, NULL, 3,
326 1, chidp->data, silc_buffer_len(chidp),
327 2, mode, sizeof(mode),
328 9, chpks->data, silc_buffer_len(chpks));
329 silc_buffer_free(chpks);
330 silc_buffer_free(chidp);
331 if (sgc->pubkeys) {
332 silc_dlist_start(sgc->pubkeys);
333 while ((public_key = silc_dlist_get(sgc->pubkeys)))
334 silc_pkcs_public_key_free(public_key);
335 silc_dlist_uninit(sgc->pubkeys);
337 silc_free(sgc);
340 static void
341 silcpurple_chat_chauth_ok(SilcPurpleChauth sgc, PurpleRequestFields *fields)
343 SilcPurple sg = sgc->sg;
344 PurpleRequestField *f;
345 SilcPublicKey public_key;
346 const char *curpass, *val;
347 int set;
349 f = purple_request_fields_get_field(fields, "passphrase");
350 val = purple_request_field_string_get_value(f);
351 curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase");
353 if (!val && curpass)
354 set = 0;
355 else if (val && !curpass)
356 set = 1;
357 else if (val && curpass && !purple_strequal(val, curpass))
358 set = 1;
359 else
360 set = -1;
362 if (set == 1) {
363 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
364 sgc->channel->channel_name, "+a", val, NULL);
365 purple_blist_node_set_string((PurpleBlistNode *)sgc->c, "passphrase", val);
366 } else if (set == 0) {
367 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
368 sgc->channel->channel_name, "-a", NULL);
369 purple_blist_node_remove_setting((PurpleBlistNode *)sgc->c, "passphrase");
372 if (sgc->pubkeys) {
373 silc_dlist_start(sgc->pubkeys);
374 while ((public_key = silc_dlist_get(sgc->pubkeys)))
375 silc_pkcs_public_key_free(public_key);
376 silc_dlist_uninit(sgc->pubkeys);
378 silc_free(sgc);
381 void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
382 SilcDList channel_pubkeys)
384 SilcPublicKey public_key;
385 SilcSILCPublicKey silc_pubkey;
386 unsigned char *pk;
387 SilcUInt32 pk_len;
388 char *fingerprint, *babbleprint;
389 SilcPublicKeyIdentifier ident;
390 char tmp2[1024];
391 const gchar *t;
392 PurpleRequestFields *fields;
393 PurpleRequestFieldGroup *g;
394 PurpleRequestField *f;
395 SilcPurpleChauth sgc;
396 const char *curpass = NULL;
398 sgc = silc_calloc(1, sizeof(*sgc));
399 if (!sgc)
400 return;
401 sgc->sg = sg;
402 sgc->channel = channel;
404 fields = purple_request_fields_new();
406 if (sgc->c)
407 curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase");
409 g = purple_request_field_group_new(NULL);
410 f = purple_request_field_string_new("passphrase", _("Channel Passphrase"),
411 curpass, FALSE);
412 purple_request_field_string_set_masked(f, TRUE);
413 purple_request_field_group_add_field(g, f);
414 purple_request_fields_add_group(fields, g);
416 g = purple_request_field_group_new(NULL);
417 f = purple_request_field_label_new("l1", _("Channel Public Keys List"));
418 purple_request_field_group_add_field(g, f);
419 purple_request_fields_add_group(fields, g);
421 t = _("Channel authentication is used to secure the channel from "
422 "unauthorized access. The authentication may be based on "
423 "passphrase and digital signatures. If passphrase is set, it "
424 "is required to be able to join. If channel public keys are set "
425 "then only users whose public keys are listed are able to join.");
427 if (!channel_pubkeys || !silc_dlist_count(channel_pubkeys)) {
428 f = purple_request_field_list_new("list", NULL);
429 purple_request_field_group_add_field(g, f);
430 purple_request_fields(sg->gc, _("Channel Authentication"),
431 _("Channel Authentication"), t, fields,
432 _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
433 _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
434 purple_request_cpar_from_connection(sg->gc), sgc);
435 if (channel_pubkeys)
436 silc_dlist_uninit(channel_pubkeys);
437 return;
439 sgc->pubkeys = channel_pubkeys;
441 g = purple_request_field_group_new(NULL);
442 f = purple_request_field_list_new("list", NULL);
443 purple_request_field_group_add_field(g, f);
444 purple_request_fields_add_group(fields, g);
446 silc_dlist_start(channel_pubkeys);
447 while ((public_key = silc_dlist_get(channel_pubkeys))) {
448 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
449 if (!pk)
450 continue;
451 fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
452 babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
454 silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
455 ident = &silc_pubkey->identifier;
457 g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s",
458 ident->realname ? ident->realname : ident->username ?
459 ident->username : "", fingerprint, babbleprint);
460 purple_request_field_list_add_icon(f, tmp2, NULL, public_key);
462 silc_free(fingerprint);
463 silc_free(babbleprint);
466 purple_request_field_list_set_multi_select(f, FALSE);
467 purple_request_fields(sg->gc, _("Channel Authentication"),
468 _("Channel Authentication"), t, fields,
469 _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
470 _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
471 purple_request_cpar_from_connection(sg->gc), sgc);
474 static void
475 silcpurple_chat_chauth(PurpleBlistNode *node, gpointer data)
477 PurpleChat *chat;
478 PurpleConnection *gc;
479 SilcPurple sg;
481 g_return_if_fail(PURPLE_IS_CHAT(node));
483 chat = (PurpleChat *) node;
484 gc = purple_account_get_connection(purple_chat_get_account(chat));
485 sg = purple_connection_get_protocol_data(gc);
487 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
488 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
489 "+C", NULL);
493 /************************** Channel Private Groups **************************/
495 /* Private groups are "virtual" channels. They are groups inside a channel.
496 This is implemented by using channel private keys. By knowing a channel
497 private key user becomes part of that group and is able to talk on that
498 group. Other users, on the same channel, won't be able to see the
499 messages of that group. It is possible to have multiple groups inside
500 a channel - and thus having multiple private keys on the channel. */
502 typedef struct {
503 SilcPurple sg;
504 PurpleChat *c;
505 const char *channel;
506 } *SilcPurpleCharPrv;
508 static void
509 silcpurple_chat_prv_add(SilcPurpleCharPrv p, PurpleRequestFields *fields)
511 SilcPurple sg = p->sg;
512 gchar *tmp;
513 PurpleRequestField *f;
514 const char *name, *passphrase, *alias;
515 GHashTable *comp;
516 PurpleGroup *g;
517 PurpleChat *cn;
519 f = purple_request_fields_get_field(fields, "name");
520 name = purple_request_field_string_get_value(f);
521 if (!name) {
522 silc_free(p);
523 return;
525 f = purple_request_fields_get_field(fields, "passphrase");
526 passphrase = purple_request_field_string_get_value(f);
527 f = purple_request_fields_get_field(fields, "alias");
528 alias = purple_request_field_string_get_value(f);
530 /* Add private group to buddy list */
531 tmp = g_strdup_printf("%s [Private Group]", name);
532 comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
533 g_hash_table_replace(comp, "channel", tmp);
534 g_hash_table_replace(comp, "passphrase", g_strdup(passphrase));
536 cn = purple_chat_new(sg->account, alias, comp);
537 g = purple_chat_get_group(p->c);
538 purple_blist_add_chat(cn, g, (PurpleBlistNode *)p->c);
540 /* Associate to a real channel */
541 purple_blist_node_set_string((PurpleBlistNode *)cn, "parentch", p->channel);
543 /* Join the group */
544 silcpurple_chat_join(sg->gc, comp);
546 silc_free(p);
549 static void
550 silcpurple_chat_prv_cancel(SilcPurpleCharPrv p, PurpleRequestFields *fields)
552 silc_free(p);
555 static void
556 silcpurple_chat_prv(PurpleBlistNode *node, gpointer data)
558 PurpleChat *chat;
559 PurpleConnection *gc;
560 SilcPurple sg;
562 SilcPurpleCharPrv p;
563 PurpleRequestFields *fields;
564 PurpleRequestFieldGroup *g;
565 PurpleRequestField *f;
566 char tmp[512];
568 g_return_if_fail(PURPLE_IS_CHAT(node));
570 chat = (PurpleChat *) node;
571 gc = purple_account_get_connection(purple_chat_get_account(chat));
572 sg = purple_connection_get_protocol_data(gc);
574 p = silc_calloc(1, sizeof(*p));
575 if (!p)
576 return;
577 p->sg = sg;
579 p->channel = g_hash_table_lookup(purple_chat_get_components(chat), "channel");
580 p->c = purple_blist_find_chat(sg->account, p->channel);
582 fields = purple_request_fields_new();
584 g = purple_request_field_group_new(NULL);
585 f = purple_request_field_string_new("name", _("Group Name"),
586 NULL, FALSE);
587 purple_request_field_group_add_field(g, f);
589 f = purple_request_field_string_new("passphrase", _("Passphrase"),
590 NULL, FALSE);
591 purple_request_field_string_set_masked(f, TRUE);
592 purple_request_field_group_add_field(g, f);
594 f = purple_request_field_string_new("alias", _("Alias"),
595 NULL, FALSE);
596 purple_request_field_group_add_field(g, f);
597 purple_request_fields_add_group(fields, g);
599 g_snprintf(tmp, sizeof(tmp),
600 _("Please enter the %s channel private group name and passphrase."),
601 p->channel);
602 purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields,
603 _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
604 _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
605 purple_request_cpar_from_connection(gc), p);
609 /****************************** Channel Modes ********************************/
611 static void
612 silcpurple_chat_permanent_reset(PurpleBlistNode *node, gpointer data)
614 PurpleChat *chat;
615 PurpleConnection *gc;
616 SilcPurple sg;
618 g_return_if_fail(PURPLE_IS_CHAT(node));
620 chat = (PurpleChat *) node;
621 gc = purple_account_get_connection(purple_chat_get_account(chat));
622 sg = purple_connection_get_protocol_data(gc);
624 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
625 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
626 "-f", NULL);
629 static void
630 silcpurple_chat_permanent(PurpleBlistNode *node, gpointer data)
632 PurpleChat *chat;
633 PurpleConnection *gc;
634 SilcPurple sg;
635 const char *channel;
637 g_return_if_fail(PURPLE_IS_CHAT(node));
639 chat = (PurpleChat *) node;
640 gc = purple_account_get_connection(purple_chat_get_account(chat));
641 sg = purple_connection_get_protocol_data(gc);
643 if (!sg->conn)
644 return;
646 /* XXX we should have ability to define which founder
647 key to use. Now we use the user's own public key
648 (default key). */
650 /* Call CMODE */
651 channel = g_hash_table_lookup(purple_chat_get_components(chat), "channel");
652 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel,
653 "+f", NULL);
656 typedef struct {
657 SilcPurple sg;
658 char *channel;
659 } *SilcPurpleChatInput;
661 static void
662 silcpurple_chat_ulimit_cb(SilcPurpleChatInput s, const char *limit)
664 SilcChannelEntry channel;
665 guint ulimit = 0;
667 channel = silc_client_get_channel(s->sg->client, s->sg->conn,
668 (char *)s->channel);
669 if (!channel)
670 return;
671 if (limit)
672 ulimit = strtoul(limit, NULL, 10);
674 if (!limit || !(*limit) || *limit == '0') {
675 if (limit && ulimit == channel->user_limit) {
676 g_free(s->channel);
677 silc_free(s);
678 return;
680 silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
681 s->channel, "-l", NULL);
683 g_free(s->channel);
684 silc_free(s);
685 return;
688 if (ulimit == channel->user_limit) {
689 g_free(s->channel);
690 silc_free(s);
691 return;
694 /* Call CMODE */
695 silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
696 s->channel, "+l", limit, NULL);
698 g_free(s->channel);
699 silc_free(s);
702 static void
703 silcpurple_chat_ulimit(PurpleBlistNode *node, gpointer data)
705 PurpleChat *chat;
706 PurpleConnection *gc;
707 SilcPurple sg;
709 SilcPurpleChatInput s;
710 SilcChannelEntry channel;
711 char *ch;
712 char tmp[32];
714 g_return_if_fail(PURPLE_IS_CHAT(node));
716 chat = (PurpleChat *) node;
717 gc = purple_account_get_connection(purple_chat_get_account(chat));
718 sg = purple_connection_get_protocol_data(gc);
720 if (!sg->conn)
721 return;
723 ch = g_strdup(g_hash_table_lookup(purple_chat_get_components(chat), "channel"));
724 channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch);
725 if (!channel)
726 return;
728 s = silc_calloc(1, sizeof(*s));
729 if (!s)
730 return;
731 s->channel = ch;
732 s->sg = sg;
733 g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit);
734 purple_request_input(gc, _("User Limit"), NULL,
735 _("Set user limit on channel. Set to zero to reset user limit."),
736 tmp, FALSE, FALSE, NULL,
737 _("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb),
738 _("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb),
739 purple_request_cpar_from_connection(gc), s);
742 static void
743 silcpurple_chat_resettopic(PurpleBlistNode *node, gpointer data)
745 PurpleChat *chat;
746 PurpleConnection *gc;
747 SilcPurple sg;
749 g_return_if_fail(PURPLE_IS_CHAT(node));
751 chat = (PurpleChat *) node;
752 gc = purple_account_get_connection(purple_chat_get_account(chat));
753 sg = purple_connection_get_protocol_data(gc);
755 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
756 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
757 "-t", NULL);
760 static void
761 silcpurple_chat_settopic(PurpleBlistNode *node, gpointer data)
763 PurpleChat *chat;
764 PurpleConnection *gc;
765 SilcPurple sg;
767 g_return_if_fail(PURPLE_IS_CHAT(node));
769 chat = (PurpleChat *) node;
770 gc = purple_account_get_connection(purple_chat_get_account(chat));
771 sg = purple_connection_get_protocol_data(gc);
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);
778 static void
779 silcpurple_chat_resetprivate(PurpleBlistNode *node, gpointer data)
781 PurpleChat *chat;
782 PurpleConnection *gc;
783 SilcPurple sg;
785 g_return_if_fail(PURPLE_IS_CHAT(node));
787 chat = (PurpleChat *) node;
788 gc = purple_account_get_connection(purple_chat_get_account(chat));
789 sg = purple_connection_get_protocol_data(gc);
791 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
792 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
793 "-p", NULL);
796 static void
797 silcpurple_chat_setprivate(PurpleBlistNode *node, gpointer data)
799 PurpleChat *chat;
800 PurpleConnection *gc;
801 SilcPurple sg;
803 g_return_if_fail(PURPLE_IS_CHAT(node));
805 chat = (PurpleChat *) node;
806 gc = purple_account_get_connection(purple_chat_get_account(chat));
807 sg = purple_connection_get_protocol_data(gc);
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);
814 static void
815 silcpurple_chat_resetsecret(PurpleBlistNode *node, gpointer data)
817 PurpleChat *chat;
818 PurpleConnection *gc;
819 SilcPurple sg;
821 g_return_if_fail(PURPLE_IS_CHAT(node));
823 chat = (PurpleChat *) node;
824 gc = purple_account_get_connection(purple_chat_get_account(chat));
825 sg = purple_connection_get_protocol_data(gc);
827 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
828 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
829 "-s", NULL);
832 static void
833 silcpurple_chat_setsecret(PurpleBlistNode *node, gpointer data)
835 PurpleChat *chat;
836 PurpleConnection *gc;
837 SilcPurple sg;
839 g_return_if_fail(PURPLE_IS_CHAT(node));
841 chat = (PurpleChat *) node;
842 gc = purple_account_get_connection(purple_chat_get_account(chat));
843 sg = purple_connection_get_protocol_data(gc);
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);
850 typedef struct {
851 SilcPurple sg;
852 SilcChannelEntry channel;
853 } *SilcPurpleChatWb;
855 static void
856 silcpurple_chat_wb(PurpleBlistNode *node, gpointer data)
858 SilcPurpleChatWb wb = data;
859 silcpurple_wb_init_ch(wb->sg, wb->channel);
860 silc_free(wb);
863 GList *silcpurple_chat_menu(PurpleChat *chat)
865 GHashTable *components = purple_chat_get_components(chat);
866 PurpleConnection *gc = purple_account_get_connection(purple_chat_get_account(chat));
867 SilcPurple sg = purple_connection_get_protocol_data(gc);
868 SilcClientConnection conn = sg->conn;
869 const char *chname = NULL;
870 SilcChannelEntry channel = NULL;
871 SilcChannelUser chu = NULL;
872 SilcUInt32 mode = 0;
874 GList *m = NULL;
875 PurpleActionMenu *act;
877 if (components)
878 chname = g_hash_table_lookup(components, "channel");
879 if (!chname)
880 return NULL;
881 channel = silc_client_get_channel(sg->client, sg->conn,
882 (char *)chname);
883 if (channel) {
884 chu = silc_client_on_channel(channel, conn->local_entry);
885 if (chu)
886 mode = chu->mode;
889 if (strstr(chname, "[Private Group]"))
890 return NULL;
892 act = purple_action_menu_new(_("Get Info"),
893 PURPLE_CALLBACK(silcpurple_chat_getinfo_menu),
894 NULL, NULL);
895 m = g_list_append(m, act);
897 if (chu) {
898 act = purple_action_menu_new(_("Add Private Group"),
899 PURPLE_CALLBACK(silcpurple_chat_prv),
900 NULL, NULL);
901 m = g_list_append(m, act);
904 if (chu && mode & SILC_CHANNEL_UMODE_CHANFO) {
905 act = purple_action_menu_new(_("Channel Authentication"),
906 PURPLE_CALLBACK(silcpurple_chat_chauth),
907 NULL, NULL);
908 m = g_list_append(m, act);
910 if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
911 act = purple_action_menu_new(_("Reset Permanent"),
912 PURPLE_CALLBACK(silcpurple_chat_permanent_reset),
913 NULL, NULL);
914 m = g_list_append(m, act);
915 } else {
916 act = purple_action_menu_new(_("Set Permanent"),
917 PURPLE_CALLBACK(silcpurple_chat_permanent),
918 NULL, NULL);
919 m = g_list_append(m, act);
923 if (chu && mode & SILC_CHANNEL_UMODE_CHANOP) {
924 act = purple_action_menu_new(_("Set User Limit"),
925 PURPLE_CALLBACK(silcpurple_chat_ulimit),
926 NULL, NULL);
927 m = g_list_append(m, act);
929 if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
930 act = purple_action_menu_new(_("Reset Topic Restriction"),
931 PURPLE_CALLBACK(silcpurple_chat_resettopic),
932 NULL, NULL);
933 m = g_list_append(m, act);
934 } else {
935 act = purple_action_menu_new(_("Set Topic Restriction"),
936 PURPLE_CALLBACK(silcpurple_chat_settopic),
937 NULL, NULL);
938 m = g_list_append(m, act);
941 if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) {
942 act = purple_action_menu_new(_("Reset Private Channel"),
943 PURPLE_CALLBACK(silcpurple_chat_resetprivate),
944 NULL, NULL);
945 m = g_list_append(m, act);
946 } else {
947 act = purple_action_menu_new(_("Set Private Channel"),
948 PURPLE_CALLBACK(silcpurple_chat_setprivate),
949 NULL, NULL);
950 m = g_list_append(m, act);
953 if (channel->mode & SILC_CHANNEL_MODE_SECRET) {
954 act = purple_action_menu_new(_("Reset Secret Channel"),
955 PURPLE_CALLBACK(silcpurple_chat_resetsecret),
956 NULL, NULL);
957 m = g_list_append(m, act);
958 } else {
959 act = purple_action_menu_new(_("Set Secret Channel"),
960 PURPLE_CALLBACK(silcpurple_chat_setsecret),
961 NULL, NULL);
962 m = g_list_append(m, act);
966 if (chu && channel) {
967 SilcPurpleChatWb wb;
968 wb = silc_calloc(1, sizeof(*wb));
969 wb->sg = sg;
970 wb->channel = channel;
971 act = purple_action_menu_new(_("Draw On Whiteboard"),
972 PURPLE_CALLBACK(silcpurple_chat_wb),
973 (void *)wb, NULL);
974 m = g_list_append(m, act);
977 return m;
981 /******************************* Joining Etc. ********************************/
983 char *silcpurple_get_chat_name(GHashTable *data)
985 return g_strdup(g_hash_table_lookup(data, "channel"));
988 void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
990 SilcPurple sg = purple_connection_get_protocol_data(gc);
991 SilcClient client = sg->client;
992 SilcClientConnection conn = sg->conn;
993 const char *channel, *passphrase, *parentch;
995 if (!conn)
996 return;
998 channel = g_hash_table_lookup(data, "channel");
999 passphrase = g_hash_table_lookup(data, "passphrase");
1001 /* Check if we are joining a private group. Handle it
1002 purely locally as it's not a real channel */
1003 if (strstr(channel, "[Private Group]")) {
1004 SilcChannelEntry channel_entry;
1005 SilcChannelPrivateKey key;
1006 PurpleChat *c;
1007 SilcPurplePrvgrp grp;
1009 c = purple_blist_find_chat(sg->account, channel);
1010 parentch = purple_blist_node_get_string((PurpleBlistNode *)c, "parentch");
1011 if (!parentch)
1012 return;
1014 channel_entry = silc_client_get_channel(sg->client, sg->conn,
1015 (char *)parentch);
1016 if (!channel_entry ||
1017 !silc_client_on_channel(channel_entry, sg->conn->local_entry)) {
1018 char tmp[512];
1019 g_snprintf(tmp, sizeof(tmp),
1020 _("You have to join the %s channel before you are "
1021 "able to join the private group"), parentch);
1022 purple_notify_error(gc, _("Join Private Group"),
1023 _("Cannot join private group"), tmp,
1024 purple_request_cpar_from_connection(gc));
1025 return;
1028 /* Add channel private key */
1029 if (!silc_client_add_channel_private_key(client, conn,
1030 channel_entry, channel,
1031 NULL, NULL,
1032 (unsigned char *)passphrase,
1033 strlen(passphrase), &key))
1034 return;
1036 /* Join the group */
1037 grp = silc_calloc(1, sizeof(*grp));
1038 if (!grp)
1039 return;
1040 grp->id = ++sg->channel_ids + SILCPURPLE_PRVGRP;
1041 grp->chid = SILC_PTR_TO_32(channel_entry->context);
1042 grp->parentch = parentch;
1043 grp->channel = channel;
1044 grp->key = key;
1045 sg->grps = g_list_append(sg->grps, grp);
1046 purple_serv_got_joined_chat(gc, grp->id, channel);
1047 return;
1050 /* XXX We should have other properties here as well:
1051 1. whether to try to authenticate to the channel
1052 1a. with default key,
1053 1b. with specific key.
1054 2. whether to try to authenticate to become founder.
1055 2a. with default key,
1056 2b. with specific key.
1058 Since now such variety is not possible in the join dialog
1059 we always use -founder and -auth options, which try to
1060 do both 1 and 2 with default keys. */
1062 /* Call JOIN */
1063 if ((passphrase != NULL) && (*passphrase != '\0'))
1064 silc_client_command_call(client, conn, NULL, "JOIN",
1065 channel, passphrase, "-auth", "-founder", NULL);
1066 else
1067 silc_client_command_call(client, conn, NULL, "JOIN",
1068 channel, "-auth", "-founder", NULL);
1071 void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
1072 const char *name)
1074 SilcPurple sg = purple_connection_get_protocol_data(gc);
1075 SilcClient client = sg->client;
1076 SilcClientConnection conn = sg->conn;
1077 SilcHashTableList htl;
1078 SilcChannelUser chu;
1079 gboolean found = FALSE;
1081 if (!conn)
1082 return;
1084 /* See if we are inviting on a private group. Invite
1085 to the actual channel */
1086 if (id > SILCPURPLE_PRVGRP) {
1087 GList *l;
1088 SilcPurplePrvgrp prv;
1090 for (l = sg->grps; l; l = l->next)
1091 if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1092 break;
1093 if (!l)
1094 return;
1095 prv = l->data;
1096 id = prv->chid;
1099 /* Find channel by id */
1100 silc_hash_table_list(conn->local_entry->channels, &htl);
1101 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1102 if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1103 found = TRUE;
1104 break;
1107 silc_hash_table_list_reset(&htl);
1108 if (!found)
1109 return;
1111 /* Call INVITE */
1112 silc_client_command_call(client, conn, NULL, "INVITE",
1113 chu->channel->channel_name,
1114 name, NULL);
1117 void silcpurple_chat_leave(PurpleConnection *gc, int id)
1119 SilcPurple sg = purple_connection_get_protocol_data(gc);
1120 SilcClient client = sg->client;
1121 SilcClientConnection conn = sg->conn;
1122 SilcHashTableList htl;
1123 SilcChannelUser chu;
1124 gboolean found = FALSE;
1125 GList *l;
1126 SilcPurplePrvgrp prv;
1128 if (!conn)
1129 return;
1131 /* See if we are leaving a private group */
1132 if (id > SILCPURPLE_PRVGRP) {
1133 SilcChannelEntry channel;
1135 for (l = sg->grps; l; l = l->next)
1136 if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1137 break;
1138 if (!l)
1139 return;
1140 prv = l->data;
1141 channel = silc_client_get_channel(sg->client, sg->conn,
1142 (char *)prv->parentch);
1143 if (!channel)
1144 return;
1145 silc_client_del_channel_private_key(client, conn,
1146 channel, prv->key);
1147 silc_free(prv);
1148 sg->grps = g_list_remove(sg->grps, prv);
1149 purple_serv_got_chat_left(gc, id);
1150 return;
1153 /* Find channel by id */
1154 silc_hash_table_list(conn->local_entry->channels, &htl);
1155 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1156 if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1157 found = TRUE;
1158 break;
1161 silc_hash_table_list_reset(&htl);
1162 if (!found)
1163 return;
1165 /* Call LEAVE */
1166 silc_client_command_call(client, conn, NULL, "LEAVE",
1167 chu->channel->channel_name, NULL);
1169 purple_serv_got_chat_left(gc, id);
1171 /* Leave from private groups on this channel as well */
1172 for (l = sg->grps; l; l = l->next)
1173 if (((SilcPurplePrvgrp)l->data)->chid == (gulong)id) {
1174 prv = l->data;
1175 silc_client_del_channel_private_key(client, conn,
1176 chu->channel,
1177 prv->key);
1178 purple_serv_got_chat_left(gc, prv->id);
1179 silc_free(prv);
1180 sg->grps = g_list_remove(sg->grps, prv);
1181 if (!sg->grps)
1182 break;
1186 int silcpurple_chat_send(PurpleConnection *gc, int id, PurpleMessage *pmsg)
1188 SilcPurple sg = purple_connection_get_protocol_data(gc);
1189 SilcClient client = sg->client;
1190 SilcClientConnection conn = sg->conn;
1191 SilcHashTableList htl;
1192 SilcChannelUser chu;
1193 SilcChannelEntry channel = NULL;
1194 SilcChannelPrivateKey key = NULL;
1195 SilcMessageFlags flags;
1196 int ret = 0;
1197 const gchar *msg = purple_message_get_contents(pmsg);
1198 char *msg2, *tmp;
1199 gboolean found = FALSE;
1200 gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
1201 SilcDList list;
1202 PurpleMessageFlags msgflags = purple_message_get_flags(pmsg);
1204 if (!msg || !conn)
1205 return 0;
1207 flags = SILC_MESSAGE_FLAG_UTF8;
1209 tmp = msg2 = purple_unescape_html(msg);
1211 if (!g_ascii_strncasecmp(msg2, "/me ", 4))
1213 msg2 += 4;
1214 if (!*msg2) {
1215 g_free(tmp);
1216 return 0;
1218 flags |= SILC_MESSAGE_FLAG_ACTION;
1219 } else if (strlen(msg) > 1 && msg[0] == '/') {
1220 if (!silc_client_command_call(client, conn, msg + 1)) {
1221 purple_notify_error(gc, _("Call Command"),
1222 _("Cannot call command"), _("Unknown command"),
1223 purple_request_cpar_from_connection(gc));
1225 g_free(tmp);
1226 return 0;
1230 if (sign)
1231 flags |= SILC_MESSAGE_FLAG_SIGNED;
1233 /* Get the channel private key if we are sending on
1234 private group */
1235 if (id > SILCPURPLE_PRVGRP) {
1236 GList *l;
1237 SilcPurplePrvgrp prv;
1239 for (l = sg->grps; l; l = l->next)
1240 if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1241 break;
1242 if (!l) {
1243 g_free(tmp);
1244 return 0;
1246 prv = l->data;
1247 channel = silc_client_get_channel(sg->client, sg->conn,
1248 (char *)prv->parentch);
1249 if (!channel) {
1250 g_free(tmp);
1251 return 0;
1253 key = prv->key;
1256 if (!channel) {
1257 /* Find channel by id */
1258 silc_hash_table_list(conn->local_entry->channels, &htl);
1259 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1260 if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1261 found = TRUE;
1262 break;
1265 silc_hash_table_list_reset(&htl);
1266 if (!found) {
1267 g_free(tmp);
1268 return 0;
1270 channel = chu->channel;
1273 /* Check for images */
1274 if (msgflags & PURPLE_MESSAGE_IMAGES) {
1275 list = silcpurple_image_message(msg, &flags);
1276 if (list) {
1277 /* Send one or more MIME message. If more than one, they
1278 are MIME fragments due to over large message */
1279 SilcBuffer buf;
1281 silc_dlist_start(list);
1282 while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
1283 ret =
1284 silc_client_send_channel_message(client, conn,
1285 channel, key,
1286 flags, sg->sha1hash,
1287 buf->data,
1288 silc_buffer_len(buf));
1289 silc_mime_partial_free(list);
1290 g_free(tmp);
1292 if (ret)
1293 purple_serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, time(NULL));
1294 return ret;
1298 /* Send channel message */
1299 ret = silc_client_send_channel_message(client, conn, channel, key,
1300 flags, sg->sha1hash,
1301 (unsigned char *)msg2,
1302 strlen(msg2));
1303 if (ret) {
1304 purple_serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg,
1305 time(NULL));
1307 g_free(tmp);
1309 return ret;
1312 void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
1314 SilcPurple sg = purple_connection_get_protocol_data(gc);
1315 SilcClient client = sg->client;
1316 SilcClientConnection conn = sg->conn;
1317 SilcHashTableList htl;
1318 SilcChannelUser chu;
1319 gboolean found = FALSE;
1321 if (!conn)
1322 return;
1324 /* See if setting topic on private group. Set it
1325 on the actual channel */
1326 if (id > SILCPURPLE_PRVGRP) {
1327 GList *l;
1328 SilcPurplePrvgrp prv;
1330 for (l = sg->grps; l; l = l->next)
1331 if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1332 break;
1333 if (!l)
1334 return;
1335 prv = l->data;
1336 id = prv->chid;
1339 /* Find channel by id */
1340 silc_hash_table_list(conn->local_entry->channels, &htl);
1341 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1342 if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1343 found = TRUE;
1344 break;
1347 silc_hash_table_list_reset(&htl);
1348 if (!found)
1349 return;
1351 /* Call TOPIC */
1352 silc_client_command_call(client, conn, NULL, "TOPIC",
1353 chu->channel->channel_name, topic, NULL);
1356 PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
1358 SilcPurple sg = purple_connection_get_protocol_data(gc);
1359 SilcClient client = sg->client;
1360 SilcClientConnection conn = sg->conn;
1361 GList *fields = NULL;
1362 PurpleRoomlistField *f;
1364 if (!conn)
1365 return NULL;
1367 if (sg->roomlist)
1368 g_object_unref(sg->roomlist);
1370 sg->roomlist_cancelled = FALSE;
1372 sg->roomlist = purple_roomlist_new(purple_connection_get_account(gc));
1373 f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "channel", TRUE);
1374 fields = g_list_append(fields, f);
1375 f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT,
1376 _("Users"), "users", FALSE);
1377 fields = g_list_append(fields, f);
1378 f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
1379 _("Topic"), "topic", FALSE);
1380 fields = g_list_append(fields, f);
1381 purple_roomlist_set_fields(sg->roomlist, fields);
1383 /* Call LIST */
1384 silc_client_command_call(client, conn, "LIST");
1386 purple_roomlist_set_in_progress(sg->roomlist, TRUE);
1388 return sg->roomlist;
1391 void silcpurple_roomlist_cancel(PurpleRoomlist *list)
1393 PurpleAccount *account = purple_roomlist_get_account(list);
1394 PurpleConnection *gc = purple_account_get_connection(account);
1395 SilcPurple sg;
1397 if (!gc)
1398 return;
1399 sg = purple_connection_get_protocol_data(gc);
1401 purple_roomlist_set_in_progress(list, FALSE);
1402 if (sg->roomlist == list) {
1403 g_object_unref(sg->roomlist);
1404 sg->roomlist = NULL;
1405 sg->roomlist_cancelled = TRUE;