1 /*
2  * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 
21 #include <glib/gi18n-lib.h>
22 #include <string.h>
23 #include <libgda/binreloc/gda-binreloc.h>
24 #include "auth-dialog.h"
25 #include "browser-spinner.h"
26 #include "support.h"
27 #include <libgda/thread-wrapper/gda-thread-wrapper.h>
28 
29 /*
30  * Main static functions
31  */
32 static void auth_dialog_class_init (AuthDialogClass * class);
33 static void auth_dialog_init (AuthDialog *dialog);
34 static void auth_dialog_dispose (GObject *object);
35 
36 /* get a pointer to the parents to be able to call their destructor */
37 static GObjectClass  *parent_class = NULL;
38 
39 typedef struct {
40 	AuthDialogConnection ext;
41 	GdaDsnInfo  cncinfo;
42 	GtkWidget  *auth_widget;
43 	GString    *auth_string;
44 
45 	GdaThreadWrapper *wrapper;
46 	guint jobid;
47 } AuthData;
48 
49 static void
auth_data_free(AuthData * ad)50 auth_data_free (AuthData *ad)
51 {
52 	g_free (ad->cncinfo.name);
53 	g_free (ad->cncinfo.description);
54 	g_free (ad->cncinfo.provider);
55 	g_free (ad->cncinfo.cnc_string);
56 	g_free (ad->cncinfo.auth_string);
57 	g_free (ad->ext.cnc_string);
58 	if (ad->auth_string)
59 		g_string_free (ad->auth_string, TRUE);
60 
61 	g_object_unref (ad->wrapper);
62 	if (ad->ext.cnc_open_error)
63 		g_error_free (ad->ext.cnc_open_error);
64 	if (ad->ext.cnc)
65 		g_object_unref (ad->ext.cnc);
66 	g_free (ad);
67 }
68 
69 struct _AuthDialogPrivate
70 {
71 	GSList    *auth_list; /* list of AuthData pointers */
72 	GtkWidget *spinner;
73 	guint      source_id; /* timer to check if connections have been opened */
74 	GMainLoop *loop; /* waiting loop */
75 };
76 
77 /* module error */
auth_dialog_error_quark(void)78 GQuark auth_dialog_error_quark (void)
79 {
80         static GQuark quark;
81         if (!quark)
82                 quark = g_quark_from_static_string ("auth_dialog_error");
83         return quark;
84 }
85 
86 GType
auth_dialog_get_type(void)87 auth_dialog_get_type (void)
88 {
89 	static GType type = 0;
90 
91 	if (G_UNLIKELY (type == 0)) {
92 		static GMutex registering;
93 		static const GTypeInfo info = {
94 			sizeof (AuthDialogClass),
95 			(GBaseInitFunc) NULL,
96 			(GBaseFinalizeFunc) NULL,
97 			(GClassInitFunc) auth_dialog_class_init,
98 			NULL,
99 			NULL,
100 			sizeof (AuthDialog),
101 			0,
102 			(GInstanceInitFunc) auth_dialog_init,
103 			0
104 		};
105 
106 		g_mutex_lock (&registering);
107 		if (type == 0)
108 			type = g_type_register_static (GTK_TYPE_DIALOG, "AuthDialog", &info, 0);
109 		g_mutex_unlock (&registering);
110 	}
111 
112 	return type;
113 }
114 
115 
116 static void
auth_dialog_class_init(AuthDialogClass * class)117 auth_dialog_class_init (AuthDialogClass *class)
118 {
119 	GObjectClass *object_class = G_OBJECT_CLASS (class);
120 
121 	parent_class = g_type_class_peek_parent (class);
122 
123 	/* virtual functions */
124 	object_class->dispose = auth_dialog_dispose;
125 }
126 
127 /*
128 static void
129 auth_contents_changed_cb (GdauiAuth *auth, gboolean is_valid, AuthDialog *dialog)
130 {
131 	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, is_valid);
132 	if (is_valid)
133 		gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
134 }
135 */
136 
137 static void
update_ad_auth(AuthData * ad)138 update_ad_auth (AuthData *ad)
139 {
140 	if (ad->auth_widget && ad->cncinfo.auth_string) {
141 		/* split array in a list of named parameters, and for each parameter value,
142 		 * set the correcponding parameter in @dset */
143 		GdaSet *dset;
144 		dset = gdaui_basic_form_get_data_set (GDAUI_BASIC_FORM (ad->auth_widget));
145 		gchar **array = NULL;
146 		array = g_strsplit (ad->cncinfo.auth_string, ";", 0);
147 		if (array) {
148 			gint index = 0;
149 			gchar *tok;
150 			gchar *value;
151 			gchar *name;
152 
153 			for (index = 0; array[index]; index++) {
154 				name = strtok_r (array [index], "=", &tok);
155 				if (name)
156 					value = strtok_r (NULL, "=", &tok);
157 				else
158 					value = NULL;
159 				if (name && value) {
160 					GdaHolder *param;
161 					gda_rfc1738_decode (name);
162 					gda_rfc1738_decode (value);
163 
164 					param = gda_set_get_holder (dset, name);
165 					if (param)
166 						g_assert (gda_holder_set_value_str (param, NULL, value, NULL));
167 				}
168 			}
169 
170 			g_strfreev (array);
171 		}
172 		gdaui_basic_form_entry_grab_focus (GDAUI_BASIC_FORM (ad->auth_widget), NULL);
173 	}
174 }
175 
176  /*
177   * Update the auth part
178   */
179 static void
dsn_changed_cb(G_GNUC_UNUSED GdaConfig * config,GdaDsnInfo * info,AuthDialog * dialog)180 dsn_changed_cb (G_GNUC_UNUSED GdaConfig *config, GdaDsnInfo *info, AuthDialog *dialog)
181 {
182 	GSList *list;
183 	if (!info || !info->name) /* should not happen */
184 		return;
185 	for (list = dialog->priv->auth_list; list; list = list->next) {
186 		AuthData *ad = (AuthData*) list->data;
187 		if (! ad->cncinfo.name || strcmp (info->name, ad->cncinfo.name))
188 			continue;
189 		g_free (ad->cncinfo.auth_string);
190 		ad->cncinfo.auth_string = NULL;
191 		if (info->auth_string)
192 			ad->cncinfo.auth_string = g_strdup (info->auth_string);
193 		update_ad_auth (ad);
194 	}
195 }
196 
197 static void
auth_dialog_init(AuthDialog * dialog)198 auth_dialog_init (AuthDialog *dialog)
199 {
200 	GtkWidget *label, *hbox, *wid;
201 	char *markup, *str;
202 	GtkWidget *dcontents;
203 
204 	dialog->priv = g_new0 (AuthDialogPrivate, 1);
205 
206 	gtk_dialog_add_buttons (GTK_DIALOG (dialog),
207 				GTK_STOCK_CONNECT,
208 				GTK_RESPONSE_ACCEPT,
209 				GTK_STOCK_CANCEL,
210 				GTK_RESPONSE_REJECT, NULL);
211 
212 	dcontents = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
213 	gtk_box_set_spacing (GTK_BOX (dcontents), 5);
214 	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, TRUE);
215 
216 	str = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "pixmaps", "gda-browser-auth.png", NULL);
217 	gtk_window_set_icon_from_file (GTK_WINDOW (dialog), str, NULL);
218 	g_free (str);
219 
220 	/* label and spinner */
221 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
222 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 10);
223 	gtk_box_pack_start (GTK_BOX (dcontents), hbox, FALSE, FALSE, 0);
224 
225 	str = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "pixmaps", "gda-browser-auth-big.png", NULL);
226 	wid = gtk_image_new_from_file (str);
227 	g_free (str);
228 	gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
229 
230 	label = gtk_label_new ("");
231 	markup = g_markup_printf_escaped ("<big><b>%s\n</b></big>\n",
232 					  _("Connection opening"));
233 	gtk_label_set_markup (GTK_LABEL (label), markup);
234 	g_free (markup);
235 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
236 	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 12);
237 
238 	dialog->priv->spinner = browser_spinner_new ();
239 	gtk_box_pack_start (GTK_BOX (hbox), dialog->priv->spinner, FALSE, FALSE, 0);
240 
241 	gtk_widget_show_all (hbox);
242 	gtk_widget_hide (dialog->priv->spinner);
243 
244 	GdaConfig *conf = gda_config_get ();
245 	g_signal_connect (conf, "dsn-changed",
246 			  G_CALLBACK (dsn_changed_cb), dialog);
247 	g_object_unref (conf);
248 }
249 
250 static void
auth_dialog_dispose(GObject * object)251 auth_dialog_dispose (GObject *object)
252 {
253 	AuthDialog *dialog;
254 	dialog = AUTH_DIALOG (object);
255 	if (dialog->priv) {
256 		GdaConfig *conf = gda_config_get ();
257 		g_signal_handlers_disconnect_by_func (conf,
258 						      G_CALLBACK (dsn_changed_cb), dialog);
259 		g_object_unref (conf);
260 		if (dialog->priv->auth_list) {
261 			g_slist_foreach (dialog->priv->auth_list, (GFunc) auth_data_free, NULL);
262 			g_slist_free (dialog->priv->auth_list);
263 		}
264 		if (dialog->priv->source_id)
265 			g_source_remove (dialog->priv->source_id);
266 		if (dialog->priv->loop)
267 			g_main_loop_quit (dialog->priv->loop);
268 		g_free (dialog->priv);
269 		dialog->priv = NULL;
270 	}
271 
272 	/* parent class */
273         parent_class->dispose (object);
274 }
275 
276 /**
277  * auth_dialog_new
278  *
279  * Creates a new dialog dialog
280  *
281  * Returns: a new #AuthDialog object
282  */
283 AuthDialog *
auth_dialog_new(GtkWindow * parent)284 auth_dialog_new (GtkWindow *parent)
285 {
286 	return (AuthDialog*) g_object_new (AUTH_TYPE_DIALOG, "title", _("Authentication"),
287 					   "transient-for", parent,
288 					   "resizable", FALSE,
289 					   "border-width", 10, NULL);
290 }
291 
292 /*
293  * executed in a sub thread
294  */
295 static GdaConnection *
sub_thread_open_cnc(AuthData * ad,GError ** error)296 sub_thread_open_cnc (AuthData *ad, GError **error)
297 {
298 #ifndef DUMMY
299 	GdaConnection *cnc;
300 	GdaDsnInfo *info = &(ad->cncinfo);
301 	if (info->name)
302 		cnc = gda_connection_open_from_dsn (info->name, ad->auth_string ? ad->auth_string->str : NULL,
303 						    GDA_CONNECTION_OPTIONS_THREAD_SAFE |
304 						    GDA_CONNECTION_OPTIONS_AUTO_META_DATA,
305 						    error);
306 	else
307 		cnc = gda_connection_open_from_string (info->provider, info->cnc_string,
308 						       ad->auth_string ? ad->auth_string->str : NULL,
309 						       GDA_CONNECTION_OPTIONS_THREAD_SAFE |
310 						       GDA_CONNECTION_OPTIONS_AUTO_META_DATA,
311 						       error);
312 #ifdef HAVE_LDAP
313 	if (cnc && GDA_IS_LDAP_CONNECTION (cnc)) {
314 		/* force classes init */
315 		gda_ldap_get_class_info (GDA_LDAP_CONNECTION (cnc), "top");
316 	}
317 #endif
318 	return cnc;
319 #else /* DUMMY defined */
320 	sleep (5);
321 	g_set_error (error, GDA_TOOLS_ERROR, TOOLS_INTERNAL_COMMAND_ERROR, "%s", "Dummy error!");
322 	return NULL;
323 #endif
324 }
325 
326 static gboolean
check_for_cnc(AuthDialog * dialog)327 check_for_cnc (AuthDialog *dialog)
328 {
329 	GSList *list;
330 	gboolean finished = TRUE;
331 	for (list = dialog->priv->auth_list; list; list = list->next) {
332 		AuthData *ad = (AuthData*) list->data;
333 
334 		if (ad->jobid) {
335 			GError *lerror = NULL;
336 			ad->ext.cnc = gda_thread_wrapper_fetch_result (ad->wrapper, FALSE, ad->jobid, &lerror);
337 			if (ad->ext.cnc || (!ad->ext.cnc && lerror)) {
338 				/* waiting is finished! */
339 				if (ad->ext.cnc)
340 					g_object_set (ad->ext.cnc, "monitor-wrapped-in-mainloop", TRUE, NULL);
341 				if (lerror)
342 					ad->ext.cnc_open_error = lerror;
343 				ad->jobid = 0;
344 			}
345 			else
346 				finished = FALSE;
347 		}
348 	}
349 
350 	if (finished) {
351 		dialog->priv->source_id = 0;
352 		if (dialog->priv->loop)
353 			g_main_loop_quit (dialog->priv->loop);
354 	}
355 	return !finished;
356 }
357 
358 static void
update_dialog_focus(AuthDialog * dialog)359 update_dialog_focus (AuthDialog *dialog)
360 {
361 	GSList *list;
362 	gboolean allvalid = TRUE;
363 	for (list = dialog->priv->auth_list; list; list = list->next) {
364 		AuthData *ad;
365 		ad = (AuthData*) list->data;
366 		if (ad->auth_widget && !ad->ext.cnc &&
367 		    ! gdaui_basic_form_is_valid (GDAUI_BASIC_FORM (ad->auth_widget))) {
368 			allvalid = FALSE;
369 			gtk_widget_grab_focus (ad->auth_widget);
370 			break;
371 		}
372 	}
373 
374 	if (allvalid) {
375 		GtkWidget *wid;
376 		wid = gtk_dialog_get_widget_for_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
377 		gtk_widget_grab_focus (wid);
378 	}
379 }
380 
381 static void
auth_form_activated_cb(G_GNUC_UNUSED GdauiBasicForm * form,AuthDialog * dialog)382 auth_form_activated_cb (G_GNUC_UNUSED GdauiBasicForm *form, AuthDialog *dialog)
383 {
384 	gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
385 }
386 
387 static void
auth_contents_changed_cb(GdauiBasicForm * form,GdaHolder * h,gboolean is_user_modif,AuthDialog * dialog)388 auth_contents_changed_cb (GdauiBasicForm *form, GdaHolder *h, gboolean is_user_modif, AuthDialog *dialog)
389 {
390 	GSList *list;
391 	for (list = dialog->priv->auth_list; list; list = list->next) {
392 		AuthData *ad = (AuthData*) list->data;
393 		if (! gdaui_basic_form_is_valid (GDAUI_BASIC_FORM (ad->auth_widget)))
394 			break;
395 	}
396 
397 	gboolean is_valid;
398 	is_valid = list ? FALSE : TRUE;
399 	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, is_valid);
400         if (is_valid)
401                 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
402 }
403 
404 /**
405  * auth_dialog_add_cnc_string
406  */
407 gboolean
auth_dialog_add_cnc_string(AuthDialog * dialog,const gchar * cnc_string,GError ** error)408 auth_dialog_add_cnc_string (AuthDialog *dialog, const gchar *cnc_string, GError **error)
409 {
410 	g_return_val_if_fail (AUTH_IS_DIALOG (dialog), FALSE);
411 	g_return_val_if_fail (cnc_string, FALSE);
412 
413 	gchar *real_cnc_string;
414 	GdaDsnInfo *info;
415         gchar *user, *pass, *real_cnc, *real_provider, *real_auth_string = NULL;
416 
417 	/* if cnc string is a regular file, then use it with SQLite */
418         if (g_file_test (cnc_string, G_FILE_TEST_IS_REGULAR)) {
419                 gchar *path, *file, *e1, *e2;
420                 const gchar *pname = "SQLite";
421 
422                 path = g_path_get_dirname (cnc_string);
423                 file = g_path_get_basename (cnc_string);
424                 if (g_str_has_suffix (file, ".mdb")) {
425                         pname = "MSAccess";
426                         file [strlen (file) - 4] = 0;
427                 }
428                 else if (g_str_has_suffix (file, ".db"))
429                         file [strlen (file) - 3] = 0;
430                 e1 = gda_rfc1738_encode (path);
431                 e2 = gda_rfc1738_encode (file);
432                 g_free (path);
433                 g_free (file);
434                 real_cnc_string = g_strdup_printf ("%s://DB_DIR=%s;EXTRA_FUNCTIONS=TRUE;DB_NAME=%s", pname, e1, e2);
435                 g_free (e1);
436                 g_free (e2);
437                 gda_connection_string_split (real_cnc_string, &real_cnc, &real_provider, &user, &pass);
438         }
439         else {
440                 gda_connection_string_split (cnc_string, &real_cnc, &real_provider, &user, &pass);
441                 real_cnc_string = g_strdup (cnc_string);
442         }
443         if (!real_cnc) {
444                 g_free (user);
445                 g_free (pass);
446                 g_free (real_provider);
447                 g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_DSN_NOT_FOUND_ERROR,
448                              _("Malformed connection string '%s'"), cnc_string);
449                 g_free (real_cnc_string);
450                 return FALSE;
451         }
452 
453 	AuthData *ad;
454 	ad = g_new0 (AuthData, 1);
455 	ad->wrapper = gda_thread_wrapper_new ();
456 	/*g_print ("Auth dialog: new thread wrapper %p\n", ad->wrapper);*/
457 	ad->ext.cnc_string = g_strdup (cnc_string);
458 	ad->auth_string = NULL;
459 	info = gda_config_get_dsn_info (real_cnc);
460         if (info && !real_provider) {
461 		ad->cncinfo.name = g_strdup (info->name);
462 		ad->cncinfo.provider = g_strdup (info->provider);
463 		if (info->description)
464 			ad->cncinfo.description = g_strdup (info->description);
465 		if (info->cnc_string)
466 			ad->cncinfo.cnc_string = g_strdup (info->cnc_string);
467 		if (info->auth_string)
468 			ad->cncinfo.auth_string = g_strdup (info->auth_string);
469 	}
470 	else {
471 		ad->cncinfo.name = NULL;
472 		ad->cncinfo.provider = real_provider;
473 		real_provider = NULL;
474 		ad->cncinfo.cnc_string = real_cnc;
475 		real_cnc = NULL;
476 		ad->cncinfo.auth_string = real_auth_string;
477 		real_auth_string = NULL;
478 	}
479 
480 	if (! ad->cncinfo.provider) {
481 		g_free (user);
482                 g_free (pass);
483                 g_free (real_provider);
484                 g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_DSN_NOT_FOUND_ERROR,
485                              _("Malformed connection string '%s'"), cnc_string);
486                 g_free (real_cnc_string);
487 		auth_data_free (ad);
488 		return FALSE;
489 	}
490 
491 	if (user || pass) {
492 		gchar *s1;
493 		s1 = gda_rfc1738_encode (user);
494 		if (pass) {
495 			gchar *s2;
496 			s2 = gda_rfc1738_encode (pass);
497 			real_auth_string = g_strdup_printf ("USERNAME=%s;PASSWORD=%s", s1, s2);
498 			g_free (s2);
499 		}
500 		else
501 			real_auth_string = g_strdup_printf ("USERNAME=%s", s1);
502 		g_free (s1);
503 	}
504 	if (real_auth_string) {
505 		if (ad->cncinfo.auth_string)
506 			g_free (ad->cncinfo.auth_string);
507 		ad->cncinfo.auth_string = real_auth_string;
508 		real_auth_string = NULL;
509 	}
510 
511 	dialog->priv->auth_list = g_slist_append (dialog->priv->auth_list, ad);
512 
513 	/* build widget */
514 	gboolean auth_needed = FALSE;
515 	GdaProviderInfo *pinfo;
516 	pinfo = gda_config_get_provider_info (ad->cncinfo.provider);
517 	if (pinfo && pinfo->auth_params && pinfo->auth_params->holders)
518 		auth_needed = TRUE;
519 	if (auth_needed) {
520 		GdaSet *set;
521 
522                 set = gda_set_copy (pinfo->auth_params);
523                 ad->auth_widget = gdaui_basic_form_new (set);
524                 g_signal_connect (G_OBJECT (ad->auth_widget), "activated",
525 				  G_CALLBACK (auth_form_activated_cb), dialog);
526                 g_signal_connect (G_OBJECT (ad->auth_widget), "holder-changed",
527 				  G_CALLBACK (auth_contents_changed_cb), dialog);
528                 g_object_unref (set);
529 
530 		/* add widget */
531 		GtkWidget *hbox, *label;
532 		gchar *str, *tmp, *ptr;
533 		GtkWidget *dcontents;
534 
535 		dcontents = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
536 		label = gtk_label_new ("");
537 		tmp = g_strdup (ad->ext.cnc_string);
538 		for (ptr = tmp; *ptr; ptr++) {
539 			if (*ptr == ':') {
540 				/* remove everything up to the '@' */
541 				gchar *ptr2;
542 				for (ptr2 = ptr+1; *ptr2; ptr2++) {
543 					if (*ptr2 == '@') {
544 						memmove (ptr, ptr2, strlen (ptr2) + 1);
545 						break;
546 					}
547 				}
548 				break;
549 			}
550 		}
551 		str = g_strdup_printf ("<b>%s: %s</b>\n%s", _("For connection"), tmp,
552 				       _("enter authentication information"));
553 		g_free (tmp);
554 		gtk_label_set_markup (GTK_LABEL (label), str);
555 		gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
556 		g_free (str);
557 		gtk_box_pack_start (GTK_BOX (dcontents), label, FALSE, FALSE, 0);
558 		gtk_widget_show (label);
559 
560 		hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* HIG */
561 		gtk_box_pack_start (GTK_BOX (dcontents), hbox, TRUE, TRUE, 0);
562 		label = gtk_label_new ("      ");
563 		gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
564 		gtk_box_pack_start (GTK_BOX (hbox), ad->auth_widget, TRUE, TRUE, 0);
565 		gtk_widget_show_all (hbox);
566 
567 		/* set values */
568 		if (ad->cncinfo.auth_string)
569 			update_ad_auth (ad);
570 	}
571 	else {
572 		/* open connection right away */
573 		ad->jobid = gda_thread_wrapper_execute (ad->wrapper,
574 							(GdaThreadWrapperFunc) sub_thread_open_cnc,
575 							(gpointer) ad,
576 							(GDestroyNotify) NULL,
577 							&(ad->ext.cnc_open_error));
578 		if (dialog->priv->source_id == 0) {
579 			dialog->priv->source_id = g_timeout_add (200, (GSourceFunc) check_for_cnc, dialog);
580 		}
581 	}
582 
583 	g_free (real_cnc_string);
584         g_free (real_cnc);
585         g_free (user);
586         g_free (pass);
587         g_free (real_provider);
588         g_free (real_auth_string);
589 
590 	update_dialog_focus (dialog);
591 
592 	return TRUE;
593 }
594 
595 /**
596  * auth_dialog_run
597  * @dialog: a #GdaAuth object
598  * @retry: if set to %TRUE, then this method returns only when either a connection has been opened or the
599  *         user gave up
600  * @error: a place to store errors, or %NULL
601  *
602  * Displays the dialog and let the user open some connections. this function returns either only when
603  * all the connections have been opened, or when the user cancelled.
604  *
605  * Return: %TRUE if all the connections have been opened, and %FALSE if the user cancelled.
606  */
607 gboolean
auth_dialog_run(AuthDialog * dialog)608 auth_dialog_run (AuthDialog *dialog)
609 {
610 	gboolean allopened = FALSE;
611 
612 	g_return_val_if_fail (AUTH_IS_DIALOG (dialog), FALSE);
613 
614 	gtk_widget_show (GTK_WIDGET (dialog));
615 
616 	while (1) {
617 		gint result;
618 		GSList *list;
619 		gboolean needs_running = FALSE;
620 
621 		/* determine if we need to run the dialog */
622 		for (list = dialog->priv->auth_list; list; list = list->next) {
623 			AuthData *ad;
624 			ad = (AuthData *) list->data;
625 			if (ad->auth_widget) {
626 				needs_running = TRUE;
627 				break;
628 			}
629 		}
630 
631 		if (needs_running)
632 			result = gtk_dialog_run (GTK_DIALOG (dialog));
633 		else
634 			result = GTK_RESPONSE_ACCEPT;
635 
636 		gtk_widget_show (dialog->priv->spinner);
637 		browser_spinner_start (BROWSER_SPINNER (dialog->priv->spinner));
638 
639 		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, FALSE);
640 		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT, FALSE);
641 
642 		if (result == GTK_RESPONSE_ACCEPT) {
643 			for (list = dialog->priv->auth_list; list; list = list->next) {
644 				AuthData *ad;
645 				ad = (AuthData *) list->data;
646 				if (ad->auth_widget && !ad->jobid) {
647 					GSList *plist;
648 					GdaSet *set;
649 					set = gdaui_basic_form_get_data_set (GDAUI_BASIC_FORM (ad->auth_widget));
650 					if (ad->auth_string) {
651 						g_string_free (ad->auth_string, TRUE);
652 						ad->auth_string = NULL;
653 					}
654 					for (plist = set ? set->holders : NULL;
655 					     plist; plist = plist->next) {
656 						GdaHolder *holder = GDA_HOLDER (plist->data);
657 						const GValue *cvalue = NULL;
658 						if (gda_holder_is_valid (holder))
659 							cvalue = gda_holder_get_value (holder);
660 						if (cvalue && (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL)) {
661 							gchar *r1, *r2;
662 							r1 = gda_value_stringify (cvalue);
663 							r2 = gda_rfc1738_encode (r1);
664 							g_free (r1);
665 							if (r2) {
666 								r1 = gda_rfc1738_encode (gda_holder_get_id (holder));
667 								if (ad->auth_string)
668 									g_string_append_c (ad->auth_string, ';');
669 								else
670 									ad->auth_string = g_string_new ("");
671 								g_string_append (ad->auth_string, r1);
672 								g_string_append_c (ad->auth_string, '=');
673 								g_string_append (ad->auth_string, r2);
674 
675 								g_free (r1);
676 								g_free (r2);
677 							}
678 						}
679 					}
680 					gtk_widget_set_sensitive (ad->auth_widget, FALSE);
681 					ad->jobid = gda_thread_wrapper_execute (ad->wrapper,
682 										(GdaThreadWrapperFunc) sub_thread_open_cnc,
683 										(gpointer) ad,
684 										(GDestroyNotify) NULL,
685 										&(ad->ext.cnc_open_error));
686 					if (dialog->priv->source_id == 0) {
687 						dialog->priv->source_id =
688 							g_timeout_add (200, (GSourceFunc) check_for_cnc, dialog);
689 					}
690 				}
691 			}
692 
693 			if (dialog->priv->source_id != 0) {
694 				dialog->priv->loop = g_main_loop_new (NULL, FALSE);
695 				g_main_loop_run (dialog->priv->loop);
696 				g_main_loop_unref (dialog->priv->loop);
697 				dialog->priv->loop = NULL;
698 			}
699 
700 			allopened = TRUE;
701 			for (list = dialog->priv->auth_list; list; list = list->next) {
702 				AuthData *ad;
703 
704 				ad = (AuthData *) list->data;
705 				if (ad->auth_widget && !ad->ext.cnc) {
706 					g_print ("ERROR: %s\n", ad->ext.cnc_open_error && ad->ext.cnc_open_error->message ?
707 						 ad->ext.cnc_open_error->message : _("No detail"));
708 					browser_show_error (GTK_WINDOW (dialog), _("Could not open connection:\n%s"),
709 							    ad->ext.cnc_open_error && ad->ext.cnc_open_error->message ?
710 							    ad->ext.cnc_open_error->message : _("No detail"));
711 					allopened = FALSE;
712 					gtk_widget_set_sensitive (ad->auth_widget, TRUE);
713 				}
714 			}
715 			if (allopened)
716 				goto out;
717 		}
718 		else {
719 			/* cancelled connection opening */
720 			goto out;
721 		}
722 
723 		browser_spinner_stop (BROWSER_SPINNER (dialog->priv->spinner));
724 		gtk_widget_hide (dialog->priv->spinner);
725 		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, TRUE);
726 		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT, TRUE);
727 	}
728 
729  out:
730 	return allopened;
731 }
732 
733 
734 /**
735  * auth_dialog_get_connections
736  *
737  * Returns: a list of pointers to AuthDialogConnection structures.
738  */
739 const GSList *
auth_dialog_get_connections(AuthDialog * dialog)740 auth_dialog_get_connections (AuthDialog *dialog)
741 {
742 	g_return_val_if_fail (AUTH_IS_DIALOG (dialog), NULL);
743 	return dialog->priv->auth_list;
744 }
745