Status area: add NetworkManager indicator

Adds an implementation of nm-applet in javascript. Uses the new
introspection from NetworkManager, and temporarily requires
nm-applet to be running for the secret service.
Features a renewed interface, with each device controllable through
a switch, which if toggled off disconnects, and if toggled on
connects to the most recently used valid connection. More esoteric
features like creation of ad-hoc networks have been moved to the
control center panel.

https://bugzilla.gnome.org/show_bug.cgi?id=621707
This commit is contained in:
Giovanni Campagna 2011-01-25 22:08:12 +01:00
parent 7f67c34b39
commit c8ac3fd4f5
10 changed files with 3301 additions and 1 deletions

View File

@ -153,6 +153,14 @@ StTooltip StLabel {
spacing: .5em;
}
.popup-inactive-menu-item {
font-style: italic;
}
.popup-subtitle-menu-item {
font-weight: bold;
}
.popup-menu-icon {
icon-size: 1.14em;
}

View File

@ -8,6 +8,7 @@ nobase_dist_js_DATA = \
misc/format.js \
misc/gnomeSession.js \
misc/history.js \
misc/modemManager.js \
misc/params.js \
misc/util.js \
perf/core.js \
@ -50,6 +51,7 @@ nobase_dist_js_DATA = \
ui/statusMenu.js \
ui/status/accessibility.js \
ui/status/keyboard.js \
ui/status/network.js \
ui/status/power.js \
ui/status/volume.js \
ui/status/bluetooth.js \

225
js/misc/modemManager.js Normal file
View File

@ -0,0 +1,225 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
const DBus = imports.dbus;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
// The following are not the complete interfaces, just the methods we need
// (or may need in the future)
const ModemGsmNetworkInterface = {
name: 'org.freedesktop.ModemManager.Modem.Gsm.Network',
methods: [
{ name: 'GetRegistrationInfo', inSignature: '', outSignature: 'uss' },
{ name: 'GetSignalQuality', inSignature: '', outSignature: 'u' }
],
properties: [
{ name: 'AccessTechnology', signature: 'u', access: 'read' }
],
signals: [
{ name: 'SignalQuality', inSignature: 'u' },
{ name: 'RegistrationInfo', inSignature: 'uss' }
]
};
const ModemGsmNetworkProxy = DBus.makeProxyClass(ModemGsmNetworkInterface);
const ModemCdmaInterface = {
name: 'org.freedesktop.ModemManager.Modem.Cdma',
methods: [
{ name: 'GetSignalQuality', inSignature: '', outSignature: 'u' },
{ name: 'GetServingSystem', inSignature: '', outSignature: 'usu' }
],
signals: [
{ name: 'SignalQuality', inSignature: 'u' }
]
};
const ModemCdmaProxy = DBus.makeProxyClass(ModemCdmaInterface);
let _providersTable;
function _getProvidersTable() {
if (_providersTable)
return _providersTable;
let [providers, countryCodes] = Shell.mobile_providers_parse();
return _providersTable = providers;
}
function ModemGsm() {
this._init.apply(this, arguments);
}
ModemGsm.prototype = {
_init: function(path) {
this._proxy = new ModemGsmNetworkProxy(DBus.system, 'org.freedesktop.ModemManager', path);
this.signal_quality = 0;
this.operator_name = null;
// Code is duplicated because the function have different signatures
this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
this.signal_quality = quality;
this.emit('notify::signal-quality');
}));
this._proxy.connect('RegistrationInfo', Lang.bind(this, function(proxy, status, code, name) {
this.operator_name = this._findOperatorName(name, code);
this.emit('notify::operator-name');
}));
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function(result, err) {
if (err) {
log(err);
return;
}
let [status, code, name] = result;
this.operator_name = this._findOperatorName(name, code);
this.emit('notify::operator-name');
}));
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
if (err) {
// it will return an error if the device is not connected
this.signal_quality = 0;
} else {
let [quality] = result;
this.signal_quality = quality;
}
this.emit('notify::signal-quality');
}));
},
_findOperatorName: function(name, opCode) {
if (name.length != 0 && (name.length > 6 || name.length < 5)) {
// this looks like a valid name, i.e. not an MCCMNC (that some
// devices return when not yet connected
return name;
}
if (isNaN(parseInt(name))) {
// name is definitely not a MCCMNC, so it may be a name
// after all; return that
return name;
}
let needle;
if (name.length == 0 && opCode)
needle = opCode;
else if (name.length == 6 || name.length == 5)
needle = name;
else // nothing to search
return null;
return this._findProviderForMCCMNC(needle);
},
_findProviderForMCCMNC: function(needle) {
let table = _getProvidersTable();
let needlemcc = needle.substring(0, 3);
let needlemnc = needle.substring(3, needle.length);
let name2, name3;
for (let iter in table) {
let providers = table[iter];
// Search through each country's providers
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
// Search through MCC/MNC list
let list = provider.get_gsm_mcc_mnc();
for (let j = 0; j < list.length; j++) {
let mccmnc = list[j];
// Match both 2-digit and 3-digit MNC; prefer a
// 3-digit match if found, otherwise a 2-digit one.
if (mccmnc.mcc != needlemcc)
continue; // MCC was wrong
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
name3 = provider.name;
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
name2 = provider.name;
if (name2 && name3)
break;
}
}
}
return name3 || name2 || null;
}
}
Signals.addSignalMethods(ModemGsm.prototype);
function ModemCdma() {
this._init.apply(this, arguments);
}
ModemCdma.prototype = {
_init: function(path) {
this._proxy = new ModemCdmaProxy(DBus.system, 'org.freedesktop.ModemManager', path);
this.signal_quality = 0;
this.operator_name = null;
this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
this.signal_quality = quality;
this.emit('notify::signal-quality');
// receiving this signal means the device got activated
// and we can finally call GetServingSystem
if (this.operator_name == null)
this._refreshServingSystem();
}));
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
if (err) {
// it will return an error if the device is not connected
this.signal_quality = 0;
} else {
let [quality] = result;
this.signal_quality = quality;
}
this.emit('notify::signal-quality');
}));
},
_refreshServingSystem: function() {
this._proxy.GetServingSystemRemote(Lang.bind(this, function(result, err) {
if (err) {
// it will return an error if the device is not connected
this.operator_name = null;
} else {
let [bandClass, band, id] = result;
if (name.length > 0)
this.operator_name = this._findProviderForSid(id);
else
this.operator_name = null;
}
this.emit('notify::operator-name');
}));
},
_findProviderForSid: function(sid) {
if (sid == 0)
return null;
let table = _getProvidersTable();
// Search through each country
for (let iter in table) {
let providers = table[iter];
// Search through each country's providers
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
let cdma_sid = provider.get_cdma_sid();
// Search through CDMA SID list
for (let j = 0; j < cdma_sid.length; j++) {
if (cdma_sid[j] == sid)
return provider.name;
}
}
}
return null;
}
};
Signals.addSignalMethods(ModemCdma.prototype);

