root/trunk/libtinymail-maemo/tny-maemo-conic-dummy-device.c

Revision 3666 (checked in by jdapena, 7 months ago)

* Use GOnce registering all types in tinymail to make

registering thread-safe.

Line 
1 /* libtinymail-camel - The Tiny Mail base library for Maemo
2  * Copyright (C) 2006-2007 Philip Van Hoof <pvanhoof@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with self library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include <config.h>
21 #include <glib.h>
22 #include <glib-object.h>
23 #include <tny-maemo-conic-device.h>
24 #include <conicevent.h>
25 #include <coniciap.h>
26 #include <conicconnection.h>
27 #include <conicconnectionevent.h>
28 #include <string.h>
29 #include <tny-error.h>
30 #include <gtk/gtkmessagedialog.h>
31
32 static gboolean on_dummy_connection_check (gpointer user_data);
33 static gboolean tny_maemo_conic_device_is_online (TnyDevice *self);
34
35 typedef struct {
36         TnyMaemoConicDevice *self;
37         gchar* iap_id;
38         gpointer user_data;
39         TnyMaemoConicDeviceConnectCallback callback;
40 } ConnectInfo;
41
42 /* #include "coniciap-private.h"
43  * This is not installed, so we predeclare the struct instead. Of course, this
44  * is a hack and could break if the private API changes. It would be better for
45  * libconic to support scratchbox. */
46
47 struct _ConIcIap
48 {
49         GObject parent_instance;
50         gboolean dispose_has_run;
51         gchar *id;
52         gchar *name;
53         gchar *bearer;
54 };
55
56 #define MAEMO_CONIC_DUMMY_IAP_ID_FILENAME "maemo_conic_dummy_id"
57 #define MAEMO_CONIC_DUMMY_IAP_ID_NONE "none"
58
59 static GObjectClass *parent_class = NULL;
60
61
62 static gboolean
63 dnsmasq_has_resolv (void)
64 {
65         /* This is because silly Conic does not have a blocking API that tells
66          * us immediately when the device is online. */
67
68         if (!g_file_test ("/var/run/resolv.conf", G_FILE_TEST_EXISTS))
69                 if (!g_file_test ("/tmp/resolv.conf.wlan0", G_FILE_TEST_EXISTS))
70                         if (!g_file_test ("/tmp/resolv.conf.ppp0", G_FILE_TEST_EXISTS))
71                                 return FALSE;
72
73         return TRUE;
74 }
75
76 typedef struct {
77         ConIcConnection *cnx;
78         gboolean is_online;
79         gchar *iap;
80         gboolean forced; /* Whether the is_online value is forced rather than real. */
81         ConnectInfo *connect_slot;
82         /* When non-NULL, we are waiting for the success or failure signal. */
83         gint dummy_env_check_timeout;
84 } TnyMaemoConicDevicePriv;
85
86 #define TNY_MAEMO_CONIC_DEVICE_GET_PRIVATE(o)   \
87         (G_TYPE_INSTANCE_GET_PRIVATE ((o), TNY_TYPE_MAEMO_CONIC_DEVICE, TnyMaemoConicDevicePriv))
88
89 typedef struct {
90         GObject *self;
91         gboolean status;
92 } EmitStatusInfo;
93
94 static gboolean
95 conic_emit_status_idle (gpointer user_data)
96 {
97         EmitStatusInfo *info = (EmitStatusInfo *) user_data;
98
99         /* We lock the gdk thread because tinymail wants implementations to do
100          * this before emitting signals from within a g_idle_add_full callback.
101          * See http://www.tinymail.org/trac/tinymail/wiki/HowTnyLockable */
102
103         gdk_threads_enter ();
104         g_signal_emit (info->self, tny_device_signals [TNY_DEVICE_CONNECTION_CHANGED],
105                 0, info->status);
106         gdk_threads_leave ();
107
108         return FALSE;
109 }
110
111 static void
112 conic_emit_status_destroy (gpointer user_data)
113 {
114         EmitStatusInfo *info = (EmitStatusInfo *) user_data;
115         g_object_unref (info->self);
116         g_slice_free (EmitStatusInfo, info);
117         return;
118 }
119
120 static void
121 conic_emit_status (TnyDevice *self, gboolean status)
122 {
123         /* Emit it in an idle handler: */
124         EmitStatusInfo *info = g_slice_new (EmitStatusInfo);
125         guint time = 1000;
126         info->self = g_object_ref (self);
127         info->status = status;
128
129         if (!dnsmasq_has_resolv())
130                 time = 5000;
131
132         g_timeout_add_full (G_PRIORITY_DEFAULT, time, conic_emit_status_idle,
133                 info, conic_emit_status_destroy);
134
135         return;
136 }
137
138 static void
139 tny_maemo_conic_device_reset (TnyDevice *device)
140 {
141         TnyMaemoConicDevice *self;
142         TnyMaemoConicDevicePriv *priv;
143         gboolean status_before = FALSE;
144
145         g_return_if_fail (TNY_IS_DEVICE(device));
146         self = TNY_MAEMO_CONIC_DEVICE (device);
147         priv = TNY_MAEMO_CONIC_DEVICE_GET_PRIVATE (self);
148
149         status_before = tny_maemo_conic_device_is_online (device);
150         priv->forced = FALSE;
151
152         if (status_before != tny_maemo_conic_device_is_online (device))
153                 conic_emit_status (device, !status_before);
154
155         return;
156 }
157
158
159 static gchar*
160 get_dummy_filename ()
161 {
162         gchar *filename = g_build_filename (
163                 g_get_home_dir (),
164                 MAEMO_CONIC_DUMMY_IAP_ID_FILENAME,
165                 NULL);
166         return filename;
167 }
168
169
170 static void
171 dummy_con_ic_connection_connect_by_id_async_cb (GtkWidget *dialog, 
172                                                 gint response,
173                                                 gpointer user_data)
174 {
175         ConnectInfo *info = (ConnectInfo *) user_data;
176         GError *error = NULL;
177         gboolean canceled = FALSE;
178
179         if (response == GTK_RESPONSE_OK) {
180                 /* Make a connection, by setting a name in our dummy text file,
181                  * which will be read later: */
182                 gchar *filename = get_dummy_filename ();
183
184                 g_file_set_contents (filename, "debug id0", -1, &error);
185                 if(error) {
186                         g_warning("%s: error from g_file_set_contents(): %s\n",
187                                 __FUNCTION__, error->message);
188                 }
189                 g_free (filename);
190         }
191         else
192                 canceled = TRUE;
193
194         /* No need to gdk_threads_enter/leave here. We are in Gtk+'s context
195          * already (being a signal handler for a Gtk+ component) */
196
197         if (info->callback)
198                 info->callback (info->self, info->iap_id, canceled, error, info->user_data);
199
200         if (error)
201                 g_error_free (error);
202         error = NULL;
203
204         gtk_widget_destroy (dialog);
205
206         g_free (info->iap_id);
207         g_object_unref (info->self);
208         g_slice_free (ConnectInfo, info);
209
210         return;
211 }
212
213 static void
214 dummy_con_ic_connection_connect_by_id_async (TnyMaemoConicDevice *self,
215                                              const gchar* iap_id,
216                                              TnyMaemoConicDeviceConnectCallback callback,
217                                              gpointer user_data)
218 {
219         ConnectInfo *info = g_slice_new0 (ConnectInfo);
220         GtkDialog *dialog;
221
222         info->self = (TnyMaemoConicDevice *) g_object_ref (self);
223         info->callback = callback;
224         info->iap_id = g_strdup (iap_id);
225         info->user_data = user_data;
226
227         /* Show a dialog, because libconic would show a dialog here,
228          * and give the user a chance to refuse a new connection, because libconic would allow that too.
229          * This allows us to see roughly similar behaviour in scratchbox as on the device. */
230
231         dialog = GTK_DIALOG (gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL,
232                         GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
233                         "TnyMaemoConicDevice fake scratchbox implementation:\nThe application requested a connection. Make a fake connection?"));
234
235         g_signal_connect (dialog, "response",
236                           G_CALLBACK (dummy_con_ic_connection_connect_by_id_async_cb),
237                           info);
238
239         gtk_widget_show (GTK_WIDGET (dialog));
240
241 }
242
243
244 /**
245  * tny_maemo_conic_device_connect:
246  * @self: a #TnyDevice object
247  * @iap_id: the id of the Internet Access Point (IAP), or NULL for 'any;
248  * @user_requested: whether or not the connection was automatically requested or by an user action
249  * @callback: a #TnyMaemoConicDeviceConnectCallback
250  * @user_data: user data for @callback
251  *
252  * Try to connect to a specific IAP, or to any if @iap_id == NULL
253  * this calls con_ic_connection_connect(_by_id).
254  * This may show a dialog to allow the user to select a connection, or
255  * may otherwise take a significant amount of time.
256  **/
257 void 
258 tny_maemo_conic_device_connect_async (TnyMaemoConicDevice *self,
259                                       const gchar* iap_id,
260                                       gboolean user_requested,
261                                       TnyMaemoConicDeviceConnectCallback callback,
262                                       gpointer user_data)
263 {
264         dummy_con_ic_connection_connect_by_id_async (self, iap_id, callback, user_data);
265         return;
266 }
267
268
269 /**
270  * tny_maemo_conic_device_disconnect:
271  * @self: a #TnyDevice object
272  * @iap_id: the id of the Internet Access Point (IAP), or NULL for 'any';
273  *
274  * try to disconnect from a specific IAP, or to any if @iap_id == NULL
275  * this calls con_ic_connection_disconnect(_by_id)
276  *
277  * Returns TRUE if sending the command worked, FALSE otherwise
278  **/
279 gboolean
280 tny_maemo_conic_device_disconnect (TnyMaemoConicDevice *self, const gchar* iap_id)
281 {
282         /* don't try to disconnect if we're in dummy mode, as we're not "really"
283          * connected in that case either
284          */
285
286         return TRUE;
287 }
288
289
290 static gboolean
291 on_dummy_connection_check (gpointer user_data)
292 {
293         TnyMaemoConicDevice *self = NULL;
294         TnyMaemoConicDevicePriv *priv = NULL;
295         gchar *filename = NULL;
296         gchar *contents = NULL;
297         GError* error = NULL;
298         gboolean test = FALSE;
299         static gboolean first_time = TRUE;
300                
301         self = TNY_MAEMO_CONIC_DEVICE (user_data);
302
303         priv = TNY_MAEMO_CONIC_DEVICE_GET_PRIVATE (self);
304        
305         /* Check whether the enviroment variable has changed,
306          * so we can fake a connection change: */
307         filename = get_dummy_filename ();
308        
309         test = g_file_get_contents (filename, &contents, NULL, &error);
310
311         if(error) {
312                 /* g_debug("%s: error from g_file_get_contents(): %s\n", __FUNCTION__, error->message); */
313                 g_error_free (error);
314                 error = NULL;
315         }
316        
317         if (!test || !contents) {
318                 /* g_debug ("DEBUG1: %s: priv->iap = %s\n", priv->iap); */
319                 /* Default to the first debug connection: */
320                 contents = g_strdup ("debug id0");
321         }
322        
323         if (contents)
324                 g_strstrip(contents);
325
326         if ((priv->iap == NULL) || (strcmp (contents, priv->iap) != 0)) {
327                 if (priv->iap) {
328                         g_free (priv->iap);
329                         priv->iap = NULL;
330                 }
331                        
332                 /* We store even the special "none" text, so we can detect changes. */
333                 priv->iap = g_strdup (contents);
334                
335                 if (strcmp (priv->iap, MAEMO_CONIC_DUMMY_IAP_ID_NONE) == 0) {
336                         priv->is_online = FALSE;
337                 } else {
338                         priv->is_online = TRUE;
339                 }
340
341                 /* Do not need to emit it the first time because it's
342                    called from the instance init */
343                 if (!first_time)               
344                         conic_emit_status (TNY_DEVICE (self), priv->is_online);
345                 else
346                         first_time = FALSE;
347         }
348        
349         g_free (contents);
350         g_free (filename);
351        
352         return TRUE;
353 }
354
355 /**
356  * tny_maemo_conic_device_get_current_iap_id:
357  * @self: a #TnyDevice object
358  *
359  * retrieve the iap-id of the connection that is currently active; a precondition is
360  * that we _are_ connected.
361  *
362  * Returns: the iap-id for the current connection, or NULL in case of error
363  **/
364 const gchar*
365 tny_maemo_conic_device_get_current_iap_id (TnyMaemoConicDevice *self)
366 {
367         TnyMaemoConicDevicePriv *priv = NULL;
368
369         g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE(self), NULL);
370         g_return_val_if_fail (tny_maemo_conic_device_is_online(TNY_DEVICE(self)), NULL);
371
372         priv = TNY_MAEMO_CONIC_DEVICE_GET_PRIVATE (self);
373
374         on_dummy_connection_check (self);
375
376         /* Handle the special "none" text: */
377         if (priv->iap && (strcmp (priv->iap, MAEMO_CONIC_DUMMY_IAP_ID_NONE) == 0))
378                 return NULL;
379
380         return priv->iap;
381 }
382
383
384
385 /**
386  * tny_maemo_conic_device_get_iap:
387  * @self: a #TnyDevice object
388  * @iap_id: the id of the IAP to get
389  *
390  * get the IAP object (#ConIcIap) for the given iap-id. The returned GObject must be
391  * freed with g_object_unref after use. Refer to the ConIc documentation for details about
392  * the #ConICIap.
393  
394  *
395  * Returns: ConIcIap object or NULL in case of error
396  **/
397 ConIcIap*
398 tny_maemo_conic_device_get_iap (TnyMaemoConicDevice *self, const gchar *iap_id)
399 {
400         ConIcIap *iap = NULL;
401
402         g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE(self), NULL);
403         g_return_val_if_fail (iap_id, NULL);
404
405         /* Note that we have re-declared the private struct so that we
406          * can do this, which is very bad and fragile: */
407
408         iap = g_object_new (CON_IC_TYPE_IAP, NULL);
409         iap->id = g_strdup(iap_id);
410         iap->name = g_strdup_printf("%s name", iap->id);
411
412         return iap;
413 }
414
415
416 /**
417  * tny_maemo_conic_device_get_iap_list:
418  * @self: a #TnyDevice object
419  *
420  * get a list of all IAP objects (#ConIcIap) that are available; it should be freed
421  * with #tny_maemo_conic_device_free_iap_list. This function uses
422  * con_ic_connection_get_all_iaps
423  * 
424  * Returns: the list or NULL in case of error
425  **/
426 GSList*
427 tny_maemo_conic_device_get_iap_list (TnyMaemoConicDevice *self)
428 {
429         GSList* result = NULL;
430         int i = 0;
431        
432         g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE(self), NULL);
433
434         /* libconic does not return a list of connections when running in
435          * scratchbox, though it might do this in future when "ethernet support"
436          * is implemented. So we return a fake list so we can exercise
437          * functionality that uses connections: */
438
439         for (i = 0; i < 10; ++i)
440         {
441                 /* Note that we have re-declared the private struct so that we
442                  * can do this, which is very bad and fragile: */
443
444                 ConIcIap *iap = g_object_new (CON_IC_TYPE_IAP, NULL);
445                 iap->id = g_strdup_printf("debug id%d", i);
446                 iap->name = g_strdup_printf("%s name", iap->id);
447
448                 result = g_slist_append (result, iap);
449         }
450
451         return result;
452 }
453
454
455 /**
456  * tny_maemo_conic_device_free_iap_list:
457  * @self: a #TnyDevice object
458  * 
459  * free a  list of IAP objects retrieved from tny_maemo_conic_device_get_iap_list
460  * 
461  **/
462 void
463 tny_maemo_conic_device_free_iap_list (TnyMaemoConicDevice *self, GSList* cnx_list)
464 {
465         GSList *cur = cnx_list;
466         while (cur) {
467                 g_object_unref (G_OBJECT(cur->data));
468                 cur = g_slist_next (cur);
469         }
470         g_slist_free (cnx_list);
471 }
472
473
474
475 static void
476 tny_maemo_conic_device_force_online (TnyDevice *device)
477 {
478         TnyMaemoConicDevice *self;
479         TnyMaemoConicDevicePriv *priv;
480         gboolean already_online = FALSE;
481         g_return_if_fail (TNY_IS_DEVICE(device));
482         self = TNY_MAEMO_CONIC_DEVICE (device);
483         priv = TNY_MAEMO_CONIC_DEVICE_GET_PRIVATE (self);
484
485         already_online = tny_maemo_conic_device_is_online (device);
486
487         priv->forced = TRUE;
488         priv->is_online = TRUE;
489
490         /* Signal if it changed: */
491         if (!already_online)
492                 g_signal_emit (device, tny_device_signals [TNY_DEVICE_CONNECTION_CHANGED], 0, TRUE);
493
494         return;
495 }
496
497
498 static void
499 tny_maemo_conic_device_force_offline (TnyDevice *device)
500 {
501         TnyMaemoConicDevice *self;
502         TnyMaemoConicDevicePriv *priv;
503         gboolean already_offline = FALSE;
504        
505         g_return_if_fail (TNY_IS_DEVICE(device));
506         self = TNY_MAEMO_CONIC_DEVICE (device);
507         priv = TNY_MAEMO_CONIC_DEVICE_GET_PRIVATE (self);
508
509         already_offline = !tny_maemo_conic_device_is_online (device);
510         priv->forced = TRUE;
511         priv->is_online = FALSE;
512
513         /* Signal if it changed: */
514         if (!already_offline)
515                 conic_emit_status (device, FALSE);
516
517         return;
518 }
519
520 static gboolean
521 tny_maemo_conic_device_is_online (TnyDevice *self)
522 {
523         g_return_val_if_fail (TNY_IS_DEVICE(self), FALSE);
524
525         on_dummy_connection_check (self);
526
527         return TNY_MAEMO_CONIC_DEVICE_GET_PRIVATE (self)->is_online;
528 }
529
530
531 static void
532 tny_maemo_conic_device_instance_init (GTypeInstance *instance, gpointer g_class)
533 {
534         TnyMaemoConicDevice *self = (TnyMaemoConicDevice *)instance;
535         TnyMaemoConicDevicePriv *priv = TNY_MAEMO_CONIC_DEVICE_GET_PRIVATE (self);
536
537         /* We should not have a real is_online, based on what libconic has told us: */
538         priv->forced = FALSE;
539         priv->iap = NULL;
540         priv->is_online = dnsmasq_has_resolv ();
541
542         /* Check if we're online right now */
543         on_dummy_connection_check (self);
544
545         /* Allow debuggers to fake a connection change by setting an environment
546          * variable, which we check ever 1 second. This should match one of the
547          * fake iap IDs that we created in tny_maemo_conic_device_get_iap_list().*/
548         priv->dummy_env_check_timeout =
549                 g_timeout_add (1000, on_dummy_connection_check, self);
550
551         return;
552 }
553
554
555 /**
556  * tny_maemo_conic_device_new:
557  *
558  * Return value: A new #TnyDevice instance
559  **/
560 TnyDevice*
561 tny_maemo_conic_device_new (void)
562 {
563         TnyMaemoConicDevice *self = g_object_new (TNY_TYPE_MAEMO_CONIC_DEVICE, NULL);
564         return TNY_DEVICE (self);
565 }
566
567 static void
568 tny_maemo_conic_device_finalize (GObject *obj)
569 {
570         TnyMaemoConicDevicePriv *priv = TNY_MAEMO_CONIC_DEVICE_GET_PRIVATE (obj);
571
572         if (priv->dummy_env_check_timeout) {
573                 g_source_remove (priv->dummy_env_check_timeout);
574                 priv->dummy_env_check_timeout = 0;
575         }
576
577         if (priv->iap) {
578                 g_free (priv->iap);
579                 priv->iap = NULL;
580         }
581        
582
583         (*parent_class->finalize) (obj);
584 }
585
586
587 static void
588 tny_device_init (gpointer g, gpointer iface_data)
589 {
590         TnyDeviceIface *klass = (TnyDeviceIface *)g;
591
592         klass->is_online     = tny_maemo_conic_device_is_online;
593         klass->reset         = tny_maemo_conic_device_reset;
594         klass->force_offline = tny_maemo_conic_device_force_offline;
595         klass->force_online  = tny_maemo_conic_device_force_online;
596 }
597
598
599
600 static void
601 tny_maemo_conic_device_class_init (TnyMaemoConicDeviceClass *class)
602 {
603         GObjectClass *object_class;
604
605         parent_class = g_type_class_peek_parent (class);
606         object_class = (GObjectClass*) class;
607
608         object_class->finalize = tny_maemo_conic_device_finalize;
609
610         g_type_class_add_private (object_class, sizeof (TnyMaemoConicDevicePriv));
611 }
612
613 static gpointer
614 tny_maemo_conic_device_register_type (gpointer notused)
615 {
616         GType type = 0;
617
618         static const GTypeInfo info =
619                 {
620                         sizeof (TnyMaemoConicDeviceClass),
621                         NULL,   /* base_init */
622                         NULL,   /* base_finalize */
623                         (GClassInitFunc) tny_maemo_conic_device_class_init,   /* class_init */
624                         NULL,   /* class_finalize */
625                         NULL,   /* class_data */
626                         sizeof (TnyMaemoConicDevice),
627                         0,      /* n_preallocs */
628                         tny_maemo_conic_device_instance_init    /* instance_init */
629                 };
630        
631         static const GInterfaceInfo tny_device_info =
632                 {
633                         (GInterfaceInitFunc) tny_device_init, /* interface_init */
634                         NULL,         /* interface_finalize */
635                         NULL          /* interface_data */
636                 };
637        
638         type = g_type_register_static (G_TYPE_OBJECT,
639                                        "TnyMaemoConicDevice",
640                                        &info, 0);
641        
642         g_type_add_interface_static (type, TNY_TYPE_DEVICE,
643                                      &tny_device_info);
644
645         return GUINT_TO_POINTER (type);
646 }
647
648 GType
649 tny_maemo_conic_device_get_type (void)
650 {
651         static GOnce once = G_ONCE_INIT;
652         g_once (&once, tny_maemo_conic_device_register_type, NULL);
653         return GPOINTER_TO_UINT (once.retval);
654 }
655
656 static gboolean
657 dummy_con_ic_connection_connect_by_id (TnyMaemoConicDevice *self, const gchar* iap_id)
658 {
659         int response = 0;
660
661         /* Show a dialog, because libconic would show a dialog here,
662          * and give the user a chance to refuse a new connection, because libconic would allow that too.
663          * This allows us to see roughly similar behaviour in scratchbox as on the device. */
664         GtkDialog *dialog = GTK_DIALOG (gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL,
665                         GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
666                         "TnyMaemoConicDevice fake scratchbox implementation:\nThe application requested a connection. Make a fake connection?"));
667
668         response = gtk_dialog_run (dialog);
669         gtk_widget_hide (GTK_WIDGET (dialog));
670         gtk_widget_destroy (GTK_WIDGET (dialog));
671        
672         if (response == GTK_RESPONSE_OK) {
673                 GError* error = NULL;
674                 /* Make a connection, by setting a name in our dummy text file,
675                  * which will be read later: */
676                 gchar *filename = get_dummy_filename ();
677
678                 g_file_set_contents (filename, "debug id0", -1, &error);
679                 if(error) {
680                         g_warning("%s: error from g_file_set_contents(): %s\n", __FUNCTION__, error->message);
681                         g_error_free (error);
682                         error = NULL;
683                 }
684
685                 g_free (filename);
686
687                 return TRUE;
688         }
689         else
690                 return FALSE;
691 }
692
693 gboolean
694 tny_maemo_conic_device_connect (TnyMaemoConicDevice *self, const gchar* iap_id, gboolean user_requested)
695 {
696         return dummy_con_ic_connection_connect_by_id (self, iap_id);
697 }
Note: See TracBrowser for help on using the browser.