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.
21 PURPLE_BEGIN_IGNORE_CAST_ALIGN
23 PURPLE_END_IGNORE_CAST_ALIGN
24 #include "silcclient.h"
25 #include "silcpurple.h"
28 /***************************** Channel Routines ******************************/
30 GList
*silcpurple_chat_info(PurpleConnection
*gc
)
33 PurpleProtocolChatEntry
*pce
;
35 pce
= g_new0(PurpleProtocolChatEntry
, 1);
36 pce
->label
= _("_Channel:");
37 pce
->identifier
= "channel";
39 ci
= g_list_append(ci
, pce
);
41 pce
= g_new0(PurpleProtocolChatEntry
, 1);
42 pce
->label
= _("_Passphrase:");
43 pce
->identifier
= "passphrase";
45 ci
= g_list_append(ci
, pce
);
50 GHashTable
*silcpurple_chat_info_defaults(PurpleConnection
*gc
, const char *chat_name
)
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
));
63 silcpurple_chat_getinfo(PurpleConnection
*gc
, GHashTable
*components
);
66 silcpurple_chat_getinfo_res(SilcClient client
,
67 SilcClientConnection conn
,
72 GHashTable
*components
= context
;
73 PurpleConnection
*gc
= client
->application
;
77 chname
= g_hash_table_lookup(components
, "channel");
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
));
90 silcpurple_chat_getinfo(gc
, components
);
95 silcpurple_chat_getinfo(PurpleConnection
*gc
, GHashTable
*components
)
97 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
101 SilcChannelEntry channel
;
102 SilcHashTableList htl
;
108 chname
= g_hash_table_lookup(components
, "channel");
111 channel
= silc_client_get_channel(sg
->client
, sg
->conn
,
114 silc_client_get_channel_resolve(sg
->client
, sg
->conn
,
116 silcpurple_chat_getinfo_res
,
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
);
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"),
139 silc_hash_table_list_reset(&htl
);
142 g_string_append_printf(s
, _("<br><b>Channel Cipher:</b> %s"),
146 /* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */
147 g_string_append_printf(s
, _("<br><b>Channel HMAC:</b> %s"),
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
);
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
;
166 pk
= silc_pkcs_public_key_encode(channel
->founder_key
, &pk_len
);
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
);
180 purple_notify_formatted(gc
, NULL
, _("Channel Information"), NULL
, s
->str
, NULL
, NULL
);
181 g_string_free(s
, TRUE
);
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 ****************************/
199 SilcChannelEntry channel
;
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];
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
);
221 purple_notify_error(client
->application
,
222 _("Add Channel Public Key"),
223 _("Could not load public key"), NULL
, NULL
);
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
;
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
);
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
);
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
);
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
);
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
;
281 SilcPublicKey public_key
;
282 SilcBuffer chpks
, pk
, chidp
;
283 SilcUInt16 c
= 0, ct
;
284 unsigned char mode
[4];
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
);
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
);
312 silc_buffer_free(chpks
);
315 SILC_PUT16_MSB(c
, chpks
->head
);
317 m
= sgc
->channel
->mode
;
319 m
&= ~SILC_CHANNEL_MODE_CHANNEL_AUTH
;
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
);
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
);
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
;
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");
355 else if (val
&& !curpass
)
357 else if (val
&& curpass
&& !purple_strequal(val
, curpass
))
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");
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
);
381 void silcpurple_chat_chauth_show(SilcPurple sg
, SilcChannelEntry channel
,
382 SilcDList channel_pubkeys
)
384 SilcPublicKey public_key
;
385 SilcSILCPublicKey silc_pubkey
;
388 char *fingerprint
, *babbleprint
;
389 SilcPublicKeyIdentifier ident
;
392 PurpleRequestFields
*fields
;
393 PurpleRequestFieldGroup
*g
;
394 PurpleRequestField
*f
;
395 SilcPurpleChauth sgc
;
396 const char *curpass
= NULL
;
398 sgc
= silc_calloc(1, sizeof(*sgc
));
402 sgc
->channel
= channel
;
404 fields
= purple_request_fields_new();
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"),
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
);
436 silc_dlist_uninit(channel_pubkeys
);
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
);
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
);
475 silcpurple_chat_chauth(PurpleBlistNode
*node
, gpointer data
)
478 PurpleConnection
*gc
;
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"),
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. */
506 } *SilcPurpleCharPrv
;
509 silcpurple_chat_prv_add(SilcPurpleCharPrv p
, PurpleRequestFields
*fields
)
511 SilcPurple sg
= p
->sg
;
513 PurpleRequestField
*f
;
514 const char *name
, *passphrase
, *alias
;
519 f
= purple_request_fields_get_field(fields
, "name");
520 name
= purple_request_field_string_get_value(f
);
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
);
544 silcpurple_chat_join(sg
->gc
, comp
);
550 silcpurple_chat_prv_cancel(SilcPurpleCharPrv p
, PurpleRequestFields
*fields
)
556 silcpurple_chat_prv(PurpleBlistNode
*node
, gpointer data
)
559 PurpleConnection
*gc
;
563 PurpleRequestFields
*fields
;
564 PurpleRequestFieldGroup
*g
;
565 PurpleRequestField
*f
;
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
));
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"),
587 purple_request_field_group_add_field(g
, f
);
589 f
= purple_request_field_string_new("passphrase", _("Passphrase"),
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"),
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."),
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 ********************************/
612 silcpurple_chat_permanent_reset(PurpleBlistNode
*node
, gpointer data
)
615 PurpleConnection
*gc
;
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"),
630 silcpurple_chat_permanent(PurpleBlistNode
*node
, gpointer data
)
633 PurpleConnection
*gc
;
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
);
646 /* XXX we should have ability to define which founder
647 key to use. Now we use the user's own public key
651 channel
= g_hash_table_lookup(purple_chat_get_components(chat
), "channel");
652 silc_client_command_call(sg
->client
, sg
->conn
, NULL
, "CMODE", channel
,
659 } *SilcPurpleChatInput
;
662 silcpurple_chat_ulimit_cb(SilcPurpleChatInput s
, const char *limit
)
664 SilcChannelEntry channel
;
667 channel
= silc_client_get_channel(s
->sg
->client
, s
->sg
->conn
,
672 ulimit
= strtoul(limit
, NULL
, 10);
674 if (!limit
|| !(*limit
) || *limit
== '0') {
675 if (limit
&& ulimit
== channel
->user_limit
) {
680 silc_client_command_call(s
->sg
->client
, s
->sg
->conn
, NULL
, "CMODE",
681 s
->channel
, "-l", NULL
);
688 if (ulimit
== channel
->user_limit
) {
695 silc_client_command_call(s
->sg
->client
, s
->sg
->conn
, NULL
, "CMODE",
696 s
->channel
, "+l", limit
, NULL
);
703 silcpurple_chat_ulimit(PurpleBlistNode
*node
, gpointer data
)
706 PurpleConnection
*gc
;
709 SilcPurpleChatInput s
;
710 SilcChannelEntry channel
;
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
);
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
);
728 s
= silc_calloc(1, sizeof(*s
));
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
);
743 silcpurple_chat_resettopic(PurpleBlistNode
*node
, gpointer data
)
746 PurpleConnection
*gc
;
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"),
761 silcpurple_chat_settopic(PurpleBlistNode
*node
, gpointer data
)
764 PurpleConnection
*gc
;
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"),
779 silcpurple_chat_resetprivate(PurpleBlistNode
*node
, gpointer data
)
782 PurpleConnection
*gc
;
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"),
797 silcpurple_chat_setprivate(PurpleBlistNode
*node
, gpointer data
)
800 PurpleConnection
*gc
;
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"),
815 silcpurple_chat_resetsecret(PurpleBlistNode
*node
, gpointer data
)
818 PurpleConnection
*gc
;
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"),
833 silcpurple_chat_setsecret(PurpleBlistNode
*node
, gpointer data
)
836 PurpleConnection
*gc
;
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"),
852 SilcChannelEntry channel
;
856 silcpurple_chat_wb(PurpleBlistNode
*node
, gpointer data
)
858 SilcPurpleChatWb wb
= data
;
859 silcpurple_wb_init_ch(wb
->sg
, wb
->channel
);
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
;
875 PurpleActionMenu
*act
;
878 chname
= g_hash_table_lookup(components
, "channel");
881 channel
= silc_client_get_channel(sg
->client
, sg
->conn
,
884 chu
= silc_client_on_channel(channel
, conn
->local_entry
);
889 if (strstr(chname
, "[Private Group]"))
892 act
= purple_action_menu_new(_("Get Info"),
893 PURPLE_CALLBACK(silcpurple_chat_getinfo_menu
),
895 m
= g_list_append(m
, act
);
898 act
= purple_action_menu_new(_("Add Private Group"),
899 PURPLE_CALLBACK(silcpurple_chat_prv
),
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
),
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
),
914 m
= g_list_append(m
, act
);
916 act
= purple_action_menu_new(_("Set Permanent"),
917 PURPLE_CALLBACK(silcpurple_chat_permanent
),
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
),
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
),
933 m
= g_list_append(m
, act
);
935 act
= purple_action_menu_new(_("Set Topic Restriction"),
936 PURPLE_CALLBACK(silcpurple_chat_settopic
),
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
),
945 m
= g_list_append(m
, act
);
947 act
= purple_action_menu_new(_("Set Private Channel"),
948 PURPLE_CALLBACK(silcpurple_chat_setprivate
),
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
),
957 m
= g_list_append(m
, act
);
959 act
= purple_action_menu_new(_("Set Secret Channel"),
960 PURPLE_CALLBACK(silcpurple_chat_setsecret
),
962 m
= g_list_append(m
, act
);
966 if (chu
&& channel
) {
968 wb
= silc_calloc(1, sizeof(*wb
));
970 wb
->channel
= channel
;
971 act
= purple_action_menu_new(_("Draw On Whiteboard"),
972 PURPLE_CALLBACK(silcpurple_chat_wb
),
974 m
= g_list_append(m
, act
);
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
;
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
;
1007 SilcPurplePrvgrp grp
;
1009 c
= purple_blist_find_chat(sg
->account
, channel
);
1010 parentch
= purple_blist_node_get_string((PurpleBlistNode
*)c
, "parentch");
1014 channel_entry
= silc_client_get_channel(sg
->client
, sg
->conn
,
1016 if (!channel_entry
||
1017 !silc_client_on_channel(channel_entry
, sg
->conn
->local_entry
)) {
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
));
1028 /* Add channel private key */
1029 if (!silc_client_add_channel_private_key(client
, conn
,
1030 channel_entry
, channel
,
1032 (unsigned char *)passphrase
,
1033 strlen(passphrase
), &key
))
1036 /* Join the group */
1037 grp
= silc_calloc(1, sizeof(*grp
));
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
;
1045 sg
->grps
= g_list_append(sg
->grps
, grp
);
1046 purple_serv_got_joined_chat(gc
, grp
->id
, channel
);
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. */
1063 if ((passphrase
!= NULL
) && (*passphrase
!= '\0'))
1064 silc_client_command_call(client
, conn
, NULL
, "JOIN",
1065 channel
, passphrase
, "-auth", "-founder", NULL
);
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
,
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
;
1084 /* See if we are inviting on a private group. Invite
1085 to the actual channel */
1086 if (id
> SILCPURPLE_PRVGRP
) {
1088 SilcPurplePrvgrp prv
;
1090 for (l
= sg
->grps
; l
; l
= l
->next
)
1091 if (((SilcPurplePrvgrp
)l
->data
)->id
== (gulong
)id
)
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
) {
1107 silc_hash_table_list_reset(&htl
);
1112 silc_client_command_call(client
, conn
, NULL
, "INVITE",
1113 chu
->channel
->channel_name
,
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
;
1126 SilcPurplePrvgrp prv
;
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
)
1141 channel
= silc_client_get_channel(sg
->client
, sg
->conn
,
1142 (char *)prv
->parentch
);
1145 silc_client_del_channel_private_key(client
, conn
,
1148 sg
->grps
= g_list_remove(sg
->grps
, prv
);
1149 purple_serv_got_chat_left(gc
, id
);
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
) {
1161 silc_hash_table_list_reset(&htl
);
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
) {
1175 silc_client_del_channel_private_key(client
, conn
,
1178 purple_serv_got_chat_left(gc
, prv
->id
);
1180 sg
->grps
= g_list_remove(sg
->grps
, prv
);
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
;
1197 const gchar
*msg
= purple_message_get_contents(pmsg
);
1199 gboolean found
= FALSE
;
1200 gboolean sign
= purple_account_get_bool(sg
->account
, "sign-verify", FALSE
);
1202 PurpleMessageFlags msgflags
= purple_message_get_flags(pmsg
);
1207 flags
= SILC_MESSAGE_FLAG_UTF8
;
1209 tmp
= msg2
= purple_unescape_html(msg
);
1211 if (!g_ascii_strncasecmp(msg2
, "/me ", 4))
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
));
1231 flags
|= SILC_MESSAGE_FLAG_SIGNED
;
1233 /* Get the channel private key if we are sending on
1235 if (id
> SILCPURPLE_PRVGRP
) {
1237 SilcPurplePrvgrp prv
;
1239 for (l
= sg
->grps
; l
; l
= l
->next
)
1240 if (((SilcPurplePrvgrp
)l
->data
)->id
== (gulong
)id
)
1247 channel
= silc_client_get_channel(sg
->client
, sg
->conn
,
1248 (char *)prv
->parentch
);
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
) {
1265 silc_hash_table_list_reset(&htl
);
1270 channel
= chu
->channel
;
1273 /* Check for images */
1274 if (msgflags
& PURPLE_MESSAGE_IMAGES
) {
1275 list
= silcpurple_image_message(msg
, &flags
);
1277 /* Send one or more MIME message. If more than one, they
1278 are MIME fragments due to over large message */
1281 silc_dlist_start(list
);
1282 while ((buf
= silc_dlist_get(list
)) != SILC_LIST_END
)
1284 silc_client_send_channel_message(client
, conn
,
1286 flags
, sg
->sha1hash
,
1288 silc_buffer_len(buf
));
1289 silc_mime_partial_free(list
);
1293 purple_serv_got_chat_in(gc
, id
, purple_connection_get_display_name(gc
), msgflags
, msg
, time(NULL
));
1298 /* Send channel message */
1299 ret
= silc_client_send_channel_message(client
, conn
, channel
, key
,
1300 flags
, sg
->sha1hash
,
1301 (unsigned char *)msg2
,
1304 purple_serv_got_chat_in(gc
, id
, purple_connection_get_display_name(gc
), msgflags
, msg
,
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
;
1324 /* See if setting topic on private group. Set it
1325 on the actual channel */
1326 if (id
> SILCPURPLE_PRVGRP
) {
1328 SilcPurplePrvgrp prv
;
1330 for (l
= sg
->grps
; l
; l
= l
->next
)
1331 if (((SilcPurplePrvgrp
)l
->data
)->id
== (gulong
)id
)
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
) {
1347 silc_hash_table_list_reset(&htl
);
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
;
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
);
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
);
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
;