View File

@ -178,3 +178,83 @@ function killall(processName) {
logError(e, 'Failed to kill ' + processName);
}
}
// This was ported from network-manager-applet
// Copyright 2007 - 2011 Red Hat, Inc.
// Author: Dan Williams <dcbw@redhat.com>
const _IGNORED_WORDS = [
'Semiconductor',
'Components',
'Corporation',
'Communications',
'Company',
'Corp.',
'Corp',
'Co.',
'Inc.',
'Inc',
'Incorporated',
'Ltd.',
'Limited.',
'Intel?',
'chipset',
'adapter',
'[hex]',
'NDIS',
'Module'
];
const _IGNORED_PHRASES = [
'Multiprotocol MAC/baseband processor',
'Wireless LAN Controller',
'Wireless LAN Adapter',
'Wireless Adapter',
'Network Connection',
'Wireless Cardbus Adapter',
'Wireless CardBus Adapter',
'54 Mbps Wireless PC Card',
'Wireless PC Card',
'Wireless PC',
'PC Card with XJACK(r) Antenna',
'Wireless cardbus',
'Wireless LAN PC Card',
'Technology Group Ltd.',
'Communication S.p.A.',
'Business Mobile Networks BV',
'Mobile Broadband Minicard Composite Device',
'Mobile Communications AB',
'(PC-Suite Mode)'
];
function fixupPCIDescription(desc) {
desc.replace(/[_,]/, ' ');
/* Attempt to shorten ID by ignoring certain phrases */
for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
let item = _IGNORED_PHRASES[i];
let pos = desc.indexOf(item);
if (pos != -1) {
let before = desc.substring(0, pos);
let after = desc.substring(pos + item.length, desc.length);
desc = before + after;
}
}
/* Attmept to shorten ID by ignoring certain individual words */
let words = desc.split(' ');
let out = [ ];
for (let i = 0; i < words; i++) {
let item = words[i];
// skip empty items (that come out from consecutive spaces)
if (item.length == 0)
continue;
if (_IGNORED_WORDS.indexOf(item) == -1) {
out.push(item);
}
}
return out.join(' ');
}

