Rename *_TYPE_*_IFACE to more standard *_TYPE_*.
[pidgin-git.git] / libpurple / protocols / silc / silc.c
blobf82f905b12280373655d0083874dc0381343da0f
1 /*
3 silcpurple.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 #include "plugins.h"
22 PURPLE_BEGIN_IGNORE_CAST_ALIGN
23 #include "silc.h"
24 PURPLE_END_IGNORE_CAST_ALIGN
25 #include "silcclient.h"
26 #include "silcpurple.h"
27 #include "version.h"
28 #include "wb.h"
29 #include "core.h"
31 extern SilcClientOperations ops;
33 static PurpleProtocol *my_protocol = NULL;
34 static GSList *cmds = NULL;
36 /* Error log message callback */
38 static SilcBool silcpurple_log_error(SilcLogType type, char *message,
39 void *context)
41 silc_say(NULL, NULL, SILC_CLIENT_MESSAGE_ERROR, message);
42 return TRUE;
45 static const char *
46 silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b)
48 return (const char *)"silc";
51 static GList *
52 silcpurple_away_states(PurpleAccount *account)
54 PurpleStatusType *type;
55 GList *types = NULL;
57 type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE);
58 types = g_list_append(types, type);
59 type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE);
60 types = g_list_append(types, type);
61 type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE);
62 types = g_list_append(types, type);
63 type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE);
64 types = g_list_append(types, type);
65 type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE);
66 types = g_list_append(types, type);
67 type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE);
68 types = g_list_append(types, type);
69 type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE);
70 types = g_list_append(types, type);
72 return types;
75 static void
76 silcpurple_set_status(PurpleAccount *account, PurpleStatus *status)
78 PurpleConnection *gc = purple_account_get_connection(account);
79 SilcPurple sg = NULL;
80 SilcUInt32 mode;
81 SilcBuffer idp;
82 unsigned char mb[4];
83 const char *state;
85 if (gc != NULL)
86 sg = purple_connection_get_protocol_data(gc);
88 if (status == NULL)
89 return;
91 state = purple_status_get_id(status);
93 if (state == NULL)
94 return;
96 if ((sg == NULL) || (sg->conn == NULL))
97 return;
99 mode = sg->conn->local_entry->mode;
100 mode &= ~(SILC_UMODE_GONE |
101 SILC_UMODE_HYPER |
102 SILC_UMODE_BUSY |
103 SILC_UMODE_INDISPOSED |
104 SILC_UMODE_PAGE);
106 if (purple_strequal(state, "hyper"))
107 mode |= SILC_UMODE_HYPER;
108 else if (purple_strequal(state, "away"))
109 mode |= SILC_UMODE_GONE;
110 else if (purple_strequal(state, "busy"))
111 mode |= SILC_UMODE_BUSY;
112 else if (purple_strequal(state, "indisposed"))
113 mode |= SILC_UMODE_INDISPOSED;
114 else if (purple_strequal(state, "page"))
115 mode |= SILC_UMODE_PAGE;
117 /* Send UMODE */
118 idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT);
119 SILC_PUT32_MSB(mode, mb);
120 silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE,
121 silcpurple_command_reply, NULL, 2,
122 1, idp->data, silc_buffer_len(idp),
123 2, mb, sizeof(mb));
124 silc_buffer_free(idp);
128 /*************************** Connection Routines *****************************/
130 static void
131 silcpurple_keepalive(PurpleConnection *gc)
133 SilcPurple sg = purple_connection_get_protocol_data(gc);
134 silc_packet_send(sg->conn->stream, SILC_PACKET_HEARTBEAT, 0,
135 NULL, 0);
138 #if __SILC_TOOLKIT_VERSION < SILC_VERSION(1,1,1)
139 static gboolean
140 silcpurple_scheduler(gpointer *context)
142 SilcClient client = (SilcClient)context;
143 silc_client_run_one(client);
144 return TRUE;
146 #else
147 typedef struct {
148 SilcPurple sg;
149 SilcUInt32 fd;
150 guint tag;
151 } *SilcPurpleTask;
153 /* A timeout occurred. Call SILC scheduler. */
155 static gboolean
156 silcpurple_scheduler_timeout(gpointer context)
158 SilcPurpleTask task = (SilcPurpleTask)context;
159 silc_client_run_one(task->sg->client);
160 silc_dlist_del(task->sg->tasks, task);
161 silc_free(task);
162 return FALSE;
165 /* An fd task event occurred. Call SILC scheduler. */
167 static void
168 silcpurple_scheduler_fd(gpointer data, gint fd, PurpleInputCondition cond)
170 SilcClient client = (SilcClient)data;
171 silc_client_run_one(client);
174 /* SILC Scheduler notify callback. This is called whenever task is added to
175 or deleted from SILC scheduler. It's also called when fd task events
176 change. Here we add same tasks to glib's main loop. */
178 static void
179 silcpurple_scheduler(SilcSchedule schedule,
180 SilcBool added, SilcTask task,
181 SilcBool fd_task, SilcUInt32 fd,
182 SilcTaskEvent event,
183 long seconds, long useconds,
184 void *context)
186 SilcClient client = (SilcClient)context;
187 PurpleConnection *gc = client->application;
188 SilcPurple sg = purple_connection_get_protocol_data(gc);
189 SilcPurpleTask ptask = NULL;
191 if (added) {
192 if (fd_task) {
193 /* Add fd or change fd events */
194 PurpleInputCondition e = 0;
196 silc_dlist_start(sg->tasks);
197 while ((ptask = silc_dlist_get(sg->tasks)))
198 if (ptask->fd == fd) {
199 purple_input_remove(ptask->tag);
200 break;
203 if (event & SILC_TASK_READ)
204 e |= PURPLE_INPUT_READ;
205 if (event & SILC_TASK_WRITE)
206 e |= PURPLE_INPUT_WRITE;
208 if (e) {
209 if (!ptask) {
210 ptask = silc_calloc(1, sizeof(*ptask));
211 ptask->fd = fd;
212 silc_dlist_add(sg->tasks, ptask);
214 ptask->tag = purple_input_add(fd, e, silcpurple_scheduler_fd,
215 client);
216 } else if (ptask) {
217 silc_dlist_del(sg->tasks, ptask);
218 silc_free(ptask);
220 } else {
221 /* Add timeout */
222 ptask = silc_calloc(1, sizeof(*ptask));
223 ptask->sg = sg;
224 ptask->tag = g_timeout_add((seconds * 1000) +
225 (useconds / 1000),
226 silcpurple_scheduler_timeout,
227 ptask);
228 silc_dlist_add(sg->tasks, ptask);
230 } else {
231 if (fd_task) {
232 /* Remove fd */
233 silc_dlist_start(sg->tasks);
234 while ((ptask = silc_dlist_get(sg->tasks)))
235 if (ptask->fd == fd) {
236 purple_input_remove(ptask->tag);
237 silc_dlist_del(sg->tasks, ptask);
238 silc_free(ptask);
239 break;
244 #endif /* __SILC_TOOLKIT_VERSION */
246 static void
247 silcpurple_connect_cb(SilcClient client, SilcClientConnection conn,
248 SilcClientConnectionStatus status, SilcStatus error,
249 const char *message, void *context)
251 PurpleConnection *gc = context;
252 SilcPurple sg;
253 SilcUInt32 mask;
254 char tz[16];
255 PurpleImage *img;
256 #ifdef HAVE_SYS_UTSNAME_H
257 struct utsname u;
258 #endif
260 sg = purple_connection_get_protocol_data(gc);
262 switch (status) {
263 case SILC_CLIENT_CONN_SUCCESS:
264 case SILC_CLIENT_CONN_SUCCESS_RESUME:
265 sg->conn = conn;
267 /* Connection created successfully */
268 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
270 /* Send the server our buddy list */
271 silcpurple_send_buddylist(gc);
273 g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
275 /* Send any UMODEs configured for account */
276 if (purple_account_get_bool(sg->account, "block-ims", FALSE)) {
277 silc_client_command_call(sg->client, sg->conn, NULL,
278 "UMODE", "+P", NULL);
281 /* Set default attributes */
282 mask = SILC_ATTRIBUTE_MOOD_NORMAL;
283 silc_client_attribute_add(client, conn,
284 SILC_ATTRIBUTE_STATUS_MOOD,
285 SILC_32_TO_PTR(mask),
286 sizeof(SilcUInt32));
287 mask = SILC_ATTRIBUTE_CONTACT_CHAT;
288 silc_client_attribute_add(client, conn,
289 SILC_ATTRIBUTE_PREFERRED_CONTACT,
290 SILC_32_TO_PTR(mask),
291 sizeof(SilcUInt32));
292 #ifdef HAVE_SYS_UTSNAME_H
293 if (!uname(&u)) {
294 SilcAttributeObjDevice dev;
295 memset(&dev, 0, sizeof(dev));
296 dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
297 dev.version = u.release;
298 dev.model = u.sysname;
299 silc_client_attribute_add(client, conn,
300 SILC_ATTRIBUTE_DEVICE_INFO,
301 (void *)&dev, sizeof(dev));
303 #endif
304 silc_timezone(tz, sizeof(tz));
305 silc_client_attribute_add(client, conn,
306 SILC_ATTRIBUTE_TIMEZONE,
307 (void *)tz, strlen(tz));
309 /* Set our buddy icon */
310 img = purple_buddy_icons_find_account_icon(sg->account);
311 silcpurple_buddy_set_icon(gc, img);
312 g_object_unref(img);
314 return;
315 break;
317 case SILC_CLIENT_CONN_DISCONNECTED:
318 /* Disconnected */
319 if (sg->resuming && !sg->detaching)
320 g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
322 /* Close the connection */
323 if (!sg->detaching)
324 purple_connection_error(gc,
325 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
326 _("Disconnected by server"));
327 else
328 /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
329 purple_account_disconnect(purple_connection_get_account(gc));
330 break;
332 case SILC_CLIENT_CONN_ERROR:
333 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
334 _("Error connecting to SILC Server"));
335 g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
336 break;
338 case SILC_CLIENT_CONN_ERROR_KE:
339 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
340 _("Key Exchange failed"));
341 break;
343 case SILC_CLIENT_CONN_ERROR_AUTH:
344 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
345 _("Authentication failed"));
346 break;
348 case SILC_CLIENT_CONN_ERROR_RESUME:
349 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
350 _("Resuming detached session failed. "
351 "Press Reconnect to create new connection."));
352 g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
353 break;
355 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
356 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
357 _("Connection timed out"));
358 break;
361 /* Error */
362 sg->conn = NULL;
365 static void
366 silcpurple_stream_created(SilcSocketStreamStatus status, SilcStream stream,
367 void *context)
369 PurpleConnection *gc = context;
370 SilcPurple sg;
371 SilcClient client;
372 SilcClientConnectionParams params;
373 const char *dfile;
375 sg = purple_connection_get_protocol_data(gc);
377 if (status != SILC_SOCKET_OK) {
378 purple_connection_error(gc,
379 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
380 _("Connection failed"));
381 silc_pkcs_public_key_free(sg->public_key);
382 silc_pkcs_private_key_free(sg->private_key);
383 silc_free(sg);
384 purple_connection_set_protocol_data(gc, NULL);
385 return;
388 client = sg->client;
390 /* Get session detachment data, if available */
391 memset(&params, 0, sizeof(params));
392 dfile = silcpurple_session_file(purple_account_get_username(sg->account));
393 params.detach_data = (unsigned char *)silc_file_readfile(dfile, &params.detach_data_len);
394 if (params.detach_data)
395 params.detach_data[params.detach_data_len] = 0;
396 params.ignore_requested_attributes = FALSE;
397 params.pfs = purple_account_get_bool(sg->account, "pfs", FALSE);
399 /* Progress */
400 if (params.detach_data) {
401 purple_connection_update_progress(gc, _("Resuming session"), 2, 5);
402 sg->resuming = TRUE;
403 } else {
404 purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5);
407 /* Perform SILC Key Exchange. */
408 silc_client_key_exchange(client, &params, sg->public_key,
409 sg->private_key, stream, SILC_CONN_SERVER,
410 silcpurple_connect_cb, gc);
412 silc_free(params.detach_data);
415 static void
416 silcpurple_login_connected(gpointer data, gint source, const gchar *error_message)
418 PurpleConnection *gc = data;
419 SilcPurple sg;
421 g_return_if_fail(gc != NULL);
423 sg = purple_connection_get_protocol_data(gc);
425 if (source < 0) {
426 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
427 _("Connection failed"));
428 silc_pkcs_public_key_free(sg->public_key);
429 silc_pkcs_private_key_free(sg->private_key);
430 silc_free(sg);
431 purple_connection_set_protocol_data(gc, NULL);
432 return;
435 silc_hash_alloc((unsigned char *)"sha1", &sg->sha1hash);
437 /* Wrap socket to TCP stream */
438 silc_socket_tcp_stream_create(source, TRUE, FALSE,
439 sg->client->schedule,
440 silcpurple_stream_created, gc);
443 static void silcpurple_continue_running(SilcPurple sg)
445 PurpleConnection *gc = sg->gc;
446 PurpleAccount *account = purple_connection_get_account(gc);
448 /* Connect to the SILC server */
449 if (purple_proxy_connect(gc, account,
450 purple_account_get_string(account, "server",
451 "silc.silcnet.org"),
452 purple_account_get_int(account, "port", 706),
453 silcpurple_login_connected, gc) == NULL)
455 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
456 _("Unable to connect"));
457 purple_connection_set_protocol_data(gc, NULL);
458 silc_free(sg);
459 return;
463 static void silcpurple_got_password_cb(PurpleConnection *gc, PurpleRequestFields *fields)
465 SilcPurple sg = purple_connection_get_protocol_data(gc);
466 PurpleAccount *account = purple_connection_get_account(gc);
467 char pkd[256], prd[256];
468 const char *password;
469 gboolean remember;
471 /* TODO: the password prompt dialog doesn't get disposed if the account disconnects */
472 PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
474 password = purple_request_fields_get_string(fields, "password");
475 remember = purple_request_fields_get_bool(fields, "remember");
477 if (!password || !*password)
479 purple_notify_error(gc, NULL,
480 _("Password is required to sign on."), NULL,
481 purple_request_cpar_from_connection(gc));
482 purple_connection_set_protocol_data(gc, NULL);
483 silc_free(sg);
484 return;
487 if (remember)
488 purple_account_set_remember_password(account, TRUE);
490 purple_account_set_password(account, password, NULL, NULL);
492 /* Load SILC key pair */
493 g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
494 g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
495 if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
496 (char *)purple_account_get_string(account, "private-key", prd),
497 password,
498 &sg->public_key, &sg->private_key)) {
499 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
500 _("Unable to load SILC key pair"));
501 purple_connection_set_protocol_data(gc, NULL);
502 silc_free(sg);
503 return;
505 silcpurple_continue_running(sg);
508 static void silcpurple_no_password_cb(PurpleConnection *gc, PurpleRequestFields *fields)
510 SilcPurple sg;
512 /* TODO: the password prompt dialog doesn't get disposed if the account disconnects */
513 PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
515 sg = purple_connection_get_protocol_data(gc);
516 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
517 _("Unable to load SILC key pair"));
518 purple_connection_set_protocol_data(gc, NULL);
519 silc_free(sg);
522 static void silcpurple_running(SilcClient client, void *context)
524 SilcPurple sg = context;
525 PurpleConnection *gc = sg->gc;
526 PurpleAccount *account = purple_connection_get_account(gc);
527 char pkd[256], prd[256];
530 /* Progress */
531 purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5);
533 /* Load SILC key pair */
534 g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
535 g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
536 if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
537 (char *)purple_account_get_string(account, "private-key", prd),
538 (purple_connection_get_password(gc) == NULL) ? "" : purple_connection_get_password(gc),
539 &sg->public_key, &sg->private_key)) {
540 if (!purple_connection_get_password(gc)) {
541 purple_account_request_password(account, G_CALLBACK(silcpurple_got_password_cb),
542 G_CALLBACK(silcpurple_no_password_cb), gc);
543 return;
545 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
546 _("Unable to load SILC key pair"));
547 purple_connection_set_protocol_data(gc, NULL);
548 silc_free(sg);
549 return;
551 silcpurple_continue_running(sg);
554 static void
555 silcpurple_login(PurpleAccount *account)
557 SilcClient client;
558 PurpleConnection *gc;
559 SilcPurple sg;
560 SilcClientParams params;
561 const char *cipher, *hmac;
562 char *username, *hostname, *realname, **up;
563 int i;
565 gc = purple_account_get_connection(account);
566 if (!gc)
567 return;
568 purple_connection_set_protocol_data(gc, NULL);
570 memset(&params, 0, sizeof(params));
571 strcat(params.nickname_format, "%n#a");
573 /* Allocate SILC client */
574 client = silc_client_alloc(&ops, &params, gc, NULL);
575 if (!client) {
576 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
577 _("Out of memory"));
578 return;
581 /* Get username, real name and local hostname for SILC library */
582 if (!purple_account_get_username(account))
583 purple_account_set_username(account, silc_get_username());
585 username = (char *)purple_account_get_username(account);
586 up = g_strsplit(username, "@", 2);
587 username = g_strdup(up[0]);
588 g_strfreev(up);
590 if (!purple_account_get_user_info(account)) {
591 purple_account_set_user_info(account, silc_get_real_name());
592 if (!purple_account_get_user_info(account))
593 purple_account_set_user_info(account,
594 "John T. Noname");
596 realname = (char *)purple_account_get_user_info(account);
597 hostname = silc_net_localhost();
599 purple_connection_set_display_name(gc, username);
601 /* Register requested cipher and HMAC */
602 cipher = purple_account_get_string(account, "cipher",
603 SILC_DEFAULT_CIPHER);
604 for (i = 0; silc_default_ciphers[i].name; i++)
605 if (purple_strequal(silc_default_ciphers[i].name, cipher)) {
606 silc_cipher_register(&(silc_default_ciphers[i]));
607 break;
609 hmac = purple_account_get_string(account, "hmac", SILC_DEFAULT_HMAC);
610 for (i = 0; silc_default_hmacs[i].name; i++)
611 if (purple_strequal(silc_default_hmacs[i].name, hmac)) {
612 silc_hmac_register(&(silc_default_hmacs[i]));
613 break;
616 sg = silc_calloc(1, sizeof(*sg));
617 if (!sg)
618 return;
619 sg->client = client;
620 sg->gc = gc;
621 sg->account = account;
622 purple_connection_set_protocol_data(gc, sg);
624 /* Init SILC client */
625 if (!silc_client_init(client, username, hostname, realname,
626 silcpurple_running, sg)) {
627 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
628 _("Unable to initialize SILC protocol"));
629 purple_connection_set_protocol_data(gc, NULL);
630 silc_free(sg);
631 silc_free(hostname);
632 g_free(username);
633 return;
635 silc_free(hostname);
636 g_free(username);
638 /* Check the ~/.silc dir and create it, and new key pair if necessary. */
639 if (!silcpurple_check_silc_dir(gc)) {
640 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
641 _("Error loading SILC key pair"));
642 purple_connection_set_protocol_data(gc, NULL);
643 silc_free(sg);
644 return;
647 #if __SILC_TOOLKIT_VERSION < SILC_VERSION(1,1,1)
648 /* Schedule SILC using Glib's event loop */
649 sg->scheduler = g_timeout_add(300, (GSourceFunc)silcpurple_scheduler, client);
650 #else
651 /* Run SILC scheduler */
652 sg->tasks = silc_dlist_init();
653 silc_schedule_set_notify(client->schedule, silcpurple_scheduler,
654 client);
655 silc_client_run_one(client);
656 #endif /* __SILC_TOOLKIT_VERSION */
659 static int
660 silcpurple_close_final(gpointer *context)
662 SilcPurple sg = (SilcPurple)context;
664 purple_debug_info("silc", "Finalizing SilcPurple %p\n", sg);
666 silc_client_stop(sg->client, NULL, NULL);
667 silc_client_free(sg->client);
668 if (sg->sha1hash)
669 silc_hash_free(sg->sha1hash);
670 if (sg->mimeass)
671 silc_mime_assembler_free(sg->mimeass);
672 silc_free(sg);
673 return 0;
676 static void
677 silcpurple_close(PurpleConnection *gc)
679 SilcPurple sg = purple_connection_get_protocol_data(gc);
680 #if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1)
681 SilcPurpleTask task;
682 #endif /* __SILC_TOOLKIT_VERSION */
683 GHashTable *ui_info;
684 const char *ui_name = NULL, *ui_website = NULL;
685 char *quit_msg;
687 g_return_if_fail(sg != NULL);
689 ui_info = purple_core_get_ui_info();
691 if(ui_info) {
692 ui_name = g_hash_table_lookup(ui_info, "name");
693 ui_website = g_hash_table_lookup(ui_info, "website");
696 if(!ui_name || !ui_website) {
697 ui_name = "Pidgin";
698 ui_website = PURPLE_WEBSITE;
700 quit_msg = g_strdup_printf(_("Download %s: %s"),
701 ui_name, ui_website);
703 /* Send QUIT */
704 silc_client_command_call(sg->client, sg->conn, NULL,
705 "QUIT", quit_msg,
706 NULL);
707 g_free(quit_msg);
709 if (sg->conn)
710 silc_client_close_connection(sg->client, sg->conn);
712 #if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1)
713 if (sg->conn)
714 silc_client_run_one(sg->client);
715 silc_schedule_set_notify(sg->client->schedule, NULL, NULL);
717 silc_dlist_start(sg->tasks);
718 while ((task = silc_dlist_get(sg->tasks))) {
719 purple_input_remove(task->tag);
720 silc_free(task);
722 silc_dlist_uninit(sg->tasks);
723 #endif /* __SILC_TOOLKIT_VERSION */
725 if (sg->scheduler)
726 g_source_remove(sg->scheduler);
728 purple_debug_info("silc", "Scheduling destruction of SilcPurple %p\n", sg);
729 g_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg);
733 /****************************** Protocol Actions *****************************/
735 static void
736 silcpurple_attrs_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
738 /* Nothing */
741 static void
742 silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields)
744 SilcPurple sg = purple_connection_get_protocol_data(gc);
745 SilcClient client = sg->client;
746 SilcClientConnection conn = sg->conn;
747 PurpleRequestField *f;
748 char *tmp;
749 SilcUInt32 tmp_len, mask;
750 SilcAttributeObjService service;
751 SilcAttributeObjDevice dev;
752 SilcVCardStruct vcard;
753 const char *val;
755 sg = purple_connection_get_protocol_data(gc);
756 if (!sg)
757 return;
759 memset(&service, 0, sizeof(service));
760 memset(&dev, 0, sizeof(dev));
761 memset(&vcard, 0, sizeof(vcard));
763 silc_client_attribute_del(client, conn,
764 SILC_ATTRIBUTE_USER_INFO, NULL);
765 silc_client_attribute_del(client, conn,
766 SILC_ATTRIBUTE_SERVICE, NULL);
767 silc_client_attribute_del(client, conn,
768 SILC_ATTRIBUTE_STATUS_MOOD, NULL);
769 silc_client_attribute_del(client, conn,
770 SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
771 silc_client_attribute_del(client, conn,
772 SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
773 silc_client_attribute_del(client, conn,
774 SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
775 silc_client_attribute_del(client, conn,
776 SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
777 silc_client_attribute_del(client, conn,
778 SILC_ATTRIBUTE_TIMEZONE, NULL);
779 silc_client_attribute_del(client, conn,
780 SILC_ATTRIBUTE_GEOLOCATION, NULL);
781 silc_client_attribute_del(client, conn,
782 SILC_ATTRIBUTE_DEVICE_INFO, NULL);
784 /* Set mood */
785 mask = 0;
786 f = purple_request_fields_get_field(fields, "mood_normal");
787 if (f && purple_request_field_bool_get_value(f))
788 mask |= SILC_ATTRIBUTE_MOOD_NORMAL;
789 f = purple_request_fields_get_field(fields, "mood_happy");
790 if (f && purple_request_field_bool_get_value(f))
791 mask |= SILC_ATTRIBUTE_MOOD_HAPPY;
792 f = purple_request_fields_get_field(fields, "mood_sad");
793 if (f && purple_request_field_bool_get_value(f))
794 mask |= SILC_ATTRIBUTE_MOOD_SAD;
795 f = purple_request_fields_get_field(fields, "mood_angry");
796 if (f && purple_request_field_bool_get_value(f))
797 mask |= SILC_ATTRIBUTE_MOOD_ANGRY;
798 f = purple_request_fields_get_field(fields, "mood_jealous");
799 if (f && purple_request_field_bool_get_value(f))
800 mask |= SILC_ATTRIBUTE_MOOD_JEALOUS;
801 f = purple_request_fields_get_field(fields, "mood_ashamed");
802 if (f && purple_request_field_bool_get_value(f))
803 mask |= SILC_ATTRIBUTE_MOOD_ASHAMED;
804 f = purple_request_fields_get_field(fields, "mood_invincible");
805 if (f && purple_request_field_bool_get_value(f))
806 mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE;
807 f = purple_request_fields_get_field(fields, "mood_inlove");
808 if (f && purple_request_field_bool_get_value(f))
809 mask |= SILC_ATTRIBUTE_MOOD_INLOVE;
810 f = purple_request_fields_get_field(fields, "mood_sleepy");
811 if (f && purple_request_field_bool_get_value(f))
812 mask |= SILC_ATTRIBUTE_MOOD_SLEEPY;
813 f = purple_request_fields_get_field(fields, "mood_bored");
814 if (f && purple_request_field_bool_get_value(f))
815 mask |= SILC_ATTRIBUTE_MOOD_BORED;
816 f = purple_request_fields_get_field(fields, "mood_excited");
817 if (f && purple_request_field_bool_get_value(f))
818 mask |= SILC_ATTRIBUTE_MOOD_EXCITED;
819 f = purple_request_fields_get_field(fields, "mood_anxious");
820 if (f && purple_request_field_bool_get_value(f))
821 mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS;
822 silc_client_attribute_add(client, conn,
823 SILC_ATTRIBUTE_STATUS_MOOD,
824 SILC_32_TO_PTR(mask),
825 sizeof(SilcUInt32));
827 /* Set preferred contact */
828 mask = 0;
829 f = purple_request_fields_get_field(fields, "contact_chat");
830 if (f && purple_request_field_bool_get_value(f))
831 mask |= SILC_ATTRIBUTE_CONTACT_CHAT;
832 f = purple_request_fields_get_field(fields, "contact_email");
833 if (f && purple_request_field_bool_get_value(f))
834 mask |= SILC_ATTRIBUTE_CONTACT_EMAIL;
835 f = purple_request_fields_get_field(fields, "contact_call");
836 if (f && purple_request_field_bool_get_value(f))
837 mask |= SILC_ATTRIBUTE_CONTACT_CALL;
838 f = purple_request_fields_get_field(fields, "contact_sms");
839 if (f && purple_request_field_bool_get_value(f))
840 mask |= SILC_ATTRIBUTE_CONTACT_SMS;
841 f = purple_request_fields_get_field(fields, "contact_mms");
842 if (f && purple_request_field_bool_get_value(f))
843 mask |= SILC_ATTRIBUTE_CONTACT_MMS;
844 f = purple_request_fields_get_field(fields, "contact_video");
845 if (f && purple_request_field_bool_get_value(f))
846 mask |= SILC_ATTRIBUTE_CONTACT_VIDEO;
847 if (mask)
848 silc_client_attribute_add(client, conn,
849 SILC_ATTRIBUTE_PREFERRED_CONTACT,
850 SILC_32_TO_PTR(mask),
851 sizeof(SilcUInt32));
853 /* Set status text */
854 val = NULL;
855 f = purple_request_fields_get_field(fields, "status_text");
856 if (f)
857 val = purple_request_field_string_get_value(f);
858 if (val && *val)
859 silc_client_attribute_add(client, conn,
860 SILC_ATTRIBUTE_STATUS_FREETEXT,
861 (void *)val, strlen(val));
863 /* Set vcard */
864 val = NULL;
865 f = purple_request_fields_get_field(fields, "vcard");
866 if (f)
867 val = purple_request_field_string_get_value(f);
868 if (val && *val) {
869 purple_account_set_string(sg->account, "vcard", val);
870 tmp = silc_file_readfile(val, &tmp_len);
871 if (tmp) {
872 tmp[tmp_len] = 0;
873 if (silc_vcard_decode((unsigned char *)tmp, tmp_len, &vcard))
874 silc_client_attribute_add(client, conn,
875 SILC_ATTRIBUTE_USER_INFO,
876 (void *)&vcard,
877 sizeof(vcard));
879 silc_vcard_free(&vcard);
880 silc_free(tmp);
881 } else {
882 purple_account_set_string(sg->account, "vcard", "");
885 #ifdef HAVE_SYS_UTSNAME_H
886 /* Set device info */
887 f = purple_request_fields_get_field(fields, "device");
888 if (f && purple_request_field_bool_get_value(f)) {
889 struct utsname u;
890 if (!uname(&u)) {
891 dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
892 dev.version = u.release;
893 dev.model = u.sysname;
894 silc_client_attribute_add(client, conn,
895 SILC_ATTRIBUTE_DEVICE_INFO,
896 (void *)&dev, sizeof(dev));
899 #endif
901 /* Set timezone */
902 val = NULL;
903 f = purple_request_fields_get_field(fields, "timezone");
904 if (f)
905 val = purple_request_field_string_get_value(f);
906 if (val && *val)
907 silc_client_attribute_add(client, conn,
908 SILC_ATTRIBUTE_TIMEZONE,
909 (void *)val, strlen(val));
912 static void
913 silcpurple_attrs(PurpleProtocolAction *action)
915 PurpleConnection *gc = action->connection;
916 SilcPurple sg = purple_connection_get_protocol_data(gc);
917 SilcClient client = sg->client;
918 SilcClientConnection conn = sg->conn;
919 PurpleRequestFields *fields;
920 PurpleRequestFieldGroup *g;
921 PurpleRequestField *f;
922 SilcHashTable attrs;
923 SilcAttributePayload attr;
924 gboolean mnormal = TRUE, mhappy = FALSE, msad = FALSE,
925 mangry = FALSE, mjealous = FALSE, mashamed = FALSE,
926 minvincible = FALSE, minlove = FALSE, msleepy = FALSE,
927 mbored = FALSE, mexcited = FALSE, manxious = FALSE;
928 gboolean cemail = FALSE, ccall = FALSE, csms = FALSE,
929 cmms = FALSE, cchat = TRUE, cvideo = FALSE;
930 #ifdef HAVE_SYS_UTSNAME_H
931 gboolean device = TRUE;
932 #endif
933 char status[1024], tz[16];
935 sg = purple_connection_get_protocol_data(gc);
936 if (!sg)
937 return;
939 memset(status, 0, sizeof(status));
941 attrs = silc_client_attributes_get(client, conn);
942 if (attrs) {
943 if (silc_hash_table_find(attrs,
944 SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_MOOD),
945 NULL, (void *)&attr)) {
946 SilcUInt32 mood = 0;
947 silc_attribute_get_object(attr, &mood, sizeof(mood));
948 mnormal = !mood;
949 mhappy = (mood & SILC_ATTRIBUTE_MOOD_HAPPY);
950 msad = (mood & SILC_ATTRIBUTE_MOOD_SAD);
951 mangry = (mood & SILC_ATTRIBUTE_MOOD_ANGRY);
952 mjealous = (mood & SILC_ATTRIBUTE_MOOD_JEALOUS);
953 mashamed = (mood & SILC_ATTRIBUTE_MOOD_ASHAMED);
954 minvincible = (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE);
955 minlove = (mood & SILC_ATTRIBUTE_MOOD_INLOVE);
956 msleepy = (mood & SILC_ATTRIBUTE_MOOD_SLEEPY);
957 mbored = (mood & SILC_ATTRIBUTE_MOOD_BORED);
958 mexcited = (mood & SILC_ATTRIBUTE_MOOD_EXCITED);
959 manxious = (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS);
962 if (silc_hash_table_find(attrs,
963 SILC_32_TO_PTR(SILC_ATTRIBUTE_PREFERRED_CONTACT),
964 NULL, (void *)&attr)) {
965 SilcUInt32 contact = 0;
966 silc_attribute_get_object(attr, &contact, sizeof(contact));
967 cemail = (contact & SILC_ATTRIBUTE_CONTACT_EMAIL);
968 ccall = (contact & SILC_ATTRIBUTE_CONTACT_CALL);
969 csms = (contact & SILC_ATTRIBUTE_CONTACT_SMS);
970 cmms = (contact & SILC_ATTRIBUTE_CONTACT_MMS);
971 cchat = (contact & SILC_ATTRIBUTE_CONTACT_CHAT);
972 cvideo = (contact & SILC_ATTRIBUTE_CONTACT_VIDEO);
975 if (silc_hash_table_find(attrs,
976 SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_FREETEXT),
977 NULL, (void *)&attr))
978 silc_attribute_get_object(attr, &status, sizeof(status));
980 #ifdef HAVE_SYS_UTSNAME_H
981 if (!silc_hash_table_find(attrs,
982 SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO),
983 NULL, (void *)&attr))
984 device = FALSE;
985 #endif
988 fields = purple_request_fields_new();
990 g = purple_request_field_group_new(NULL);
991 f = purple_request_field_label_new("l3", _("Your Current Mood"));
992 purple_request_field_group_add_field(g, f);
993 f = purple_request_field_bool_new("mood_normal", _("Normal"), mnormal);
994 purple_request_field_group_add_field(g, f);
995 f = purple_request_field_bool_new("mood_happy", _("Happy"), mhappy);
996 purple_request_field_group_add_field(g, f);
997 f = purple_request_field_bool_new("mood_sad", _("Sad"), msad);
998 purple_request_field_group_add_field(g, f);
999 f = purple_request_field_bool_new("mood_angry", _("Angry"), mangry);
1000 purple_request_field_group_add_field(g, f);
1001 f = purple_request_field_bool_new("mood_jealous", _("Jealous"), mjealous);
1002 purple_request_field_group_add_field(g, f);
1003 f = purple_request_field_bool_new("mood_ashamed", _("Ashamed"), mashamed);
1004 purple_request_field_group_add_field(g, f);
1005 f = purple_request_field_bool_new("mood_invincible", _("Invincible"), minvincible);
1006 purple_request_field_group_add_field(g, f);
1007 f = purple_request_field_bool_new("mood_inlove", _("In love"), minlove);
1008 purple_request_field_group_add_field(g, f);
1009 f = purple_request_field_bool_new("mood_sleepy", _("Sleepy"), msleepy);
1010 purple_request_field_group_add_field(g, f);
1011 f = purple_request_field_bool_new("mood_bored", _("Bored"), mbored);
1012 purple_request_field_group_add_field(g, f);
1013 f = purple_request_field_bool_new("mood_excited", _("Excited"), mexcited);
1014 purple_request_field_group_add_field(g, f);
1015 f = purple_request_field_bool_new("mood_anxious", _("Anxious"), manxious);
1016 purple_request_field_group_add_field(g, f);
1018 f = purple_request_field_label_new("l4", _("\nYour Preferred Contact Methods"));
1019 purple_request_field_group_add_field(g, f);
1020 f = purple_request_field_bool_new("contact_chat", _("Chat"), cchat);
1021 purple_request_field_group_add_field(g, f);
1022 f = purple_request_field_bool_new("contact_email", _("Email"), cemail);
1023 purple_request_field_group_add_field(g, f);
1024 f = purple_request_field_bool_new("contact_call", _("Phone"), ccall);
1025 purple_request_field_group_add_field(g, f);
1026 f = purple_request_field_bool_new("contact_sms", _("SMS"), csms);
1027 purple_request_field_group_add_field(g, f);
1028 f = purple_request_field_bool_new("contact_mms", _("MMS"), cmms);
1029 purple_request_field_group_add_field(g, f);
1030 f = purple_request_field_bool_new("contact_video", _("Video conferencing"), cvideo);
1031 purple_request_field_group_add_field(g, f);
1032 purple_request_fields_add_group(fields, g);
1034 g = purple_request_field_group_new(NULL);
1035 f = purple_request_field_string_new("status_text", _("Your Current Status"),
1036 status[0] ? status : NULL, TRUE);
1037 purple_request_field_group_add_field(g, f);
1038 purple_request_fields_add_group(fields, g);
1040 g = purple_request_field_group_new(NULL);
1041 #ifdef HAVE_SYS_UTSNAME_H
1042 f = purple_request_field_bool_new("device",
1043 _("Let others see what computer you are using"),
1044 device);
1045 purple_request_field_group_add_field(g, f);
1046 #endif
1047 purple_request_fields_add_group(fields, g);
1049 g = purple_request_field_group_new(NULL);
1050 f = purple_request_field_string_new("vcard", _("Your VCard File"),
1051 purple_account_get_string(sg->account, "vcard", ""),
1052 FALSE);
1053 purple_request_field_group_add_field(g, f);
1055 silc_timezone(tz, sizeof(tz));
1056 f = purple_request_field_string_new("timezone", _("Timezone (UTC)"), tz, FALSE);
1057 purple_request_field_group_add_field(g, f);
1058 purple_request_fields_add_group(fields, g);
1060 purple_request_fields(gc, _("User Online Status Attributes"),
1061 _("User Online Status Attributes"),
1062 _("You can let other users see your online status information "
1063 "and your personal information. Please fill the information "
1064 "you would like other users to see about yourself."),
1065 fields,
1066 _("OK"), G_CALLBACK(silcpurple_attrs_cb),
1067 _("Cancel"), G_CALLBACK(silcpurple_attrs_cancel),
1068 purple_request_cpar_from_connection(gc), gc);
1071 static void
1072 silcpurple_detach(PurpleProtocolAction *action)
1074 PurpleConnection *gc = action->connection;
1075 SilcPurple sg;
1077 if (!gc)
1078 return;
1079 sg = purple_connection_get_protocol_data(gc);
1080 if (!sg)
1081 return;
1083 /* Call DETACH */
1084 silc_client_command_call(sg->client, sg->conn, "DETACH");
1085 sg->detaching = TRUE;
1088 static void
1089 silcpurple_view_motd(PurpleProtocolAction *action)
1091 PurpleConnection *gc = action->connection;
1092 SilcPurple sg;
1093 char *tmp;
1095 if (!gc)
1096 return;
1097 sg = purple_connection_get_protocol_data(gc);
1098 if (!sg)
1099 return;
1101 if (!sg->motd) {
1102 purple_notify_error(gc, _("Message of the Day"), _("No Message "
1103 "of the Day available"), _("There is no Message of the "
1104 "Day associated with this connection"),
1105 purple_request_cpar_from_connection(gc));
1106 return;
1109 tmp = g_markup_escape_text(sg->motd, -1);
1110 purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL,
1111 tmp, NULL, NULL);
1112 g_free(tmp);
1115 static void
1116 silcpurple_create_keypair_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
1118 /* Nothing */
1121 static void
1122 silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields)
1124 SilcPurple sg;
1125 PurpleRequestField *f;
1126 const char *val, *pkfile = NULL, *prfile = NULL;
1127 const char *pass1 = NULL, *pass2 = NULL, *un = NULL, *hn = NULL;
1128 const char *rn = NULL, *e = NULL, *o = NULL, *c = NULL;
1129 char *identifier;
1130 int keylen = SILCPURPLE_DEF_PKCS_LEN;
1131 SilcPublicKey public_key;
1133 sg = purple_connection_get_protocol_data(gc);
1134 if (!sg)
1135 return;
1137 val = NULL;
1138 f = purple_request_fields_get_field(fields, "pass1");
1139 if (f)
1140 val = purple_request_field_string_get_value(f);
1141 if (val && *val)
1142 pass1 = val;
1143 else
1144 pass1 = "";
1145 val = NULL;
1146 f = purple_request_fields_get_field(fields, "pass2");
1147 if (f)
1148 val = purple_request_field_string_get_value(f);
1149 if (val && *val)
1150 pass2 = val;
1151 else
1152 pass2 = "";
1154 if (!purple_strequal(pass1, pass2)) {
1155 purple_notify_error(gc, _("Create New SILC Key Pair"),
1156 _("Passphrases do not match"), NULL,
1157 purple_request_cpar_from_connection(gc));
1158 return;
1161 val = NULL;
1162 f = purple_request_fields_get_field(fields, "key");
1163 if (f)
1164 val = purple_request_field_string_get_value(f);
1165 if (val && *val)
1166 keylen = atoi(val);
1167 f = purple_request_fields_get_field(fields, "pkfile");
1168 if (f)
1169 pkfile = purple_request_field_string_get_value(f);
1170 f = purple_request_fields_get_field(fields, "prfile");
1171 if (f)
1172 prfile = purple_request_field_string_get_value(f);
1174 f = purple_request_fields_get_field(fields, "un");
1175 if (f)
1176 un = purple_request_field_string_get_value(f);
1177 f = purple_request_fields_get_field(fields, "hn");
1178 if (f)
1179 hn = purple_request_field_string_get_value(f);
1180 f = purple_request_fields_get_field(fields, "rn");
1181 if (f)
1182 rn = purple_request_field_string_get_value(f);
1183 f = purple_request_fields_get_field(fields, "e");
1184 if (f)
1185 e = purple_request_field_string_get_value(f);
1186 f = purple_request_fields_get_field(fields, "o");
1187 if (f)
1188 o = purple_request_field_string_get_value(f);
1189 f = purple_request_fields_get_field(fields, "c");
1190 if (f)
1191 c = purple_request_field_string_get_value(f);
1193 identifier = silc_pkcs_silc_encode_identifier((char *)un, (char *)hn,
1194 (char *)rn, (char *)e,
1195 (char *)o, (char *)c,
1196 NULL);
1198 /* Create the key pair */
1199 if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile,
1200 identifier, pass1, &public_key, NULL,
1201 FALSE)) {
1202 purple_notify_error(gc, _("Create New SILC Key Pair"),
1203 _("Key Pair Generation failed"), NULL,
1204 purple_request_cpar_from_connection(gc));
1205 return;
1208 silcpurple_show_public_key(sg, NULL, public_key, NULL, NULL);
1210 silc_pkcs_public_key_free(public_key);
1211 silc_free(identifier);
1214 static void
1215 silcpurple_create_keypair(PurpleProtocolAction *action)
1217 PurpleConnection *gc = action->connection;
1218 SilcPurple sg = purple_connection_get_protocol_data(gc);
1219 PurpleRequestFields *fields;
1220 PurpleRequestFieldGroup *g;
1221 PurpleRequestField *f;
1222 const char *username, *realname;
1223 char *hostname, **u;
1224 char tmp[256], pkd[256], pkd2[256], prd[256], prd2[256];
1226 username = purple_account_get_username(sg->account);
1227 u = g_strsplit(username, "@", 2);
1228 username = u[0];
1229 realname = purple_account_get_user_info(sg->account);
1230 hostname = silc_net_localhost();
1231 g_snprintf(tmp, sizeof(tmp), "%s@%s", username, hostname);
1233 g_snprintf(pkd2, sizeof(pkd2), "%s" G_DIR_SEPARATOR_S"public_key.pub", silcpurple_silcdir());
1234 g_snprintf(prd2, sizeof(prd2), "%s" G_DIR_SEPARATOR_S"private_key.prv", silcpurple_silcdir());
1235 g_snprintf(pkd, sizeof(pkd) - 1, "%s",
1236 purple_account_get_string(purple_connection_get_account(gc), "public-key", pkd2));
1237 g_snprintf(prd, sizeof(prd) - 1, "%s",
1238 purple_account_get_string(purple_connection_get_account(gc), "private-key", prd2));
1240 fields = purple_request_fields_new();
1242 g = purple_request_field_group_new(NULL);
1243 f = purple_request_field_string_new("key", _("Key length"), "2048", FALSE);
1244 purple_request_field_group_add_field(g, f);
1245 f = purple_request_field_string_new("pkfile", _("Public key file"), pkd, FALSE);
1246 purple_request_field_group_add_field(g, f);
1247 f = purple_request_field_string_new("prfile", _("Private key file"), prd, FALSE);
1248 purple_request_field_group_add_field(g, f);
1249 purple_request_fields_add_group(fields, g);
1251 g = purple_request_field_group_new(NULL);
1252 f = purple_request_field_string_new("un", _("Username"), username ? username : "", FALSE);
1253 purple_request_field_group_add_field(g, f);
1254 f = purple_request_field_string_new("hn", _("Hostname"), hostname ? hostname : "", FALSE);
1255 purple_request_field_group_add_field(g, f);
1256 f = purple_request_field_string_new("rn", _("Real name"), realname ? realname : "", FALSE);
1257 purple_request_field_group_add_field(g, f);
1258 f = purple_request_field_string_new("e", _("Email"), tmp, FALSE);
1259 purple_request_field_group_add_field(g, f);
1260 f = purple_request_field_string_new("o", _("Organization"), "", FALSE);
1261 purple_request_field_group_add_field(g, f);
1262 f = purple_request_field_string_new("c", _("Country"), "", FALSE);
1263 purple_request_field_group_add_field(g, f);
1264 purple_request_fields_add_group(fields, g);
1266 g = purple_request_field_group_new(NULL);
1267 f = purple_request_field_string_new("pass1", _("Passphrase"), "", FALSE);
1268 purple_request_field_string_set_masked(f, TRUE);
1269 purple_request_field_group_add_field(g, f);
1270 f = purple_request_field_string_new("pass2", _("Passphrase (retype)"), "", FALSE);
1271 purple_request_field_string_set_masked(f, TRUE);
1272 purple_request_field_group_add_field(g, f);
1273 purple_request_fields_add_group(fields, g);
1275 purple_request_fields(gc, _("Create New SILC Key Pair"),
1276 _("Create New SILC Key Pair"), NULL, fields,
1277 _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
1278 _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
1279 purple_request_cpar_from_connection(gc), gc);
1281 g_strfreev(u);
1282 silc_free(hostname);
1285 static void
1286 silcpurple_change_pass(PurpleProtocolAction *action)
1288 PurpleConnection *gc = action->connection;
1289 purple_account_request_change_password(purple_connection_get_account(gc));
1292 static void
1293 silcpurple_change_passwd(PurpleConnection *gc, const char *old, const char *new)
1295 char prd[256];
1296 g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir());
1297 silc_change_private_key_passphrase(purple_account_get_string(purple_connection_get_account(gc),
1298 "private-key",
1299 prd), old ? old : "", new ? new : "");
1302 static void
1303 silcpurple_show_set_info(PurpleProtocolAction *action)
1305 PurpleConnection *gc = action->connection;
1306 purple_account_request_change_user_info(purple_connection_get_account(gc));
1309 static void
1310 silcpurple_set_info(PurpleConnection *gc, const char *text)
1314 static GList *
1315 silcpurple_get_actions(PurpleConnection *gc)
1317 GList *list = NULL;
1318 PurpleProtocolAction *act;
1320 act = purple_protocol_action_new(_("Online Status"),
1321 silcpurple_attrs);
1322 list = g_list_append(list, act);
1324 act = purple_protocol_action_new(_("Detach From Server"),
1325 silcpurple_detach);
1326 list = g_list_append(list, act);
1328 act = purple_protocol_action_new(_("View Message of the Day"),
1329 silcpurple_view_motd);
1330 list = g_list_append(list, act);
1332 act = purple_protocol_action_new(_("Create SILC Key Pair..."),
1333 silcpurple_create_keypair);
1334 list = g_list_append(list, act);
1336 act = purple_protocol_action_new(_("Change Password..."),
1337 silcpurple_change_pass);
1338 list = g_list_append(list, act);
1340 act = purple_protocol_action_new(_("Set User Info..."),
1341 silcpurple_show_set_info);
1342 list = g_list_append(list, act);
1344 return list;
1348 /******************************* IM Routines *********************************/
1350 typedef struct {
1351 char *nick;
1352 char *message;
1353 SilcUInt32 message_len;
1354 SilcMessageFlags flags;
1355 PurpleMessageFlags gflags;
1356 } *SilcPurpleIM;
1358 static void
1359 silcpurple_send_im_resolved(SilcClient client,
1360 SilcClientConnection conn,
1361 SilcStatus status,
1362 SilcDList clients,
1363 void *context)
1365 PurpleConnection *gc = client->application;
1366 SilcPurple sg = purple_connection_get_protocol_data(gc);
1367 SilcPurpleIM im = context;
1368 PurpleIMConversation *convo;
1369 char tmp[256];
1370 SilcClientEntry client_entry;
1371 SilcDList list;
1372 gboolean free_list = FALSE;
1374 convo = purple_conversations_find_im_with_account(im->nick,
1375 sg->account);
1376 if (!convo)
1377 return;
1379 if (!clients)
1380 goto err;
1382 if (silc_dlist_count(clients) > 1) {
1383 /* Find the correct one. The im->nick might be a formatted nick
1384 so this will find the correct one. */
1385 clients = silc_client_get_clients_local(client, conn,
1386 im->nick, FALSE);
1387 if (!clients)
1388 goto err;
1390 free_list = TRUE;
1393 silc_dlist_start(clients);
1394 client_entry = silc_dlist_get(clients);
1396 /* Check for images */
1397 if (im->gflags & PURPLE_MESSAGE_IMAGES) {
1398 list = silcpurple_image_message(im->message, &im->flags);
1399 if (list) {
1400 /* Send one or more MIME message. If more than one, they
1401 are MIME fragments due to over large message */
1402 SilcBuffer buf;
1404 silc_dlist_start(list);
1405 while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
1406 silc_client_send_private_message(client, conn,
1407 client_entry, im->flags, sg->sha1hash,
1408 buf->data,
1409 silc_buffer_len(buf));
1410 silc_mime_partial_free(list);
1411 purple_conversation_write_message(PURPLE_CONVERSATION(convo),
1412 purple_message_new_outgoing(
1413 conn->local_entry->nickname, im->message, 0));
1414 goto out;
1418 /* Send the message */
1419 silc_client_send_private_message(client, conn, client_entry, im->flags,
1420 sg->sha1hash, (unsigned char *)im->message, im->message_len);
1421 purple_conversation_write_message(PURPLE_CONVERSATION(convo),
1422 purple_message_new_outgoing(conn->local_entry->nickname, im->message, 0));
1423 goto out;
1425 err:
1426 g_snprintf(tmp, sizeof(tmp),
1427 _("User <I>%s</I> is not present in the network"), im->nick);
1428 purple_conversation_write_system_message(
1429 PURPLE_CONVERSATION(convo), tmp, 0);
1431 out:
1432 if (free_list) {
1433 silc_client_list_free(client, conn, clients);
1435 g_free(im->nick);
1436 g_free(im->message);
1437 silc_free(im);
1440 static int
1441 silcpurple_send_im(PurpleConnection *gc, PurpleMessage *pmsg)
1443 SilcPurple sg = purple_connection_get_protocol_data(gc);
1444 SilcClient client = sg->client;
1445 SilcClientConnection conn = sg->conn;
1446 SilcDList clients;
1447 SilcClientEntry client_entry;
1448 SilcMessageFlags mflags;
1449 char *msg, *tmp;
1450 int ret = 0;
1451 gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
1452 SilcDList list;
1453 const gchar *rcpt = purple_message_get_recipient(pmsg);
1454 const gchar *message = purple_message_get_contents(pmsg);
1455 PurpleMessageFlags flags = purple_message_get_flags(pmsg);
1457 if (!rcpt || purple_message_is_empty(pmsg))
1458 return 0;
1460 mflags = SILC_MESSAGE_FLAG_UTF8;
1462 tmp = msg = purple_unescape_html(message);
1464 if (!g_ascii_strncasecmp(msg, "/me ", 4)) {
1465 msg += 4;
1466 if (!*msg) {
1467 g_free(tmp);
1468 return 0;
1470 mflags |= SILC_MESSAGE_FLAG_ACTION;
1471 } else if (strlen(msg) > 1 && msg[0] == '/') {
1472 if (!silc_client_command_call(client, conn, msg + 1))
1473 purple_notify_error(gc, _("Call Command"),
1474 _("Cannot call command"),
1475 _("Unknown command"),
1476 purple_request_cpar_from_connection(gc));
1477 g_free(tmp);
1478 return 0;
1481 if (sign)
1482 mflags |= SILC_MESSAGE_FLAG_SIGNED;
1484 /* Find client entry */
1485 clients = silc_client_get_clients_local(client, conn, rcpt, FALSE);
1486 if (!clients) {
1487 /* Resolve unknown user */
1488 SilcPurpleIM im = silc_calloc(1, sizeof(*im));
1489 if (!im) {
1490 g_free(tmp);
1491 return 0;
1493 im->nick = g_strdup(rcpt);
1494 im->message = g_strdup(message);
1495 im->message_len = strlen(im->message);
1496 im->flags = mflags;
1497 im->gflags = flags;
1498 silc_client_get_clients(client, conn, rcpt, NULL,
1499 silcpurple_send_im_resolved, im);
1500 g_free(tmp);
1501 return 0;
1503 silc_dlist_start(clients);
1504 client_entry = silc_dlist_get(clients);
1506 /* Check for images */
1507 if (flags & PURPLE_MESSAGE_IMAGES) {
1508 list = silcpurple_image_message(message, &mflags);
1509 if (list) {
1510 /* Send one or more MIME message. If more than one, they
1511 are MIME fragments due to over large message */
1512 SilcBuffer buf;
1514 silc_dlist_start(list);
1515 while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
1516 ret =
1517 silc_client_send_private_message(client, conn,
1518 client_entry, mflags, sg->sha1hash,
1519 buf->data,
1520 silc_buffer_len(buf));
1521 silc_mime_partial_free(list);
1522 g_free(tmp);
1523 silc_client_list_free(client, conn, clients);
1524 return ret;
1528 /* Send private message directly */
1529 ret = silc_client_send_private_message(client, conn, client_entry,
1530 mflags, sg->sha1hash,
1531 (unsigned char *)msg,
1532 strlen(msg));
1534 g_free(tmp);
1535 silc_client_list_free(client, conn, clients);
1536 return ret;
1540 static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) {
1541 /* split this single menu building function back into the two
1542 original: one for buddies and one for chats */
1543 if(PURPLE_IS_CHAT(node)) {
1544 return silcpurple_chat_menu((PurpleChat *) node);
1545 } else if(PURPLE_IS_BUDDY(node)) {
1546 return silcpurple_buddy_menu((PurpleBuddy *) node);
1547 } else {
1548 g_return_val_if_reached(NULL);
1552 /********************************* Commands **********************************/
1554 static PurpleCmdRet silcpurple_cmd_chat_part(PurpleConversation *conv,
1555 const char *cmd, char **args, char **error, void *data)
1557 PurpleConnection *gc;
1558 PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(conv);
1559 int id = 0;
1561 gc = purple_conversation_get_connection(conv);
1563 if (gc == NULL)
1564 return PURPLE_CMD_RET_FAILED;
1566 if(args && args[0])
1567 chat = purple_conversations_find_chat_with_account(args[0],
1568 purple_connection_get_account(gc));
1570 if (chat != NULL)
1571 id = purple_chat_conversation_get_id(chat);
1573 if (id == 0)
1574 return PURPLE_CMD_RET_FAILED;
1576 silcpurple_chat_leave(gc, id);
1578 return PURPLE_CMD_RET_OK;
1582 static PurpleCmdRet silcpurple_cmd_chat_topic(PurpleConversation *conv,
1583 const char *cmd, char **args, char **error, void *data)
1585 PurpleConnection *gc;
1586 int id = 0;
1587 char *buf, *tmp, *tmp2;
1588 const char *topic;
1590 gc = purple_conversation_get_connection(conv);
1591 id = purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv));
1593 if (gc == NULL || id == 0)
1594 return PURPLE_CMD_RET_FAILED;
1596 if (!args || !args[0]) {
1597 topic = purple_chat_conversation_get_topic(PURPLE_CHAT_CONVERSATION(conv));
1598 if (topic) {
1599 tmp = g_markup_escape_text(topic, -1);
1600 tmp2 = purple_markup_linkify(tmp);
1601 buf = g_strdup_printf(_("current topic is: %s"), tmp2);
1602 g_free(tmp);
1603 g_free(tmp2);
1604 } else
1605 buf = g_strdup(_("No topic is set"));
1606 purple_conversation_write_system_message(conv,
1607 buf, PURPLE_MESSAGE_NO_LOG);
1608 g_free(buf);
1612 if (args && args[0] && (strlen(args[0]) > 255)) {
1613 *error = g_strdup(_("Topic too long"));
1614 return PURPLE_CMD_RET_FAILED;
1617 silcpurple_chat_set_topic(gc, id, args ? args[0] : NULL);
1619 return PURPLE_CMD_RET_OK;
1622 static PurpleCmdRet silcpurple_cmd_chat_join(PurpleConversation *conv,
1623 const char *cmd, char **args, char **error, void *data)
1625 GHashTable *comp;
1627 if(!args || !args[0])
1628 return PURPLE_CMD_RET_FAILED;
1630 comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
1632 g_hash_table_replace(comp, "channel", args[0]);
1633 if(args[1])
1634 g_hash_table_replace(comp, "passphrase", args[1]);
1636 silcpurple_chat_join(purple_conversation_get_connection(conv), comp);
1638 g_hash_table_destroy(comp);
1639 return PURPLE_CMD_RET_OK;
1642 static PurpleCmdRet silcpurple_cmd_chat_list(PurpleConversation *conv,
1643 const char *cmd, char **args, char **error, void *data)
1645 PurpleConnection *gc;
1646 gc = purple_conversation_get_connection(conv);
1647 purple_roomlist_show_with_account(purple_connection_get_account(gc));
1648 return PURPLE_CMD_RET_OK;
1651 static PurpleCmdRet silcpurple_cmd_whois(PurpleConversation *conv,
1652 const char *cmd, char **args, char **error, void *data)
1654 PurpleConnection *gc;
1656 gc = purple_conversation_get_connection(conv);
1658 if (gc == NULL)
1659 return PURPLE_CMD_RET_FAILED;
1661 silcpurple_get_info(gc, args[0]);
1663 return PURPLE_CMD_RET_OK;
1666 static PurpleCmdRet silcpurple_cmd_msg(PurpleConversation *conv,
1667 const char *cmd, char **args, char **error, void *data)
1669 int ret;
1670 PurpleConnection *gc;
1672 gc = purple_conversation_get_connection(conv);
1674 if (gc == NULL)
1675 return PURPLE_CMD_RET_FAILED;
1677 ret = silcpurple_send_im(gc,
1678 purple_message_new_outgoing(args[0], args[1], 0));
1680 if (ret)
1681 return PURPLE_CMD_RET_OK;
1682 else
1683 return PURPLE_CMD_RET_FAILED;
1686 static PurpleCmdRet silcpurple_cmd_query(PurpleConversation *conv,
1687 const char *cmd, char **args, char **error, void *data)
1689 int ret = 1;
1690 PurpleIMConversation *im;
1691 PurpleConnection *gc;
1692 PurpleAccount *account;
1694 if (!args || !args[0]) {
1695 *error = g_strdup(_("You must specify a nick"));
1696 return PURPLE_CMD_RET_FAILED;
1699 gc = purple_conversation_get_connection(conv);
1701 if (gc == NULL)
1702 return PURPLE_CMD_RET_FAILED;
1704 account = purple_connection_get_account(gc);
1706 im = purple_im_conversation_new(account, args[0]);
1708 if (args[1]) {
1709 PurpleMessage *msg = purple_message_new_outgoing(
1710 args[0], args[1], 0);
1712 ret = silcpurple_send_im(gc, msg);
1713 purple_conversation_write_message(PURPLE_CONVERSATION(im), msg);
1716 if (ret)
1717 return PURPLE_CMD_RET_OK;
1718 else
1719 return PURPLE_CMD_RET_FAILED;
1722 static PurpleCmdRet silcpurple_cmd_motd(PurpleConversation *conv,
1723 const char *cmd, char **args, char **error, void *data)
1725 PurpleConnection *gc;
1726 SilcPurple sg;
1727 char *tmp;
1729 gc = purple_conversation_get_connection(conv);
1731 if (gc == NULL)
1732 return PURPLE_CMD_RET_FAILED;
1734 sg = purple_connection_get_protocol_data(gc);
1736 if (sg == NULL)
1737 return PURPLE_CMD_RET_FAILED;
1739 if (!sg->motd) {
1740 *error = g_strdup(_("There is no Message of the Day associated with this connection"));
1741 return PURPLE_CMD_RET_FAILED;
1744 tmp = g_markup_escape_text(sg->motd, -1);
1745 purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL,
1746 tmp, NULL, NULL);
1747 g_free(tmp);
1749 return PURPLE_CMD_RET_OK;
1752 static PurpleCmdRet silcpurple_cmd_detach(PurpleConversation *conv,
1753 const char *cmd, char **args, char **error, void *data)
1755 PurpleConnection *gc;
1756 SilcPurple sg;
1758 gc = purple_conversation_get_connection(conv);
1760 if (gc == NULL)
1761 return PURPLE_CMD_RET_FAILED;
1763 sg = purple_connection_get_protocol_data(gc);
1765 if (sg == NULL)
1766 return PURPLE_CMD_RET_FAILED;
1768 silc_client_command_call(sg->client, sg->conn, "DETACH");
1769 sg->detaching = TRUE;
1771 return PURPLE_CMD_RET_OK;
1774 static PurpleCmdRet silcpurple_cmd_cmode(PurpleConversation *conv,
1775 const char *cmd, char **args, char **error, void *data)
1777 PurpleConnection *gc;
1778 SilcPurple sg;
1779 SilcChannelEntry channel;
1780 char *silccmd, *silcargs, *msg, tmp[256];
1781 const char *chname;
1783 gc = purple_conversation_get_connection(conv);
1785 if (gc == NULL || !args || purple_connection_get_protocol_data(gc) == NULL)
1786 return PURPLE_CMD_RET_FAILED;
1788 sg = purple_connection_get_protocol_data(gc);
1790 if (args[0])
1791 chname = args[0];
1792 else
1793 chname = purple_conversation_get_name(conv);
1795 if (!args[1]) {
1796 channel = silc_client_get_channel(sg->client, sg->conn,
1797 (char *)chname);
1798 if (!channel) {
1799 *error = g_strdup_printf(_("channel %s not found"), chname);
1800 return PURPLE_CMD_RET_FAILED;
1802 if (channel->mode) {
1803 silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
1804 msg = g_strdup_printf(_("channel modes for %s: %s"), chname, tmp);
1805 } else {
1806 msg = g_strdup_printf(_("no channel modes are set on %s"), chname);
1808 purple_conversation_write_system_message(conv, msg, PURPLE_MESSAGE_NO_LOG);
1809 g_free(msg);
1810 return PURPLE_CMD_RET_OK;
1813 silcargs = g_strjoinv(" ", args);
1814 silccmd = g_strconcat(cmd, " ", silcargs, NULL);
1815 g_free(silcargs);
1816 if (!silc_client_command_call(sg->client, sg->conn, silccmd)) {
1817 g_free(silccmd);
1818 *error = g_strdup_printf(_("Failed to set cmodes for %s"), args[0]);
1819 return PURPLE_CMD_RET_FAILED;
1821 g_free(silccmd);
1823 return PURPLE_CMD_RET_OK;
1826 static PurpleCmdRet silcpurple_cmd_generic(PurpleConversation *conv,
1827 const char *cmd, char **args, char **error, void *data)
1829 PurpleConnection *gc;
1830 SilcPurple sg;
1831 char *silccmd, *silcargs;
1833 gc = purple_conversation_get_connection(conv);
1835 if (gc == NULL)
1836 return PURPLE_CMD_RET_FAILED;
1838 sg = purple_connection_get_protocol_data(gc);
1840 if (sg == NULL)
1841 return PURPLE_CMD_RET_FAILED;
1843 silcargs = g_strjoinv(" ", args);
1844 silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL);
1845 g_free(silcargs);
1846 if (!silc_client_command_call(sg->client, sg->conn, silccmd)) {
1847 g_free(silccmd);
1848 *error = g_strdup_printf(_("Unknown command: %s, (may be a client bug)"), cmd);
1849 return PURPLE_CMD_RET_FAILED;
1851 g_free(silccmd);
1853 return PURPLE_CMD_RET_OK;
1856 static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
1857 const char *cmd, char **args, char **error, void *data)
1859 PurpleConnection *gc;
1860 SilcPurple sg;
1861 GHashTable *ui_info;
1862 const char *ui_name = NULL, *ui_website = NULL;
1863 char *quit_msg;
1865 gc = purple_conversation_get_connection(conv);
1867 if (gc == NULL)
1868 return PURPLE_CMD_RET_FAILED;
1870 sg = purple_connection_get_protocol_data(gc);
1872 if (sg == NULL)
1873 return PURPLE_CMD_RET_FAILED;
1875 ui_info = purple_core_get_ui_info();
1877 if(ui_info) {
1878 ui_name = g_hash_table_lookup(ui_info, "name");
1879 ui_website = g_hash_table_lookup(ui_info, "website");
1882 if(!ui_name || !ui_website) {
1883 ui_name = "Pidgin";
1884 ui_website = PURPLE_WEBSITE;
1886 quit_msg = g_strdup_printf(_("Download %s: %s"),
1887 ui_name, ui_website);
1889 silc_client_command_call(sg->client, sg->conn, NULL,
1890 "QUIT", (args && args[0]) ? args[0] : quit_msg, NULL);
1891 g_free(quit_msg);
1893 return PURPLE_CMD_RET_OK;
1896 static PurpleCmdRet silcpurple_cmd_call(PurpleConversation *conv,
1897 const char *cmd, char **args, char **error, void *data)
1899 PurpleConnection *gc;
1900 SilcPurple sg;
1902 gc = purple_conversation_get_connection(conv);
1904 if (gc == NULL)
1905 return PURPLE_CMD_RET_FAILED;
1907 sg = purple_connection_get_protocol_data(gc);
1909 if (sg == NULL)
1910 return PURPLE_CMD_RET_FAILED;
1912 if (!silc_client_command_call(sg->client, sg->conn, args[0])) {
1913 *error = g_strdup_printf(_("Unknown command: %s"), args[0]);
1914 return PURPLE_CMD_RET_FAILED;
1917 return PURPLE_CMD_RET_OK;
1921 /************************** Plugin Initialization ****************************/
1923 static void
1924 silcpurple_register_commands(void)
1926 PurpleCmdId id;
1928 id = purple_cmd_register("part", "w", PURPLE_CMD_P_PROTOCOL,
1929 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
1930 PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
1931 "prpl-silc", silcpurple_cmd_chat_part, _("part [channel]: Leave the chat"), NULL);
1932 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1934 id = purple_cmd_register("leave", "w", PURPLE_CMD_P_PROTOCOL,
1935 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
1936 PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
1937 "prpl-silc", silcpurple_cmd_chat_part, _("leave [channel]: Leave the chat"), NULL);
1938 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1940 id = purple_cmd_register("topic", "s", PURPLE_CMD_P_PROTOCOL,
1941 PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
1942 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc",
1943 silcpurple_cmd_chat_topic, _("topic [&lt;new topic&gt;]: View or change the topic"), NULL);
1944 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1946 id = purple_cmd_register("join", "ws", PURPLE_CMD_P_PROTOCOL,
1947 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
1948 PURPLE_CMD_FLAG_PROTOCOL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
1949 "prpl-silc", silcpurple_cmd_chat_join,
1950 _("join &lt;channel&gt; [&lt;password&gt;]: Join a chat on this network"), NULL);
1951 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1953 id = purple_cmd_register("list", "", PURPLE_CMD_P_PROTOCOL,
1954 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
1955 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc",
1956 silcpurple_cmd_chat_list, _("list: List channels on this network"), NULL);
1957 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1959 id = purple_cmd_register("whois", "w", PURPLE_CMD_P_PROTOCOL,
1960 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
1961 "prpl-silc",
1962 silcpurple_cmd_whois, _("whois &lt;nick&gt;: View nick's information"), NULL);
1963 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1965 id = purple_cmd_register("msg", "ws", PURPLE_CMD_P_PROTOCOL,
1966 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
1967 "prpl-silc", silcpurple_cmd_msg,
1968 _("msg &lt;nick&gt; &lt;message&gt;: Send a private message to a user"), NULL);
1969 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1971 id = purple_cmd_register("query", "ws", PURPLE_CMD_P_PROTOCOL,
1972 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
1973 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_query,
1974 _("query &lt;nick&gt; [&lt;message&gt;]: Send a private message to a user"), NULL);
1975 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1977 id = purple_cmd_register("motd", "", PURPLE_CMD_P_PROTOCOL,
1978 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
1979 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_motd,
1980 _("motd: View the server's Message Of The Day"), NULL);
1981 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1983 id = purple_cmd_register("detach", "", PURPLE_CMD_P_PROTOCOL,
1984 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
1985 "prpl-silc", silcpurple_cmd_detach,
1986 _("detach: Detach this session"), NULL);
1987 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1989 id = purple_cmd_register("quit", "s", PURPLE_CMD_P_PROTOCOL,
1990 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
1991 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_quit,
1992 _("quit [message]: Disconnect from the server, with an optional message"), NULL);
1993 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
1995 id = purple_cmd_register("call", "s", PURPLE_CMD_P_PROTOCOL,
1996 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
1997 "prpl-silc", silcpurple_cmd_call,
1998 _("call &lt;command&gt;: Call any silc client command"), NULL);
1999 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2001 /* These below just get passed through for the silc client library to deal
2002 * with */
2003 id = purple_cmd_register("kill", "ws", PURPLE_CMD_P_PROTOCOL,
2004 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
2005 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
2006 _("kill &lt;nick&gt; [-pubkey|&lt;reason&gt;]: Kill nick"), NULL);
2007 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2009 id = purple_cmd_register("nick", "w", PURPLE_CMD_P_PROTOCOL,
2010 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
2011 "prpl-silc", silcpurple_cmd_generic,
2012 _("nick &lt;newnick&gt;: Change your nickname"), NULL);
2013 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2015 id = purple_cmd_register("whowas", "ww", PURPLE_CMD_P_PROTOCOL,
2016 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
2017 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
2018 _("whowas &lt;nick&gt;: View nick's information"), NULL);
2019 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2021 id = purple_cmd_register("cmode", "wws", PURPLE_CMD_P_PROTOCOL,
2022 PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
2023 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_cmode,
2024 _("cmode &lt;channel&gt; [+|-&lt;modes&gt;] [arguments]: Change or display channel modes"), NULL);
2025 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2027 id = purple_cmd_register("cumode", "wws", PURPLE_CMD_P_PROTOCOL,
2028 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
2029 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
2030 _("cumode &lt;channel&gt; +|-&lt;modes&gt; &lt;nick&gt;: Change nick's modes on channel"), NULL);
2031 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2033 id = purple_cmd_register("umode", "w", PURPLE_CMD_P_PROTOCOL,
2034 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
2035 "prpl-silc", silcpurple_cmd_generic,
2036 _("umode &lt;usermodes&gt;: Set your modes in the network"), NULL);
2037 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2039 id = purple_cmd_register("oper", "s", PURPLE_CMD_P_PROTOCOL,
2040 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
2041 "prpl-silc", silcpurple_cmd_generic,
2042 _("oper &lt;nick&gt; [-pubkey]: Get server operator privileges"), NULL);
2043 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2045 id = purple_cmd_register("invite", "ws", PURPLE_CMD_P_PROTOCOL,
2046 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
2047 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
2048 _("invite &lt;channel&gt; [-|+]&lt;nick&gt;: invite nick or add/remove from channel invite list"), NULL);
2049 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2051 id = purple_cmd_register("kick", "wws", PURPLE_CMD_P_PROTOCOL,
2052 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
2053 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
2054 _("kick &lt;channel&gt; &lt;nick&gt; [comment]: Kick client from channel"), NULL);
2055 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2057 id = purple_cmd_register("info", "w", PURPLE_CMD_P_PROTOCOL,
2058 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
2059 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
2060 _("info [server]: View server administrative details"), NULL);
2061 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2063 id = purple_cmd_register("ban", "ww", PURPLE_CMD_P_PROTOCOL,
2064 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY |
2065 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
2066 _("ban [&lt;channel&gt; +|-&lt;nick&gt;]: Ban client from channel"), NULL);
2067 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2069 id = purple_cmd_register("getkey", "w", PURPLE_CMD_P_PROTOCOL,
2070 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
2071 "prpl-silc", silcpurple_cmd_generic,
2072 _("getkey &lt;nick|server&gt;: Retrieve client's or server's public key"), NULL);
2073 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2075 id = purple_cmd_register("stats", "", PURPLE_CMD_P_PROTOCOL,
2076 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
2077 "prpl-silc", silcpurple_cmd_generic,
2078 _("stats: View server and network statistics"), NULL);
2079 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2081 id = purple_cmd_register("ping", "", PURPLE_CMD_P_PROTOCOL,
2082 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
2083 "prpl-silc", silcpurple_cmd_generic,
2084 _("ping: Send PING to the connected server"), NULL);
2085 cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
2088 static void
2089 silcpurple_unregister_commands(void)
2091 while (cmds) {
2092 PurpleCmdId id = GPOINTER_TO_UINT(cmds->data);
2093 purple_cmd_unregister(id);
2094 cmds = g_slist_delete_link(cmds, cmds);
2098 static PurpleWhiteboardOps silcpurple_wb_ops =
2100 silcpurple_wb_start,
2101 silcpurple_wb_end,
2102 silcpurple_wb_get_dimensions,
2103 silcpurple_wb_set_dimensions,
2104 silcpurple_wb_get_brush,
2105 silcpurple_wb_set_brush,
2106 silcpurple_wb_send,
2107 silcpurple_wb_clear,
2109 /* padding */
2110 NULL,
2111 NULL,
2112 NULL,
2113 NULL
2117 static void
2118 silcpurple_protocol_init(PurpleProtocol *protocol)
2120 PurpleAccountOption *option;
2121 PurpleAccountUserSplit *split;
2122 char tmp[256];
2123 int i;
2124 PurpleKeyValuePair *kvp;
2125 GList *list = NULL;
2127 protocol->id = "prpl-silc";
2128 protocol->name = "SILC";
2129 protocol->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
2130 OPT_PROTO_PASSWORD_OPTIONAL |
2131 OPT_PROTO_SLASH_COMMANDS_NATIVE;
2132 protocol->icon_spec = purple_buddy_icon_spec_new("jpeg,gif,png,bmp",
2133 0, 0, 96, 96, 0,
2134 PURPLE_ICON_SCALE_DISPLAY);
2136 protocol->whiteboard_ops = &silcpurple_wb_ops;
2138 split = purple_account_user_split_new(_("Network"), "silcnet.org", '@');
2139 protocol->user_splits = g_list_append(protocol->user_splits, split);
2141 /* Account options */
2142 option = purple_account_option_string_new(_("Connect server"),
2143 "server",
2144 "silc.silcnet.org");
2145 protocol->account_options = g_list_append(protocol->account_options, option);
2146 option = purple_account_option_int_new(_("Port"), "port", 706);
2147 protocol->account_options = g_list_append(protocol->account_options, option);
2148 g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
2149 option = purple_account_option_string_new(_("Public Key file"),
2150 "public-key", tmp);
2151 protocol->account_options = g_list_append(protocol->account_options, option);
2152 g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
2153 option = purple_account_option_string_new(_("Private Key file"),
2154 "private-key", tmp);
2155 protocol->account_options = g_list_append(protocol->account_options, option);
2157 for (i = 0; silc_default_ciphers[i].name; i++) {
2158 kvp = g_new0(PurpleKeyValuePair, 1);
2159 kvp->key = g_strdup(silc_default_ciphers[i].name);
2160 kvp->value = g_strdup(silc_default_ciphers[i].name);
2161 list = g_list_append(list, kvp);
2163 option = purple_account_option_list_new(_("Cipher"), "cipher", list);
2164 protocol->account_options = g_list_append(protocol->account_options, option);
2166 list = NULL;
2167 for (i = 0; silc_default_hmacs[i].name; i++) {
2168 kvp = g_new0(PurpleKeyValuePair, 1);
2169 kvp->key = g_strdup(silc_default_hmacs[i].name);
2170 kvp->value = g_strdup(silc_default_hmacs[i].name);
2171 list = g_list_append(list, kvp);
2173 option = purple_account_option_list_new(_("HMAC"), "hmac", list);
2174 protocol->account_options = g_list_append(protocol->account_options, option);
2176 option = purple_account_option_bool_new(_("Use Perfect Forward Secrecy"),
2177 "pfs", FALSE);
2178 protocol->account_options = g_list_append(protocol->account_options, option);
2180 option = purple_account_option_bool_new(_("Public key authentication"),
2181 "pubkey-auth", FALSE);
2182 protocol->account_options = g_list_append(protocol->account_options, option);
2183 option = purple_account_option_bool_new(_("Block IMs without Key Exchange"),
2184 "block-ims", FALSE);
2185 protocol->account_options = g_list_append(protocol->account_options, option);
2186 option = purple_account_option_bool_new(_("Block messages to whiteboard"),
2187 "block-wb", FALSE);
2188 protocol->account_options = g_list_append(protocol->account_options, option);
2189 option = purple_account_option_bool_new(_("Automatically open whiteboard"),
2190 "open-wb", FALSE);
2191 protocol->account_options = g_list_append(protocol->account_options, option);
2192 option = purple_account_option_bool_new(_("Digitally sign and verify all messages"),
2193 "sign-verify", FALSE);
2194 protocol->account_options = g_list_append(protocol->account_options, option);
2197 static void
2198 silcpurple_protocol_class_init(PurpleProtocolClass *klass)
2200 klass->login = silcpurple_login;
2201 klass->close = silcpurple_close;
2202 klass->status_types = silcpurple_away_states;
2203 klass->list_icon = silcpurple_list_icon;
2206 static void
2207 silcpurple_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
2209 client_iface->get_actions = silcpurple_get_actions;
2210 client_iface->status_text = silcpurple_status_text;
2211 client_iface->tooltip_text = silcpurple_tooltip_text;
2212 client_iface->blist_node_menu = silcpurple_blist_node_menu;
2215 static void
2216 silcpurple_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
2218 server_iface->set_info = silcpurple_set_info;
2219 server_iface->get_info = silcpurple_get_info;
2220 server_iface->set_status = silcpurple_set_status;
2221 server_iface->set_idle = silcpurple_idle_set;
2222 server_iface->change_passwd = silcpurple_change_passwd;
2223 server_iface->add_buddy = silcpurple_add_buddy;
2224 server_iface->remove_buddy = silcpurple_remove_buddy;
2225 server_iface->keepalive = silcpurple_keepalive;
2226 server_iface->set_buddy_icon = silcpurple_buddy_set_icon;
2229 static void
2230 silcpurple_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
2232 im_iface->send = silcpurple_send_im;
2235 static void
2236 silcpurple_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
2238 chat_iface->info = silcpurple_chat_info;
2239 chat_iface->info_defaults = silcpurple_chat_info_defaults;
2240 chat_iface->join = silcpurple_chat_join;
2241 chat_iface->get_name = silcpurple_get_chat_name;
2242 chat_iface->invite = silcpurple_chat_invite;
2243 chat_iface->leave = silcpurple_chat_leave;
2244 chat_iface->send = silcpurple_chat_send;
2245 chat_iface->set_topic = silcpurple_chat_set_topic;
2248 static void
2249 silcpurple_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *roomlist_iface)
2251 roomlist_iface->get_list = silcpurple_roomlist_get_list;
2252 roomlist_iface->cancel = silcpurple_roomlist_cancel;
2255 static void
2256 silcpurple_protocol_xfer_iface_init(PurpleProtocolXferInterface *xfer_iface)
2258 xfer_iface->send_file = silcpurple_ftp_send_file;
2259 xfer_iface->new_xfer = silcpurple_ftp_new_xfer;
2262 PURPLE_DEFINE_TYPE_EXTENDED(
2263 SilcProtocol, silcpurple_protocol, PURPLE_TYPE_PROTOCOL, 0,
2265 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT,
2266 silcpurple_protocol_client_iface_init)
2268 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER,
2269 silcpurple_protocol_server_iface_init)
2271 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM,
2272 silcpurple_protocol_im_iface_init)
2274 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT,
2275 silcpurple_protocol_chat_iface_init)
2277 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
2278 silcpurple_protocol_roomlist_iface_init)
2280 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER,
2281 silcpurple_protocol_xfer_iface_init)
2284 static PurplePluginInfo *
2285 plugin_query(GError **error)
2287 const gchar * const authors[] = {
2288 "Pekka Riikonen",
2289 NULL
2292 return purple_plugin_info_new(
2293 "id", "prpl-silc",
2294 "name", "SILC Protocol",
2295 "version", "1.1",
2296 "category", N_("Protocol"),
2297 "summary", N_("SILC Protocol Plugin"),
2298 "description", N_("Secure Internet Live Conferencing (SILC) Protocol"),
2299 "authors", authors,
2300 "website", "http://silcnet.org/",
2301 "abi-version", PURPLE_ABI_VERSION,
2302 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
2303 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
2304 NULL
2308 static gboolean
2309 plugin_load(PurplePlugin *plugin, GError **error)
2311 silcpurple_protocol_register_type(plugin);
2313 my_protocol = purple_protocols_add(SILCPURPLE_TYPE_PROTOCOL, error);
2314 if (!my_protocol)
2315 return FALSE;
2317 purple_prefs_remove("/plugins/prpl/silc");
2319 silc_log_set_callback(SILC_LOG_ERROR, silcpurple_log_error, NULL);
2320 silcpurple_register_commands();
2322 return TRUE;
2325 static gboolean
2326 plugin_unload(PurplePlugin *plugin, GError **error)
2328 silcpurple_unregister_commands();
2330 if (!purple_protocols_remove(my_protocol, error))
2331 return FALSE;
2333 return TRUE;
2336 PURPLE_PLUGIN_INIT(silc, plugin_query, plugin_load, plugin_unload);