gnome-shell-3.6.3.1/src/shell-mobile-providers.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found shell-mobile-providers.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2 /*
  3  * This library is free software; you can redistribute it and/or
  4  * modify it under the terms of the GNU Lesser General Public
  5  * License as published by the Free Software Foundation; either
  6  * version 2 of the License, or (at your option) any later version.
  7  *
  8  * This library is distributed in the hope that it will be useful,
  9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 11  * Lesser General Public License for more details.
 12  *
 13  * You should have received a copy of the GNU Lesser General Public
 14  * License along with this library; if not, write to the
 15  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 16  * Boston, MA 02110-1301 USA.
 17  *
 18  * Copyright (C) 2009 Novell, Inc.
 19  * Author: Tambet Ingo (tambet@gmail.com).
 20  *
 21  * Copyright (C) 2009 - 2010 Red Hat, Inc.
 22  */
 23 
 24 #include "config.h"
 25 
 26 #include <string.h>
 27 #include <errno.h>
 28 #include <stdlib.h>
 29 
 30 #include <glib/gi18n.h>
 31 
 32 #include "shell-mobile-providers.h"
 33 
 34 #ifndef MOBILE_BROADBAND_PROVIDER_INFO
 35 #define MOBILE_BROADBAND_PROVIDER_INFO DATADIR "/mobile-broadband-provider-info/serviceproviders.xml"
 36 #endif
 37 
 38 #define ISO_3166_COUNTRY_CODES DATADIR "/zoneinfo/iso3166.tab"
 39 
 40 
 41 
 42 static GHashTable *
 43 read_country_codes (void)
 44 {
 45     GHashTable *table;
 46     GIOChannel *channel;
 47     GString *buffer;
 48     GError *error = NULL;
 49     GIOStatus status;
 50 
 51     channel = g_io_channel_new_file (ISO_3166_COUNTRY_CODES, "r", &error);
 52     if (!channel) {
 53         if (error) {
 54             g_warning ("Could not read " ISO_3166_COUNTRY_CODES ": %s", error->message);
 55             g_error_free (error);
 56         } else
 57             g_warning ("Could not read " ISO_3166_COUNTRY_CODES ": Unknown error");
 58 
 59         return NULL;
 60     }
 61 
 62     table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 63     buffer = g_string_sized_new (32);
 64 
 65     status = G_IO_STATUS_NORMAL;
 66     while (status == G_IO_STATUS_NORMAL) {
 67         status = g_io_channel_read_line_string (channel, buffer, NULL, &error);
 68 
 69         switch (status) {
 70         case G_IO_STATUS_NORMAL:
 71             if (buffer->str[0] != '#') {
 72                 char **pieces;
 73 
 74                 pieces = g_strsplit (buffer->str, "\t", 2);
 75 
 76                 /* Hack for rh#556292; iso3166.tab is just wrong */
 77                 pieces[1] = pieces[1] ? g_strchomp (pieces[1]) : NULL;
 78                 if (pieces[1] && !strcmp (pieces[1], "Britain (UK)")) {
 79                     g_free (pieces[1]);
 80                     pieces[1] = g_strdup (_("United Kingdom"));
 81                 }
 82 
 83                 g_hash_table_insert (table, pieces[0], pieces[1]);
 84                 g_free (pieces);
 85             }
 86 
 87             g_string_truncate (buffer, 0);
 88             break;
 89         case G_IO_STATUS_EOF:
 90             break;
 91         case G_IO_STATUS_ERROR:
 92             g_warning ("Error while reading: %s", error->message);
 93             g_error_free (error);
 94             break;
 95         case G_IO_STATUS_AGAIN:
 96             /* FIXME: Try again a few times, but really, it never happes, right? */
 97             break;
 98         }
 99     }
100 
101     g_string_free (buffer, TRUE);
102     g_io_channel_unref (channel);
103 
104     return table;
105 }
106 
107 /* XML Parser */
108 
109 typedef enum {
110     PARSER_TOPLEVEL = 0,
111     PARSER_COUNTRY,
112     PARSER_PROVIDER,
113     PARSER_METHOD_GSM,
114     PARSER_METHOD_GSM_APN,
115     PARSER_METHOD_CDMA,
116     PARSER_ERROR
117 } MobileContextState;
118 
119 typedef struct {
120     GHashTable *country_codes;
121     GHashTable *table;
122 
123     char *current_country;
124     GSList *current_providers;
125     ShellMobileProvider *current_provider;
126     ShellMobileAccessMethod *current_method;
127 
128     char *text_buffer;
129     MobileContextState state;
130 } MobileParser;
131 
132 static ShellGsmMccMnc *
133 mcc_mnc_new (const char *mcc, const char *mnc)
134 {
135     ShellGsmMccMnc *m;
136 
137     m = g_slice_new (ShellGsmMccMnc);
138     m->mcc = g_strstrip (g_strdup (mcc));
139     m->mnc = g_strstrip (g_strdup (mnc));
140     return m;
141 }
142 
143 /* added in porting */
144 static ShellGsmMccMnc *
145 mcc_mnc_copy (const ShellGsmMccMnc *other) {
146     ShellGsmMccMnc *ret;
147 
148     ret = g_slice_new (ShellGsmMccMnc);
149     ret->mcc = g_strdup (other->mcc);
150     ret->mnc = g_strdup (other->mnc);
151     return ret;
152 }
153 
154 static void
155 mcc_mnc_free (ShellGsmMccMnc *m)
156 {
157     g_return_if_fail (m != NULL);
158     g_free (m->mcc);
159     g_free (m->mnc);
160     g_slice_free (ShellGsmMccMnc, m);
161 }
162 
163 /* added in porting */
164 G_DEFINE_BOXED_TYPE (ShellGsmMccMnc, shell_gsm_mcc_mnc, mcc_mnc_copy, mcc_mnc_free)
165 
166 static ShellMobileAccessMethod *
167 access_method_new (void)
168 {
169     ShellMobileAccessMethod *method;
170 
171     method = g_slice_new0 (ShellMobileAccessMethod);
172     method->refs = 1;
173     method->lcl_names = g_hash_table_new_full (g_str_hash, g_str_equal,
174                                                (GDestroyNotify) g_free,
175                                                (GDestroyNotify) g_free);
176 
177     return method;
178 }
179 
180 ShellMobileAccessMethod *
181 shell_mobile_access_method_ref (ShellMobileAccessMethod *method)
182 {
183     g_return_val_if_fail (method != NULL, NULL);
184     g_return_val_if_fail (method->refs > 0, NULL);
185 
186     method->refs++;
187 
188     return method;
189 }
190 
191 void
192 shell_mobile_access_method_unref (ShellMobileAccessMethod *method)
193 {
194     g_return_if_fail (method != NULL);
195     g_return_if_fail (method->refs > 0);
196 
197     if (--method->refs == 0) {
198         g_free (method->name);
199         g_hash_table_destroy (method->lcl_names);
200         g_free (method->username);
201         g_free (method->password);
202         g_free (method->gateway);
203         g_free (method->gsm_apn);
204         g_slist_foreach (method->dns, (GFunc) g_free, NULL);
205         g_slist_free (method->dns);
206 
207         g_slice_free (ShellMobileAccessMethod, method);
208     }
209 }
210 
211 GType
212 shell_mobile_access_method_get_type (void)
213 {
214     static GType type = 0;
215 
216     if (G_UNLIKELY (type == 0)) {
217         type = g_boxed_type_register_static ("ShellMobileAccessMethod",
218                                              (GBoxedCopyFunc) shell_mobile_access_method_ref,
219                                              (GBoxedFreeFunc) shell_mobile_access_method_unref);
220     }
221     return type;
222 }
223 
224 
225 static ShellMobileProvider *
226 provider_new (void)
227 {
228     ShellMobileProvider *provider;
229 
230     provider = g_slice_new0 (ShellMobileProvider);
231     provider->refs = 1;
232     provider->lcl_names = g_hash_table_new_full (g_str_hash, g_str_equal,
233                                                  (GDestroyNotify) g_free,
234                                                  (GDestroyNotify) g_free);
235 
236     return provider;
237 }
238 
239 ShellMobileProvider *
240 shell_mobile_provider_ref (ShellMobileProvider *provider)
241 {
242     provider->refs++;
243 
244     return provider;
245 }
246 
247 void
248 shell_mobile_provider_unref (ShellMobileProvider *provider)
249 {
250     if (--provider->refs == 0) {
251         g_free (provider->name);
252         g_hash_table_destroy (provider->lcl_names);
253 
254         g_slist_foreach (provider->methods, (GFunc) shell_mobile_access_method_unref, NULL);
255         g_slist_free (provider->methods);
256 
257         g_slist_foreach (provider->gsm_mcc_mnc, (GFunc) mcc_mnc_free, NULL);
258         g_slist_free (provider->gsm_mcc_mnc);
259 
260         g_slist_free (provider->cdma_sid);
261 
262         g_slice_free (ShellMobileProvider, provider);
263     }
264 }
265 
266 GType
267 shell_mobile_provider_get_type (void)
268 {
269     static GType type = 0;
270 
271     if (G_UNLIKELY (type == 0)) {
272         type = g_boxed_type_register_static ("ShellMobileProvider",
273                                              (GBoxedCopyFunc) shell_mobile_provider_ref,
274                                              (GBoxedFreeFunc) shell_mobile_provider_unref);
275     }
276     return type;
277 }
278 
279 static void
280 provider_list_free (gpointer data)
281 {
282     GSList *list = (GSList *) data;
283 
284     while (list) {
285         shell_mobile_provider_unref ((ShellMobileProvider *) list->data);
286         list = g_slist_delete_link (list, list);
287     }
288 }
289 
290 static void
291 parser_toplevel_start (MobileParser *parser,
292                        const char *name,
293                        const char **attribute_names,
294                        const char **attribute_values)
295 {
296     int i;
297 
298     if (!strcmp (name, "serviceproviders")) {
299         for (i = 0; attribute_names && attribute_names[i]; i++) {
300             if (!strcmp (attribute_names[i], "format")) {
301                 if (strcmp (attribute_values[i], "2.0")) {
302                     g_warning ("%s: mobile broadband provider database format '%s'"
303                                " not supported.", __func__, attribute_values[i]);
304                     parser->state = PARSER_ERROR;
305                     break;
306                 }
307             }
308         }
309     } else if (!strcmp (name, "country")) {
310         for (i = 0; attribute_names && attribute_names[i]; i++) {
311             if (!strcmp (attribute_names[i], "code")) {
312                 char *country_code;
313                 char *country;
314 
315                 country_code = g_ascii_strup (attribute_values[i], -1);
316                 country = g_hash_table_lookup (parser->country_codes, country_code);
317                 if (country) {
318                     parser->current_country = g_strdup (country);
319                     g_free (country_code);
320                 } else
321                     parser->current_country = country_code;
322 
323                 parser->state = PARSER_COUNTRY;
324                 break;
325             }
326         }
327     }
328 }
329 
330 static void
331 parser_country_start (MobileParser *parser,
332                       const char *name,
333                       const char **attribute_names,
334                       const char **attribute_values)
335 {
336     if (!strcmp (name, "provider")) {
337         parser->state = PARSER_PROVIDER;
338         parser->current_provider = provider_new ();
339     }
340 }
341 
342 static void
343 parser_provider_start (MobileParser *parser,
344                        const char *name,
345                        const char **attribute_names,
346                        const char **attribute_values)
347 {
348     if (!strcmp (name, "gsm"))
349         parser->state = PARSER_METHOD_GSM;
350     else if (!strcmp (name, "cdma")) {
351         parser->state = PARSER_METHOD_CDMA;
352         parser->current_method = access_method_new ();
353     }
354 }
355 
356 static void
357 parser_gsm_start (MobileParser *parser,
358                   const char *name,
359                   const char **attribute_names,
360                   const char **attribute_values)
361 {
362     if (!strcmp (name, "network-id")) {
363         const char *mcc = NULL, *mnc = NULL;
364         int i;
365 
366         for (i = 0; attribute_names && attribute_names[i]; i++) {
367             if (!strcmp (attribute_names[i], "mcc"))
368                 mcc = attribute_values[i];
369             else if (!strcmp (attribute_names[i], "mnc"))
370                 mnc = attribute_values[i];
371 
372             if (mcc && strlen (mcc) && mnc && strlen (mnc)) {
373                 parser->current_provider->gsm_mcc_mnc = g_slist_prepend (parser->current_provider->gsm_mcc_mnc,
374                                                                          mcc_mnc_new (mcc, mnc));
375                 break;
376             }
377         }
378     } else if (!strcmp (name, "apn")) {
379         int i;
380 
381         for (i = 0; attribute_names && attribute_names[i]; i++) {
382             if (!strcmp (attribute_names[i], "value")) {
383 
384                 parser->state = PARSER_METHOD_GSM_APN;
385                 parser->current_method = access_method_new ();
386                 parser->current_method->gsm_apn = g_strstrip (g_strdup (attribute_values[i]));
387                 break;
388             }
389         }
390     }
391 }
392 
393 static void
394 parser_cdma_start (MobileParser *parser,
395                    const char *name,
396                    const char **attribute_names,
397                    const char **attribute_values)
398 {
399     if (!strcmp (name, "sid")) {
400         int i;
401 
402         for (i = 0; attribute_names && attribute_names[i]; i++) {
403             if (!strcmp (attribute_names[i], "value")) {
404                 unsigned long tmp;
405 
406                 errno = 0;
407                 tmp = strtoul (attribute_values[i], NULL, 10);
408                 if (errno == 0 && tmp > 0)
409                     parser->current_provider->cdma_sid = g_slist_prepend (parser->current_provider->cdma_sid,
410                                                                           GUINT_TO_POINTER ((guint32) tmp));
411                 break;
412             }
413         }
414     }
415 }
416 
417 static void
418 mobile_parser_start_element (GMarkupParseContext *context,
419                              const gchar *element_name,
420                              const gchar **attribute_names,
421                              const gchar **attribute_values,
422                              gpointer data,
423                              GError **error)
424 {
425     MobileParser *parser = (MobileParser *) data;
426 
427     if (parser->text_buffer) {
428         g_free (parser->text_buffer);
429         parser->text_buffer = NULL;
430     }
431 
432     switch (parser->state) {
433     case PARSER_TOPLEVEL:
434         parser_toplevel_start (parser, element_name, attribute_names, attribute_values);
435         break;
436     case PARSER_COUNTRY:
437         parser_country_start (parser, element_name, attribute_names, attribute_values);
438         break;
439     case PARSER_PROVIDER:
440         parser_provider_start (parser, element_name, attribute_names, attribute_values);
441         break;
442     case PARSER_METHOD_GSM:
443         parser_gsm_start (parser, element_name, attribute_names, attribute_values);
444         break;
445     case PARSER_METHOD_CDMA:
446         parser_cdma_start (parser, element_name, attribute_names, attribute_values);
447         break;
448     default:
449         break;
450     }
451 }
452 
453 static void
454 parser_country_end (MobileParser *parser,
455                     const char *name)
456 {
457     if (!strcmp (name, "country")) {
458         g_hash_table_insert (parser->table, parser->current_country, parser->current_providers);
459         parser->current_country = NULL;
460         parser->current_providers = NULL;
461         parser->text_buffer = NULL;
462         parser->state = PARSER_TOPLEVEL;
463     }
464 }
465 
466 static void
467 parser_provider_end (MobileParser *parser,
468                      const char *name)
469 {
470     if (!strcmp (name, "name")) {
471         if (!parser->current_provider->name) {
472             /* Use the first one. */
473             parser->current_provider->name = parser->text_buffer;
474             parser->text_buffer = NULL;
475         }
476     } else if (!strcmp (name, "provider")) {
477         parser->current_provider->methods = g_slist_reverse (parser->current_provider->methods);
478 
479         parser->current_provider->gsm_mcc_mnc = g_slist_reverse (parser->current_provider->gsm_mcc_mnc);
480         parser->current_provider->cdma_sid = g_slist_reverse (parser->current_provider->cdma_sid);
481 
482         parser->current_providers = g_slist_prepend (parser->current_providers, parser->current_provider);
483         parser->current_provider = NULL;
484         parser->text_buffer = NULL;
485         parser->state = PARSER_COUNTRY;
486     }
487 }
488 
489 static void
490 parser_gsm_end (MobileParser *parser,
491                  const char *name)
492 {
493     if (!strcmp (name, "gsm")) {
494         parser->text_buffer = NULL;
495         parser->state = PARSER_PROVIDER;
496     }
497 }
498 
499 static void
500 parser_gsm_apn_end (MobileParser *parser,
501                     const char *name)
502 {
503     if (!strcmp (name, "name")) {
504         if (!parser->current_method->name) {
505             /* Use the first one. */
506             parser->current_method->name = parser->text_buffer;
507             parser->text_buffer = NULL;
508         }
509     } else if (!strcmp (name, "username")) {
510         parser->current_method->username = parser->text_buffer;
511         parser->text_buffer = NULL;
512     } else if (!strcmp (name, "password")) {
513         parser->current_method->password = parser->text_buffer;
514         parser->text_buffer = NULL;
515     } else if (!strcmp (name, "dns")) {
516         parser->current_method->dns = g_slist_prepend (parser->current_method->dns, parser->text_buffer);
517         parser->text_buffer = NULL;
518     } else if (!strcmp (name, "gateway")) {
519         parser->current_method->gateway = parser->text_buffer;
520         parser->text_buffer = NULL;
521     } else if (!strcmp (name, "apn")) {
522         parser->current_method->type = SHELL_MOBILE_ACCESS_METHOD_TYPE_GSM;
523         parser->current_method->dns = g_slist_reverse (parser->current_method->dns);
524 
525         if (!parser->current_method->name)
526             parser->current_method->name = g_strdup (_("Default"));
527 
528         parser->current_provider->methods = g_slist_prepend (parser->current_provider->methods,
529                                                              parser->current_method);
530         parser->current_method = NULL;
531         parser->text_buffer = NULL;
532         parser->state = PARSER_METHOD_GSM;
533     }
534 }
535 
536 static void
537 parser_cdma_end (MobileParser *parser,
538                  const char *name)
539 {
540     if (!strcmp (name, "username")) {
541         parser->current_method->username = parser->text_buffer;
542         parser->text_buffer = NULL;
543     } else if (!strcmp (name, "password")) {
544         parser->current_method->password = parser->text_buffer;
545         parser->text_buffer = NULL;
546     } else if (!strcmp (name, "dns")) {
547         parser->current_method->dns = g_slist_prepend (parser->current_method->dns, parser->text_buffer);
548         parser->text_buffer = NULL;
549     } else if (!strcmp (name, "gateway")) {
550         parser->current_method->gateway = parser->text_buffer;
551         parser->text_buffer = NULL;
552     } else if (!strcmp (name, "cdma")) {
553         parser->current_method->type = SHELL_MOBILE_ACCESS_METHOD_TYPE_CDMA;
554         parser->current_method->dns = g_slist_reverse (parser->current_method->dns);
555 
556         if (!parser->current_method->name)
557             parser->current_method->name = g_strdup (parser->current_provider->name);
558 
559         parser->current_provider->methods = g_slist_prepend (parser->current_provider->methods,
560                                                              parser->current_method);
561         parser->current_method = NULL;
562         parser->text_buffer = NULL;
563         parser->state = PARSER_PROVIDER;
564     }
565 }
566 
567 static void
568 mobile_parser_end_element (GMarkupParseContext *context,
569                            const gchar *element_name,
570                            gpointer data,
571                            GError **error)
572 {
573     MobileParser *parser = (MobileParser *) data;
574 
575     switch (parser->state) {
576     case PARSER_COUNTRY:
577         parser_country_end (parser, element_name);
578         break;
579     case PARSER_PROVIDER:
580         parser_provider_end (parser, element_name);
581         break;
582     case PARSER_METHOD_GSM:
583         parser_gsm_end (parser, element_name);
584         break;
585     case PARSER_METHOD_GSM_APN:
586         parser_gsm_apn_end (parser, element_name);
587         break;
588     case PARSER_METHOD_CDMA:
589         parser_cdma_end (parser, element_name);
590         break;
591     default:
592         break;
593     }
594 }
595 
596 static void
597 mobile_parser_characters (GMarkupParseContext *context,
598                           const gchar *text,
599                           gsize text_len,
600                           gpointer data,
601                           GError **error)
602 {
603     MobileParser *parser = (MobileParser *) data;
604 
605     g_free (parser->text_buffer);
606     parser->text_buffer = g_strdup (text);
607 }
608 
609 static const GMarkupParser mobile_parser = {
610     mobile_parser_start_element,
611     mobile_parser_end_element,
612     mobile_parser_characters,
613     NULL, /* passthrough */
614     NULL /* error */
615 };
616 
617 /**
618  * shell_mobile_providers_parse:
619  * @out_ccs: (out) (allow-none): (element-type utf8 utf8): a #GHashTable containing
620  *   country codes
621  *
622  * Returns: (element-type utf8 GList) (transfer container): a
623  *   hash table where keys are country names #gchar, values are a #GSList
624  *   of #ShellMobileProvider. Everything is destroyed with g_hash_table_destroy().
625 */
626 GHashTable *
627 shell_mobile_providers_parse (GHashTable **out_ccs)
628 {
629     GMarkupParseContext *ctx;
630     GIOChannel *channel;
631     MobileParser parser;
632     GError *error = NULL;
633     char buffer[4096];
634     GIOStatus status;
635     gsize len = 0;
636 
637     memset (&parser, 0, sizeof (MobileParser));
638 
639     parser.country_codes = read_country_codes ();
640     if (!parser.country_codes)
641         goto out;
642 
643     channel = g_io_channel_new_file (MOBILE_BROADBAND_PROVIDER_INFO, "r", &error);
644     if (!channel) {
645         if (error) {
646             g_warning ("Could not read " MOBILE_BROADBAND_PROVIDER_INFO ": %s", error->message);
647             g_error_free (error);
648         } else
649             g_warning ("Could not read " MOBILE_BROADBAND_PROVIDER_INFO ": Unknown error");
650 
651         goto out;
652     }
653 
654     parser.table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, provider_list_free);
655     parser.state = PARSER_TOPLEVEL;
656 
657     ctx = g_markup_parse_context_new (&mobile_parser, 0, &parser, NULL);
658 
659     status = G_IO_STATUS_NORMAL;
660     while (status == G_IO_STATUS_NORMAL) {
661         status = g_io_channel_read_chars (channel, buffer, sizeof (buffer), &len, &error);
662 
663         switch (status) {
664         case G_IO_STATUS_NORMAL:
665             if (!g_markup_parse_context_parse (ctx, buffer, len, &error)) {
666                 status = G_IO_STATUS_ERROR;
667                 g_warning ("Error while parsing XML: %s", error->message);
668                 g_error_free (error);;
669             }
670             break;
671         case G_IO_STATUS_EOF:
672             break;
673         case G_IO_STATUS_ERROR:
674             g_warning ("Error while reading: %s", error->message);
675             g_error_free (error);
676             break;
677         case G_IO_STATUS_AGAIN:
678             /* FIXME: Try again a few times, but really, it never happes, right? */
679             break;
680         }
681     }
682 
683     g_io_channel_unref (channel);
684     g_markup_parse_context_free (ctx);
685 
686     if (parser.current_provider) {
687         g_warning ("pending current provider");
688         shell_mobile_provider_unref (parser.current_provider);
689     }
690 
691     if (parser.current_providers) {
692         g_warning ("pending current providers");
693         provider_list_free (parser.current_providers);
694     }
695 
696     g_free (parser.current_country);
697     g_free (parser.text_buffer);
698 
699  out:
700     if (parser.country_codes) {
701         if (out_ccs)
702             *out_ccs = parser.country_codes;
703         else
704             g_hash_table_destroy (parser.country_codes);
705     }
706 
707     return parser.table;
708 }
709 
710 static void
711 dump_generic (ShellMobileAccessMethod *method)
712 {
713     GSList *iter;
714     GString *dns;
715 
716     g_print ("        username: %s\n", method->username ? method->username : "");
717     g_print ("        password: %s\n", method->password ? method->password : "");
718 
719     dns = g_string_new (NULL);
720     for (iter = method->dns; iter; iter = g_slist_next (iter))
721         g_string_append_printf (dns, "%s%s", dns->len ? ", " : "", (char *) iter->data);
722     g_print ("        dns     : %s\n", dns->str);
723     g_string_free (dns, TRUE);
724 
725     g_print ("        gateway : %s\n", method->gateway ? method->gateway : "");
726 }
727 
728 static void
729 dump_cdma (ShellMobileAccessMethod *method)
730 {
731     g_print ("     CDMA: %s\n", method->name);
732 
733     dump_generic (method);
734 }
735 
736 static void
737 dump_gsm (ShellMobileAccessMethod *method)
738 {
739     g_print ("     APN: %s (%s)\n", method->name, method->gsm_apn);
740 
741     dump_generic (method);
742 }
743 
744 static void
745 dump_country (gpointer key, gpointer value, gpointer user_data)
746 {
747     GSList *citer, *miter;
748 
749     for (citer = value; citer; citer = g_slist_next (citer)) {
750         ShellMobileProvider *provider = citer->data;
751 
752         g_print ("Provider: %s (%s)\n", provider->name, (const char *) key);
753         for (miter = provider->methods; miter; miter = g_slist_next (miter)) {
754             ShellMobileAccessMethod *method = miter->data;
755             GSList *liter;
756 
757 
758             for (liter = provider->gsm_mcc_mnc; liter; liter = g_slist_next (liter)) {
759                 ShellGsmMccMnc *m = liter->data;
760                 g_print ("        MCC/MNC: %s-%s\n", m->mcc, m->mnc);
761             }
762 
763             for (liter = provider->cdma_sid; liter; liter = g_slist_next (liter))
764                 g_print ("        SID: %d\n", GPOINTER_TO_UINT (liter->data));
765 
766             switch (method->type) {
767             case SHELL_MOBILE_ACCESS_METHOD_TYPE_CDMA:
768                 dump_cdma (method);
769                 break;
770             case SHELL_MOBILE_ACCESS_METHOD_TYPE_GSM:
771                 dump_gsm (method);
772                 break;
773             default:
774                 break;
775             }
776             g_print ("\n");
777         }
778     }
779 }
780 
781 void
782 shell_mobile_providers_dump (GHashTable *providers)
783 {
784     g_return_if_fail (providers != NULL);
785     g_hash_table_foreach (providers, dump_country, NULL);
786 }
787 
788 /* All the following don't exist in nm-applet, because C doesn't need
789    those. They're only needed for the introspection annotations
790 */
791 
792 /**
793  * shell_mobile_provider_get_gsm_mcc_mnc:
794  * @provider: a #ShellMobileProvider
795  *
796  * Returns: (element-type Shell.GsmMccMnc) (transfer none): the
797  *   list of #ShellGsmMccMnc this provider exposes
798  */
799 GSList *
800 shell_mobile_provider_get_gsm_mcc_mnc (ShellMobileProvider *provider)
801 {
802     return provider->gsm_mcc_mnc;
803 }
804 
805 /**
806  * shell_mobile_provider_get_cdma_sid:
807  * @provider: a #ShellMobileProvider
808  *
809  * Returns: (element-type guint32) (transfer none): the
810  *   list of CDMA sids this provider exposes
811  */
812 GSList *
813 shell_mobile_provider_get_cdma_sid (ShellMobileProvider *provider)
814 {
815     return provider->cdma_sid;
816 }