View File

@ -44,6 +44,12 @@ const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
if (Config.HAVE_BLUETOOTH)
STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
try {
STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION['network'] = imports.ui.status.network.NMApplet;
} catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
// To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of

2062
js/ui/status/network.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@ js/ui/statusMenu.js
js/ui/status/accessibility.js
js/ui/status/bluetooth.js
js/ui/status/keyboard.js
js/ui/status/network.js
js/ui/status/power.js
js/ui/status/volume.js
js/ui/telepathyClient.js
@ -31,6 +32,7 @@ src/gdmuser/gdm-user.c
src/main.c
src/shell-app-system.c
src/shell-global.c
src/shell-mobile-providers.c
src/shell-polkit-authentication-agent.c
src/shell-util.c

View File

@ -62,7 +62,8 @@ gnome_shell_cflags = \
$(GNOME_SHELL_CFLAGS) \
-I$(srcdir)/tray \
-DVERSION=\"$(VERSION)\" \
-DLOCALEDIR=\"$(datadir)/locale\" \
-DLOCALEDIR=\"$(datadir)/locale\" \
-DDATADIR=\"$(datadir)\" \
-DGNOME_SHELL_LIBEXECDIR=\"$(libexecdir)\" \
-DGNOME_SHELL_DATADIR=\"$(pkgdatadir)\" \
-DGNOME_SHELL_PKGLIBDIR=\"$(pkglibdir)\" \
@ -90,6 +91,7 @@ shell_public_headers_h = \
shell-generic-container.h \
shell-gtk-embed.h \
shell-global.h \
shell-mobile-providers.h \
shell-perf-log.h \
shell-slicer.h \
shell-stack.h \
@ -121,6 +123,7 @@ libgnome_shell_la_SOURCES = \
shell-generic-container.c \
shell-gtk-embed.c \
shell-global.c \
shell-mobile-providers.c \
shell-perf-log.c \
shell-polkit-authentication-agent.h \
shell-polkit-authentication-agent.c \

View File

