1 /**
2  * @file gntaccount.c GNT Account API
3  * @ingroup finch
4  */
5 
6 /* finch
7  *
8  * Finch is the legal property of its developers, whose names are too numerous
9  * to list here.  Please refer to the COPYRIGHT file distributed with this
10  * source distribution.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
25  */
26 #include <internal.h>
27 
28 #include <gnt.h>
29 #include <gntbox.h>
30 #include <gntbutton.h>
31 #include <gntcheckbox.h>
32 #include <gntcombobox.h>
33 #include <gntentry.h>
34 #include <gntlabel.h>
35 #include <gntline.h>
36 #include <gnttree.h>
37 #include <gntutils.h>
38 #include <gntwindow.h>
39 
40 #include "finch.h"
41 
42 #include <account.h>
43 #include <accountopt.h>
44 #include <connection.h>
45 #include <notify.h>
46 #include <plugin.h>
47 #include <request.h>
48 #include <savedstatuses.h>
49 
50 #include "gntaccount.h"
51 #include "gntblist.h"
52 
53 #include <string.h>
54 
55 typedef struct
56 {
57 	GntWidget *window;
58 	GntWidget *tree;
59 } FinchAccountList;
60 
61 static FinchAccountList accounts;
62 
63 typedef struct
64 {
65 	PurpleAccount *account;          /* NULL for a new account */
66 
67 	GntWidget *window;
68 
69 	GntWidget *protocol;
70 	GntWidget *username;
71 	GntWidget *password;
72 	GntWidget *alias;
73 
74 	GntWidget *splits;
75 	GList *split_entries;
76 
77 	GList *prpl_entries;
78 	GntWidget *prpls;
79 
80 	GntWidget *newmail;
81 	GntWidget *remember;
82 	GntWidget *regserver;
83 } AccountEditDialog;
84 
85 /* This is necessary to close an edit-dialog when an account is deleted */
86 static GList *accountdialogs;
87 
88 static void
account_add(PurpleAccount * account)89 account_add(PurpleAccount *account)
90 {
91 	gnt_tree_add_choice(GNT_TREE(accounts.tree), account,
92 			gnt_tree_create_row(GNT_TREE(accounts.tree),
93 				purple_account_get_username(account),
94 				purple_account_get_protocol_name(account)),
95 			NULL, NULL);
96 	gnt_tree_set_choice(GNT_TREE(accounts.tree), account,
97 			purple_account_get_enabled(account, FINCH_UI));
98 }
99 
100 static void
edit_dialog_destroy(AccountEditDialog * dialog)101 edit_dialog_destroy(AccountEditDialog *dialog)
102 {
103 	accountdialogs = g_list_remove(accountdialogs, dialog);
104 	g_list_free(dialog->prpl_entries);
105 	g_list_free(dialog->split_entries);
106 	g_free(dialog);
107 }
108 
109 static void
save_account_cb(AccountEditDialog * dialog)110 save_account_cb(AccountEditDialog *dialog)
111 {
112 	PurpleAccount *account;
113 	PurplePlugin *plugin;
114 	PurplePluginProtocolInfo *prplinfo;
115 	const char *value;
116 	GString *username;
117 
118 	/* XXX: Do some error checking first. */
119 
120 	plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol));
121 	prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
122 
123 	/* Username && user-splits */
124 	value = gnt_entry_get_text(GNT_ENTRY(dialog->username));
125 
126 	if (value == NULL || *value == '\0')
127 	{
128 		purple_notify_error(NULL, _("Error"),
129 				dialog->account ? _("Account was not modified") : _("Account was not added"),
130 				_("Username of an account must be non-empty."));
131 		return;
132 	}
133 
134 	username = g_string_new(value);
135 
136 	if (prplinfo != NULL)
137 	{
138 		GList *iter, *entries;
139 		for (iter = prplinfo->user_splits, entries = dialog->split_entries;
140 				iter && entries; iter = iter->next, entries = entries->next)
141 		{
142 			PurpleAccountUserSplit *split = iter->data;
143 			GntWidget *entry = entries->data;
144 
145 			value = gnt_entry_get_text(GNT_ENTRY(entry));
146 			if (value == NULL || *value == '\0')
147 				value = purple_account_user_split_get_default_value(split);
148 			g_string_append_printf(username, "%c%s",
149 					purple_account_user_split_get_separator(split),
150 					value);
151 		}
152 	}
153 
154 	if (dialog->account == NULL)
155 	{
156 		account = purple_account_new(username->str, purple_plugin_get_id(plugin));
157 		purple_accounts_add(account);
158 	}
159 	else
160 	{
161 		account = dialog->account;
162 
163 		/* Protocol */
164 		if (purple_account_is_disconnected(account)) {
165 			purple_account_set_protocol_id(account, purple_plugin_get_id(plugin));
166 			purple_account_set_username(account, username->str);
167 		} else {
168 			const char *old = purple_account_get_protocol_id(account);
169 			char *oldprpl;
170 			if (!purple_strequal(old, purple_plugin_get_id(plugin))) {
171 				purple_notify_error(NULL, _("Error"), _("Account was not modified"),
172 						_("The account's protocol cannot be changed while it is connected to the server."));
173 				return;
174 			}
175 
176 			oldprpl = g_strdup(purple_normalize(account, purple_account_get_username(account)));
177 			if (g_utf8_collate(oldprpl, purple_normalize(account, username->str))) {
178 				purple_notify_error(NULL, _("Error"), _("Account was not modified"),
179 						_("The account's username cannot be changed while it is connected to the server."));
180 				g_free(oldprpl);
181 				return;
182 			}
183 			g_free(oldprpl);
184 			purple_account_set_username(account, username->str);
185 		}
186 	}
187 	g_string_free(username, TRUE);
188 
189 	/* Alias */
190 	value = gnt_entry_get_text(GNT_ENTRY(dialog->alias));
191 	purple_account_set_alias(account, value);
192 
193 	/* Remember password and password */
194 	purple_account_set_remember_password(account,
195 			gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->remember)));
196 	value = gnt_entry_get_text(GNT_ENTRY(dialog->password));
197 	if (value && *value)
198 		purple_account_set_password(account, value);
199 	else
200 		purple_account_set_password(account, NULL);
201 
202 	/* Mail notification */
203 	purple_account_set_check_mail(account,
204 			gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->newmail)));
205 
206 	/* Protocol options */
207 	if (prplinfo)
208 	{
209 		GList *iter, *entries;
210 
211 		for (iter = prplinfo->protocol_options, entries = dialog->prpl_entries;
212 				iter && entries; iter = iter->next, entries = entries->next)
213 		{
214 			PurpleAccountOption *option = iter->data;
215 			GntWidget *entry = entries->data;
216 			PurplePrefType type = purple_account_option_get_type(option);
217 			const char *setting = purple_account_option_get_setting(option);
218 
219 			if (type == PURPLE_PREF_STRING)
220 			{
221 				const char *value = gnt_entry_get_text(GNT_ENTRY(entry));
222 				purple_account_set_string(account, setting, value);
223 			}
224 			else if (type == PURPLE_PREF_INT)
225 			{
226 				const char *str = gnt_entry_get_text(GNT_ENTRY(entry));
227 				int value = 0;
228 				if (str)
229 					value = atoi(str);
230 				purple_account_set_int(account, setting, value);
231 			}
232 			else if (type == PURPLE_PREF_BOOLEAN)
233 			{
234 				gboolean value = gnt_check_box_get_checked(GNT_CHECK_BOX(entry));
235 				purple_account_set_bool(account, setting, value);
236 			}
237 			else if (type == PURPLE_PREF_STRING_LIST)
238 			{
239 				gchar *value = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(entry));
240 				purple_account_set_string(account, setting, value);
241 			}
242 			else
243 			{
244 				g_assert_not_reached();
245 			}
246 		}
247 	}
248 
249 	/* XXX: Proxy options */
250 
251 	if (accounts.window && accounts.tree) {
252 		gnt_tree_set_selected(GNT_TREE(accounts.tree), account);
253 		gnt_box_give_focus_to_child(GNT_BOX(accounts.window), accounts.tree);
254 	}
255 
256 	if (prplinfo && prplinfo->register_user &&
257 			gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->regserver))) {
258 		purple_account_register(account);
259 	} else if (dialog->account == NULL) {
260 		/* This is a new account. Set it to the current status. */
261 		/* Xerox from gtkaccount.c :D */
262 		const PurpleSavedStatus *saved_status;
263 		saved_status = purple_savedstatus_get_current();
264 		if (saved_status != NULL) {
265 			purple_savedstatus_activate_for_account(saved_status, account);
266 			purple_account_set_enabled(account, FINCH_UI, TRUE);
267 		}
268 	}
269 
270 	/* In case of a new account, the 'Accounts' window is updated from the account-added
271 	 * callback. In case of changes in an existing account, we need to explicitly do it
272 	 * here.
273 	 */
274 	if (dialog->account != NULL && accounts.window) {
275 		gnt_tree_change_text(GNT_TREE(accounts.tree), dialog->account,
276 				0, purple_account_get_username(dialog->account));
277 		gnt_tree_change_text(GNT_TREE(accounts.tree), dialog->account,
278 				1, purple_account_get_protocol_name(dialog->account));
279 	}
280 
281 	gnt_widget_destroy(dialog->window);
282 }
283 
284 static void
update_user_splits(AccountEditDialog * dialog)285 update_user_splits(AccountEditDialog *dialog)
286 {
287 	GntWidget *hbox;
288 	PurplePlugin *plugin;
289 	PurplePluginProtocolInfo *prplinfo;
290 	GList *iter, *entries;
291 	char *username = NULL;
292 
293 	if (dialog->splits)
294 	{
295 		gnt_box_remove_all(GNT_BOX(dialog->splits));
296 		g_list_free(dialog->split_entries);
297 	}
298 	else
299 	{
300 		dialog->splits = gnt_vbox_new(FALSE);
301 		gnt_box_set_pad(GNT_BOX(dialog->splits), 0);
302 		gnt_box_set_fill(GNT_BOX(dialog->splits), TRUE);
303 	}
304 
305 	dialog->split_entries = NULL;
306 
307 	plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol));
308 	if (!plugin)
309 		return;
310 	prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
311 
312 	username = dialog->account ? g_strdup(purple_account_get_username(dialog->account)) : NULL;
313 
314 	for (iter = prplinfo->user_splits; iter; iter = iter->next)
315 	{
316 		PurpleAccountUserSplit *split = iter->data;
317 		GntWidget *entry;
318 		char *buf;
319 
320 		hbox = gnt_hbox_new(TRUE);
321 		gnt_box_add_widget(GNT_BOX(dialog->splits), hbox);
322 
323 		buf = g_strdup_printf("%s:", purple_account_user_split_get_text(split));
324 		gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(buf));
325 
326 		entry = gnt_entry_new(NULL);
327 		gnt_box_add_widget(GNT_BOX(hbox), entry);
328 
329 		dialog->split_entries = g_list_append(dialog->split_entries, entry);
330 		g_free(buf);
331 	}
332 
333 	for (iter = g_list_last(prplinfo->user_splits), entries = g_list_last(dialog->split_entries);
334 			iter && entries; iter = iter->prev, entries = entries->prev)
335 	{
336 		GntWidget *entry = entries->data;
337 		PurpleAccountUserSplit *split = iter->data;
338 		const char *value = NULL;
339 		char *s;
340 
341 		if (dialog->account)
342 		{
343 			if(purple_account_user_split_get_reverse(split))
344 				s = strrchr(username, purple_account_user_split_get_separator(split));
345 			else
346 				s = strchr(username, purple_account_user_split_get_separator(split));
347 
348 			if (s != NULL)
349 			{
350 				*s = '\0';
351 				s++;
352 				value = s;
353 			}
354 		}
355 		if (value == NULL)
356 			value = purple_account_user_split_get_default_value(split);
357 
358 		if (value != NULL)
359 			gnt_entry_set_text(GNT_ENTRY(entry), value);
360 	}
361 
362 	if (username != NULL)
363 		gnt_entry_set_text(GNT_ENTRY(dialog->username), username);
364 
365 	g_free(username);
366 }
367 
368 static void
add_protocol_options(AccountEditDialog * dialog)369 add_protocol_options(AccountEditDialog *dialog)
370 {
371 	PurplePlugin *plugin;
372 	PurplePluginProtocolInfo *prplinfo;
373 	GList *iter;
374 	GntWidget *vbox, *box;
375 	PurpleAccount *account;
376 
377 	if (dialog->prpls)
378 		gnt_box_remove_all(GNT_BOX(dialog->prpls));
379 	else
380 	{
381 		dialog->prpls = vbox = gnt_vbox_new(FALSE);
382 		gnt_box_set_pad(GNT_BOX(vbox), 0);
383 		gnt_box_set_alignment(GNT_BOX(vbox), GNT_ALIGN_LEFT);
384 		gnt_box_set_fill(GNT_BOX(vbox), TRUE);
385 	}
386 
387 	if (dialog->prpl_entries)
388 	{
389 		g_list_free(dialog->prpl_entries);
390 		dialog->prpl_entries = NULL;
391 	}
392 
393 	vbox = dialog->prpls;
394 
395 	plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol));
396 	if (!plugin)
397 		return;
398 
399 	prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
400 
401 	account = dialog->account;
402 
403 	for (iter = prplinfo->protocol_options; iter; iter = iter->next)
404 	{
405 		PurpleAccountOption *option = iter->data;
406 		PurplePrefType type = purple_account_option_get_type(option);
407 
408 		box = gnt_hbox_new(TRUE);
409 		gnt_box_set_pad(GNT_BOX(box), 0);
410 		gnt_box_add_widget(GNT_BOX(vbox), box);
411 
412 		if (type == PURPLE_PREF_BOOLEAN)
413 		{
414 			GntWidget *widget = gnt_check_box_new(purple_account_option_get_text(option));
415 			gnt_box_add_widget(GNT_BOX(box), widget);
416 			dialog->prpl_entries = g_list_append(dialog->prpl_entries, widget);
417 
418 			if (account)
419 				gnt_check_box_set_checked(GNT_CHECK_BOX(widget),
420 						purple_account_get_bool(account,
421 							purple_account_option_get_setting(option),
422 							purple_account_option_get_default_bool(option)));
423 			else
424 				gnt_check_box_set_checked(GNT_CHECK_BOX(widget),
425 						purple_account_option_get_default_bool(option));
426 		}
427 		else
428 		{
429 			gnt_box_add_widget(GNT_BOX(box),
430 					gnt_label_new(purple_account_option_get_text(option)));
431 
432 			if (type == PURPLE_PREF_STRING_LIST)
433 			{
434 				GntWidget *combo = gnt_combo_box_new();
435 				GList *opt_iter = purple_account_option_get_list(option);
436 				const char *dv = purple_account_option_get_default_list_value(option);
437 				const char *active = dv;
438 
439 				if (account)
440 					active = purple_account_get_string(account,
441 						purple_account_option_get_setting(option), dv);
442 
443 				gnt_box_add_widget(GNT_BOX(box), combo);
444 				dialog->prpl_entries = g_list_append(dialog->prpl_entries, combo);
445 
446 				for ( ; opt_iter; opt_iter = opt_iter->next)
447 				{
448 					PurpleKeyValuePair *kvp = opt_iter->data;
449 					gnt_combo_box_add_data(GNT_COMBO_BOX(combo), kvp->value, kvp->key);
450 
451 					if (purple_strequal(kvp->value, active))
452 						gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), kvp->value);
453 				}
454 			}
455 			else
456 			{
457 				GntWidget *entry = gnt_entry_new(NULL);
458 				gnt_box_add_widget(GNT_BOX(box), entry);
459 				dialog->prpl_entries = g_list_append(dialog->prpl_entries, entry);
460 
461 				if (type == PURPLE_PREF_STRING)
462 				{
463 					const char *dv = purple_account_option_get_default_string(option);
464 
465 					if (account)
466 						gnt_entry_set_text(GNT_ENTRY(entry),
467 								purple_account_get_string(account,
468 									purple_account_option_get_setting(option), dv));
469 					else
470 						gnt_entry_set_text(GNT_ENTRY(entry), dv);
471 				}
472 				else if (type == PURPLE_PREF_INT)
473 				{
474 					char str[32];
475 					int value = purple_account_option_get_default_int(option);
476 					if (account)
477 						value = purple_account_get_int(account,
478 								purple_account_option_get_setting(option), value);
479 					snprintf(str, sizeof(str), "%d", value);
480 					gnt_entry_set_flag(GNT_ENTRY(entry), GNT_ENTRY_FLAG_INT);
481 					gnt_entry_set_text(GNT_ENTRY(entry), str);
482 				}
483 				else
484 				{
485 					g_assert_not_reached();
486 				}
487 			}
488 		}
489 	}
490 
491 	/* Show the registration checkbox only in a new account dialog,
492 	 * and when the selected prpl has the support for it. */
493 	gnt_widget_set_visible(dialog->regserver, account == NULL &&
494 			prplinfo->register_user != NULL);
495 }
496 
497 static void
update_user_options(AccountEditDialog * dialog)498 update_user_options(AccountEditDialog *dialog)
499 {
500 	PurplePlugin *plugin;
501 	PurplePluginProtocolInfo *prplinfo;
502 
503 	plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol));
504 	if (!plugin)
505 		return;
506 
507 	prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
508 
509 	if (dialog->newmail == NULL)
510 		dialog->newmail = gnt_check_box_new(_("New mail notifications"));
511 	if (dialog->account)
512 		gnt_check_box_set_checked(GNT_CHECK_BOX(dialog->newmail),
513 				purple_account_get_check_mail(dialog->account));
514 	if (!prplinfo || !(prplinfo->options & OPT_PROTO_MAIL_CHECK))
515 		gnt_widget_set_visible(dialog->newmail, FALSE);
516 	else
517 		gnt_widget_set_visible(dialog->newmail, TRUE);
518 
519 	if (dialog->remember == NULL)
520 		dialog->remember = gnt_check_box_new(_("Remember password"));
521 	if (dialog->account)
522 		gnt_check_box_set_checked(GNT_CHECK_BOX(dialog->remember),
523 				purple_account_get_remember_password(dialog->account));
524 }
525 
526 static void
prpl_changed_cb(GntWidget * combo,PurplePlugin * old,PurplePlugin * new,AccountEditDialog * dialog)527 prpl_changed_cb(GntWidget *combo, PurplePlugin *old, PurplePlugin *new, AccountEditDialog *dialog)
528 {
529 	update_user_splits(dialog);
530 	add_protocol_options(dialog);
531 	update_user_options(dialog);  /* This may not be necessary here */
532 	gnt_box_readjust(GNT_BOX(dialog->window));
533 	gnt_widget_draw(dialog->window);
534 }
535 
536 static void
edit_account(PurpleAccount * account)537 edit_account(PurpleAccount *account)
538 {
539 	GntWidget *window, *hbox;
540 	GntWidget *combo, *button, *entry;
541 	GList *list, *iter;
542 	AccountEditDialog *dialog;
543 	PurplePlugin *plugin;
544 
545 	if (account)
546 	{
547 		GList *iter;
548 		for (iter = accountdialogs; iter; iter = iter->next)
549 		{
550 			AccountEditDialog *dlg = iter->data;
551 			if (dlg->account == account)
552 				return;
553 		}
554 	}
555 
556 	list = purple_plugins_get_protocols();
557 	if (list == NULL) {
558 		purple_notify_error(NULL, _("Error"),
559 				_("There are no protocol plugins installed."),
560 				_("(You probably forgot to 'make install'.)"));
561 		return;
562 	}
563 
564 	dialog = g_new0(AccountEditDialog, 1);
565 	accountdialogs = g_list_prepend(accountdialogs, dialog);
566 
567 	dialog->window = window = gnt_vbox_new(FALSE);
568 	dialog->account = account;
569 	gnt_box_set_toplevel(GNT_BOX(window), TRUE);
570 	gnt_box_set_title(GNT_BOX(window), account ? _("Modify Account") : _("New Account"));
571 	gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
572 	gnt_box_set_pad(GNT_BOX(window), 0);
573 	gnt_widget_set_name(window, "edit-account");
574 	gnt_box_set_fill(GNT_BOX(window), TRUE);
575 
576 	hbox = gnt_hbox_new(TRUE);
577 	gnt_box_set_pad(GNT_BOX(hbox), 0);
578 	gnt_box_add_widget(GNT_BOX(window), hbox);
579 
580 	dialog->protocol = combo = gnt_combo_box_new();
581 	for (iter = list; iter; iter = iter->next)
582 	{
583 		gnt_combo_box_add_data(GNT_COMBO_BOX(combo), iter->data,
584 				((PurplePlugin*)iter->data)->info->name);
585 	}
586 
587 	plugin = purple_plugins_find_with_id(purple_account_get_protocol_id(account));
588 
589 	if (account && plugin)
590 		gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), plugin);
591 	else
592 		gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), list->data);
593 
594 	g_signal_connect(G_OBJECT(combo), "selection-changed", G_CALLBACK(prpl_changed_cb), dialog);
595 
596 	gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Protocol:")));
597 	gnt_box_add_widget(GNT_BOX(hbox), combo);
598 
599 	hbox = gnt_hbox_new(TRUE);
600 	gnt_box_set_pad(GNT_BOX(hbox), 0);
601 	gnt_box_add_widget(GNT_BOX(window), hbox);
602 
603 	dialog->username = entry = gnt_entry_new(NULL);
604 	gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Username:")));
605 	gnt_box_add_widget(GNT_BOX(hbox), entry);
606 
607 	/* User splits */
608 	update_user_splits(dialog);
609 	gnt_box_add_widget(GNT_BOX(window), dialog->splits);
610 
611 	hbox = gnt_hbox_new(TRUE);
612 	gnt_box_set_pad(GNT_BOX(hbox), 0);
613 	gnt_box_add_widget(GNT_BOX(window), hbox);
614 
615 	dialog->password = entry = gnt_entry_new(NULL);
616 	gnt_entry_set_masked(GNT_ENTRY(entry), TRUE);
617 	gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Password:")));
618 	gnt_box_add_widget(GNT_BOX(hbox), entry);
619 	if (account)
620 		gnt_entry_set_text(GNT_ENTRY(entry), purple_account_get_password(account));
621 
622 	hbox = gnt_hbox_new(TRUE);
623 	gnt_box_set_pad(GNT_BOX(hbox), 0);
624 	gnt_box_add_widget(GNT_BOX(window), hbox);
625 
626 	dialog->alias = entry = gnt_entry_new(NULL);
627 	gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Alias:")));
628 	gnt_box_add_widget(GNT_BOX(hbox), entry);
629 	if (account)
630 		gnt_entry_set_text(GNT_ENTRY(entry), purple_account_get_alias(account));
631 
632 	/* User options */
633 	update_user_options(dialog);
634 	gnt_box_add_widget(GNT_BOX(window), dialog->remember);
635 	gnt_box_add_widget(GNT_BOX(window), dialog->newmail);
636 
637 	/* Register checkbox */
638 	dialog->regserver = gnt_check_box_new(_("Create this account on the server"));
639 	gnt_box_add_widget(GNT_BOX(window), dialog->regserver);
640 
641 	gnt_box_add_widget(GNT_BOX(window), gnt_line_new(FALSE));
642 
643 	/* The advanced box */
644 	add_protocol_options(dialog);
645 	gnt_box_add_widget(GNT_BOX(window), dialog->prpls);
646 
647 	/* TODO: Add proxy options */
648 
649 	/* The button box */
650 	hbox = gnt_hbox_new(FALSE);
651 	gnt_box_add_widget(GNT_BOX(window), hbox);
652 	gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID);
653 
654 	button = gnt_button_new(_("Cancel"));
655 	gnt_box_add_widget(GNT_BOX(hbox), button);
656 	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), window);
657 
658 	button = gnt_button_new(_("Save"));
659 	gnt_box_add_widget(GNT_BOX(hbox), button);
660 	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(save_account_cb), dialog);
661 
662 	g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(edit_dialog_destroy), dialog);
663 
664 	gnt_widget_show(window);
665 	gnt_box_readjust(GNT_BOX(window));
666 	gnt_widget_draw(window);
667 }
668 
669 static void
add_account_cb(GntWidget * widget,gpointer null)670 add_account_cb(GntWidget *widget, gpointer null)
671 {
672 	edit_account(NULL);
673 }
674 
675 static void
modify_account_cb(GntWidget * widget,GntTree * tree)676 modify_account_cb(GntWidget *widget, GntTree *tree)
677 {
678 	PurpleAccount *account = gnt_tree_get_selection_data(tree);
679 	if (!account)
680 		return;
681 	edit_account(account);
682 }
683 
684 static void
really_delete_account(PurpleAccount * account)685 really_delete_account(PurpleAccount *account)
686 {
687 	GList *iter;
688 	for (iter = accountdialogs; iter; iter = iter->next)
689 	{
690 		AccountEditDialog *dlg = iter->data;
691 		if (dlg->account == account)
692 		{
693 			gnt_widget_destroy(dlg->window);
694 			break;
695 		}
696 	}
697 	purple_request_close_with_handle(account); /* Close any other opened delete window */
698 	purple_accounts_delete(account);
699 }
700 
701 static void
delete_account_cb(GntWidget * widget,GntTree * tree)702 delete_account_cb(GntWidget *widget, GntTree *tree)
703 {
704 	PurpleAccount *account;
705 	char *prompt;
706 
707 	account  = gnt_tree_get_selection_data(tree);
708 	if (!account)
709 		return;
710 
711 	prompt = g_strdup_printf(_("Are you sure you want to delete %s?"),
712 			purple_account_get_username(account));
713 
714 	purple_request_action(account, _("Delete Account"), prompt, NULL,
715 						  PURPLE_DEFAULT_ACTION_NONE,
716 						  account, NULL, NULL, account, 2,
717 						  _("Delete"), really_delete_account,
718 						  _("Cancel"), NULL);
719 	g_free(prompt);
720 }
721 
722 static void
account_toggled(GntWidget * widget,void * key,gpointer null)723 account_toggled(GntWidget *widget, void *key, gpointer null)
724 {
725 	PurpleAccount *account = key;
726 	gboolean enabled = gnt_tree_get_choice(GNT_TREE(widget), key);
727 
728 	if (enabled)
729 		purple_savedstatus_activate_for_account(purple_savedstatus_get_current(),
730 												account);
731 
732 	purple_account_set_enabled(account, FINCH_UI, enabled);
733 }
734 
735 static gboolean
account_list_key_pressed_cb(GntWidget * widget,const char * text,gpointer null)736 account_list_key_pressed_cb(GntWidget *widget, const char *text, gpointer null)
737 {
738 	GntTree *tree = GNT_TREE(widget);
739 	PurpleAccount *account = gnt_tree_get_selection_data(tree);
740 	int move, pos, count;
741 	GList *accounts;
742 
743 	if (!account)
744 		return FALSE;
745 
746 	switch (text[0]) {
747 		case '-':
748 			move = -1;
749 			break;
750 		case '=':
751 			move = 2;  /* XXX: This seems to be a bug in libpurple */
752 			break;
753 		default:
754 			return FALSE;
755 	}
756 
757 	accounts = purple_accounts_get_all();
758 	count = g_list_length(accounts);
759 	pos = g_list_index(accounts, account);
760 	pos = (move + pos + count + 1) % (count + 1);
761 	purple_accounts_reorder(account, pos);
762 
763 	/* I don't like this, but recreating the entire list seems to be
764 	 * the easiest way of doing it */
765 	gnt_tree_remove_all(tree);
766 	accounts = purple_accounts_get_all();
767 	for (; accounts; accounts = accounts->next)
768 		account_add(accounts->data);
769 	gnt_tree_set_selected(tree, account);
770 
771 	return TRUE;
772 }
773 
774 static void
reset_accounts_win(GntWidget * widget,gpointer null)775 reset_accounts_win(GntWidget *widget, gpointer null)
776 {
777 	accounts.window = NULL;
778 	accounts.tree = NULL;
779 }
780 
finch_accounts_show_all()781 void finch_accounts_show_all()
782 {
783 	GList *iter;
784 	GntWidget *box, *button;
785 
786 	if (accounts.window) {
787 		gnt_window_present(accounts.window);
788 		return;
789 	}
790 
791 	accounts.window = gnt_vbox_new(FALSE);
792 	gnt_box_set_toplevel(GNT_BOX(accounts.window), TRUE);
793 	gnt_box_set_title(GNT_BOX(accounts.window), _("Accounts"));
794 	gnt_box_set_pad(GNT_BOX(accounts.window), 0);
795 	gnt_box_set_alignment(GNT_BOX(accounts.window), GNT_ALIGN_MID);
796 	gnt_widget_set_name(accounts.window, "accounts");
797 
798 	gnt_box_add_widget(GNT_BOX(accounts.window),
799 			gnt_label_new(_("You can enable/disable accounts from the following list.")));
800 
801 	gnt_box_add_widget(GNT_BOX(accounts.window), gnt_line_new(FALSE));
802 
803 	accounts.tree = gnt_tree_new_with_columns(2);
804 	gnt_widget_set_has_border(accounts.tree, FALSE);
805 
806 	for (iter = purple_accounts_get_all(); iter; iter = iter->next)
807 	{
808 		PurpleAccount *account = iter->data;
809 		account_add(account);
810 	}
811 
812 	g_signal_connect(G_OBJECT(accounts.tree), "toggled", G_CALLBACK(account_toggled), NULL);
813 	g_signal_connect(G_OBJECT(accounts.tree), "key_pressed", G_CALLBACK(account_list_key_pressed_cb), NULL);
814 
815 	gnt_tree_set_col_width(GNT_TREE(accounts.tree), 0, 40);
816 	gnt_tree_set_col_width(GNT_TREE(accounts.tree), 1, 10);
817 	gnt_box_add_widget(GNT_BOX(accounts.window), accounts.tree);
818 
819 	gnt_box_add_widget(GNT_BOX(accounts.window), gnt_line_new(FALSE));
820 
821 	box = gnt_hbox_new(FALSE);
822 
823 	button = gnt_button_new(_("Add"));
824 	gnt_box_add_widget(GNT_BOX(box), button);
825 	gnt_util_set_trigger_widget(GNT_WIDGET(accounts.tree), GNT_KEY_INS, button);
826 	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(add_account_cb), NULL);
827 
828 	button = gnt_button_new(_("Modify"));
829 	gnt_box_add_widget(GNT_BOX(box), button);
830 	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(modify_account_cb), accounts.tree);
831 
832 	button = gnt_button_new(_("Delete"));
833 	gnt_box_add_widget(GNT_BOX(box), button);
834 	gnt_util_set_trigger_widget(GNT_WIDGET(accounts.tree), GNT_KEY_DEL, button);
835 	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(delete_account_cb), accounts.tree);
836 
837 	gnt_box_add_widget(GNT_BOX(accounts.window), box);
838 
839 	g_signal_connect(G_OBJECT(accounts.window), "destroy", G_CALLBACK(reset_accounts_win), NULL);
840 
841 	gnt_widget_show(accounts.window);
842 }
843 
finch_account_dialog_show(PurpleAccount * account)844 void finch_account_dialog_show(PurpleAccount *account)
845 {
846 	edit_account(account);
847 }
848 
849 static gpointer
finch_accounts_get_handle(void)850 finch_accounts_get_handle(void)
851 {
852 	static int handle;
853 
854 	return &handle;
855 }
856 
857 static void
account_added_callback(PurpleAccount * account)858 account_added_callback(PurpleAccount *account)
859 {
860 	if (accounts.window == NULL)
861 		return;
862 	account_add(account);
863 	gnt_widget_draw(accounts.tree);
864 }
865 
866 static void
account_removed_callback(PurpleAccount * account)867 account_removed_callback(PurpleAccount *account)
868 {
869 	if (accounts.window == NULL)
870 		return;
871 
872 	gnt_tree_remove(GNT_TREE(accounts.tree), account);
873 }
874 
875 static void
account_abled_cb(PurpleAccount * account,gpointer user_data)876 account_abled_cb(PurpleAccount *account, gpointer user_data)
877 {
878 	if (accounts.window == NULL)
879 		return;
880 	gnt_tree_set_choice(GNT_TREE(accounts.tree), account,
881 			GPOINTER_TO_INT(user_data));
882 }
883 
finch_accounts_init()884 void finch_accounts_init()
885 {
886 	GList *iter;
887 
888 	purple_signal_connect(purple_accounts_get_handle(), "account-added",
889 			finch_accounts_get_handle(), PURPLE_CALLBACK(account_added_callback),
890 			NULL);
891 	purple_signal_connect(purple_accounts_get_handle(), "account-removed",
892 			finch_accounts_get_handle(), PURPLE_CALLBACK(account_removed_callback),
893 			NULL);
894 	purple_signal_connect(purple_accounts_get_handle(), "account-disabled",
895 			finch_accounts_get_handle(),
896 			PURPLE_CALLBACK(account_abled_cb), GINT_TO_POINTER(FALSE));
897 	purple_signal_connect(purple_accounts_get_handle(), "account-enabled",
898 			finch_accounts_get_handle(),
899 			PURPLE_CALLBACK(account_abled_cb), GINT_TO_POINTER(TRUE));
900 
901 	iter = purple_accounts_get_all();
902 	if (iter) {
903 		for (; iter; iter = iter->next) {
904 			if (purple_account_get_enabled(iter->data, FINCH_UI))
905 				break;
906 		}
907 		if (!iter)
908 			finch_accounts_show_all();
909 	} else {
910 		edit_account(NULL);
911 		finch_accounts_show_all();
912 	}
913 }
914 
finch_accounts_uninit()915 void finch_accounts_uninit()
916 {
917 	if (accounts.window)
918 		gnt_widget_destroy(accounts.window);
919 }
920 
921 /* The following uiops stuff are copied from gtkaccount.c */
922 typedef struct
923 {
924 	PurpleAccount *account;
925 	char *username;
926 	char *alias;
927 } AddUserData;
928 
929 static char *
make_info(PurpleAccount * account,PurpleConnection * gc,const char * remote_user,const char * id,const char * alias,const char * msg)930 make_info(PurpleAccount *account, PurpleConnection *gc, const char *remote_user,
931           const char *id, const char *alias, const char *msg)
932 {
933 	if (msg != NULL && *msg == '\0')
934 		msg = NULL;
935 
936 	return g_strdup_printf(_("%s%s%s%s has made %s his or her buddy%s%s"),
937 	                       remote_user,
938 	                       (alias != NULL ? " ("  : ""),
939 	                       (alias != NULL ? alias : ""),
940 	                       (alias != NULL ? ")"   : ""),
941 	                       (id != NULL
942 	                        ? id
943 	                        : (purple_connection_get_display_name(gc) != NULL
944 	                           ? purple_connection_get_display_name(gc)
945 	                           : purple_account_get_username(account))),
946 	                       (msg != NULL ? ": " : "."),
947 	                       (msg != NULL ? msg  : ""));
948 }
949 
950 static void
notify_added(PurpleAccount * account,const char * remote_user,const char * id,const char * alias,const char * msg)951 notify_added(PurpleAccount *account, const char *remote_user,
952 			const char *id, const char *alias,
953 			const char *msg)
954 {
955 	char *buffer;
956 	PurpleConnection *gc;
957 
958 	gc = purple_account_get_connection(account);
959 
960 	buffer = make_info(account, gc, remote_user, id, alias, msg);
961 
962 	purple_notify_info(NULL, NULL, buffer, NULL);
963 
964 	g_free(buffer);
965 }
966 
967 static void
free_add_user_data(AddUserData * data)968 free_add_user_data(AddUserData *data)
969 {
970 	g_free(data->username);
971 
972 	if (data->alias != NULL)
973 		g_free(data->alias);
974 
975 	g_free(data);
976 }
977 
978 static void
add_user_cb(AddUserData * data)979 add_user_cb(AddUserData *data)
980 {
981 	PurpleConnection *gc = purple_account_get_connection(data->account);
982 
983 	if (g_list_find(purple_connections_get_all(), gc))
984 	{
985 		purple_blist_request_add_buddy(data->account, data->username,
986 									 NULL, data->alias);
987 	}
988 
989 	free_add_user_data(data);
990 }
991 
992 static void
request_add(PurpleAccount * account,const char * remote_user,const char * id,const char * alias,const char * msg)993 request_add(PurpleAccount *account, const char *remote_user,
994 		  const char *id, const char *alias,
995 		  const char *msg)
996 {
997 	char *buffer;
998 	PurpleConnection *gc;
999 	AddUserData *data;
1000 
1001 	gc = purple_account_get_connection(account);
1002 
1003 	data = g_new0(AddUserData, 1);
1004 	data->account  = account;
1005 	data->username = g_strdup(remote_user);
1006 	data->alias    = (alias != NULL ? g_strdup(alias) : NULL);
1007 
1008 	buffer = make_info(account, gc, remote_user, id, alias, msg);
1009 	purple_request_action(NULL, NULL, _("Add buddy to your list?"),
1010 	                    buffer, PURPLE_DEFAULT_ACTION_NONE,
1011 						account, remote_user, NULL,
1012 						data, 2,
1013 	                    _("Add"),    G_CALLBACK(add_user_cb),
1014 	                    _("Cancel"), G_CALLBACK(free_add_user_data));
1015 	g_free(buffer);
1016 }
1017 
1018 /* Copied from gtkaccount.c */
1019 typedef struct {
1020 	PurpleAccountRequestAuthorizationCb auth_cb;
1021 	PurpleAccountRequestAuthorizationCb deny_cb;
1022 	void *data;
1023 	char *username;
1024 	char *alias;
1025 	PurpleAccount *account;
1026 } auth_and_add;
1027 
1028 static void
free_auth_and_add(auth_and_add * aa)1029 free_auth_and_add(auth_and_add *aa)
1030 {
1031 	g_free(aa->username);
1032 	g_free(aa->alias);
1033 	g_free(aa);
1034 }
1035 
1036 static void
authorize_and_add_cb(auth_and_add * aa)1037 authorize_and_add_cb(auth_and_add *aa)
1038 {
1039 	aa->auth_cb(aa->data);
1040 	purple_blist_request_add_buddy(aa->account, aa->username,
1041 	 	                    NULL, aa->alias);
1042 }
1043 
1044 static void
deny_no_add_cb(auth_and_add * aa)1045 deny_no_add_cb(auth_and_add *aa)
1046 {
1047 	aa->deny_cb(aa->data);
1048 }
1049 
1050 static void *
finch_request_authorize(PurpleAccount * account,const char * remote_user,const char * id,const char * alias,const char * message,gboolean on_list,PurpleAccountRequestAuthorizationCb auth_cb,PurpleAccountRequestAuthorizationCb deny_cb,void * user_data)1051 finch_request_authorize(PurpleAccount *account,
1052                         const char *remote_user,
1053                         const char *id,
1054                         const char *alias,
1055                         const char *message,
1056                         gboolean on_list,
1057                         PurpleAccountRequestAuthorizationCb auth_cb,
1058                         PurpleAccountRequestAuthorizationCb deny_cb,
1059                         void *user_data)
1060 {
1061 	char *buffer;
1062 	PurpleConnection *gc;
1063 	void *uihandle;
1064 
1065 	gc = purple_account_get_connection(account);
1066 	if (message != NULL && *message == '\0')
1067 		message = NULL;
1068 
1069 	buffer = g_strdup_printf(_("%s%s%s%s wants to add %s to his or her buddy list%s%s"),
1070 				remote_user,
1071 	 	                (alias != NULL ? " ("  : ""),
1072 		                (alias != NULL ? alias : ""),
1073 		                (alias != NULL ? ")"   : ""),
1074 		                (id != NULL
1075 		                ? id
1076 		                : (purple_connection_get_display_name(gc) != NULL
1077 		                ? purple_connection_get_display_name(gc)
1078 		                : purple_account_get_username(account))),
1079 		                (message != NULL ? ": " : "."),
1080 		                (message != NULL ? message  : ""));
1081 	if (!on_list) {
1082 		GntWidget *widget;
1083 		GList *iter;
1084 		auth_and_add *aa = g_new(auth_and_add, 1);
1085 
1086 		aa->auth_cb = auth_cb;
1087 		aa->deny_cb = deny_cb;
1088 		aa->data = user_data;
1089 		aa->username = g_strdup(remote_user);
1090 		aa->alias = g_strdup(alias);
1091 		aa->account = account;
1092 
1093 		uihandle = gnt_vwindow_new(FALSE);
1094 		gnt_box_set_title(GNT_BOX(uihandle), _("Authorize buddy?"));
1095 		gnt_box_set_pad(GNT_BOX(uihandle), 0);
1096 
1097 		widget = purple_request_action(NULL, _("Authorize buddy?"), buffer, NULL,
1098 			PURPLE_DEFAULT_ACTION_NONE,
1099 			account, remote_user, NULL,
1100 			aa, 2,
1101 			_("Authorize"), authorize_and_add_cb,
1102 			_("Deny"), deny_no_add_cb);
1103 		/* Since GntWindow is a GntBox, hide it so it's unmapped, then
1104 		 * add it to the outer window, and make it visible again. */
1105 		gnt_widget_hide(widget);
1106 		gnt_box_set_toplevel(GNT_BOX(widget), FALSE);
1107 		gnt_box_add_widget(GNT_BOX(uihandle), widget);
1108 		gnt_widget_set_visible(widget, TRUE);
1109 
1110 		gnt_box_add_widget(GNT_BOX(uihandle), gnt_hline_new());
1111 
1112 		widget = finch_retrieve_user_info(purple_account_get_connection(account), remote_user);
1113 		for (iter = gnt_box_get_children(GNT_BOX(widget)); iter;
1114 		     iter = g_list_delete_link(iter, iter)) {
1115 			if (GNT_IS_BUTTON(iter->data)) {
1116 				gnt_widget_destroy(iter->data);
1117 				gnt_box_remove(GNT_BOX(widget), iter->data);
1118 				g_list_free(iter);
1119 				break;
1120 			}
1121 		}
1122 		/* Since GntWindow is a GntBox, hide it so it's unmapped, then
1123 		 * add it to the outer window, and make it visible again. */
1124 		gnt_widget_hide(widget);
1125 		gnt_box_set_toplevel(GNT_BOX(widget), FALSE);
1126 		gnt_box_add_widget(GNT_BOX(uihandle), widget);
1127 		gnt_widget_set_visible(widget, TRUE);
1128 
1129 		gnt_widget_show(uihandle);
1130 
1131 		g_signal_connect_swapped(G_OBJECT(uihandle), "destroy", G_CALLBACK(free_auth_and_add), aa);
1132 	} else {
1133 		uihandle = purple_request_action(NULL, _("Authorize buddy?"), buffer, NULL,
1134 			PURPLE_DEFAULT_ACTION_NONE,
1135 			account, remote_user, NULL,
1136 			user_data, 2,
1137 			_("Authorize"), auth_cb,
1138 			_("Deny"), deny_cb);
1139 	}
1140 	g_signal_connect(G_OBJECT(uihandle), "destroy",
1141 		G_CALLBACK(purple_account_request_close), NULL);
1142 	g_free(buffer);
1143 	return uihandle;
1144 }
1145 
1146 static void
finch_request_close(void * uihandle)1147 finch_request_close(void *uihandle)
1148 {
1149 	purple_request_close(PURPLE_REQUEST_ACTION, uihandle);
1150 }
1151 
1152 static PurpleAccountUiOps ui_ops =
1153 {
1154 	notify_added,
1155 	NULL,
1156 	request_add,
1157 	finch_request_authorize,
1158 	finch_request_close,
1159 	NULL,
1160 	NULL,
1161 	NULL,
1162 	NULL
1163 };
1164 
finch_accounts_get_ui_ops()1165 PurpleAccountUiOps *finch_accounts_get_ui_ops()
1166 {
1167 	return &ui_ops;
1168 }
1169 
1170