text: // SPDX-License-Identifier: GPL-2.0+
  /* NetworkManager Applet -- allow user control over networking
   *
   * Dan Williams <dcbw@redhat.com>
   *
   * Copyright 2010 - 2017 Red Hat, Inc.
   */
  
  #include "nm-default.h"
  
  #include <ctype.h>
  
  #include <libsecret/secret.h>
  
  #include "utils.h"
  #include "mobile-helpers.h"
  #include "applet-dialogs.h"
  
  GdkPixbuf *
  mobile_helper_get_status_pixbuf (guint32 quality,
                                   gboolean quality_valid,
                                   guint32 state,
                                   guint32 access_tech,
                                   NMApplet *applet)
  {
  	GdkPixbuf *pixbuf, *qual_pixbuf, *tmp;
  
  	if (!quality_valid)
  		quality = 0;
  	qual_pixbuf = nma_icon_check_and_load (mobile_helper_get_quality_icon_name (quality), applet);
  
  	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
  	                         TRUE,
  	                         qual_pixbuf ? gdk_pixbuf_get_bits_per_sample (qual_pixbuf) : 8,
  	                         qual_pixbuf ? gdk_pixbuf_get_width (qual_pixbuf) : 22,
  	                         qual_pixbuf ? gdk_pixbuf_get_height (qual_pixbuf) : 22);
  	gdk_pixbuf_fill (pixbuf, 0xFFFFFF00);
  
  	/* Composite the tower icon into the final icon at the bottom layer */
  	tmp = nma_icon_check_and_load ("nm-wwan-tower", applet);
  	if (tmp) {
  		gdk_pixbuf_composite (tmp, pixbuf,
  		                      0, 0,
  		                      gdk_pixbuf_get_width (tmp),
  		                      gdk_pixbuf_get_height (tmp),
  		                      0, 0, 1.0, 1.0,
  		                      GDK_INTERP_BILINEAR, 255);
  	}
  
  	/* Composite the signal quality onto the icon on top of the WWAN tower */
  	if (qual_pixbuf) {
  		gdk_pixbuf_composite (qual_pixbuf, pixbuf,
  		                      0, 0,
  		                      gdk_pixbuf_get_width (qual_pixbuf),
  		                      gdk_pixbuf_get_height (qual_pixbuf),
  		                      0, 0, 1.0, 1.0,
  		                      GDK_INTERP_BILINEAR, 255);
  	}
  
  	/* And finally the roaming or technology icon */
  	if (state == MB_STATE_ROAMING) {
  		tmp = nma_icon_check_and_load ("nm-mb-roam", applet);
  		if (tmp) {
  			gdk_pixbuf_composite (tmp, pixbuf, 0, 0,
  			                      gdk_pixbuf_get_width (tmp),
  			                      gdk_pixbuf_get_height (tmp),
  			                       0, 0, 1.0, 1.0,
  			                      GDK_INTERP_BILINEAR, 255);
  		}
  	} else {
  		const gchar *tech_icon_name;
  
  		/* Only try to add the access tech info icon if we get a valid
  		 * access tech reported. */
  		tech_icon_name = mobile_helper_get_tech_icon_name (access_tech);
  		if (tech_icon_name) {
  			tmp = nma_icon_check_and_load (tech_icon_name, applet);
  			if (tmp) {
  				gdk_pixbuf_composite (tmp, pixbuf, 0, 0,
  				                      gdk_pixbuf_get_width (tmp),
  				                      gdk_pixbuf_get_height (tmp),
  				                      0, 0, 1.0, 1.0,
  				                      GDK_INTERP_BILINEAR, 255);
  			}
  		}
  	}
  
  	/* 'pixbuf' will be freed by the caller */
  	return pixbuf;
  }
  
  const char *
  mobile_helper_get_quality_icon_name (guint32 quality)
  {
  	if (quality > 80)
  		return "nm-signal-100";
  	else if (quality > 55)
  		return "nm-signal-75";
  	else if (quality > 30)
  		return "nm-signal-50";
  	else if (quality > 5)
  		return "nm-signal-25";
  	else
  		return "nm-signal-00";
  }
  
  const char *
  mobile_helper_get_tech_icon_name (guint32 tech)
  {
  	switch (tech) {
  	case MB_TECH_1XRTT:
  		return "nm-tech-cdma-1x";
  	case MB_TECH_EVDO:
  		return "nm-tech-evdo";
  	case MB_TECH_GSM:
  	case MB_TECH_GPRS:
  		return "nm-tech-gprs";
  	case MB_TECH_EDGE:
  		return "nm-tech-edge";
  	case MB_TECH_UMTS:
  		return "nm-tech-umts";
  	case MB_TECH_HSDPA:
  	case MB_TECH_HSUPA:
  	case MB_TECH_HSPA:
  	case MB_TECH_HSPA_PLUS:
  		return "nm-tech-hspa";
  	case MB_TECH_LTE:
  		return "nm-tech-lte";
  	default:
  		return NULL;
  	}
  }
  
  /********************************************************************/
  
  typedef struct {
  	AppletNewAutoConnectionCallback callback;
  	gpointer callback_data;
  	NMDeviceModemCapabilities requested_capability;
  } AutoWizardInfo;
  
  static void
  mobile_wizard_done (NMAMobileWizard *wizard,
                      gboolean cancelled,
                      NMAMobileWizardAccessMethod *method,
                      gpointer user_data)
  {
  	AutoWizardInfo *info = user_data;
  	NMConnection *connection = NULL;
  
  	if (!cancelled && method) {
  		NMSetting *setting;
  		char *uuid, *id;
  		const char *setting_name;
  
  		if (method->devtype != info->requested_capability) {
  			g_warning ("Unexpected device type");
  			cancelled = TRUE;
  			goto done;
  		}
  
  		connection = nm_simple_connection_new ();
  
  		if (method->devtype == NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) {
  			setting_name = NM_SETTING_CDMA_SETTING_NAME;
  			setting = nm_setting_cdma_new ();
  			g_object_set (setting,
  			              NM_SETTING_CDMA_NUMBER, "#777",
  			              NM_SETTING_CDMA_USERNAME, method->username,
  			              NM_SETTING_CDMA_PASSWORD, method->password,
  			              NULL);
  			nm_connection_add_setting (connection, setting);
  		} else if (method->devtype == NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS) {
  			setting_name = NM_SETTING_GSM_SETTING_NAME;
  			setting = nm_setting_gsm_new ();
  			g_object_set (setting,
  			              NM_SETTING_GSM_NUMBER, "*99#",
  			              NM_SETTING_GSM_USERNAME, method->username,
  			              NM_SETTING_GSM_PASSWORD, method->password,
  			              NM_SETTING_GSM_APN, method->gsm_apn,
  			              NULL);
  			nm_connection_add_setting (connection, setting);
  		} else
  			g_assert_not_reached ();
  
  		/* Default to IPv4 & IPv6 'automatic' addressing */
  		setting = nm_setting_ip4_config_new ();
  		g_object_set (setting, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
  		nm_connection_add_setting (connection, setting);
  
  		setting = nm_setting_ip6_config_new ();
  		g_object_set (setting, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL);
  		nm_connection_add_setting (connection, setting);
  
  		nm_connection_add_setting (connection, nm_setting_ppp_new ());
  
  		setting = nm_setting_connection_new ();
  		id = utils_create_mobile_connection_id (method->provider_name, method->plan_name);
  		uuid = nm_utils_uuid_generate ();
  		g_object_set (setting,
  		              NM_SETTING_CONNECTION_ID, id,
  		              NM_SETTING_CONNECTION_TYPE, setting_name,
  		              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
  		              NM_SETTING_CONNECTION_UUID, uuid,
  		              NULL);
  		/* Make the new connection available only for the current user */
  		nm_setting_connection_add_permission ((NMSettingConnection *) setting,
  		                                      "user", g_get_user_name (), NULL);
  		g_free (uuid);
  		g_free (id);
  		nm_connection_add_setting (connection, setting);
  	}
  
  done:
  	(*(info->callback)) (connection, TRUE, cancelled, info->callback_data);
  
  	if (wizard)
  		nma_mobile_wizard_destroy (wizard);
  	g_free (info);
  }
  
  gboolean
  mobile_helper_wizard (NMDeviceModemCapabilities capabilities,
                        AppletNewAutoConnectionCallback callback,
                        gpointer callback_data)
  {
  	NMAMobileWizard *wizard;
  	AutoWizardInfo *info;
  	NMAMobileWizardAccessMethod *method;
  	NMDeviceModemCapabilities wizard_capability;
  
  	/* Convert the input capabilities mask into a single value */
  	if (capabilities & NM_DEVICE_MODEM_CAPABILITY_LTE)
  		/* All LTE modems treated as GSM/UMTS for the wizard */
  		wizard_capability = NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS;
  	else if (capabilities & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)
  		wizard_capability = NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS;
  	else if (capabilities & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
  		wizard_capability = NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO;
  	else {
  		g_warning ("Unknown modem capabilities (0x%X): can't launch wizard", capabilities);
  		return FALSE;
  	}
  
  	info = g_malloc0 (sizeof (AutoWizardInfo));
  	info->callback = callback;
  	info->callback_data = callback_data;
  	info->requested_capability = wizard_capability;
  
  	wizard = nma_mobile_wizard_new (NULL,
  	                                NULL,
  	                                wizard_capability,
  	                                FALSE,
  	                                mobile_wizard_done,
  	                                info);
  	if (wizard) {
  		nma_mobile_wizard_present (wizard);
  		return TRUE;
  	}
  
  	/* Fall back to something */
  	method = g_malloc0 (sizeof (NMAMobileWizardAccessMethod));
  	method->devtype = wizard_capability;
  
  	if (wizard_capability == NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)
  		method->provider_name = _("GSM");
  	else if (wizard_capability == NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
  		method->provider_name = _("CDMA");
  
  	g_assert (   wizard_capability == NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS
  	          || wizard_capability == NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO);
  
  	mobile_wizard_done (NULL, FALSE, method, info);
  	g_free (method);
  
  	return TRUE;
  }
  
  /********************************************************************/
  
  const SecretSchema mobile_secret_schema = {
  	"org.freedesktop.NetworkManager.Mobile",
  	SECRET_SCHEMA_DONT_MATCH_NAME,
  	{
  		{ "devid", SECRET_SCHEMA_ATTRIBUTE_STRING },
  		{ "simid", SECRET_SCHEMA_ATTRIBUTE_STRING },
  		{ NULL, 0 },
  	}
  };
  
  static void
  save_pin_cb (GObject *source,
               GAsyncResult *result,
               gpointer user_data)
  {
  	GError *error = NULL;
  	gchar *error_msg = user_data;
  
  	secret_password_store_finish (result, &error);
  	if (error != NULL) {
  		g_warning ("%s: %s", error_msg, error->message);
  		g_error_free (error);
  	}
  
  	g_free (error_msg);
  }
  
  void
  mobile_helper_save_pin_in_keyring (const char *devid,
                                     const char *simid,
                                     const char *pin)
  {
  	char *name;
  	char *error_msg;
  
  	name = g_strdup_printf (_("PIN code for SIM card “%s” on “%s”"),
  	                        simid ? simid : "unknown",
  	                        devid);
  
  	error_msg = g_strdup_printf ("Saving PIN code in keyring for devid:%s simid:%s failed",
  	                             devid, simid ? simid : "(unknown)");
  
  	secret_password_store (&mobile_secret_schema,
  	                       NULL, name, pin,
  	                       NULL, save_pin_cb, error_msg,
  	                       "devid", devid,
  	                       simid ? "simid" : NULL, simid,
  	                       NULL);
  
  	g_free (name);
  }
  
  void
  mobile_helper_delete_pin_in_keyring (const char *devid)
  {
  	secret_password_clear (&mobile_secret_schema, NULL, NULL, NULL,
  	                       "devid", devid,
  	                       NULL);
  }
  
  /********************************************************************/
  
  static void
  free_secrets_info (SecretsRequest *req)
  {
  	MobileHelperSecretsInfo *info = (MobileHelperSecretsInfo *) req;
  
  	if (info->dialog) {
  		gtk_widget_hide (info->dialog);
  		gtk_widget_destroy (info->dialog);
  	}
  
  	g_free (info->secret_name);
  }
  
  static void
  get_secrets_cb (GtkDialog *dialog,
                  gint response,
                  gpointer user_data)
  {
  	SecretsRequest *req = user_data;
  	MobileHelperSecretsInfo *info = (MobileHelperSecretsInfo *) req;
  	GError *error = NULL;
  
  	if (response == GTK_RESPONSE_OK) {
  		if (info->capability == NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS) {
  			NMSettingGsm *setting;
  
  			setting = nm_connection_get_setting_gsm (req->connection);
  			if (setting) {
  				g_object_set (G_OBJECT (setting),
  				              info->secret_name, gtk_entry_get_text (info->secret_entry),
  				              NULL);
  			} else {
  				error = g_error_new (NM_SECRET_AGENT_ERROR,
  				                     NM_SECRET_AGENT_ERROR_FAILED,
  				                     "%s.%d (%s): no GSM setting",
  				                     __FILE__, __LINE__, __func__);
  			}
  		} else if (info->capability == NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) {
  				NMSettingCdma *setting;
  
  				setting = nm_connection_get_setting_cdma (req->connection);
  				if (setting) {
  					g_object_set (G_OBJECT (setting),
  					              info->secret_name, gtk_entry_get_text (info->secret_entry),
  					              NULL);
  				} else {
  					error = g_error_new (NM_SECRET_AGENT_ERROR,
  					                     NM_SECRET_AGENT_ERROR_FAILED,
  					                     "%s.%d (%s): no CDMA setting",
  					                     __FILE__, __LINE__, __func__);
  				}
  		} else
  			g_assert_not_reached ();
  	} else {
  		error = g_error_new (NM_SECRET_AGENT_ERROR,
  		                     NM_SECRET_AGENT_ERROR_USER_CANCELED,
  		                     "%s.%d (%s): canceled",
  		                     __FILE__, __LINE__, __func__);
  	}
  
  	if (info->capability == NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)
  		applet_secrets_request_complete_setting (req, NM_SETTING_GSM_SETTING_NAME, error);
  	else if (info->capability == NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
  		applet_secrets_request_complete_setting (req, NM_SETTING_CDMA_SETTING_NAME, error);
  	else
  		g_assert_not_reached ();
  
  	applet_secrets_request_free (req);
  	g_clear_error (&error);
  }
  
  static void
  pin_entry_changed (GtkEditable *editable, gpointer user_data)
  {
  	GtkWidget *ok_button = GTK_WIDGET (user_data);
  	const char *s;
  	int i;
  	gboolean valid = FALSE;
  	guint32 len;
  
  	s = gtk_entry_get_text (GTK_ENTRY (editable));
  	if (s) {
  		len = strlen (s);
  		if ((len >= 4) && (len <= 8)) {
  			valid = TRUE;
  			for (i = 0; i < len; i++) {
  				if (!g_ascii_isdigit (s[i])) {
  					valid = FALSE;
  					break;
  				}
  			}
  		}
  	}
  
  	gtk_widget_set_sensitive (ok_button, valid);
  }
  
  static GtkWidget *
  ask_for_pin (GtkEntry **out_secret_entry)
  {
  	GtkDialog *dialog;
  	GtkWidget *w = NULL, *ok_button = NULL;
  	GtkBox *box = NULL, *vbox = NULL;
  
  	dialog = GTK_DIALOG (gtk_dialog_new ());
  	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
  	gtk_window_set_title (GTK_WINDOW (dialog), _("PIN code required"));
  
  	gtk_dialog_add_button (dialog, _("_Cancel"), GTK_RESPONSE_REJECT);
  	ok_button = gtk_dialog_add_button (dialog, _("_OK"), GTK_RESPONSE_OK);
  	gtk_window_set_default (GTK_WINDOW (dialog), ok_button);
  
  	vbox = GTK_BOX (gtk_dialog_get_content_area (dialog));
  
  	w = gtk_label_new (_("PIN code is needed for the mobile broadband device"));
  	gtk_box_pack_start (vbox, w, TRUE, TRUE, 0);
  
  	w = gtk_alignment_new (0.5, 0.5, 0, 1.0);
  	gtk_box_pack_start (vbox, w, TRUE, TRUE, 0);
  
  	box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6));
  	gtk_container_set_border_width (GTK_CONTAINER (box), 6);
  	gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (box));
  
  	gtk_box_pack_start (box, gtk_label_new ("PIN:"), FALSE, FALSE, 0);
  
  	w = gtk_entry_new ();
  	*out_secret_entry = GTK_ENTRY (w);
  	gtk_entry_set_max_length (GTK_ENTRY (w), 8);
  	gtk_entry_set_width_chars (GTK_ENTRY (w), 8);
  	gtk_entry_set_activates_default (GTK_ENTRY (w), TRUE);
  	gtk_entry_set_visibility (GTK_ENTRY (w), FALSE);
  	gtk_box_pack_start (box, w, FALSE, FALSE, 0);
  	g_signal_connect (w, "changed", G_CALLBACK (pin_entry_changed), ok_button);
  	pin_entry_changed (GTK_EDITABLE (w), ok_button);
  
  	gtk_widget_show_all (GTK_WIDGET (vbox));
  	return GTK_WIDGET (dialog);
  }
  
  gboolean
  mobile_helper_get_secrets (NMDeviceModemCapabilities capabilities,
                             SecretsRequest *req,
                             GError **error)
  {
  	MobileHelperSecretsInfo *info = (MobileHelperSecretsInfo *) req;
  	GtkWidget *widget;
  	GtkEntry *secret_entry = NULL;
  
  	applet_secrets_request_set_free_func (req, free_secrets_info);
  
  	if (!req->hints || !g_strv_length (req->hints)) {
  		g_set_error (error,
  		             NM_SECRET_AGENT_ERROR,
  		             NM_SECRET_AGENT_ERROR_FAILED,
  		             "%s.%d (%s): missing secrets hints.",
  		             __FILE__, __LINE__, __func__);
  		return FALSE;
  	}
  	info->secret_name = g_strdup (req->hints[0]);
  
  	/* Convert the input capabilities mask into a single value */
  	if (capabilities & NM_DEVICE_MODEM_CAPABILITY_LTE)
  		/* All LTE modems treated as GSM/UMTS for the settings */
  		info->capability = NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS;
  	else if (capabilities & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)
  		info->capability = NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS;
  	else if (capabilities & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
  		info->capability = NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO;
  	else {
  		g_set_error (error,
  		             NM_SECRET_AGENT_ERROR,
  		             NM_SECRET_AGENT_ERROR_FAILED,
  		             "%s.%d (%s): unknown modem capabilities (0x%X).",
  		             __FILE__, __LINE__, __func__, capabilities);
  		return FALSE;
  	}
  
  	if (!strcmp (info->secret_name, NM_SETTING_GSM_PIN)) {
  		widget = ask_for_pin (&secret_entry);
  	} else if (!strcmp (info->secret_name, NM_SETTING_GSM_PASSWORD) ||
  	           !strcmp (info->secret_name, NM_SETTING_CDMA_PASSWORD))
  		widget = applet_mobile_password_dialog_new (req->connection, &secret_entry);
  	else {
  		g_set_error (error,
  		             NM_SECRET_AGENT_ERROR,
  		             NM_SECRET_AGENT_ERROR_FAILED,
  		             "%s.%d (%s): unknown secrets hint '%s'.",
  		             __FILE__, __LINE__, __func__, info->secret_name);
  		return FALSE;
  	}
  	info->dialog = widget;
  	info->secret_entry = secret_entry;
  
  	if (!widget || !secret_entry) {
  		g_set_error (error,
  		             NM_SECRET_AGENT_ERROR,
  		             NM_SECRET_AGENT_ERROR_FAILED,
  		             "%s.%d (%s): error asking for mobile secrets.",
  		             __FILE__, __LINE__, __func__);
  		return FALSE;
  	}
  
  	g_signal_connect (widget, "response", G_CALLBACK (get_secrets_cb), info);
  
  	gtk_window_set_position (GTK_WINDOW (widget), GTK_WIN_POS_CENTER_ALWAYS);
  	gtk_widget_realize (GTK_WIDGET (widget));
  	gtk_window_present (GTK_WINDOW (widget));
  
  	return TRUE;
  }
  
  /********************************************************************/
  
  void
  mobile_helper_get_icon (NMDevice *device,
                          NMDeviceState state,
                          NMConnection *connection,
                          GdkPixbuf **out_pixbuf,
                          const char **out_icon_name,
                          char **tip,
                          NMApplet *applet,
                          guint32 mb_state,
                          guint32 mb_tech,
                          guint32 quality,
                          gboolean quality_valid)
  {
  	NMSettingConnection *s_con;
  	const char *id;
  
  	g_return_if_fail (out_icon_name && !*out_icon_name);
  	g_return_if_fail (tip && !*tip);
  
  	id = nm_device_get_iface (NM_DEVICE (device));
  	if (connection) {
  		s_con = nm_connection_get_setting_connection (connection);
  		id = nm_setting_connection_get_id (s_con);
  	}
  
  	switch (state) {
  	case NM_DEVICE_STATE_PREPARE:
  		*tip = g_strdup_printf (_("Preparing mobile broadband connection “%s”…"), id);
  		break;
  	case NM_DEVICE_STATE_CONFIG:
  		*tip = g_strdup_printf (_("Configuring mobile broadband connection “%s”…"), id);
  		break;
  	case NM_DEVICE_STATE_NEED_AUTH:
  		*tip = g_strdup_printf (_("User authentication required for mobile broadband connection “%s”…"), id);
  		break;
  	case NM_DEVICE_STATE_IP_CONFIG:
  		*tip = g_strdup_printf (_("Requesting a network address for “%s”…"), id);
  		break;
  	case NM_DEVICE_STATE_ACTIVATED:
  		*out_pixbuf = mobile_helper_get_status_pixbuf (quality,
  		                                               quality_valid,
  		                                               mb_state,
  		                                               mb_tech,
  		                                               applet);
  		*out_icon_name = mobile_helper_get_quality_icon_name (quality_valid ?
  		                                                      quality : 0);
  
  		if ((mb_state != MB_STATE_UNKNOWN) && quality_valid) {
  			gboolean roaming = (mb_state == MB_STATE_ROAMING);
  
  			*tip = g_strdup_printf (_("Mobile broadband connection “%s” active: (%d%%%s%s)"),
  			                        id, quality,
  			                        roaming ? ", " : "",
  			                        roaming ? _("roaming") : "");
  		} else
  			*tip = g_strdup_printf (_("Mobile broadband connection “%s” active"), id);
  		break;
  	default:
  		break;
  	}
  }
  
  /********************************************************************/
  
  char *
  mobile_helper_parse_3gpp_operator_name (NMAMobileProvidersDatabase **mpd, /* I/O */
                                          const char *orig,
                                          const char *op_code)
  {
  	NMAMobileProvider *provider;
  	guint i, orig_len;
  
  	g_assert (mpd != NULL);
  
  	/* Some devices return the MCC/MNC if they haven't fully initialized
  	 * or gotten all the info from the network yet.  Handle that.
  	 */
  
  	orig_len = orig ? strlen (orig) : 0;
  	if (orig_len == 0) {
  		/* If the operator name isn't valid, maybe we can look up the MCC/MNC
  		 * from the operator code instead.
  		 */
  		if (op_code && strlen (op_code)) {
  			orig = op_code;
  			orig_len = strlen (orig);
  		} else
  			return NULL;
  	} else if (orig_len < 5 || orig_len > 6)
  		return g_strdup (orig);  /* not an MCC/MNC */
  
  	for (i = 0; i < orig_len; i++) {
  		if (!isdigit (orig[i]))
  			return strdup (orig);
  	}
  
  	/* At this point we have a 5 or 6 character all-digit string; that's
  	 * probably an MCC/MNC.  Look that up.
  	 */
  
  	if (*mpd == NULL) {
  		GError *error = NULL;
  
  		*mpd = nma_mobile_providers_database_new_sync (NULL, NULL, NULL, &error);
  		if (*mpd == NULL) {
  			g_warning ("Couldn't read database: %s", error->message);
  			g_error_free (error);
  			return strdup (orig);
  		}
  	}
  
  	provider = nma_mobile_providers_database_lookup_3gpp_mcc_mnc (*mpd, orig);
  	return (provider ? g_strdup (nma_mobile_provider_get_name (provider)) : NULL);
  }
  
  char *
  mobile_helper_parse_3gpp2_operator_name (NMAMobileProvidersDatabase **mpd, /* I/O */
                                           guint32 sid)
  {
  	NMAMobileProvider *provider;
  
  	g_assert (mpd != NULL);
  
  	if (!sid)
  		return NULL;
  
  	if (*mpd == NULL) {
  		GError *error = NULL;
  
  		*mpd = nma_mobile_providers_database_new_sync (NULL, NULL, NULL, &error);
  		if (*mpd == NULL) {
  			g_warning ("Couldn't read database: %s", error->message);
  			g_error_free (error);
  			return NULL;
  		}
  	}
  
  	provider = nma_mobile_providers_database_lookup_cdma_sid (*mpd, sid);
  	return (provider ? g_strdup (nma_mobile_provider_get_name (provider)) : NULL);
  }
  