1 /*
2 
3   silcpurple.c
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 2004 - 2007 Pekka Riikonen
8 
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12 
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17 
18 */
19 
20 #include "internal.h"
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "silcpurple.h"
24 #include "version.h"
25 #include "wb.h"
26 #include "core.h"
27 
28 extern SilcClientOperations ops;
29 static PurplePlugin *silc_plugin = NULL;
30 
31 /* Error log message callback */
32 
silcpurple_log_error(SilcLogType type,char * message,void * context)33 static SilcBool silcpurple_log_error(SilcLogType type, char *message,
34 				     void *context)
35 {
36 	silc_say(NULL, NULL, SILC_CLIENT_MESSAGE_ERROR, message);
37 	return TRUE;
38 }
39 
40 static const char *
silcpurple_list_icon(PurpleAccount * a,PurpleBuddy * b)41 silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b)
42 {
43 	return (const char *)"silc";
44 }
45 
46 static GList *
silcpurple_away_states(PurpleAccount * account)47 silcpurple_away_states(PurpleAccount *account)
48 {
49 	PurpleStatusType *type;
50 	GList *types = NULL;
51 
52 	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE);
53 	types = g_list_append(types, type);
54 	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE);
55 	types = g_list_append(types, type);
56 	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE);
57 	types = g_list_append(types, type);
58 	type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE);
59 	types = g_list_append(types, type);
60 	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE);
61 	types = g_list_append(types, type);
62 	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE);
63 	types = g_list_append(types, type);
64 	type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE);
65 	types = g_list_append(types, type);
66 
67 	return types;
68 }
69 
70 static void
silcpurple_set_status(PurpleAccount * account,PurpleStatus * status)71 silcpurple_set_status(PurpleAccount *account, PurpleStatus *status)
72 {
73 	PurpleConnection *gc = purple_account_get_connection(account);
74 	SilcPurple sg = NULL;
75 	SilcUInt32 mode;
76 	SilcBuffer idp;
77 	unsigned char mb[4];
78 	const char *state;
79 
80 	if (gc != NULL)
81 		sg = gc->proto_data;
82 
83 	if (status == NULL)
84 		return;
85 
86 	state = purple_status_get_id(status);
87 
88 	if (state == NULL)
89 		return;
90 
91 	if ((sg == NULL) || (sg->conn == NULL))
92 		return;
93 
94 	mode = sg->conn->local_entry->mode;
95 	mode &= ~(SILC_UMODE_GONE |
96 		  SILC_UMODE_HYPER |
97 		  SILC_UMODE_BUSY |
98 		  SILC_UMODE_INDISPOSED |
99 		  SILC_UMODE_PAGE);
100 
101 	if (purple_strequal(state, "hyper"))
102 		mode |= SILC_UMODE_HYPER;
103 	else if (purple_strequal(state, "away"))
104 		mode |= SILC_UMODE_GONE;
105 	else if (purple_strequal(state, "busy"))
106 		mode |= SILC_UMODE_BUSY;
107 	else if (purple_strequal(state, "indisposed"))
108 		mode |= SILC_UMODE_INDISPOSED;
109 	else if (purple_strequal(state, "page"))
110 		mode |= SILC_UMODE_PAGE;
111 
112 	/* Send UMODE */
113 	idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT);
114 	SILC_PUT32_MSB(mode, mb);
115 	silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE,
116 				 silcpurple_command_reply, NULL, 2,
117 				 1, idp->data, silc_buffer_len(idp),
118 				 2, mb, sizeof(mb));
119 	silc_buffer_free(idp);
120 }
121 
122 
123 /*************************** Connection Routines *****************************/
124 
125 static void
silcpurple_keepalive(PurpleConnection * gc)126 silcpurple_keepalive(PurpleConnection *gc)
127 {
128 	SilcPurple sg = gc->proto_data;
129 	silc_packet_send(sg->conn->stream, SILC_PACKET_HEARTBEAT, 0,
130 			 NULL, 0);
131 }
132 
133 #if __SILC_TOOLKIT_VERSION < SILC_VERSION(1,1,1)
134 static gboolean
silcpurple_scheduler(gpointer * context)135 silcpurple_scheduler(gpointer *context)
136 {
137 	SilcClient client = (SilcClient)context;
138 	silc_client_run_one(client);
139 	return TRUE;
140 }
141 #else
142 typedef struct {
143   SilcPurple sg;
144   SilcUInt32 fd;
145   guint tag;
146 } *SilcPurpleTask;
147 
148 /* A timeout occurred.  Call SILC scheduler. */
149 
150 static gboolean
silcpurple_scheduler_timeout(gpointer context)151 silcpurple_scheduler_timeout(gpointer context)
152 {
153 	SilcPurpleTask task = (SilcPurpleTask)context;
154 	silc_client_run_one(task->sg->client);
155 	silc_dlist_del(task->sg->tasks, task);
156 	silc_free(task);
157 	return FALSE;
158 }
159 
160 /* An fd task event occurred.  Call SILC scheduler. */
161 
162 static void
silcpurple_scheduler_fd(gpointer data,gint fd,PurpleInputCondition cond)163 silcpurple_scheduler_fd(gpointer data, gint fd, PurpleInputCondition cond)
164 {
165 	SilcClient client = (SilcClient)data;
166 	silc_client_run_one(client);
167 }
168 
169 /* SILC Scheduler notify callback.  This is called whenever task is added to
170    or deleted from SILC scheduler.  It's also called when fd task events
171    change.  Here we add same tasks to glib's main loop. */
172 
173 static void
silcpurple_scheduler(SilcSchedule schedule,SilcBool added,SilcTask task,SilcBool fd_task,SilcUInt32 fd,SilcTaskEvent event,long seconds,long useconds,void * context)174 silcpurple_scheduler(SilcSchedule schedule,
175 		     SilcBool added, SilcTask task,
176 		     SilcBool fd_task, SilcUInt32 fd,
177 		     SilcTaskEvent event,
178 		     long seconds, long useconds,
179 		     void *context)
180 {
181 	SilcClient client = (SilcClient)context;
182 	PurpleConnection *gc = client->application;
183 	SilcPurple sg = gc->proto_data;
184 	SilcPurpleTask ptask = NULL;
185 
186 	if (added) {
187 	  if (fd_task) {
188 	    /* Add fd or change fd events */
189 	    PurpleInputCondition e = 0;
190 
191 	    silc_dlist_start(sg->tasks);
192 	    while ((ptask = silc_dlist_get(sg->tasks)))
193 	      if (ptask->fd == fd) {
194 		purple_input_remove(ptask->tag);
195 		break;
196 	      }
197 
198 	    if (event & SILC_TASK_READ)
199 	      e |= PURPLE_INPUT_READ;
200 	    if (event & SILC_TASK_WRITE)
201 	      e |= PURPLE_INPUT_WRITE;
202 
203 	    if (e) {
204 	      if (!ptask) {
205 		ptask = silc_calloc(1, sizeof(*ptask));
206 		ptask->fd = fd;
207 		silc_dlist_add(sg->tasks, ptask);
208 	      }
209 	      ptask->tag = purple_input_add(fd, e, silcpurple_scheduler_fd,
210 					    client);
211 	    } else if (ptask) {
212 	      silc_dlist_del(sg->tasks, ptask);
213 	      silc_free(ptask);
214 	    }
215 	  } else {
216 	    /* Add timeout */
217 	    ptask = silc_calloc(1, sizeof(*ptask));
218 	    ptask->sg = sg;
219 	    ptask->tag = purple_timeout_add((seconds * 1000) +
220 					    (useconds / 1000),
221 					    silcpurple_scheduler_timeout,
222 					    ptask);
223 	    silc_dlist_add(sg->tasks, ptask);
224 	  }
225 	} else {
226 	  if (fd_task) {
227 	    /* Remove fd */
228 	    silc_dlist_start(sg->tasks);
229 	    while ((ptask = silc_dlist_get(sg->tasks)))
230 	      if (ptask->fd == fd) {
231 		purple_input_remove(ptask->tag);
232 		silc_dlist_del(sg->tasks, ptask);
233 		silc_free(ptask);
234 		break;
235 	      }
236 	  }
237 	}
238 }
239 #endif /* __SILC_TOOLKIT_VERSION */
240 
241 static void
silcpurple_connect_cb(SilcClient client,SilcClientConnection conn,SilcClientConnectionStatus status,SilcStatus error,const char * message,void * context)242 silcpurple_connect_cb(SilcClient client, SilcClientConnection conn,
243 		      SilcClientConnectionStatus status, SilcStatus error,
244 		      const char *message, void *context)
245 {
246 	PurpleConnection *gc = context;
247 	SilcPurple sg;
248 	SilcUInt32 mask;
249 	char tz[16];
250 	PurpleStoredImage *img;
251 #ifdef HAVE_SYS_UTSNAME_H
252 	struct utsname u;
253 #endif
254 
255 	sg = gc->proto_data;
256 
257 	switch (status) {
258 	case SILC_CLIENT_CONN_SUCCESS:
259 	case SILC_CLIENT_CONN_SUCCESS_RESUME:
260 		sg->conn = conn;
261 
262 		/* Connection created successfully */
263 		purple_connection_set_state(gc, PURPLE_CONNECTED);
264 
265 		/* Send the server our buddy list */
266 		silcpurple_send_buddylist(gc);
267 
268 		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
269 
270 		/* Send any UMODEs configured for account */
271 		if (purple_account_get_bool(sg->account, "block-ims", FALSE)) {
272 			silc_client_command_call(sg->client, sg->conn, NULL,
273 						 "UMODE", "+P", NULL);
274 		}
275 
276 		/* Set default attributes */
277 		mask = SILC_ATTRIBUTE_MOOD_NORMAL;
278 		silc_client_attribute_add(client, conn,
279 					  SILC_ATTRIBUTE_STATUS_MOOD,
280 					  SILC_32_TO_PTR(mask),
281 					  sizeof(SilcUInt32));
282 		mask = SILC_ATTRIBUTE_CONTACT_CHAT;
283 		silc_client_attribute_add(client, conn,
284 					  SILC_ATTRIBUTE_PREFERRED_CONTACT,
285 					  SILC_32_TO_PTR(mask),
286 					  sizeof(SilcUInt32));
287 #ifdef HAVE_SYS_UTSNAME_H
288 		if (!uname(&u)) {
289 			SilcAttributeObjDevice dev;
290 			memset(&dev, 0, sizeof(dev));
291 			dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
292 			dev.version = u.release;
293 			dev.model = u.sysname;
294 			silc_client_attribute_add(client, conn,
295 						  SILC_ATTRIBUTE_DEVICE_INFO,
296 						  (void *)&dev, sizeof(dev));
297 		}
298 #endif
299 		silc_timezone(tz, sizeof(tz));
300 		silc_client_attribute_add(client, conn,
301 					  SILC_ATTRIBUTE_TIMEZONE,
302 					  (void *)tz, strlen(tz));
303 
304 		/* Set our buddy icon */
305 		img = purple_buddy_icons_find_account_icon(sg->account);
306 		silcpurple_buddy_set_icon(gc, img);
307 		purple_imgstore_unref(img);
308 
309 		return;
310 		break;
311 
312 	case SILC_CLIENT_CONN_DISCONNECTED:
313 		/* Disconnected */
314 		if (sg->resuming && !sg->detaching)
315 		  g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
316 
317 		/* Close the connection */
318 		if (!sg->detaching)
319 		  purple_connection_error_reason(gc,
320 		                                 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
321 		                                 _("Disconnected by server"));
322 		else
323 		  /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
324 		  purple_account_disconnect(purple_connection_get_account(gc));
325 		break;
326 
327 	case SILC_CLIENT_CONN_ERROR:
328 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
329 		                             _("Error connecting to SILC Server"));
330 		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
331 		break;
332 
333 	case SILC_CLIENT_CONN_ERROR_KE:
334 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
335 		                             _("Key Exchange failed"));
336 		break;
337 
338 	case SILC_CLIENT_CONN_ERROR_AUTH:
339 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
340 		                             _("Authentication failed"));
341 		break;
342 
343 	case SILC_CLIENT_CONN_ERROR_RESUME:
344 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
345 		                             _("Resuming detached session failed. "
346 		                               "Press Reconnect to create new connection."));
347 		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
348 		break;
349 
350 	case SILC_CLIENT_CONN_ERROR_TIMEOUT:
351 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
352 		                             _("Connection timed out"));
353 		break;
354 	}
355 
356 	/* Error */
357 	sg->conn = NULL;
358 }
359 
360 static void
silcpurple_stream_created(SilcSocketStreamStatus status,SilcStream stream,void * context)361 silcpurple_stream_created(SilcSocketStreamStatus status, SilcStream stream,
362 			  void *context)
363 {
364 	PurpleConnection *gc = context;
365 	SilcPurple sg;
366 	SilcClient client;
367 	SilcClientConnectionParams params;
368 	const char *dfile;
369 
370 	sg = gc->proto_data;
371 
372 	if (status != SILC_SOCKET_OK) {
373 		purple_connection_error_reason(gc,
374 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
375 			_("Connection failed"));
376 		silc_pkcs_public_key_free(sg->public_key);
377 		silc_pkcs_private_key_free(sg->private_key);
378 		silc_free(sg);
379 		gc->proto_data = NULL;
380 		return;
381 	}
382 
383 	client = sg->client;
384 
385 	/* Get session detachment data, if available */
386 	memset(&params, 0, sizeof(params));
387 	dfile = silcpurple_session_file(purple_account_get_username(sg->account));
388 	params.detach_data = (unsigned char *)silc_file_readfile(dfile, &params.detach_data_len);
389 	if (params.detach_data)
390 		params.detach_data[params.detach_data_len] = 0;
391 	params.ignore_requested_attributes = FALSE;
392 	params.pfs = purple_account_get_bool(sg->account, "pfs", FALSE);
393 
394 	/* Progress */
395 	if (params.detach_data) {
396 		purple_connection_update_progress(gc, _("Resuming session"), 2, 5);
397 		sg->resuming = TRUE;
398 	} else {
399 		purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5);
400 	}
401 
402 	/* Perform SILC Key Exchange. */
403 	silc_client_key_exchange(client, &params, sg->public_key,
404 				 sg->private_key, stream, SILC_CONN_SERVER,
405 				 silcpurple_connect_cb, gc);
406 
407 	silc_free(params.detach_data);
408 }
409 
410 static void
silcpurple_login_connected(gpointer data,gint source,const gchar * error_message)411 silcpurple_login_connected(gpointer data, gint source, const gchar *error_message)
412 {
413 	PurpleConnection *gc = data;
414 	SilcPurple sg;
415 
416 	g_return_if_fail(gc != NULL);
417 
418 	sg = gc->proto_data;
419 
420 	if (source < 0) {
421 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
422 		                             _("Connection failed"));
423 		silc_pkcs_public_key_free(sg->public_key);
424 		silc_pkcs_private_key_free(sg->private_key);
425 		silc_free(sg);
426 		gc->proto_data = NULL;
427 		return;
428 	}
429 
430 	silc_hash_alloc((unsigned char *)"sha1", &sg->sha1hash);
431 
432 	/* Wrap socket to TCP stream */
433 	silc_socket_tcp_stream_create(source, TRUE, FALSE,
434 				      sg->client->schedule,
435 				      silcpurple_stream_created, gc);
436 }
437 
silcpurple_continue_running(SilcPurple sg)438 static void silcpurple_continue_running(SilcPurple sg)
439 {
440 	PurpleConnection *gc = sg->gc;
441 	PurpleAccount *account = purple_connection_get_account(gc);
442 
443 	/* Connect to the SILC server */
444 	if (purple_proxy_connect(gc, account,
445 				 purple_account_get_string(account, "server",
446 							   "silc.silcnet.org"),
447 				 purple_account_get_int(account, "port", 706),
448 				 silcpurple_login_connected, gc) == NULL)
449 	{
450 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
451 		                             _("Unable to connect"));
452 		gc->proto_data = NULL;
453 		silc_free(sg);
454 		return;
455 	}
456 }
457 
silcpurple_got_password_cb(PurpleConnection * gc,PurpleRequestFields * fields)458 static void silcpurple_got_password_cb(PurpleConnection *gc, PurpleRequestFields *fields)
459 {
460 	SilcPurple sg = (SilcPurple)gc->proto_data;
461 	PurpleAccount *account = purple_connection_get_account(gc);
462 	char pkd[256], prd[256];
463 	const char *password;
464 	gboolean remember;
465 
466 	/* The password prompt dialog doesn't get disposed if the account disconnects */
467 	if (!PURPLE_CONNECTION_IS_VALID(gc))
468 		return;
469 
470 	password = purple_request_fields_get_string(fields, "password");
471 	remember = purple_request_fields_get_bool(fields, "remember");
472 
473 	if (!password || !*password)
474 	{
475 		purple_notify_error(gc, NULL, _("Password is required to sign on."), NULL);
476 		gc->proto_data = NULL;
477 		silc_free(sg);
478 		return;
479 	}
480 
481 	if (remember)
482 		purple_account_set_remember_password(account, TRUE);
483 
484 	purple_account_set_password(account, password);
485 
486 	/* Load SILC key pair */
487 	g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
488 	g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
489 	if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
490 				(char *)purple_account_get_string(account, "private-key", prd),
491 				password,
492 				&sg->public_key, &sg->private_key)) {
493 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
494 		                             _("Unable to load SILC key pair"));
495 		gc->proto_data = NULL;
496 		silc_free(sg);
497 		return;
498 	}
499 	silcpurple_continue_running(sg);
500 }
501 
silcpurple_no_password_cb(PurpleConnection * gc,PurpleRequestFields * fields)502 static void silcpurple_no_password_cb(PurpleConnection *gc, PurpleRequestFields *fields)
503 {
504 	SilcPurple sg;
505 	/* The password prompt dialog doesn't get disposed if the account disconnects */
506 	if (!PURPLE_CONNECTION_IS_VALID(gc))
507 		return;
508 	sg = gc->proto_data;
509 	purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
510 			_("Unable to load SILC key pair"));
511 	gc->proto_data = NULL;
512 	silc_free(sg);
513 }
514 
silcpurple_running(SilcClient client,void * context)515 static void silcpurple_running(SilcClient client, void *context)
516 {
517 	SilcPurple sg = context;
518 	PurpleConnection *gc = sg->gc;
519 	PurpleAccount *account = purple_connection_get_account(gc);
520 	char pkd[256], prd[256];
521 
522 
523 	/* Progress */
524 	purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5);
525 
526 	/* Load SILC key pair */
527 	g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
528 	g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
529 	if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
530 				(char *)purple_account_get_string(account, "private-key", prd),
531 				(gc->password == NULL) ? "" : gc->password,
532 				&sg->public_key, &sg->private_key)) {
533 		if (!purple_account_get_password(account)) {
534 			purple_account_request_password(account, G_CALLBACK(silcpurple_got_password_cb),
535 											G_CALLBACK(silcpurple_no_password_cb), gc);
536 			return;
537 		}
538 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
539 		                             _("Unable to load SILC key pair"));
540 		gc->proto_data = NULL;
541 		silc_free(sg);
542 		return;
543 	}
544 	silcpurple_continue_running(sg);
545 }
546 
547 static void
silcpurple_login(PurpleAccount * account)548 silcpurple_login(PurpleAccount *account)
549 {
550 	SilcClient client;
551 	PurpleConnection *gc;
552 	SilcPurple sg;
553 	SilcClientParams params;
554 	const char *cipher, *hmac;
555 	char *username, *hostname, *realname, **up;
556 	int i;
557 
558 	gc = account->gc;
559 	if (!gc)
560 		return;
561 	gc->proto_data = NULL;
562 
563 	memset(&params, 0, sizeof(params));
564 	strcat(params.nickname_format, "%n#a");
565 
566 	/* Allocate SILC client */
567 	client = silc_client_alloc(&ops, &params, gc, NULL);
568 	if (!client) {
569 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
570 		                             _("Out of memory"));
571 		return;
572 	}
573 
574 	/* Get username, real name and local hostname for SILC library */
575 	if (!purple_account_get_username(account))
576 		purple_account_set_username(account, silc_get_username());
577 
578 	username = (char *)purple_account_get_username(account);
579 	up = g_strsplit(username, "@", 2);
580 	username = g_strdup(up[0]);
581 	g_strfreev(up);
582 
583 	if (!purple_account_get_user_info(account)) {
584 		purple_account_set_user_info(account, silc_get_real_name());
585 		if (!purple_account_get_user_info(account))
586 			purple_account_set_user_info(account,
587 						     "John T. Noname");
588 	}
589 	realname = (char *)purple_account_get_user_info(account);
590 	hostname = silc_net_localhost();
591 
592 	purple_connection_set_display_name(gc, username);
593 
594 	/* Register requested cipher and HMAC */
595 	cipher = purple_account_get_string(account, "cipher",
596 					   SILC_DEFAULT_CIPHER);
597 	for (i = 0; silc_default_ciphers[i].name; i++)
598 		if (purple_strequal(silc_default_ciphers[i].name, cipher)) {
599 			silc_cipher_register(&(silc_default_ciphers[i]));
600 			break;
601 		}
602 	hmac = purple_account_get_string(account, "hmac", SILC_DEFAULT_HMAC);
603 	for (i = 0; silc_default_hmacs[i].name; i++)
604 		if (purple_strequal(silc_default_hmacs[i].name, hmac)) {
605 			silc_hmac_register(&(silc_default_hmacs[i]));
606 			break;
607 		}
608 
609 	sg = silc_calloc(1, sizeof(*sg));
610 	if (!sg)
611 		return;
612 	sg->client = client;
613 	sg->gc = gc;
614 	sg->account = account;
615 	gc->proto_data = sg;
616 
617 	/* Init SILC client */
618 	if (!silc_client_init(client, username, hostname, realname,
619 			      silcpurple_running, sg)) {
620 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
621 		                             _("Unable to initialize SILC protocol"));
622 		gc->proto_data = NULL;
623 		silc_free(sg);
624 		silc_free(hostname);
625 		g_free(username);
626 		return;
627 	}
628 	silc_free(hostname);
629 	g_free(username);
630 
631 	/* Check the ~/.silc dir and create it, and new key pair if necessary. */
632 	if (!silcpurple_check_silc_dir(gc)) {
633 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
634 		                             _("Error loading SILC key pair"));
635 		gc->proto_data = NULL;
636 		silc_free(sg);
637 		return;
638 	}
639 
640 #if __SILC_TOOLKIT_VERSION < SILC_VERSION(1,1,1)
641 	/* Schedule SILC using Glib's event loop */
642 	sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, client);
643 #else
644 	/* Run SILC scheduler */
645 	sg->tasks = silc_dlist_init();
646 	silc_schedule_set_notify(client->schedule, silcpurple_scheduler,
647 				 client);
648 	silc_client_run_one(client);
649 #endif /* __SILC_TOOLKIT_VERSION */
650 }
651 
652 static int
silcpurple_close_final(gpointer * context)653 silcpurple_close_final(gpointer *context)
654 {
655 	SilcPurple sg = (SilcPurple)context;
656 
657 	purple_debug_info("silc", "Finalizing SilcPurple %p\n", sg);
658 
659 	silc_client_stop(sg->client, NULL, NULL);
660 	silc_client_free(sg->client);
661 	if (sg->sha1hash)
662 		silc_hash_free(sg->sha1hash);
663 	if (sg->mimeass)
664 		silc_mime_assembler_free(sg->mimeass);
665 	silc_free(sg);
666 	return 0;
667 }
668 
669 static void
silcpurple_close(PurpleConnection * gc)670 silcpurple_close(PurpleConnection *gc)
671 {
672 	SilcPurple sg = gc->proto_data;
673 #if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1)
674 	SilcPurpleTask task;
675 #endif /* __SILC_TOOLKIT_VERSION */
676 	GHashTable *ui_info;
677 	const char *ui_name = NULL, *ui_website = NULL;
678 	char *quit_msg;
679 
680 	g_return_if_fail(sg != NULL);
681 
682 	ui_info = purple_core_get_ui_info();
683 
684 	if(ui_info) {
685 		ui_name = g_hash_table_lookup(ui_info, "name");
686 		ui_website = g_hash_table_lookup(ui_info, "website");
687 	}
688 
689 	if(!ui_name || !ui_website) {
690 		ui_name = "Pidgin";
691 		ui_website = PURPLE_WEBSITE;
692 	}
693 	quit_msg = g_strdup_printf(_("Download %s: %s"),
694 							   ui_name, ui_website);
695 
696 	/* Send QUIT */
697 	silc_client_command_call(sg->client, sg->conn, NULL,
698 				 "QUIT", quit_msg,
699 				 NULL);
700 	g_free(quit_msg);
701 
702 	if (sg->conn)
703 		silc_client_close_connection(sg->client, sg->conn);
704 
705 #if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1)
706 	if (sg->conn)
707 	  silc_client_run_one(sg->client);
708 	silc_schedule_set_notify(sg->client->schedule, NULL, NULL);
709 
710 	silc_dlist_start(sg->tasks);
711 	while ((task = silc_dlist_get(sg->tasks))) {
712 	  purple_input_remove(task->tag);
713 	  silc_free(task);
714 	}
715 	silc_dlist_uninit(sg->tasks);
716 #endif /* __SILC_TOOLKIT_VERSION */
717 
718 	purple_timeout_remove(sg->scheduler);
719 
720 	purple_debug_info("silc", "Scheduling destruction of SilcPurple %p\n", sg);
721 	purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg);
722 }
723 
724 
725 /****************************** Protocol Actions *****************************/
726 
727 static void
silcpurple_attrs_cancel(PurpleConnection * gc,PurpleRequestFields * fields)728 silcpurple_attrs_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
729 {
730 	/* Nothing */
731 }
732 
733 static void
silcpurple_attrs_cb(PurpleConnection * gc,PurpleRequestFields * fields)734 silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields)
735 {
736 	SilcPurple sg = gc->proto_data;
737 	SilcClient client = sg->client;
738 	SilcClientConnection conn = sg->conn;
739 	PurpleRequestField *f;
740 	char *tmp;
741 	SilcUInt32 tmp_len, mask;
742 	SilcAttributeObjService service;
743 	SilcAttributeObjDevice dev;
744 	SilcVCardStruct vcard;
745 	const char *val;
746 
747 	sg = gc->proto_data;
748 	if (!sg)
749 		return;
750 
751 	memset(&service, 0, sizeof(service));
752 	memset(&dev, 0, sizeof(dev));
753 	memset(&vcard, 0, sizeof(vcard));
754 
755 	silc_client_attribute_del(client, conn,
756 				  SILC_ATTRIBUTE_USER_INFO, NULL);
757 	silc_client_attribute_del(client, conn,
758 				  SILC_ATTRIBUTE_SERVICE, NULL);
759 	silc_client_attribute_del(client, conn,
760 				  SILC_ATTRIBUTE_STATUS_MOOD, NULL);
761 	silc_client_attribute_del(client, conn,
762 				  SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
763 	silc_client_attribute_del(client, conn,
764 				  SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
765 	silc_client_attribute_del(client, conn,
766 				  SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
767 	silc_client_attribute_del(client, conn,
768 				  SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
769 	silc_client_attribute_del(client, conn,
770 				  SILC_ATTRIBUTE_TIMEZONE, NULL);
771 	silc_client_attribute_del(client, conn,
772 				  SILC_ATTRIBUTE_GEOLOCATION, NULL);
773 	silc_client_attribute_del(client, conn,
774 				  SILC_ATTRIBUTE_DEVICE_INFO, NULL);
775 
776 	/* Set mood */
777 	mask = 0;
778 	f = purple_request_fields_get_field(fields, "mood_normal");
779 	if (f && purple_request_field_bool_get_value(f))
780 		mask |= SILC_ATTRIBUTE_MOOD_NORMAL;
781 	f = purple_request_fields_get_field(fields, "mood_happy");
782 	if (f && purple_request_field_bool_get_value(f))
783 		mask |= SILC_ATTRIBUTE_MOOD_HAPPY;
784 	f = purple_request_fields_get_field(fields, "mood_sad");
785 	if (f && purple_request_field_bool_get_value(f))
786 		mask |= SILC_ATTRIBUTE_MOOD_SAD;
787 	f = purple_request_fields_get_field(fields, "mood_angry");
788 	if (f && purple_request_field_bool_get_value(f))
789 		mask |= SILC_ATTRIBUTE_MOOD_ANGRY;
790 	f = purple_request_fields_get_field(fields, "mood_jealous");
791 	if (f && purple_request_field_bool_get_value(f))
792 		mask |= SILC_ATTRIBUTE_MOOD_JEALOUS;
793 	f = purple_request_fields_get_field(fields, "mood_ashamed");
794 	if (f && purple_request_field_bool_get_value(f))
795 		mask |= SILC_ATTRIBUTE_MOOD_ASHAMED;
796 	f = purple_request_fields_get_field(fields, "mood_invincible");
797 	if (f && purple_request_field_bool_get_value(f))
798 		mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE;
799 	f = purple_request_fields_get_field(fields, "mood_inlove");
800 	if (f && purple_request_field_bool_get_value(f))
801 		mask |= SILC_ATTRIBUTE_MOOD_INLOVE;
802 	f = purple_request_fields_get_field(fields, "mood_sleepy");
803 	if (f && purple_request_field_bool_get_value(f))
804 		mask |= SILC_ATTRIBUTE_MOOD_SLEEPY;
805 	f = purple_request_fields_get_field(fields, "mood_bored");
806 	if (f && purple_request_field_bool_get_value(f))
807 		mask |= SILC_ATTRIBUTE_MOOD_BORED;
808 	f = purple_request_fields_get_field(fields, "mood_excited");
809 	if (f && purple_request_field_bool_get_value(f))
810 		mask |= SILC_ATTRIBUTE_MOOD_EXCITED;
811 	f = purple_request_fields_get_field(fields, "mood_anxious");
812 	if (f && purple_request_field_bool_get_value(f))
813 		mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS;
814 	silc_client_attribute_add(client, conn,
815 				  SILC_ATTRIBUTE_STATUS_MOOD,
816 				  SILC_32_TO_PTR(mask),
817 				  sizeof(SilcUInt32));
818 
819 	/* Set preferred contact */
820 	mask = 0;
821 	f = purple_request_fields_get_field(fields, "contact_chat");
822 	if (f && purple_request_field_bool_get_value(f))
823 		mask |= SILC_ATTRIBUTE_CONTACT_CHAT;
824 	f = purple_request_fields_get_field(fields, "contact_email");
825 	if (f && purple_request_field_bool_get_value(f))
826 		mask |= SILC_ATTRIBUTE_CONTACT_EMAIL;
827 	f = purple_request_fields_get_field(fields, "contact_call");
828 	if (f && purple_request_field_bool_get_value(f))
829 		mask |= SILC_ATTRIBUTE_CONTACT_CALL;
830 	f = purple_request_fields_get_field(fields, "contact_sms");
831 	if (f && purple_request_field_bool_get_value(f))
832 		mask |= SILC_ATTRIBUTE_CONTACT_SMS;
833 	f = purple_request_fields_get_field(fields, "contact_mms");
834 	if (f && purple_request_field_bool_get_value(f))
835 		mask |= SILC_ATTRIBUTE_CONTACT_MMS;
836 	f = purple_request_fields_get_field(fields, "contact_video");
837 	if (f && purple_request_field_bool_get_value(f))
838 		mask |= SILC_ATTRIBUTE_CONTACT_VIDEO;
839 	if (mask)
840 		silc_client_attribute_add(client, conn,
841 					  SILC_ATTRIBUTE_PREFERRED_CONTACT,
842 					  SILC_32_TO_PTR(mask),
843 					  sizeof(SilcUInt32));
844 
845 	/* Set status text */
846 	val = NULL;
847 	f = purple_request_fields_get_field(fields, "status_text");
848 	if (f)
849 		val = purple_request_field_string_get_value(f);
850 	if (val && *val)
851 		silc_client_attribute_add(client, conn,
852 					  SILC_ATTRIBUTE_STATUS_FREETEXT,
853 					  (void *)val, strlen(val));
854 
855 	/* Set vcard */
856 	val = NULL;
857 	f = purple_request_fields_get_field(fields, "vcard");
858 	if (f)
859 		val = purple_request_field_string_get_value(f);
860 	if (val && *val) {
861 		purple_account_set_string(sg->account, "vcard", val);
862 		tmp = silc_file_readfile(val, &tmp_len);
863 		if (tmp) {
864 			tmp[tmp_len] = 0;
865 			if (silc_vcard_decode((unsigned char *)tmp, tmp_len, &vcard))
866 				silc_client_attribute_add(client, conn,
867 							  SILC_ATTRIBUTE_USER_INFO,
868 							  (void *)&vcard,
869 							  sizeof(vcard));
870 		}
871 		silc_vcard_free(&vcard);
872 		silc_free(tmp);
873 	} else {
874 		purple_account_set_string(sg->account, "vcard", "");
875 	}
876 
877 #ifdef HAVE_SYS_UTSNAME_H
878 	/* Set device info */
879 	f = purple_request_fields_get_field(fields, "device");
880 	if (f && purple_request_field_bool_get_value(f)) {
881 		struct utsname u;
882 		if (!uname(&u)) {
883 			dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
884 			dev.version = u.release;
885 			dev.model = u.sysname;
886 			silc_client_attribute_add(client, conn,
887 						  SILC_ATTRIBUTE_DEVICE_INFO,
888 						  (void *)&dev, sizeof(dev));
889 		}
890 	}
891 #endif
892 
893 	/* Set timezone */
894 	val = NULL;
895 	f = purple_request_fields_get_field(fields, "timezone");
896 	if (f)
897 		val = purple_request_field_string_get_value(f);
898 	if (val && *val)
899 		silc_client_attribute_add(client, conn,
900 					  SILC_ATTRIBUTE_TIMEZONE,
901 					  (void *)val, strlen(val));
902 }
903 
904 static void
silcpurple_attrs(PurplePluginAction * action)905 silcpurple_attrs(PurplePluginAction *action)
906 {
907 	PurpleConnection *gc = (PurpleConnection *) action->context;
908 	SilcPurple sg = gc->proto_data;
909 	SilcClient client = sg->client;
910 	SilcClientConnection conn = sg->conn;
911 	PurpleRequestFields *fields;
912 	PurpleRequestFieldGroup *g;
913 	PurpleRequestField *f;
914 	SilcHashTable attrs;
915 	SilcAttributePayload attr;
916 	gboolean mnormal = TRUE, mhappy = FALSE, msad = FALSE,
917 		mangry = FALSE, mjealous = FALSE, mashamed = FALSE,
918 		minvincible = FALSE, minlove = FALSE, msleepy = FALSE,
919 		mbored = FALSE, mexcited = FALSE, manxious = FALSE;
920 	gboolean cemail = FALSE, ccall = FALSE, csms = FALSE,
921 		cmms = FALSE, cchat = TRUE, cvideo = FALSE;
922 	gboolean device = TRUE;
923 	char status[1024], tz[16];
924 
925 	sg = gc->proto_data;
926 	if (!sg)
927 		return;
928 
929 	memset(status, 0, sizeof(status));
930 
931 	attrs = silc_client_attributes_get(client, conn);
932 	if (attrs) {
933 		if (silc_hash_table_find(attrs,
934 					 SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_MOOD),
935 					 NULL, (void *)&attr)) {
936 			SilcUInt32 mood = 0;
937 			silc_attribute_get_object(attr, &mood, sizeof(mood));
938 			mnormal = !mood;
939 			mhappy = (mood & SILC_ATTRIBUTE_MOOD_HAPPY);
940 			msad = (mood & SILC_ATTRIBUTE_MOOD_SAD);
941 			mangry = (mood & SILC_ATTRIBUTE_MOOD_ANGRY);
942 			mjealous = (mood & SILC_ATTRIBUTE_MOOD_JEALOUS);
943 			mashamed = (mood & SILC_ATTRIBUTE_MOOD_ASHAMED);
944 			minvincible = (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE);
945 			minlove = (mood & SILC_ATTRIBUTE_MOOD_INLOVE);
946 			msleepy = (mood & SILC_ATTRIBUTE_MOOD_SLEEPY);
947 			mbored = (mood & SILC_ATTRIBUTE_MOOD_BORED);
948 			mexcited = (mood & SILC_ATTRIBUTE_MOOD_EXCITED);
949 			manxious = (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS);
950 		}
951 
952 		if (silc_hash_table_find(attrs,
953 					 SILC_32_TO_PTR(SILC_ATTRIBUTE_PREFERRED_CONTACT),
954 					 NULL, (void *)&attr)) {
955 			SilcUInt32 contact = 0;
956 			silc_attribute_get_object(attr, &contact, sizeof(contact));
957 			cemail = (contact & SILC_ATTRIBUTE_CONTACT_EMAIL);
958 			ccall = (contact & SILC_ATTRIBUTE_CONTACT_CALL);
959 			csms = (contact & SILC_ATTRIBUTE_CONTACT_SMS);
960 			cmms = (contact & SILC_ATTRIBUTE_CONTACT_MMS);
961 			cchat = (contact & SILC_ATTRIBUTE_CONTACT_CHAT);
962 			cvideo = (contact & SILC_ATTRIBUTE_CONTACT_VIDEO);
963 		}
964 
965 		if (silc_hash_table_find(attrs,
966 					 SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_FREETEXT),
967 					 NULL, (void *)&attr))
968 			silc_attribute_get_object(attr, &status, sizeof(status));
969 
970 		if (!silc_hash_table_find(attrs,
971 					  SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO),
972 					  NULL, (void *)&attr))
973 			device = FALSE;
974 	}
975 
976 	fields = purple_request_fields_new();
977 
978 	g = purple_request_field_group_new(NULL);
979 	f = purple_request_field_label_new("l3", _("Your Current Mood"));
980 	purple_request_field_group_add_field(g, f);
981 	f = purple_request_field_bool_new("mood_normal", _("Normal"), mnormal);
982 	purple_request_field_group_add_field(g, f);
983 	f = purple_request_field_bool_new("mood_happy", _("Happy"), mhappy);
984 	purple_request_field_group_add_field(g, f);
985 	f = purple_request_field_bool_new("mood_sad", _("Sad"), msad);
986 	purple_request_field_group_add_field(g, f);
987 	f = purple_request_field_bool_new("mood_angry", _("Angry"), mangry);
988 	purple_request_field_group_add_field(g, f);
989 	f = purple_request_field_bool_new("mood_jealous", _("Jealous"), mjealous);
990 	purple_request_field_group_add_field(g, f);
991 	f = purple_request_field_bool_new("mood_ashamed", _("Ashamed"), mashamed);
992 	purple_request_field_group_add_field(g, f);
993 	f = purple_request_field_bool_new("mood_invincible", _("Invincible"), minvincible);
994 	purple_request_field_group_add_field(g, f);
995 	f = purple_request_field_bool_new("mood_inlove", _("In love"), minlove);
996 	purple_request_field_group_add_field(g, f);
997 	f = purple_request_field_bool_new("mood_sleepy", _("Sleepy"), msleepy);
998 	purple_request_field_group_add_field(g, f);
999 	f = purple_request_field_bool_new("mood_bored", _("Bored"), mbored);
1000 	purple_request_field_group_add_field(g, f);
1001 	f = purple_request_field_bool_new("mood_excited", _("Excited"), mexcited);
1002 	purple_request_field_group_add_field(g, f);
1003 	f = purple_request_field_bool_new("mood_anxious", _("Anxious"), manxious);
1004 	purple_request_field_group_add_field(g, f);
1005 
1006 	f = purple_request_field_label_new("l4", _("\nYour Preferred Contact Methods"));
1007 	purple_request_field_group_add_field(g, f);
1008 	f = purple_request_field_bool_new("contact_chat", _("Chat"), cchat);
1009 	purple_request_field_group_add_field(g, f);
1010 	f = purple_request_field_bool_new("contact_email", _("Email"), cemail);
1011 	purple_request_field_group_add_field(g, f);
1012 	f = purple_request_field_bool_new("contact_call", _("Phone"), ccall);
1013 	purple_request_field_group_add_field(g, f);
1014 	f = purple_request_field_bool_new("contact_sms", _("SMS"), csms);
1015 	purple_request_field_group_add_field(g, f);
1016 	f = purple_request_field_bool_new("contact_mms", _("MMS"), cmms);
1017 	purple_request_field_group_add_field(g, f);
1018 	f = purple_request_field_bool_new("contact_video", _("Video conferencing"), cvideo);
1019 	purple_request_field_group_add_field(g, f);
1020 	purple_request_fields_add_group(fields, g);
1021 
1022 	g = purple_request_field_group_new(NULL);
1023 	f = purple_request_field_string_new("status_text", _("Your Current Status"),
1024 					  status[0] ? status : NULL, TRUE);
1025 	purple_request_field_group_add_field(g, f);
1026 	purple_request_fields_add_group(fields, g);
1027 
1028 	g = purple_request_field_group_new(NULL);
1029 #if 0
1030 	f = purple_request_field_label_new("l2", _("Online Services"));
1031 	purple_request_field_group_add_field(g, f);
1032 	f = purple_request_field_bool_new("services",
1033 					_("Let others see what services you are using"),
1034 					TRUE);
1035 	purple_request_field_group_add_field(g, f);
1036 #endif
1037 #ifdef HAVE_SYS_UTSNAME_H
1038 	f = purple_request_field_bool_new("device",
1039 					_("Let others see what computer you are using"),
1040 					device);
1041 	purple_request_field_group_add_field(g, f);
1042 #endif
1043 	purple_request_fields_add_group(fields, g);
1044 
1045 	g = purple_request_field_group_new(NULL);
1046 	f = purple_request_field_string_new("vcard", _("Your VCard File"),
1047 					  purple_account_get_string(sg->account, "vcard", ""),
1048 					  FALSE);
1049 	purple_request_field_group_add_field(g, f);
1050 
1051 	silc_timezone(tz, sizeof(tz));
1052 	f = purple_request_field_string_new("timezone", _("Timezone (UTC)"), tz, FALSE);
1053 	purple_request_field_group_add_field(g, f);
1054 	purple_request_fields_add_group(fields, g);
1055 
1056 	purple_request_fields(gc, _("User Online Status Attributes"),
1057 			    _("User Online Status Attributes"),
1058 			    _("You can let other users see your online status information "
1059 			      "and your personal information. Please fill the information "
1060 			      "you would like other users to see about yourself."),
1061 			    fields,
1062 			    _("OK"), G_CALLBACK(silcpurple_attrs_cb),
1063 			    _("Cancel"), G_CALLBACK(silcpurple_attrs_cancel),
1064 				gc->account, NULL, NULL, gc);
1065 }
1066 
1067 static void
silcpurple_detach(PurplePluginAction * action)1068 silcpurple_detach(PurplePluginAction *action)
1069 {
1070 	PurpleConnection *gc = (PurpleConnection *) action->context;
1071 	SilcPurple sg;
1072 
1073 	if (!gc)
1074 		return;
1075 	sg = gc->proto_data;
1076 	if (!sg)
1077 		return;
1078 
1079 	/* Call DETACH */
1080 	silc_client_command_call(sg->client, sg->conn, "DETACH");
1081 	sg->detaching = TRUE;
1082 }
1083 
1084 static void
silcpurple_view_motd(PurplePluginAction * action)1085 silcpurple_view_motd(PurplePluginAction *action)
1086 {
1087 	PurpleConnection *gc = (PurpleConnection *) action->context;
1088 	SilcPurple sg;
1089 	char *tmp;
1090 
1091 	if (!gc)
1092 		return;
1093 	sg = gc->proto_data;
1094 	if (!sg)
1095 		return;
1096 
1097 	if (!sg->motd) {
1098 		purple_notify_error(
1099 		     gc, _("Message of the Day"), _("No Message of the Day available"),
1100 		     _("There is no Message of the Day associated with this connection"));
1101 		return;
1102 	}
1103 
1104 	tmp = g_markup_escape_text(sg->motd, -1);
1105 	purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL,
1106 			      tmp, NULL, NULL);
1107 	g_free(tmp);
1108 }
1109 
1110 static void
silcpurple_create_keypair_cancel(PurpleConnection * gc,PurpleRequestFields * fields)1111 silcpurple_create_keypair_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
1112 {
1113 	/* Nothing */
1114 }
1115 
1116 static void
silcpurple_create_keypair_cb(PurpleConnection * gc,PurpleRequestFields * fields)1117 silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields)
1118 {
1119 	SilcPurple sg;
1120 	PurpleRequestField *f;
1121 	const char *val, *pkfile = NULL, *prfile = NULL;
1122 	const char *pass1 = NULL, *pass2 = NULL, *un = NULL, *hn = NULL;
1123 	const char *rn = NULL, *e = NULL, *o = NULL, *c = NULL;
1124 	char *identifier;
1125 	int keylen = SILCPURPLE_DEF_PKCS_LEN;
1126 	SilcPublicKey public_key;
1127 
1128 	sg = gc->proto_data;
1129 	if (!sg)
1130 		return;
1131 
1132 	val = NULL;
1133 	f = purple_request_fields_get_field(fields, "pass1");
1134 	if (f)
1135 		val = purple_request_field_string_get_value(f);
1136 	if (val && *val)
1137 		pass1 = val;
1138 	else
1139 		pass1 = "";
1140 	val = NULL;
1141 	f = purple_request_fields_get_field(fields, "pass2");
1142 	if (f)
1143 		val = purple_request_field_string_get_value(f);
1144 	if (val && *val)
1145 		pass2 = val;
1146 	else
1147 		pass2 = "";
1148 
1149 	if (!purple_strequal(pass1, pass2)) {
1150 		purple_notify_error(
1151 		     gc, _("Create New SILC Key Pair"), _("Passphrases do not match"), NULL);
1152 		return;
1153 	}
1154 
1155 	val = NULL;
1156 	f = purple_request_fields_get_field(fields, "key");
1157 	if (f)
1158 		val = purple_request_field_string_get_value(f);
1159 	if (val && *val)
1160 		keylen = atoi(val);
1161 	f = purple_request_fields_get_field(fields, "pkfile");
1162 	if (f)
1163 		pkfile = purple_request_field_string_get_value(f);
1164 	f = purple_request_fields_get_field(fields, "prfile");
1165 	if (f)
1166 		prfile = purple_request_field_string_get_value(f);
1167 
1168 	f = purple_request_fields_get_field(fields, "un");
1169 	if (f)
1170 		un = purple_request_field_string_get_value(f);
1171 	f = purple_request_fields_get_field(fields, "hn");
1172 	if (f)
1173 		hn = purple_request_field_string_get_value(f);
1174 	f = purple_request_fields_get_field(fields, "rn");
1175 	if (f)
1176 		rn = purple_request_field_string_get_value(f);
1177 	f = purple_request_fields_get_field(fields, "e");
1178 	if (f)
1179 		e = purple_request_field_string_get_value(f);
1180 	f = purple_request_fields_get_field(fields, "o");
1181 	if (f)
1182 		o = purple_request_field_string_get_value(f);
1183 	f = purple_request_fields_get_field(fields, "c");
1184 	if (f)
1185 		c = purple_request_field_string_get_value(f);
1186 
1187 	identifier = silc_pkcs_silc_encode_identifier((char *)un, (char *)hn,
1188 						      (char *)rn, (char *)e,
1189 						      (char *)o, (char *)c,
1190 						      NULL);
1191 
1192 	/* Create the key pair */
1193 	if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile,
1194 				  identifier, pass1, &public_key, NULL,
1195 				  FALSE)) {
1196 		purple_notify_error(
1197 		     gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL);
1198 		return;
1199 	}
1200 
1201 	silcpurple_show_public_key(sg, NULL, public_key, NULL, NULL);
1202 
1203 	silc_pkcs_public_key_free(public_key);
1204 	silc_free(identifier);
1205 }
1206 
1207 static void
silcpurple_create_keypair(PurplePluginAction * action)1208 silcpurple_create_keypair(PurplePluginAction *action)
1209 {
1210 	PurpleConnection *gc = (PurpleConnection *) action->context;
1211 	SilcPurple sg = gc->proto_data;
1212 	PurpleRequestFields *fields;
1213 	PurpleRequestFieldGroup *g;
1214 	PurpleRequestField *f;
1215 	const char *username, *realname;
1216 	char *hostname, **u;
1217 	char tmp[256], pkd[256], pkd2[256], prd[256], prd2[256];
1218 
1219 	username = purple_account_get_username(sg->account);
1220 	u = g_strsplit(username, "@", 2);
1221 	username = u[0];
1222 	realname = purple_account_get_user_info(sg->account);
1223 	hostname = silc_net_localhost();
1224 	g_snprintf(tmp, sizeof(tmp), "%s@%s", username, hostname);
1225 
1226 	g_snprintf(pkd2, sizeof(pkd2), "%s" G_DIR_SEPARATOR_S"public_key.pub", silcpurple_silcdir());
1227 	g_snprintf(prd2, sizeof(prd2), "%s" G_DIR_SEPARATOR_S"private_key.prv", silcpurple_silcdir());
1228 	g_snprintf(pkd, sizeof(pkd) - 1, "%s",
1229 		   purple_account_get_string(gc->account, "public-key", pkd2));
1230 	g_snprintf(prd, sizeof(prd) - 1, "%s",
1231 		   purple_account_get_string(gc->account, "private-key", prd2));
1232 
1233 	fields = purple_request_fields_new();
1234 
1235 	g = purple_request_field_group_new(NULL);
1236 	f = purple_request_field_string_new("key", _("Key length"), "2048", FALSE);
1237 	purple_request_field_group_add_field(g, f);
1238 	f = purple_request_field_string_new("pkfile", _("Public key file"), pkd, FALSE);
1239 	purple_request_field_group_add_field(g, f);
1240 	f = purple_request_field_string_new("prfile", _("Private key file"), prd, FALSE);
1241 	purple_request_field_group_add_field(g, f);
1242 	purple_request_fields_add_group(fields, g);
1243 
1244 	g = purple_request_field_group_new(NULL);
1245 	f = purple_request_field_string_new("un", _("Username"), username ? username : "", FALSE);
1246 	purple_request_field_group_add_field(g, f);
1247 	f = purple_request_field_string_new("hn", _("Hostname"), hostname ? hostname : "", FALSE);
1248 	purple_request_field_group_add_field(g, f);
1249 	f = purple_request_field_string_new("rn", _("Real name"), realname ? realname : "", FALSE);
1250 	purple_request_field_group_add_field(g, f);
1251 	f = purple_request_field_string_new("e", _("Email"), tmp, FALSE);
1252 	purple_request_field_group_add_field(g, f);
1253 	f = purple_request_field_string_new("o", _("Organization"), "", FALSE);
1254 	purple_request_field_group_add_field(g, f);
1255 	f = purple_request_field_string_new("c", _("Country"), "", FALSE);
1256 	purple_request_field_group_add_field(g, f);
1257 	purple_request_fields_add_group(fields, g);
1258 
1259 	g = purple_request_field_group_new(NULL);
1260 	f = purple_request_field_string_new("pass1", _("Passphrase"), "", FALSE);
1261 	purple_request_field_string_set_masked(f, TRUE);
1262 	purple_request_field_group_add_field(g, f);
1263 	f = purple_request_field_string_new("pass2", _("Passphrase (retype)"), "", FALSE);
1264 	purple_request_field_string_set_masked(f, TRUE);
1265 	purple_request_field_group_add_field(g, f);
1266 	purple_request_fields_add_group(fields, g);
1267 
1268 	purple_request_fields(gc, _("Create New SILC Key Pair"),
1269 			      _("Create New SILC Key Pair"), NULL, fields,
1270 			      _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
1271 			      _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
1272 			      gc->account, NULL, NULL, gc);
1273 
1274 	g_strfreev(u);
1275 	silc_free(hostname);
1276 }
1277 
1278 static void
silcpurple_change_pass(PurplePluginAction * action)1279 silcpurple_change_pass(PurplePluginAction *action)
1280 {
1281 	PurpleConnection *gc = (PurpleConnection *) action->context;
1282 	purple_account_request_change_password(purple_connection_get_account(gc));
1283 }
1284 
1285 static void
silcpurple_change_passwd(PurpleConnection * gc,const char * old,const char * new)1286 silcpurple_change_passwd(PurpleConnection *gc, const char *old, const char *new)
1287 {
1288 	char prd[256];
1289 	g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir());
1290 	silc_change_private_key_passphrase(purple_account_get_string(gc->account,
1291 								     "private-key",
1292 								     prd), old ? old : "", new ? new : "");
1293 }
1294 
1295 static void
silcpurple_show_set_info(PurplePluginAction * action)1296 silcpurple_show_set_info(PurplePluginAction *action)
1297 {
1298 	PurpleConnection *gc = (PurpleConnection *) action->context;
1299 	purple_account_request_change_user_info(purple_connection_get_account(gc));
1300 }
1301 
1302 static void
silcpurple_set_info(PurpleConnection * gc,const char * text)1303 silcpurple_set_info(PurpleConnection *gc, const char *text)
1304 {
1305 }
1306 
1307 static GList *
silcpurple_actions(PurplePlugin * plugin,gpointer context)1308 silcpurple_actions(PurplePlugin *plugin, gpointer context)
1309 {
1310 	GList *list = NULL;
1311 	PurplePluginAction *act;
1312 
1313 	act = purple_plugin_action_new(_("Online Status"),
1314 			silcpurple_attrs);
1315 	list = g_list_append(list, act);
1316 
1317 	act = purple_plugin_action_new(_("Detach From Server"),
1318 			silcpurple_detach);
1319 	list = g_list_append(list, act);
1320 
1321 	act = purple_plugin_action_new(_("View Message of the Day"),
1322 			silcpurple_view_motd);
1323 	list = g_list_append(list, act);
1324 
1325 	act = purple_plugin_action_new(_("Create SILC Key Pair..."),
1326 			silcpurple_create_keypair);
1327 	list = g_list_append(list, act);
1328 
1329 	act = purple_plugin_action_new(_("Change Password..."),
1330 			silcpurple_change_pass);
1331 	list = g_list_append(list, act);
1332 
1333 	act = purple_plugin_action_new(_("Set User Info..."),
1334 			silcpurple_show_set_info);
1335 	list = g_list_append(list, act);
1336 
1337 	return list;
1338 }
1339 
1340 
1341 /******************************* IM Routines *********************************/
1342 
1343 typedef struct {
1344 	char *nick;
1345 	char *message;
1346 	SilcUInt32 message_len;
1347 	SilcMessageFlags flags;
1348 	PurpleMessageFlags gflags;
1349 } *SilcPurpleIM;
1350 
1351 static void
silcpurple_send_im_resolved(SilcClient client,SilcClientConnection conn,SilcStatus status,SilcDList clients,void * context)1352 silcpurple_send_im_resolved(SilcClient client,
1353 			    SilcClientConnection conn,
1354 			    SilcStatus status,
1355 			    SilcDList clients,
1356 			    void *context)
1357 {
1358 	PurpleConnection *gc = client->application;
1359 	SilcPurple sg = gc->proto_data;
1360 	SilcPurpleIM im = context;
1361 	PurpleConversation *convo;
1362 	char tmp[256];
1363 	SilcClientEntry client_entry;
1364 	SilcDList list;
1365 	gboolean free_list = FALSE;
1366 
1367 	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick,
1368 						      sg->account);
1369 	if (!convo)
1370 		return;
1371 
1372 	if (!clients)
1373 		goto err;
1374 
1375 	if (silc_dlist_count(clients) > 1) {
1376 		/* Find the correct one. The im->nick might be a formatted nick
1377 		   so this will find the correct one. */
1378 		clients = silc_client_get_clients_local(client, conn,
1379 							im->nick, FALSE);
1380 		if (!clients)
1381 			goto err;
1382 
1383 		free_list = TRUE;
1384 	}
1385 
1386 	silc_dlist_start(clients);
1387 	client_entry = silc_dlist_get(clients);
1388 
1389 	/* Check for images */
1390 	if (im->gflags & PURPLE_MESSAGE_IMAGES) {
1391 		list = silcpurple_image_message(im->message, &im->flags);
1392 		if (list) {
1393 			/* Send one or more MIME message.  If more than one, they
1394 			   are MIME fragments due to over large message */
1395 			SilcBuffer buf;
1396 
1397 			silc_dlist_start(list);
1398 			while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
1399 				silc_client_send_private_message(client, conn,
1400 								 client_entry, im->flags, sg->sha1hash,
1401 								 buf->data,
1402 								 silc_buffer_len(buf));
1403 			silc_mime_partial_free(list);
1404 			purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
1405 					     im->message, 0, time(NULL));
1406 			goto out;
1407 		}
1408 	}
1409 
1410 	/* Send the message */
1411 	silc_client_send_private_message(client, conn, client_entry, im->flags,
1412 					 sg->sha1hash, (unsigned char *)im->message, im->message_len);
1413 	purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
1414 			     im->message, 0, time(NULL));
1415 	goto out;
1416 
1417  err:
1418 	g_snprintf(tmp, sizeof(tmp),
1419 		   _("User <I>%s</I> is not present in the network"), im->nick);
1420 	purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
1421 
1422  out:
1423 	if (free_list) {
1424 		silc_client_list_free(client, conn, clients);
1425 	}
1426 	g_free(im->nick);
1427 	g_free(im->message);
1428 	silc_free(im);
1429 }
1430 
1431 static int
silcpurple_send_im(PurpleConnection * gc,const char * who,const char * message,PurpleMessageFlags flags)1432 silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
1433 		   PurpleMessageFlags flags)
1434 {
1435 	SilcPurple sg = gc->proto_data;
1436 	SilcClient client = sg->client;
1437 	SilcClientConnection conn = sg->conn;
1438 	SilcDList clients;
1439 	SilcClientEntry client_entry;
1440 	SilcMessageFlags mflags;
1441 	char *msg, *tmp;
1442 	int ret = 0;
1443 	gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
1444 	SilcDList list;
1445 
1446 	if (!who || !message)
1447 		return 0;
1448 
1449 	mflags = SILC_MESSAGE_FLAG_UTF8;
1450 
1451 	tmp = msg = purple_unescape_html(message);
1452 
1453 	if (!g_ascii_strncasecmp(msg, "/me ", 4)) {
1454 		msg += 4;
1455 		if (!*msg) {
1456 			g_free(tmp);
1457 			return 0;
1458 		}
1459 		mflags |= SILC_MESSAGE_FLAG_ACTION;
1460 	} else if (strlen(msg) > 1 && msg[0] == '/') {
1461 		if (!silc_client_command_call(client, conn, msg + 1))
1462 			purple_notify_error(gc, _("Call Command"),
1463 					    _("Cannot call command"),
1464 					    _("Unknown command"));
1465 		g_free(tmp);
1466 		return 0;
1467 	}
1468 
1469 	if (sign)
1470 		mflags |= SILC_MESSAGE_FLAG_SIGNED;
1471 
1472 	/* Find client entry */
1473 	clients = silc_client_get_clients_local(client, conn, who, FALSE);
1474 	if (!clients) {
1475 		/* Resolve unknown user */
1476 		SilcPurpleIM im = silc_calloc(1, sizeof(*im));
1477 		if (!im) {
1478 			g_free(tmp);
1479 			return 0;
1480 		}
1481 		im->nick = g_strdup(who);
1482 		im->message = g_strdup(message);
1483 		im->message_len = strlen(im->message);
1484 		im->flags = mflags;
1485 		im->gflags = flags;
1486 		silc_client_get_clients(client, conn, who, NULL,
1487 					silcpurple_send_im_resolved, im);
1488 		g_free(tmp);
1489 		return 0;
1490 	}
1491 
1492 	silc_dlist_start(clients);
1493 	client_entry = silc_dlist_get(clients);
1494 
1495 	/* Check for images */
1496 	if (flags & PURPLE_MESSAGE_IMAGES) {
1497 		list = silcpurple_image_message(message, &mflags);
1498 		if (list) {
1499 			/* Send one or more MIME message.  If more than one, they
1500 			   are MIME fragments due to over large message */
1501 			SilcBuffer buf;
1502 
1503 			silc_dlist_start(list);
1504 			while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
1505 				ret =
1506 			 	silc_client_send_private_message(client, conn,
1507 								 client_entry, mflags, sg->sha1hash,
1508 								 buf->data,
1509 								 silc_buffer_len(buf));
1510 			silc_mime_partial_free(list);
1511 			g_free(tmp);
1512 			silc_client_list_free(client, conn, clients);
1513 			return ret;
1514 		}
1515 	}
1516 
1517 	/* Send private message directly */
1518 	ret = silc_client_send_private_message(client, conn, client_entry,
1519 					       mflags, sg->sha1hash,
1520 					       (unsigned char *)msg,
1521 					       strlen(msg));
1522 
1523 	g_free(tmp);
1524 	silc_client_list_free(client, conn, clients);
1525 	return ret;
1526 }
1527 
1528 
silcpurple_blist_node_menu(PurpleBlistNode * node)1529 static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) {
1530 	/* split this single menu building function back into the two
1531 	   original: one for buddies and one for chats */
1532 	if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
1533 		return silcpurple_chat_menu((PurpleChat *) node);
1534 	} else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
1535 		return silcpurple_buddy_menu((PurpleBuddy *) node);
1536 	} else {
1537 		g_return_val_if_reached(NULL);
1538 	}
1539 }
1540 
1541 /********************************* Commands **********************************/
1542 
silcpurple_cmd_chat_part(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1543 static PurpleCmdRet silcpurple_cmd_chat_part(PurpleConversation *conv,
1544 		const char *cmd, char **args, char **error, void *data)
1545 {
1546 	PurpleConnection *gc;
1547 	PurpleConversation *convo = conv;
1548 	int id = 0;
1549 
1550 	gc = purple_conversation_get_gc(conv);
1551 
1552 	if (gc == NULL)
1553 		return PURPLE_CMD_RET_FAILED;
1554 
1555 	if(args && args[0])
1556 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0],
1557 									gc->account);
1558 
1559 	if (convo != NULL)
1560 		id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo));
1561 
1562 	if (id == 0)
1563 		return PURPLE_CMD_RET_FAILED;
1564 
1565 	silcpurple_chat_leave(gc, id);
1566 
1567 	return PURPLE_CMD_RET_OK;
1568 
1569 }
1570 
silcpurple_cmd_chat_topic(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1571 static PurpleCmdRet silcpurple_cmd_chat_topic(PurpleConversation *conv,
1572 		const char *cmd, char **args, char **error, void *data)
1573 {
1574 	PurpleConnection *gc;
1575 	int id = 0;
1576 	char *buf, *tmp, *tmp2;
1577 	const char *topic;
1578 
1579 	gc = purple_conversation_get_gc(conv);
1580 	id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
1581 
1582 	if (gc == NULL || id == 0)
1583 		return PURPLE_CMD_RET_FAILED;
1584 
1585 	if (!args || !args[0]) {
1586 		topic = purple_conv_chat_get_topic (PURPLE_CONV_CHAT(conv));
1587 		if (topic) {
1588 			tmp = g_markup_escape_text(topic, -1);
1589 			tmp2 = purple_markup_linkify(tmp);
1590 			buf = g_strdup_printf(_("current topic is: %s"), tmp2);
1591 			g_free(tmp);
1592 			g_free(tmp2);
1593 		} else
1594 			buf = g_strdup(_("No topic is set"));
1595 		purple_conv_chat_write(PURPLE_CONV_CHAT(conv), gc->account->username, buf,
1596 							 PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
1597 		g_free(buf);
1598 
1599 	}
1600 
1601 	if (args && args[0] && (strlen(args[0]) > 255)) {
1602 		*error = g_strdup(_("Topic too long"));
1603 		return PURPLE_CMD_RET_FAILED;
1604 	}
1605 
1606 	silcpurple_chat_set_topic(gc, id, args ? args[0] : NULL);
1607 
1608 	return PURPLE_CMD_RET_OK;
1609 }
1610 
silcpurple_cmd_chat_join(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1611 static PurpleCmdRet silcpurple_cmd_chat_join(PurpleConversation *conv,
1612         const char *cmd, char **args, char **error, void *data)
1613 {
1614 	GHashTable *comp;
1615 
1616 	if(!args || !args[0])
1617 		return PURPLE_CMD_RET_FAILED;
1618 
1619 	comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
1620 
1621 	g_hash_table_replace(comp, "channel", args[0]);
1622 	if(args[1])
1623 		g_hash_table_replace(comp, "passphrase", args[1]);
1624 
1625 	silcpurple_chat_join(purple_conversation_get_gc(conv), comp);
1626 
1627 	g_hash_table_destroy(comp);
1628 	return PURPLE_CMD_RET_OK;
1629 }
1630 
silcpurple_cmd_chat_list(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1631 static PurpleCmdRet silcpurple_cmd_chat_list(PurpleConversation *conv,
1632         const char *cmd, char **args, char **error, void *data)
1633 {
1634 	PurpleConnection *gc;
1635 	gc = purple_conversation_get_gc(conv);
1636 	purple_roomlist_show_with_account(purple_connection_get_account(gc));
1637 	return PURPLE_CMD_RET_OK;
1638 }
1639 
silcpurple_cmd_whois(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1640 static PurpleCmdRet silcpurple_cmd_whois(PurpleConversation *conv,
1641 		const char *cmd, char **args, char **error, void *data)
1642 {
1643 	PurpleConnection *gc;
1644 
1645 	gc = purple_conversation_get_gc(conv);
1646 
1647 	if (gc == NULL)
1648 		return PURPLE_CMD_RET_FAILED;
1649 
1650 	silcpurple_get_info(gc, args[0]);
1651 
1652 	return PURPLE_CMD_RET_OK;
1653 }
1654 
silcpurple_cmd_msg(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1655 static PurpleCmdRet silcpurple_cmd_msg(PurpleConversation *conv,
1656 		const char *cmd, char **args, char **error, void *data)
1657 {
1658 	int ret;
1659 	PurpleConnection *gc;
1660 
1661 	gc = purple_conversation_get_gc(conv);
1662 
1663 	if (gc == NULL)
1664 		return PURPLE_CMD_RET_FAILED;
1665 
1666 	ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND);
1667 
1668 	if (ret)
1669 		return PURPLE_CMD_RET_OK;
1670 	else
1671 		return PURPLE_CMD_RET_FAILED;
1672 }
1673 
silcpurple_cmd_query(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1674 static PurpleCmdRet silcpurple_cmd_query(PurpleConversation *conv,
1675 		const char *cmd, char **args, char **error, void *data)
1676 {
1677 	int ret = 1;
1678 	PurpleConversation *convo;
1679 	PurpleConnection *gc;
1680 	PurpleAccount *account;
1681 
1682 	if (!args || !args[0]) {
1683 		*error = g_strdup(_("You must specify a nick"));
1684 		return PURPLE_CMD_RET_FAILED;
1685 	}
1686 
1687 	gc = purple_conversation_get_gc(conv);
1688 
1689 	if (gc == NULL)
1690 		return PURPLE_CMD_RET_FAILED;
1691 
1692 	account = purple_connection_get_account(gc);
1693 
1694 	convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, args[0]);
1695 
1696 	if (args[1]) {
1697 		ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND);
1698 		purple_conv_im_write(PURPLE_CONV_IM(convo), purple_connection_get_display_name(gc),
1699 				args[1], PURPLE_MESSAGE_SEND, time(NULL));
1700 	}
1701 
1702 	if (ret)
1703 		return PURPLE_CMD_RET_OK;
1704 	else
1705 		return PURPLE_CMD_RET_FAILED;
1706 }
1707 
silcpurple_cmd_motd(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1708 static PurpleCmdRet silcpurple_cmd_motd(PurpleConversation *conv,
1709 		const char *cmd, char **args, char **error, void *data)
1710 {
1711 	PurpleConnection *gc;
1712 	SilcPurple sg;
1713 	char *tmp;
1714 
1715 	gc = purple_conversation_get_gc(conv);
1716 
1717 	if (gc == NULL)
1718 		return PURPLE_CMD_RET_FAILED;
1719 
1720 	sg = gc->proto_data;
1721 
1722 	if (sg == NULL)
1723 		return PURPLE_CMD_RET_FAILED;
1724 
1725 	if (!sg->motd) {
1726 		*error = g_strdup(_("There is no Message of the Day associated with this connection"));
1727 		return PURPLE_CMD_RET_FAILED;
1728 	}
1729 
1730 	tmp = g_markup_escape_text(sg->motd, -1);
1731 	purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL,
1732 			tmp, NULL, NULL);
1733 	g_free(tmp);
1734 
1735 	return PURPLE_CMD_RET_OK;
1736 }
1737 
silcpurple_cmd_detach(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1738 static PurpleCmdRet silcpurple_cmd_detach(PurpleConversation *conv,
1739 		const char *cmd, char **args, char **error, void *data)
1740 {
1741 	PurpleConnection *gc;
1742 	SilcPurple sg;
1743 
1744 	gc = purple_conversation_get_gc(conv);
1745 
1746 	if (gc == NULL)
1747 		return PURPLE_CMD_RET_FAILED;
1748 
1749 	sg = gc->proto_data;
1750 
1751 	if (sg == NULL)
1752 		return PURPLE_CMD_RET_FAILED;
1753 
1754 	silc_client_command_call(sg->client, sg->conn, "DETACH");
1755 	sg->detaching = TRUE;
1756 
1757 	return PURPLE_CMD_RET_OK;
1758 }
1759 
silcpurple_cmd_cmode(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1760 static PurpleCmdRet silcpurple_cmd_cmode(PurpleConversation *conv,
1761 		const char *cmd, char **args, char **error, void *data)
1762 {
1763 	PurpleConnection *gc;
1764 	SilcPurple sg;
1765 	SilcChannelEntry channel;
1766 	char *silccmd, *silcargs, *msg, tmp[256];
1767 	const char *chname;
1768 
1769 	gc = purple_conversation_get_gc(conv);
1770 
1771 	if (gc == NULL || !args || gc->proto_data == NULL)
1772 		return PURPLE_CMD_RET_FAILED;
1773 
1774 	sg = gc->proto_data;
1775 
1776 	if (args[0])
1777 		chname = args[0];
1778 	else
1779 		chname = purple_conversation_get_name(conv);
1780 
1781 	if (!args[1]) {
1782 		channel = silc_client_get_channel(sg->client, sg->conn,
1783 										  (char *)chname);
1784 		if (!channel) {
1785 			*error = g_strdup_printf(_("channel %s not found"), chname);
1786 			return PURPLE_CMD_RET_FAILED;
1787 		}
1788 		if (channel->mode) {
1789 			silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
1790 			msg = g_strdup_printf(_("channel modes for %s: %s"), chname, tmp);
1791 		} else {
1792 			msg = g_strdup_printf(_("no channel modes are set on %s"), chname);
1793 		}
1794 		purple_conv_chat_write(PURPLE_CONV_CHAT(conv), "",
1795 							 msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
1796 		g_free(msg);
1797 		return PURPLE_CMD_RET_OK;
1798 	}
1799 
1800 	silcargs = g_strjoinv(" ", args);
1801 	silccmd = g_strconcat(cmd, " ", silcargs, NULL);
1802 	g_free(silcargs);
1803 	if (!silc_client_command_call(sg->client, sg->conn, silccmd)) {
1804 		g_free(silccmd);
1805 		*error = g_strdup_printf(_("Failed to set cmodes for %s"), args[0]);
1806 		return PURPLE_CMD_RET_FAILED;
1807 	}
1808 	g_free(silccmd);
1809 
1810 	return PURPLE_CMD_RET_OK;
1811 }
1812 
silcpurple_cmd_generic(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1813 static PurpleCmdRet silcpurple_cmd_generic(PurpleConversation *conv,
1814 		const char *cmd, char **args, char **error, void *data)
1815 {
1816 	PurpleConnection *gc;
1817 	SilcPurple sg;
1818 	char *silccmd, *silcargs;
1819 
1820 	gc = purple_conversation_get_gc(conv);
1821 
1822 	if (gc == NULL)
1823 		return PURPLE_CMD_RET_FAILED;
1824 
1825 	sg = gc->proto_data;
1826 
1827 	if (sg == NULL)
1828 		return PURPLE_CMD_RET_FAILED;
1829 
1830 	silcargs = g_strjoinv(" ", args);
1831 	silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL);
1832 	g_free(silcargs);
1833 	if (!silc_client_command_call(sg->client, sg->conn, silccmd)) {
1834 		g_free(silccmd);
1835 		*error = g_strdup_printf(_("Unknown command: %s, (may be a client bug)"), cmd);
1836 		return PURPLE_CMD_RET_FAILED;
1837 	}
1838 	g_free(silccmd);
1839 
1840 	return PURPLE_CMD_RET_OK;
1841 }
1842 
silcpurple_cmd_quit(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1843 static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
1844 		const char *cmd, char **args, char **error, void *data)
1845 {
1846 	PurpleConnection *gc;
1847 	SilcPurple sg;
1848 	GHashTable *ui_info;
1849 	const char *ui_name = NULL, *ui_website = NULL;
1850 	char *quit_msg;
1851 
1852 	gc = purple_conversation_get_gc(conv);
1853 
1854 	if (gc == NULL)
1855 		return PURPLE_CMD_RET_FAILED;
1856 
1857 	sg = gc->proto_data;
1858 
1859 	if (sg == NULL)
1860 		return PURPLE_CMD_RET_FAILED;
1861 
1862 	ui_info = purple_core_get_ui_info();
1863 
1864 	if(ui_info) {
1865 		ui_name = g_hash_table_lookup(ui_info, "name");
1866 		ui_website = g_hash_table_lookup(ui_info, "website");
1867 	}
1868 
1869 	if(!ui_name || !ui_website) {
1870 		ui_name = "Pidgin";
1871 		ui_website = PURPLE_WEBSITE;
1872 	}
1873 	quit_msg = g_strdup_printf(_("Download %s: %s"),
1874 							   ui_name, ui_website);
1875 
1876 	silc_client_command_call(sg->client, sg->conn, NULL,
1877 				 "QUIT", (args && args[0]) ? args[0] : quit_msg, NULL);
1878 	g_free(quit_msg);
1879 
1880 	return PURPLE_CMD_RET_OK;
1881 }
1882 
silcpurple_cmd_call(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1883 static PurpleCmdRet silcpurple_cmd_call(PurpleConversation *conv,
1884 		const char *cmd, char **args, char **error, void *data)
1885 {
1886 	PurpleConnection *gc;
1887 	SilcPurple sg;
1888 
1889 	gc = purple_conversation_get_gc(conv);
1890 
1891 	if (gc == NULL)
1892 		return PURPLE_CMD_RET_FAILED;
1893 
1894 	sg = gc->proto_data;
1895 
1896 	if (sg == NULL)
1897 		return PURPLE_CMD_RET_FAILED;
1898 
1899 	if (!silc_client_command_call(sg->client, sg->conn, args[0])) {
1900 		*error = g_strdup_printf(_("Unknown command: %s"), args[0]);
1901 		return PURPLE_CMD_RET_FAILED;
1902 	}
1903 
1904 	return PURPLE_CMD_RET_OK;
1905 }
1906 
1907 
1908 /************************** Plugin Initialization ****************************/
1909 
1910 static void
silcpurple_register_commands(void)1911 silcpurple_register_commands(void)
1912 {
1913 	purple_cmd_register("part", "w", PURPLE_CMD_P_PRPL,
1914 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
1915 			PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
1916 			"prpl-silc", silcpurple_cmd_chat_part, _("part [channel]:  Leave the chat"), NULL);
1917 	purple_cmd_register("leave", "w", PURPLE_CMD_P_PRPL,
1918 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
1919 			PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
1920 			"prpl-silc", silcpurple_cmd_chat_part, _("leave [channel]:  Leave the chat"), NULL);
1921 	purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL,
1922 			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1923 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc",
1924 			silcpurple_cmd_chat_topic, _("topic [&lt;new topic&gt;]:  View or change the topic"), NULL);
1925 	purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL,
1926 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
1927 			PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
1928 			"prpl-silc", silcpurple_cmd_chat_join,
1929 			_("join &lt;channel&gt; [&lt;password&gt;]:  Join a chat on this network"), NULL);
1930 	purple_cmd_register("list", "", PURPLE_CMD_P_PRPL,
1931 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1932 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc",
1933 			silcpurple_cmd_chat_list, _("list:  List channels on this network"), NULL);
1934 	purple_cmd_register("whois", "w", PURPLE_CMD_P_PRPL,
1935 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1936 			"prpl-silc",
1937 			silcpurple_cmd_whois, _("whois &lt;nick&gt;:  View nick's information"), NULL);
1938 	purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL,
1939 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1940 			"prpl-silc", silcpurple_cmd_msg,
1941 			_("msg &lt;nick&gt; &lt;message&gt;:  Send a private message to a user"), NULL);
1942 	purple_cmd_register("query", "ws", PURPLE_CMD_P_PRPL,
1943 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1944 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_query,
1945 			_("query &lt;nick&gt; [&lt;message&gt;]:  Send a private message to a user"), NULL);
1946 	purple_cmd_register("motd", "", PURPLE_CMD_P_PRPL,
1947 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1948 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_motd,
1949 			_("motd:  View the server's Message Of The Day"), NULL);
1950 	purple_cmd_register("detach", "", PURPLE_CMD_P_PRPL,
1951 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1952 			"prpl-silc", silcpurple_cmd_detach,
1953 			_("detach:  Detach this session"), NULL);
1954 	purple_cmd_register("quit", "s", PURPLE_CMD_P_PRPL,
1955 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1956 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_quit,
1957 			_("quit [message]:  Disconnect from the server, with an optional message"), NULL);
1958 	purple_cmd_register("call", "s", PURPLE_CMD_P_PRPL,
1959 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1960 			"prpl-silc", silcpurple_cmd_call,
1961 			_("call &lt;command&gt;:  Call any silc client command"), NULL);
1962 	/* These below just get passed through for the silc client library to deal
1963 	 * with */
1964 	purple_cmd_register("kill", "ws", PURPLE_CMD_P_PRPL,
1965 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1966 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1967 			_("kill &lt;nick&gt; [-pubkey|&lt;reason&gt;]:  Kill nick"), NULL);
1968 	purple_cmd_register("nick", "w", PURPLE_CMD_P_PRPL,
1969 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1970 			"prpl-silc", silcpurple_cmd_generic,
1971 			_("nick &lt;newnick&gt;:  Change your nickname"), NULL);
1972 	purple_cmd_register("whowas", "ww", PURPLE_CMD_P_PRPL,
1973 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1974 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1975 			_("whowas &lt;nick&gt;:  View nick's information"), NULL);
1976 	purple_cmd_register("cmode", "wws", PURPLE_CMD_P_PRPL,
1977 			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1978 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_cmode,
1979 			_("cmode &lt;channel&gt; [+|-&lt;modes&gt;] [arguments]:  Change or display channel modes"), NULL);
1980 	purple_cmd_register("cumode", "wws", PURPLE_CMD_P_PRPL,
1981 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1982 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1983 			_("cumode &lt;channel&gt; +|-&lt;modes&gt; &lt;nick&gt;:  Change nick's modes on channel"), NULL);
1984 	purple_cmd_register("umode", "w", PURPLE_CMD_P_PRPL,
1985 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1986 			"prpl-silc", silcpurple_cmd_generic,
1987 			_("umode &lt;usermodes&gt;:  Set your modes in the network"), NULL);
1988 	purple_cmd_register("oper", "s", PURPLE_CMD_P_PRPL,
1989 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1990 			"prpl-silc", silcpurple_cmd_generic,
1991 			_("oper &lt;nick&gt; [-pubkey]:  Get server operator privileges"), NULL);
1992 	purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL,
1993 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1994 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1995 			_("invite &lt;channel&gt; [-|+]&lt;nick&gt;:  invite nick or add/remove from channel invite list"), NULL);
1996 	purple_cmd_register("kick", "wws", PURPLE_CMD_P_PRPL,
1997 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1998 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1999 			_("kick &lt;channel&gt; &lt;nick&gt; [comment]:  Kick client from channel"), NULL);
2000 	purple_cmd_register("info", "w", PURPLE_CMD_P_PRPL,
2001 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
2002 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
2003 			_("info [server]:  View server administrative details"), NULL);
2004 	purple_cmd_register("ban", "ww", PURPLE_CMD_P_PRPL,
2005 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
2006 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
2007 			_("ban [&lt;channel&gt; +|-&lt;nick&gt;]:  Ban client from channel"), NULL);
2008 	purple_cmd_register("getkey", "w", PURPLE_CMD_P_PRPL,
2009 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
2010 			"prpl-silc", silcpurple_cmd_generic,
2011 			_("getkey &lt;nick|server&gt;:  Retrieve client's or server's public key"), NULL);
2012 	purple_cmd_register("stats", "", PURPLE_CMD_P_PRPL,
2013 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
2014 			"prpl-silc", silcpurple_cmd_generic,
2015 			_("stats:  View server and network statistics"), NULL);
2016 	purple_cmd_register("ping", "", PURPLE_CMD_P_PRPL,
2017 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
2018 			"prpl-silc", silcpurple_cmd_generic,
2019 			_("ping:  Send PING to the connected server"), NULL);
2020 #if 0 /* Purple doesn't handle these yet */
2021 	purple_cmd_register("users", "w", PURPLE_CMD_P_PRPL,
2022 			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
2023 			"prpl-silc", silcpurple_cmd_users,
2024 			_("users &lt;channel&gt;:  List users in channel"));
2025 	purple_cmd_register("names", "ww", PURPLE_CMD_P_PRPL,
2026 			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
2027 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_names,
2028 			_("names [-count|-ops|-halfops|-voices|-normal] &lt;channel(s)&gt;:  List specific users in channel(s)"));
2029 #endif
2030 }
2031 
2032 static PurpleWhiteboardPrplOps silcpurple_wb_ops =
2033 {
2034 	silcpurple_wb_start,
2035 	silcpurple_wb_end,
2036 	silcpurple_wb_get_dimensions,
2037 	silcpurple_wb_set_dimensions,
2038 	silcpurple_wb_get_brush,
2039 	silcpurple_wb_set_brush,
2040 	silcpurple_wb_send,
2041 	silcpurple_wb_clear,
2042 
2043 	/* padding */
2044 	NULL,
2045 	NULL,
2046 	NULL,
2047 	NULL
2048 };
2049 
2050 static PurplePluginProtocolInfo prpl_info =
2051 {
2052 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
2053 	OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE |
2054 	OPT_PROTO_SLASH_COMMANDS_NATIVE,
2055 	NULL,					/* user_splits */
2056 	NULL,					/* protocol_options */
2057 	{"jpeg,gif,png,bmp", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
2058 	silcpurple_list_icon,	                /* list_icon */
2059 	NULL,					/* list_emblems */
2060 	silcpurple_status_text,			/* status_text */
2061 	silcpurple_tooltip_text,		/* tooltip_text */
2062 	silcpurple_away_states,			/* away_states */
2063 	silcpurple_blist_node_menu,		/* blist_node_menu */
2064 	silcpurple_chat_info,			/* chat_info */
2065 	silcpurple_chat_info_defaults,	        /* chat_info_defaults */
2066 	silcpurple_login,			/* login */
2067 	silcpurple_close,			/* close */
2068 	silcpurple_send_im,			/* send_im */
2069 	silcpurple_set_info,			/* set_info */
2070 	NULL,					/* send_typing */
2071 	silcpurple_get_info,			/* get_info */
2072 	silcpurple_set_status,			/* set_status */
2073 	silcpurple_idle_set,			/* set_idle */
2074 	silcpurple_change_passwd,		/* change_passwd */
2075 	silcpurple_add_buddy,			/* add_buddy */
2076 	NULL,					/* add_buddies */
2077 	silcpurple_remove_buddy,		/* remove_buddy */
2078 	NULL,					/* remove_buddies */
2079 	NULL,					/* add_permit */
2080 	NULL,					/* add_deny */
2081 	NULL,					/* rem_permit */
2082 	NULL,					/* rem_deny */
2083 	NULL,					/* set_permit_deny */
2084 	silcpurple_chat_join,			/* join_chat */
2085 	NULL,					/* reject_chat */
2086 	silcpurple_get_chat_name,		/* get_chat_name */
2087 	silcpurple_chat_invite,			/* chat_invite */
2088 	silcpurple_chat_leave,			/* chat_leave */
2089 	NULL,					/* chat_whisper */
2090 	silcpurple_chat_send,			/* chat_send */
2091 	silcpurple_keepalive,			/* keepalive */
2092 	NULL,					/* register_user */
2093 	NULL,					/* get_cb_info */
2094 	NULL,					/* get_cb_away */
2095 	NULL,					/* alias_buddy */
2096 	NULL,					/* group_buddy */
2097 	NULL,					/* rename_group */
2098 	NULL,					/* buddy_free */
2099 	NULL,					/* convo_closed */
2100 	NULL,					/* normalize */
2101 	silcpurple_buddy_set_icon,		/* set_buddy_icon */
2102 	NULL,					/* remove_group */
2103 	NULL,					/* get_cb_real_name */
2104 	silcpurple_chat_set_topic,		/* set_chat_topic */
2105 	NULL,					/* find_blist_chat */
2106 	silcpurple_roomlist_get_list,	        /* roomlist_get_list */
2107 	silcpurple_roomlist_cancel,		/* roomlist_cancel */
2108 	NULL,				        /* roomlist_expand_category */
2109 	NULL,					/* can_receive_file */
2110 	silcpurple_ftp_send_file,		/* send_file */
2111 	silcpurple_ftp_new_xfer,		/* new_xfer */
2112 	NULL,					/* offline_message */
2113 	&silcpurple_wb_ops,			/* whiteboard_prpl_ops */
2114 	NULL,					/* send_raw */
2115 	NULL,				        /* roomlist_room_serialize */
2116 	NULL,				        /* unregister_user */
2117 	NULL,				        /* send_attention */
2118 	NULL,				        /* get_attention_types */
2119 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
2120 	NULL,				        /* get_account_text_table */
2121 	NULL,				        /* initiate_media */
2122 	NULL,				        /* get_media_caps */
2123 	NULL,				        /* get_moods */
2124 	NULL,				        /* set_public_alias */
2125 	NULL,				        /* get_public_alias */
2126 	NULL,				        /* add_buddy_with_invite */
2127 	NULL,				        /* add_buddies_with_invite */
2128 	NULL,				        /* get_cb_alias */
2129 	NULL,				        /* chat_can_receive_file */
2130 	NULL,				        /* chat_send_file */
2131 };
2132 
2133 static PurplePluginInfo info =
2134 {
2135 	PURPLE_PLUGIN_MAGIC,
2136 	PURPLE_MAJOR_VERSION,
2137 	PURPLE_MINOR_VERSION,
2138 	PURPLE_PLUGIN_PROTOCOL,			/**< type           */
2139 	NULL,					/**< ui_requirement */
2140 	0,					/**< flags          */
2141 	NULL,					/**< dependencies   */
2142 	PURPLE_PRIORITY_DEFAULT,		/**< priority       */
2143 
2144 	"prpl-silc",				/**< id             */
2145 	"SILC",					/**< name           */
2146 	"1.1",					/**< version        */
2147 	/**  summary        */
2148 	N_("SILC Protocol Plugin"),
2149 	/**  description    */
2150 	N_("Secure Internet Live Conferencing (SILC) Protocol"),
2151 	"Pekka Riikonen",			/**< author         */
2152 	"http://silcnet.org/",			/**< homepage       */
2153 
2154 	NULL,					/**< load           */
2155 	NULL,					/**< unload         */
2156 	NULL,					/**< destroy        */
2157 
2158 	NULL,					/**< ui_info        */
2159 	&prpl_info,				/**< extra_info     */
2160 	NULL,					/**< prefs_info     */
2161 	silcpurple_actions,
2162 
2163 	/* padding */
2164 	NULL,
2165 	NULL,
2166 	NULL,
2167 	NULL
2168 };
2169 
2170 #if 0
2171 static SilcBool silcpurple_debug_cb(char *file, char *function, int line,
2172 		char *message, void *context)
2173 {
2174 	purple_debug_info("SILC", "%s:%d:%s - %s\n", file ? file : "(null)", line, function ? function : "(null)", message ? message : "(null)");
2175 	return TRUE;
2176 }
2177 #endif
2178 
2179 static void
init_plugin(PurplePlugin * plugin)2180 init_plugin(PurplePlugin *plugin)
2181 {
2182 	PurpleAccountOption *option;
2183 	PurpleAccountUserSplit *split;
2184 	char tmp[256];
2185 	int i;
2186 	PurpleKeyValuePair *kvp;
2187 	GList *list = NULL;
2188 
2189 	silc_plugin = plugin;
2190 
2191 	split = purple_account_user_split_new(_("Network"), "silcnet.org", '@');
2192 	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2193 
2194 	/* Account options */
2195 	option = purple_account_option_string_new(_("Connect server"),
2196 						  "server",
2197 						  "silc.silcnet.org");
2198 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2199 	option = purple_account_option_int_new(_("Port"), "port", 706);
2200 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2201 	g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
2202 	option = purple_account_option_string_new(_("Public Key file"),
2203 						  "public-key", tmp);
2204 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2205 	g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
2206 	option = purple_account_option_string_new(_("Private Key file"),
2207 						  "private-key", tmp);
2208 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2209 
2210 	for (i = 0; silc_default_ciphers[i].name; i++) {
2211 		kvp = g_new0(PurpleKeyValuePair, 1);
2212 		kvp->key = g_strdup(silc_default_ciphers[i].name);
2213 		kvp->value = g_strdup(silc_default_ciphers[i].name);
2214 		list = g_list_append(list, kvp);
2215 	}
2216 	option = purple_account_option_list_new(_("Cipher"), "cipher", list);
2217 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2218 
2219 	list = NULL;
2220 	for (i = 0; silc_default_hmacs[i].name; i++) {
2221 		kvp = g_new0(PurpleKeyValuePair, 1);
2222 		kvp->key = g_strdup(silc_default_hmacs[i].name);
2223 		kvp->value = g_strdup(silc_default_hmacs[i].name);
2224 		list = g_list_append(list, kvp);
2225 	}
2226 	option = purple_account_option_list_new(_("HMAC"), "hmac", list);
2227 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2228 
2229 	option = purple_account_option_bool_new(_("Use Perfect Forward Secrecy"),
2230 						"pfs", FALSE);
2231 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2232 
2233 	option = purple_account_option_bool_new(_("Public key authentication"),
2234 						"pubkey-auth", FALSE);
2235 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2236 	option = purple_account_option_bool_new(_("Block IMs without Key Exchange"),
2237 						"block-ims", FALSE);
2238 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2239 	option = purple_account_option_bool_new(_("Block messages to whiteboard"),
2240 						"block-wb", FALSE);
2241 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2242 	option = purple_account_option_bool_new(_("Automatically open whiteboard"),
2243 						"open-wb", FALSE);
2244 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2245 	option = purple_account_option_bool_new(_("Digitally sign and verify all messages"),
2246 						"sign-verify", FALSE);
2247 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2248 
2249 	purple_prefs_remove("/plugins/prpl/silc");
2250 
2251 	silc_log_set_callback(SILC_LOG_ERROR, silcpurple_log_error, NULL);
2252 	silcpurple_register_commands();
2253 
2254 #if 0
2255 silc_log_debug(TRUE);
2256 silc_log_set_debug_string("*client*");
2257 silc_log_quick(TRUE);
2258 silc_log_set_debug_callbacks(silcpurple_debug_cb, NULL, NULL, NULL);
2259 #endif
2260 
2261 }
2262 
2263 PURPLE_INIT_PLUGIN(silc, init_plugin, info);
2264