@ -0,0 +1,816 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* Copyright (C) 2009 Novell, Inc.
* Author: Tambet Ingo (tambet@gmail.com).
*
* Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#include "config.h"
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <glib/gi18n.h>
#include "shell-mobile-providers.h"
#ifndef MOBILE_BROADBAND_PROVIDER_INFO
#define MOBILE_BROADBAND_PROVIDER_INFO DATADIR "/mobile-broadband-provider-info/serviceproviders.xml"
#endif
#define ISO_3166_COUNTRY_CODES DATADIR "/zoneinfo/iso3166.tab"
static GHashTable *
read_country_codes (void)
{
GHashTable *table;
GIOChannel *channel;
GString *buffer;
GError *error = NULL;
GIOStatus status;
channel = g_io_channel_new_file (ISO_3166_COUNTRY_CODES, "r", &error);
if (!channel) {
if (error) {
g_warning ("Could not read " ISO_3166_COUNTRY_CODES ": %s", error->message);
g_error_free (error);
} else
g_warning ("Could not read " ISO_3166_COUNTRY_CODES ": Unknown error");
return NULL;
}
table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
buffer = g_string_sized_new (32);
status = G_IO_STATUS_NORMAL;
while (status == G_IO_STATUS_NORMAL) {
status = g_io_channel_read_line_string (channel, buffer, NULL, &error);
switch (status) {
case G_IO_STATUS_NORMAL:
if (buffer->str[0] != '#') {
char **pieces;
pieces = g_strsplit (buffer->str, "\t", 2);
/* Hack for rh#556292; iso3166.tab is just wrong */
pieces[1] = pieces[1] ? g_strchomp (pieces[1]) : NULL;
if (pieces[1] && !strcmp (pieces[1], "Britain (UK)")) {
g_free (pieces[1]);
pieces[1] = g_strdup (_("United Kingdom"));
}
g_hash_table_insert (table, pieces[0], pieces[1]);
g_free (pieces);
}
g_string_truncate (buffer, 0);
break;
case G_IO_STATUS_EOF:
break;
case G_IO_STATUS_ERROR:
g_warning ("Error while reading: %s", error->message);
g_error_free (error);
break;
case G_IO_STATUS_AGAIN:
/* FIXME: Try again a few times, but really, it never happes, right? */
break;
}
}
g_string_free (buffer, TRUE);
g_io_channel_unref (channel);
return table;
}
/* XML Parser */
typedef enum {
PARSER_TOPLEVEL = 0,
PARSER_COUNTRY,
PARSER_PROVIDER,
PARSER_METHOD_GSM,
PARSER_METHOD_GSM_APN,
PARSER_METHOD_CDMA,
PARSER_ERROR
} MobileContextState;
typedef struct {
GHashTable *country_codes;
GHashTable *table;
char *current_country;
GSList *current_providers;
ShellMobileProvider *current_provider;
ShellMobileAccessMethod *current_method;
char *text_buffer;
MobileContextState state;
} MobileParser;
static ShellGsmMccMnc *
mcc_mnc_new (const char *mcc, const char *mnc)
{
ShellGsmMccMnc *m;
m = g_slice_new (ShellGsmMccMnc);
m->mcc = g_strstrip (g_strdup (mcc));
m->mnc = g_strstrip (g_strdup (mnc));
return m;
}
/* added in porting */
static ShellGsmMccMnc *
mcc_mnc_copy (const ShellGsmMccMnc *other) {
ShellGsmMccMnc *ret;
ret = g_slice_new (ShellGsmMccMnc);
ret->mcc = g_strdup (other->mcc);
ret->mnc = g_strdup (other->mnc);
return ret;
}
static void
mcc_mnc_free (ShellGsmMccMnc *m)
{
g_return_if_fail (m != NULL);
g_free (m->mcc);
g_free (m->mnc);
g_slice_free (ShellGsmMccMnc, m);
}
/* added in porting */
G_DEFINE_BOXED_TYPE (ShellGsmMccMnc, shell_gsm_mcc_mnc, mcc_mnc_copy, mcc_mnc_free)
static ShellMobileAccessMethod *
access_method_new (void)
{
ShellMobileAccessMethod *method;
method = g_slice_new0 (ShellMobileAccessMethod);
method->refs = 1;
method->lcl_names = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) g_free);
return method;
}
ShellMobileAccessMethod *
shell_mobile_access_method_ref (ShellMobileAccessMethod *method)
{
g_return_val_if_fail (method != NULL, NULL);
g_return_val_if_fail (method->refs > 0, NULL);
method->refs++;
return method;
}
void
shell_mobile_access_method_unref (ShellMobileAccessMethod *method)
{
g_return_if_fail (method != NULL);
g_return_if_fail (method->refs > 0);
if (--method->refs == 0) {
g_free (method->name);
g_hash_table_destroy (method->lcl_names);
g_free (method->username);
g_free (method->password);
g_free (method->gateway);
g_free (method->gsm_apn);
g_slist_foreach (method->dns, (GFunc) g_free, NULL);
g_slist_free (method->dns);
g_slice_free (ShellMobileAccessMethod, method);
}
}
GType
shell_mobile_access_method_get_type (void)
{
static GType type = 0;
if (G_UNLIKELY (type == 0)) {
type = g_boxed_type_register_static ("ShellMobileAccessMethod",
(GBoxedCopyFunc) shell_mobile_access_method_ref,
(GBoxedFreeFunc) shell_mobile_access_method_unref);
}
return type;
}
static ShellMobileProvider *
provider_new (void)
{
ShellMobileProvider *provider;
provider = g_slice_new0 (ShellMobileProvider);
provider->refs = 1;
provider->lcl_names = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) g_free);
return provider;
}
ShellMobileProvider *
shell_mobile_provider_ref (ShellMobileProvider *provider)
{
provider->refs++;
return provider;
}
void
shell_mobile_provider_unref (ShellMobileProvider *provider)
{
if (--provider->refs == 0) {
g_free (provider->name);
g_hash_table_destroy (provider->lcl_names);
g_slist_foreach (provider->methods, (GFunc) shell_mobile_access_method_unref, NULL);
g_slist_free (provider->methods);
g_slist_foreach (provider->gsm_mcc_mnc, (GFunc) mcc_mnc_free, NULL);
g_slist_free (provider->gsm_mcc_mnc);
g_slist_free (provider->cdma_sid);
g_slice_free (ShellMobileProvider, provider);
}
}
GType
shell_mobile_provider_get_type (void)
{
static GType type = 0;
if (G_UNLIKELY (type == 0)) {
type = g_boxed_type_register_static ("ShellMobileProvider",
(GBoxedCopyFunc) shell_mobile_provider_ref,
(GBoxedFreeFunc) shell_mobile_provider_unref);
}
return type;
}
static void
provider_list_free (gpointer data)
{
GSList *list = (GSList *) data;
while (list) {
shell_mobile_provider_unref ((ShellMobileProvider *) list->data);
list = g_slist_delete_link (list, list);
}
}
static void
parser_toplevel_start (MobileParser *parser,
const char *name,
const char **attribute_names,
const char **attribute_values)
{
int i;
if (!strcmp (name, "serviceproviders")) {
for (i = 0; attribute_names && attribute_names[i]; i++) {
if (!strcmp (attribute_names[i], "format")) {
if (strcmp (attribute_values[i], "2.0")) {
g_warning ("%s: mobile broadband provider database format '%s'"
" not supported.", __func__, attribute_values[i]);
parser->state = PARSER_ERROR;
break;
}
}
}
} else if (!strcmp (name, "country")) {
for (i = 0; attribute_names && attribute_names[i]; i++) {
if (!strcmp (attribute_names[i], "code")) {
char *country_code;
char *country;
country_code = g_ascii_strup (attribute_values[i], -1);
country = g_hash_table_lookup (parser->country_codes, country_code);
if (country) {
parser->current_country = g_strdup (country);
g_free (country_code);
} else
parser->current_country = country_code;
parser->state = PARSER_COUNTRY;
break;
}
}
}
}
static void
parser_country_start (MobileParser *parser,
const char *name,
const char **attribute_names,
const char **attribute_values)
{
if (!strcmp (name, "provider")) {
parser->state = PARSER_PROVIDER;
parser->current_provider = provider_new ();
}
}
static void
parser_provider_start (MobileParser *parser,
const char *name,
const char **attribute_names,
const char **attribute_values)
{
if (!strcmp (name, "gsm"))
parser->state = PARSER_METHOD_GSM;
else if (!strcmp (name, "cdma")) {
parser->state = PARSER_METHOD_CDMA;
parser->current_method = access_method_new ();
}
}
static void
parser_gsm_start (MobileParser *parser,
const char *name,
const char **attribute_names,
const char **attribute_values)
{
if (!strcmp (name, "network-id")) {
const char *mcc = NULL, *mnc = NULL;
int i;
for (i = 0; attribute_names && attribute_names[i]; i++) {
if (!strcmp (attribute_names[i], "mcc"))
mcc = attribute_values[i];
else if (!strcmp (attribute_names[i], "mnc"))
mnc = attribute_values[i];
if (mcc && strlen (mcc) && mnc && strlen (mnc)) {
parser->current_provider->gsm_mcc_mnc = g_slist_prepend (parser->current_provider->gsm_mcc_mnc,
mcc_mnc_new (mcc, mnc));
break;
}
}
} else if (!strcmp (name, "apn")) {
int i;
for (i = 0; attribute_names && attribute_names[i]; i++) {
if (!strcmp (attribute_names[i], "value")) {
parser->state = PARSER_METHOD_GSM_APN;
parser->current_method = access_method_new ();
parser->current_method->gsm_apn = g_strstrip (g_strdup (attribute_values[i]));
break;
}
}
}
}
static void
parser_cdma_start (MobileParser *parser,
const char *name,
const char **attribute_names,
const char **attribute_values)
{
if (!strcmp (name, "sid")) {
int i;
for (i = 0; attribute_names && attribute_names[i]; i++) {
if (!strcmp (attribute_names[i], "value")) {
unsigned long tmp;
errno = 0;
tmp = strtoul (attribute_values[i], NULL, 10);
if (errno == 0 && tmp > 0)
parser->current_provider->cdma_sid = g_slist_prepend (parser->current_provider->cdma_sid,
GUINT_TO_POINTER ((guint32) tmp));
break;
}
}
}
}
static void
mobile_parser_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer data,
GError **error)
{
MobileParser *parser = (MobileParser *) data;
if (parser->text_buffer) {
g_free (parser->text_buffer);
parser->text_buffer = NULL;
}
switch (parser->state) {
case PARSER_TOPLEVEL:
parser_toplevel_start (parser, element_name, attribute_names, attribute_values);
break;
case PARSER_COUNTRY:
parser_country_start (parser, element_name, attribute_names, attribute_values);
break;
case PARSER_PROVIDER:
parser_provider_start (parser, element_name, attribute_names, attribute_values);
break;
case PARSER_METHOD_GSM:
parser_gsm_start (parser, element_name, attribute_names, attribute_values);
break;
case PARSER_METHOD_CDMA:
parser_cdma_start (parser, element_name, attribute_names, attribute_values);
break;
default:
break;
}
}
static void
parser_country_end (MobileParser *parser,
const char *name)
{
if (!strcmp (name, "country")) {
g_hash_table_insert (parser->table, parser->current_country, parser->current_providers);
parser->current_country = NULL;
parser->current_providers = NULL;
parser->text_buffer = NULL;
parser->state = PARSER_TOPLEVEL;
}
}
static void
parser_provider_end (MobileParser *parser,
const char *name)
{
if (!strcmp (name, "name")) {
if (!parser->current_provider->name) {
/* Use the first one. */
parser->current_provider->name = parser->text_buffer;
parser->text_buffer = NULL;
}
} else if (!strcmp (name, "provider")) {
parser->current_provider->methods = g_slist_reverse (parser->current_provider->methods);
parser->current_provider->gsm_mcc_mnc = g_slist_reverse (parser->current_provider->gsm_mcc_mnc);
parser->current_provider->cdma_sid = g_slist_reverse (parser->current_provider->cdma_sid);
parser->current_providers = g_slist_prepend (parser->current_providers, parser->current_provider);
parser->current_provider = NULL;
parser->text_buffer = NULL;
parser->state = PARSER_COUNTRY;
}
}
static void
parser_gsm_end (MobileParser *parser,
const char *name)
{
if (!strcmp (name, "gsm")) {
parser->text_buffer = NULL;
parser->state = PARSER_PROVIDER;
}
}
static void
parser_gsm_apn_end (MobileParser *parser,
const char *name)
{
if (!strcmp (name, "name")) {
if (!parser->current_method->name) {
/* Use the first one. */
parser->current_method->name = parser->text_buffer;
parser->text_buffer = NULL;
}
} else if (!strcmp (name, "username")) {
parser->current_method->username = parser->text_buffer;
parser->text_buffer = NULL;
} else if (!strcmp (name, "password")) {
parser->current_method->password = parser->text_buffer;
parser->text_buffer = NULL;
} else if (!strcmp (name, "dns")) {
parser->current_method->dns = g_slist_prepend (parser->current_method->dns, parser->text_buffer);
parser->text_buffer = NULL;
} else if (!strcmp (name, "gateway")) {
parser->current_method->gateway = parser->text_buffer;
parser->text_buffer = NULL;
} else if (!strcmp (name, "apn")) {
parser->current_method->type = SHELL_MOBILE_ACCESS_METHOD_TYPE_GSM;
parser->current_method->dns = g_slist_reverse (parser->current_method->dns);
if (!parser->current_method->name)
parser->current_method->name = g_strdup (_("Default"));
parser->current_provider->methods = g_slist_prepend (parser->current_provider->methods,
parser->current_method);
parser->current_method = NULL;
parser->text_buffer = NULL;
parser->state = PARSER_METHOD_GSM;
}
}
static void
parser_cdma_end (MobileParser *parser,
const char *name)
{
if (!strcmp (name, "username")) {
parser->current_method->username = parser->text_buffer;
parser->text_buffer = NULL;
} else if (!strcmp (name, "password")) {
parser->current_method->password = parser->text_buffer;
parser->text_buffer = NULL;
} else if (!strcmp (name, "dns")) {
parser->current_method->dns = g_slist_prepend (parser->current_method->dns, parser->text_buffer);
parser->text_buffer = NULL;
} else if (!strcmp (name, "gateway")) {
parser->current_method->gateway = parser->text_buffer;
parser->text_buffer = NULL;
} else if (!strcmp (name, "cdma")) {
parser->current_method->type = SHELL_MOBILE_ACCESS_METHOD_TYPE_CDMA;
parser->current_method->dns = g_slist_reverse (parser->current_method->dns);
if (!parser->current_method->name)
parser->current_method->name = g_strdup (parser->current_provider->name);
parser->current_provider->methods = g_slist_prepend (parser->current_provider->methods,
parser->current_method);
parser->current_method = NULL;
parser->text_buffer = NULL;
parser->state = PARSER_PROVIDER;
}
}
static void
mobile_parser_end_element (GMarkupParseContext *context,
const gchar *element_name,
gpointer data,
GError **error)
{
MobileParser *parser = (MobileParser *) data;
switch (parser->state) {
case PARSER_COUNTRY:
parser_country_end (parser, element_name);
break;
case PARSER_PROVIDER:
parser_provider_end (parser, element_name);
break;
case PARSER_METHOD_GSM:
parser_gsm_end (parser, element_name);
break;
case PARSER_METHOD_GSM_APN:
parser_gsm_apn_end (parser, element_name);
break;
case PARSER_METHOD_CDMA:
parser_cdma_end (parser, element_name);
break;
default:
break;
}
}
static void
mobile_parser_characters (GMarkupParseContext *context,
const gchar *text,
gsize text_len,
gpointer data,
GError **error)
{
MobileParser *parser = (MobileParser *) data;
g_free (parser->text_buffer);
parser->text_buffer = g_strdup (text);
}
static const GMarkupParser mobile_parser = {
mobile_parser_start_element,
mobile_parser_end_element,
mobile_parser_characters,
NULL, /* passthrough */
NULL /* error */
};
/**
* shell_mobile_providers_parse:
* @out_ccs: (out) (allow-none): (element-type utf8 utf8): a #GHashTable containing
* country codes
*
* Returns: (element-type utf8 GList<Shell.MobileProvider>) (transfer container): a
* hash table where keys are country names 'char *', values are a 'GSList *'
* of 'ShellMobileProvider *'. Everything is destroyed with g_hash_table_destroy ().
*/
GHashTable *
shell_mobile_providers_parse (GHashTable **out_ccs)
{
GMarkupParseContext *ctx;
GIOChannel *channel;
MobileParser parser;
GError *error = NULL;
char buffer[4096];
GIOStatus status;
gsize len = 0;
memset (&parser, 0, sizeof (MobileParser));
parser.country_codes = read_country_codes ();
if (!parser.country_codes)
goto out;
channel = g_io_channel_new_file (MOBILE_BROADBAND_PROVIDER_INFO, "r", &error);
if (!channel) {
if (error) {
g_warning ("Could not read " MOBILE_BROADBAND_PROVIDER_INFO ": %s", error->message);
g_error_free (error);
} else
g_warning ("Could not read " MOBILE_BROADBAND_PROVIDER_INFO ": Unknown error");
goto out;
}
parser.table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, provider_list_free);
parser.state = PARSER_TOPLEVEL;
ctx = g_markup_parse_context_new (&mobile_parser, 0, &parser, NULL);
status = G_IO_STATUS_NORMAL;
while (status == G_IO_STATUS_NORMAL) {
status = g_io_channel_read_chars (channel, buffer, sizeof (buffer), &len, &error);
switch (status) {
case G_IO_STATUS_NORMAL:
if (!g_markup_parse_context_parse (ctx, buffer, len, &error)) {
status = G_IO_STATUS_ERROR;
g_warning ("Error while parsing XML: %s", error->message);
g_error_free (error);;
}
break;
case G_IO_STATUS_EOF:
break;
case G_IO_STATUS_ERROR:
g_warning ("Error while reading: %s", error->message);
g_error_free (error);
break;
case G_IO_STATUS_AGAIN:
/* FIXME: Try again a few times, but really, it never happes, right? */
break;
}
}
g_io_channel_unref (channel);
g_markup_parse_context_free (ctx);
if (parser.current_provider) {
g_warning ("pending current provider");
shell_mobile_provider_unref (parser.current_provider);
}
if (parser.current_providers) {
g_warning ("pending current providers");
provider_list_free (parser.current_providers);
}
g_free (parser.current_country);
g_free (parser.text_buffer);
out:
if (parser.country_codes) {
if (out_ccs)
*out_ccs = parser.country_codes;
else
g_hash_table_destroy (parser.country_codes);
}
return parser.table;
}
static void
dump_generic (ShellMobileAccessMethod *method)
{
GSList *iter;
GString *dns;
g_print (" username: %s\n", method->username ? method->username : "");
g_print (" password: %s\n", method->password ? method->password : "");
dns = g_string_new (NULL);
for (iter = method->dns; iter; iter = g_slist_next (iter))
g_string_append_printf (dns, "%s%s", dns->len ? ", " : "", (char *) iter->data);
g_print (" dns : %s\n", dns->str);
g_string_free (dns, TRUE);
g_print (" gateway : %s\n", method->gateway ? method->gateway : "");
}
static void
dump_cdma (ShellMobileAccessMethod *method)
{
g_print (" CDMA: %s\n", method->name);
dump_generic (method);
}
static void
dump_gsm (ShellMobileAccessMethod *method)
{
g_print (" APN: %s (%s)\n", method->name, method->gsm_apn);
dump_generic (method);
}
static void
dump_country (gpointer key, gpointer value, gpointer user_data)
{
GSList *citer, *miter;
for (citer = value; citer; citer = g_slist_next (citer)) {
ShellMobileProvider *provider = citer->data;
g_print ("Provider: %s (%s)\n", provider->name, (const char *) key);
for (miter = provider->methods; miter; miter = g_slist_next (miter)) {
ShellMobileAccessMethod *method = miter->data;
GSList *liter;
for (liter = provider->gsm_mcc_mnc; liter; liter = g_slist_next (liter)) {
ShellGsmMccMnc *m = liter->data;
g_print (" MCC/MNC: %s-%s\n", m->mcc, m->mnc);
}
for (liter = provider->cdma_sid; liter; liter = g_slist_next (liter))
g_print (" SID: %d\n", GPOINTER_TO_UINT (liter->data));
switch (method->type) {
case SHELL_MOBILE_ACCESS_METHOD_TYPE_CDMA:
dump_cdma (method);
break;
case SHELL_MOBILE_ACCESS_METHOD_TYPE_GSM:
dump_gsm (method);
break;
default:
break;
}
g_print ("\n");
}
}
}
void
shell_mobile_providers_dump (GHashTable *providers)
{
g_return_if_fail (providers != NULL);
g_hash_table_foreach (providers, dump_country, NULL);
}
/* All the following don't exist in nm-applet, because C doesn't need
those. They're only needed for the introspection annotations
*/
/**
* shell_mobile_provider_get_gsm_mcc_mnc:
* @provider: a #ShellMobileProvider
*
* Returns: (element-type Shell.GsmMccMnc) (transfer none): the
* list of #ShellGsmMccMnc this provider exposes
*/
GSList *
shell_mobile_provider_get_gsm_mcc_mnc (ShellMobileProvider *provider)
{
return provider->gsm_mcc_mnc;
}
/**
* shell_mobile_provider_get_cdma_sid:
* @provider: a #ShellMobileProvider
*
* Returns: (element-type guint32) (transfer none): the
* list of CDMA sids this provider exposes
*/
GSList *
shell_mobile_provider_get_cdma_sid (ShellMobileProvider *provider)
{
return provider->cdma_sid;
}

View File

@ -0,0 +1,96 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* Copyright (C) 2009 Novell, Inc.
* Author: Tambet Ingo (tambet@gmail.com).
*
* Copyright (C) 2009 - 2010 Red Hat, Inc.
*
* Ported to GNOME Shell by Giovanni Campagna <scampa.giovanni@gmail.com>
* Porting consisted only in replacing nmn with shell, to be compatible with
* GObject Introspection namespacing
*/
#ifndef SHELL_MOBILE_PROVIDERS_H
#define SHELL_MOBILE_PROVIDERS_H
#include <glib.h>
#include <glib-object.h>
#define SHELL_TYPE_MOBILE_PROVIDER (shell_mobile_provider_get_type ())
#define SHELL_TYPE_MOBILE_ACCESS_METHOD (shell_mobile_access_method_get_type ())
typedef enum {
SHELL_MOBILE_ACCESS_METHOD_TYPE_UNKNOWN = 0,
SHELL_MOBILE_ACCESS_METHOD_TYPE_GSM,
SHELL_MOBILE_ACCESS_METHOD_TYPE_CDMA
} ShellMobileAccessMethodType;
typedef struct {
char *mcc;
char *mnc;
} ShellGsmMccMnc;
typedef struct {
char *name;
/* maps lang (char *) -> name (char *) */
GHashTable *lcl_names;
char *username;
char *password;
char *gateway;
GSList *dns; /* GSList of 'char *' */
/* Only used with SHELL_PROVIDER_TYPE_GSM */
char *gsm_apn;
ShellMobileAccessMethodType type;
gint refs;
} ShellMobileAccessMethod;
typedef struct {
char *name;
/* maps lang (char *) -> name (char *) */
GHashTable *lcl_names;
GSList *methods; /* GSList of ShellMobileAccessMethod */
GSList *gsm_mcc_mnc; /* GSList of ShellGsmMccMnc */
GSList *cdma_sid; /* GSList of guint32 */
gint refs;
} ShellMobileProvider;
GType shell_gsm_mcc_mnc_get_type (void); /* added in porting */
GType shell_mobile_provider_get_type (void);
GType shell_mobile_access_method_get_type (void);
ShellMobileProvider *shell_mobile_provider_ref (ShellMobileProvider *provider);
void shell_mobile_provider_unref (ShellMobileProvider *provider);
GSList * shell_mobile_provider_get_gsm_mcc_mnc (ShellMobileProvider *provider);
GSList * shell_mobile_provider_get_cdma_sid (ShellMobileProvider *provider);
ShellMobileAccessMethod *shell_mobile_access_method_ref (ShellMobileAccessMethod *method);
void shell_mobile_access_method_unref (ShellMobileAccessMethod *method);
GHashTable *shell_mobile_providers_parse (GHashTable **out_ccs);
void shell_mobile_providers_dump (GHashTable *providers);
#endif /* SHELL_MOBILE_PROVIDERS_H */