[network-manager-applet] Initial IPv6 config support
- From: Dan Williams <dcbw src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [network-manager-applet] Initial IPv6 config support
- Date: Tue, 4 Aug 2009 19:31:44 +0000 (UTC)
commit a1a3a305ac476e95e4357a8990b30b2ca762af22
Author: Dan Winship <danw gnome org>
Date: Thu Jun 25 10:30:58 2009 -0400
Initial IPv6 config support
src/connection-editor/Makefile.am | 5 +
src/connection-editor/ce-page-ip6.glade | 456 +++++++++++++
src/connection-editor/ip6-routes-dialog.c | 617 ++++++++++++++++++
src/connection-editor/ip6-routes-dialog.h | 35 +
src/connection-editor/nm-connection-editor.c | 6 +
src/connection-editor/page-ip6.c | 880 ++++++++++++++++++++++++++
src/connection-editor/page-ip6.h | 53 ++
7 files changed, 2052 insertions(+), 0 deletions(-)
---
diff --git a/src/connection-editor/Makefile.am b/src/connection-editor/Makefile.am
index 0f59066..3eea3ee 100644
--- a/src/connection-editor/Makefile.am
+++ b/src/connection-editor/Makefile.am
@@ -40,6 +40,8 @@ nm_connection_editor_SOURCES = \
page-wireless-security.c \
page-ip4.h \
page-ip4.c \
+ page-ip6.h \
+ page-ip6.c \
page-dsl.h \
page-dsl.c \
page-mobile.h \
@@ -52,6 +54,8 @@ nm_connection_editor_SOURCES = \
vpn-helpers.c \
ip4-routes-dialog.h \
ip4-routes-dialog.c \
+ ip6-routes-dialog.h \
+ ip6-routes-dialog.c \
ppp-auth-methods-dialog.c \
ppp-auth-methods-dialog.h \
polkit-helpers.c \
@@ -80,6 +84,7 @@ glade_DATA = \
ce-page-wireless.glade \
ce-page-wireless-security.glade \
ce-page-ip4.glade \
+ ce-page-ip6.glade \
ce-page-dsl.glade \
ce-page-mobile.glade \
ce-page-ppp.glade \
diff --git a/src/connection-editor/ce-page-ip6.glade b/src/connection-editor/ce-page-ip6.glade
new file mode 100644
index 0000000..40fbe14
--- /dev/null
+++ b/src/connection-editor/ce-page-ip6.glade
@@ -0,0 +1,456 @@
+<?xml version="1.0"?>
+<glade-interface>
+ <!-- interface-requires gtk+ 2.16 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <widget class="GtkWindow" id="ip6_page_widget">
+ <property name="title" translatable="yes">window3</property>
+ <child>
+ <widget class="GtkVBox" id="IP6Page">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="spacing">18</property>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <widget class="GtkLabel" id="label38">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Method:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">ip6_method</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkComboBox" id="ip6_method">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Automatic
+Automatic with manual DNS settings
+Manual
+Link-Local
+Shared to other computers</property>
+ </widget>
+ <packing>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="ip6_addresses_box">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkLabel" id="ip6_addr_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><b>Addresses</b></property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <widget class="GtkTreeView" id="ip6_addresses">
+ <property name="height_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip" translatable="yes">IP addresses identify your computer on the network. Click the "Add" button to add an IP address.</property>
+ <property name="rules_hint">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox1">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">start</property>
+ <child>
+ <widget class="GtkButton" id="ip6_addr_add_button">
+ <property name="label">gtk-add</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="ip6_addr_delete_button">
+ <property name="label">gtk-delete</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="table8">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <widget class="GtkEntry" id="ip6_dns_searches_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">IP addresses identify your computer on the network. Click the "Add" button to add an IP address.</property>
+ <property name="invisible_char">●</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="ip6_dns_searches_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Search domains:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">ip6_dns_searches_entry</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="ip6_dns_servers_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_DNS servers:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">ip6_dns_servers_entry</property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="ip6_dns_servers_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">IP addresses identify your computer on the network. Click the "Add" button to add an IP address.</property>
+ <property name="invisible_char">●</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <widget class="GtkButton" id="ip6_routes_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="spacing">3</property>
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Routes…</property>
+ <property name="use_underline">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkDialog" id="ip6_routes_dialog">
+ <property name="width_request">450</property>
+ <property name="height_request">250</property>
+ <property name="border_width">5</property>
+ <property name="modal">True</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="icon_name">stock-peferences</property>
+ <property name="type_hint">dialog</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <widget class="GtkTreeView" id="ip6_routes">
+ <property name="height_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip" translatable="yes">IP addresses identify your computer on the network. Click the "Add" button to add an IP address.</property>
+ <property name="rules_hint">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox2">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">start</property>
+ <child>
+ <widget class="GtkButton" id="ip6_route_add_button">
+ <property name="label">gtk-add</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="ip6_route_delete_button">
+ <property name="label">gtk-delete</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="ip6_ignore_auto_routes">
+ <property name="label" translatable="yes">Ig_nore automatically obtained routes</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="ip6_never_default">
+ <property name="label" translatable="yes">Use this c_onnection only for resources on its network</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip" translatable="yes">If enabled, this connection will never be used as the default network connection.</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <widget class="GtkButton" id="cancel_button">
+ <property name="label">gtk-cancel</property>
+ <property name="response_id">-6</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="ok_button">
+ <property name="label">gtk-ok</property>
+ <property name="response_id">-5</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
diff --git a/src/connection-editor/ip6-routes-dialog.c b/src/connection-editor/ip6-routes-dialog.c
new file mode 100644
index 0000000..9470516
--- /dev/null
+++ b/src/connection-editor/ip6-routes-dialog.c
@@ -0,0 +1,617 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Connection editor -- Connection editor for NetworkManager
+ *
+ * Dan Williams <dcbw redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2008 Red Hat, Inc.
+ */
+
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glade/glade.h>
+#include <glib/gi18n.h>
+
+#include <nm-utils.h>
+
+#include "ip6-routes-dialog.h"
+
+#define COL_ADDRESS 0
+#define COL_PREFIX 1
+#define COL_NEXT_HOP 2
+#define COL_METRIC 3
+#define COL_LAST COL_METRIC
+
+static gboolean
+get_one_int (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ int column,
+ guint32 max_value,
+ gboolean fail_if_missing,
+ guint *out)
+{
+ char *item = NULL;
+ gboolean success = FALSE;
+ long int tmp_int;
+
+ gtk_tree_model_get (model, iter, column, &item, -1);
+ if (!item || !strlen (item)) {
+ g_free (item);
+ return fail_if_missing ? FALSE : TRUE;
+ }
+
+ errno = 0;
+ tmp_int = strtol (item, NULL, 10);
+ if (errno || tmp_int < 0 || tmp_int > max_value)
+ goto out;
+
+ *out = (guint) tmp_int;
+ success = TRUE;
+
+out:
+ g_free (item);
+ return success;
+}
+
+static gboolean
+get_one_addr (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ int column,
+ gboolean fail_if_missing,
+ struct in6_addr *out)
+{
+ char *item = NULL;
+ gboolean success = FALSE;
+
+ gtk_tree_model_get (model, iter, column, &item, -1);
+ if (!item || !strlen (item)) {
+ g_free (item);
+ return fail_if_missing ? FALSE : TRUE;
+ }
+
+ if (inet_pton (AF_INET6, item, out) > 0)
+ success = TRUE;
+
+ g_free (item);
+ return success;
+}
+
+static void
+validate (GtkWidget *dialog)
+{
+ GladeXML *xml;
+ GtkWidget *widget;
+ GtkTreeModel *model;
+ GtkTreeIter tree_iter;
+ gboolean valid = FALSE, iter_valid = FALSE;
+
+ g_return_if_fail (dialog != NULL);
+
+ xml = g_object_get_data (G_OBJECT (dialog), "glade-xml");
+ g_return_if_fail (xml != NULL);
+ g_return_if_fail (GLADE_IS_XML (xml));
+
+ widget = glade_xml_get_widget (xml, "ip6_routes");
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ iter_valid = gtk_tree_model_get_iter_first (model, &tree_iter);
+
+ while (iter_valid) {
+ struct in6_addr dest, next_hop;
+ guint prefix = 0, metric = 0;
+
+ /* Address */
+ if (!get_one_addr (model, &tree_iter, COL_ADDRESS, TRUE, &dest))
+ goto done;
+
+ /* Prefix */
+ if (!get_one_int (model, &tree_iter, COL_PREFIX, 128, TRUE, &prefix))
+ goto done;
+
+ /* Next hop (optional) */
+ if (!get_one_addr (model, &tree_iter, COL_NEXT_HOP, FALSE, &next_hop))
+ goto done;
+
+ /* Metric (optional) */
+ if (!get_one_int (model, &tree_iter, COL_METRIC, G_MAXUINT32, FALSE, &metric))
+ goto done;
+
+ iter_valid = gtk_tree_model_iter_next (model, &tree_iter);
+ }
+ valid = TRUE;
+
+done:
+ widget = glade_xml_get_widget (xml, "ok_button");
+ gtk_widget_set_sensitive (widget, valid);
+}
+
+static void
+route_add_clicked (GtkButton *button, gpointer user_data)
+{
+ GladeXML *xml = GLADE_XML (user_data);
+ GtkWidget *widget;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+ GtkTreePath *path;
+ GList *cells;
+
+ widget = glade_xml_get_widget (xml, "ip6_routes");
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (widget)));
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, COL_ADDRESS, "", -1);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), COL_ADDRESS);
+
+ /* FIXME: using cells->data is pretty fragile but GTK apparently doesn't
+ * have a way to get a cell renderer from a column based on path or iter
+ * or whatever.
+ */
+ cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (widget), path, column, cells->data, TRUE);
+
+ g_list_free (cells);
+ gtk_tree_path_free (path);
+
+ validate (glade_xml_get_widget (xml, "ip6_routes_dialog"));
+}
+
+static void
+route_delete_clicked (GtkButton *button, gpointer user_data)
+{
+ GladeXML *xml = GLADE_XML (user_data);
+ GtkTreeView *treeview;
+ GtkTreeSelection *selection;
+ GList *selected_rows;
+ GtkTreeModel *model = NULL;
+ GtkTreeIter iter;
+ int num_rows;
+
+ treeview = GTK_TREE_VIEW (glade_xml_get_widget (xml, "ip6_routes"));
+
+ selection = gtk_tree_view_get_selection (treeview);
+ if (gtk_tree_selection_count_selected_rows (selection) != 1)
+ return;
+
+ selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+ if (!selected_rows)
+ return;
+
+ if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) selected_rows->data))
+ gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+
+ g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected_rows);
+
+ num_rows = gtk_tree_model_iter_n_children (model, NULL);
+ if (num_rows && gtk_tree_model_iter_nth_child (model, &iter, NULL, num_rows - 1)) {
+ selection = gtk_tree_view_get_selection (treeview);
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+
+ validate (glade_xml_get_widget (xml, "ip6_routes_dialog"));
+}
+
+static void
+list_selection_changed (GtkTreeSelection *selection, gpointer user_data)
+{
+ GtkWidget *button = GTK_WIDGET (user_data);
+ GtkTreeIter iter;
+ GtkTreeModel *model = NULL;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ gtk_widget_set_sensitive (button, TRUE);
+ else
+ gtk_widget_set_sensitive (button, FALSE);
+}
+
+static void
+cell_edited (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer user_data)
+{
+ GladeXML *xml = GLADE_XML (user_data);
+ GtkWidget *widget, *dialog;
+ GtkListStore *store;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ guint32 column;
+ GtkTreeViewColumn *next_col;
+ GtkCellRenderer *next_cell;
+
+ widget = glade_xml_get_widget (xml, "ip6_routes");
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (widget)));
+ path = gtk_tree_path_new_from_string (path_string);
+ column = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (cell), "column"));
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
+ gtk_list_store_set (store, &iter, column, new_text, -1);
+
+ /* Move focus to the next column */
+ column = (column >= COL_LAST) ? 0 : column + 1;
+ next_col = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), column);
+ dialog = glade_xml_get_widget (xml, "ip6_routes_dialog");
+ next_cell = g_slist_nth_data (g_object_get_data (G_OBJECT (dialog), "renderers"), column);
+
+ gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (widget), path, next_col, next_cell, TRUE);
+ gtk_widget_grab_focus (widget);
+
+ gtk_tree_path_free (path);
+
+ validate (dialog);
+}
+
+static void
+ip_address_filter_cb (GtkEntry * entry,
+ const gchar *text,
+ gint length,
+ gint * position,
+ gpointer user_data)
+{
+ GtkWidget *ok_button = user_data;
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ int i, count = 0;
+ gchar *result = g_new (gchar, length);
+
+ for (i = 0; i < length; i++) {
+ if (g_ascii_isxdigit(text[i]) || (text[i] == ':'))
+ result[count++] = text[i];
+ }
+
+ if (count > 0) {
+ g_signal_handlers_block_by_func (G_OBJECT (editable),
+ G_CALLBACK (ip_address_filter_cb),
+ user_data);
+ gtk_editable_insert_text (editable, result, count, position);
+ g_signal_handlers_unblock_by_func (G_OBJECT (editable),
+ G_CALLBACK (ip_address_filter_cb),
+ user_data);
+ }
+
+ g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
+ g_free (result);
+
+ /* Desensitize the OK button during input to simplify input validation.
+ * All routes will be validated on focus-out, which will then re-enable
+ * the OK button if the routes are valid.
+ */
+ gtk_widget_set_sensitive (ok_button, FALSE);
+}
+
+static void
+ip6_cell_editing_started (GtkCellRenderer *cell,
+ GtkCellEditable *editable,
+ const gchar *path,
+ gpointer user_data)
+{
+ if (!GTK_IS_ENTRY (editable)) {
+ g_warning ("%s: Unexpected cell editable type.", __func__);
+ return;
+ }
+
+ /* Set up the entry filter */
+ g_signal_connect (G_OBJECT (editable), "insert-text",
+ (GCallback) ip_address_filter_cb,
+ user_data);
+}
+
+static void
+uint_filter_cb (GtkEntry * entry,
+ const gchar *text,
+ gint length,
+ gint * position,
+ gpointer user_data)
+{
+ GtkWidget *ok_button = user_data;
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ int i, count = 0;
+ gchar *result = g_new (gchar, length);
+
+ for (i = 0; i < length; i++) {
+ if ((text[i] >= '0' && text[i] <= '9'))
+ result[count++] = text[i];
+ }
+
+ if (count > 0) {
+ g_signal_handlers_block_by_func (G_OBJECT (editable),
+ G_CALLBACK (uint_filter_cb),
+ user_data);
+ gtk_editable_insert_text (editable, result, count, position);
+ g_signal_handlers_unblock_by_func (G_OBJECT (editable),
+ G_CALLBACK (uint_filter_cb),
+ user_data);
+ }
+
+ g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
+ g_free (result);
+
+ /* Desensitize the OK button during input to simplify input validation.
+ * All routes will be validated on focus-out, which will then re-enable
+ * the OK button if the routes are valid.
+ */
+ gtk_widget_set_sensitive (ok_button, FALSE);
+}
+
+static void
+uint_cell_editing_started (GtkCellRenderer *cell,
+ GtkCellEditable *editable,
+ const gchar *path,
+ gpointer user_data)
+{
+ if (!GTK_IS_ENTRY (editable)) {
+ g_warning ("%s: Unexpected cell editable type.", __func__);
+ return;
+ }
+
+ /* Set up the entry filter */
+ g_signal_connect (G_OBJECT (editable), "insert-text",
+ (GCallback) uint_filter_cb,
+ user_data);
+}
+
+GtkWidget *
+ip6_routes_dialog_new (NMSettingIP6Config *s_ip6, gboolean automatic)
+{
+ GladeXML *xml;
+ GtkWidget *dialog, *widget, *ok_button;
+ GtkListStore *store;
+ GtkTreeIter model_iter;
+ GtkTreeSelection *selection;
+ gint offset;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ int i;
+ GSList *renderers = NULL;
+
+ xml = glade_xml_new (GLADEDIR "/ce-page-ip6.glade", "ip6_routes_dialog", NULL);
+ if (!xml) {
+ g_warning ("%s: Couldn't load ip6 page glade file.", __func__);
+ return NULL;
+ }
+
+ dialog = glade_xml_get_widget (xml, "ip6_routes_dialog");
+ if (!dialog) {
+ g_warning ("%s: Couldn't load ip6 routes dialog from glade file.", __func__);
+ g_object_unref (xml);
+ return NULL;
+ }
+
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+ g_object_set_data_full (G_OBJECT (dialog), "glade-xml",
+ xml, (GDestroyNotify) g_object_unref);
+
+ ok_button = glade_xml_get_widget (xml, "ok_button");
+
+ store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+
+ /* Add existing routes */
+ for (i = 0; i < nm_setting_ip6_config_get_num_routes (s_ip6); i++) {
+ NMIP6Route *route = nm_setting_ip6_config_get_route (s_ip6, i);
+ const struct in6_addr *tmp_addr;
+ char ip_string[INET6_ADDRSTRLEN];
+ char *tmp;
+
+ if (!route) {
+ g_warning ("%s: empty IP6 route structure!", __func__);
+ continue;
+ }
+
+ gtk_list_store_append (store, &model_iter);
+
+ tmp_addr = nm_ip6_route_get_dest (route);
+ if (inet_ntop (AF_INET6, tmp_addr, ip_string, sizeof (ip_string)))
+ gtk_list_store_set (store, &model_iter, COL_ADDRESS, ip_string, -1);
+
+ tmp = g_strdup_printf ("%u", nm_ip6_route_get_prefix (route));
+ gtk_list_store_set (store, &model_iter, COL_PREFIX, tmp, -1);
+ g_free (tmp);
+
+ tmp_addr = nm_ip6_route_get_next_hop (route);
+ if (tmp_addr && !IN6_IS_ADDR_UNSPECIFIED (tmp_addr) &&
+ inet_ntop (AF_INET6, tmp_addr, ip_string, sizeof (ip_string)))
+ gtk_list_store_set (store, &model_iter, COL_NEXT_HOP, ip_string, -1);
+
+ if (nm_ip6_route_get_metric (route)) {
+ tmp = g_strdup_printf ("%u", nm_ip6_route_get_metric (route));
+ gtk_list_store_set (store, &model_iter, COL_METRIC, tmp, -1);
+ g_free (tmp);
+ }
+ }
+
+ widget = glade_xml_get_widget (xml, "ip6_routes");
+ gtk_tree_view_set_model (GTK_TREE_VIEW (widget), GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ /* IP Address column */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "editable", TRUE, NULL);
+ g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited), xml);
+ g_object_set_data (G_OBJECT (renderer), "column", GUINT_TO_POINTER (COL_ADDRESS));
+ g_signal_connect (renderer, "editing-started", G_CALLBACK (ip6_cell_editing_started), ok_button);
+ renderers = g_slist_append (renderers, renderer);
+
+ offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (widget),
+ -1, _("Address"), renderer,
+ "text", COL_ADDRESS,
+ NULL);
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), offset - 1);
+ gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
+ gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+
+ /* Prefix column */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "editable", TRUE, NULL);
+ g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited), xml);
+ g_object_set_data (G_OBJECT (renderer), "column", GUINT_TO_POINTER (COL_PREFIX));
+ g_signal_connect (renderer, "editing-started", G_CALLBACK (uint_cell_editing_started), ok_button);
+ renderers = g_slist_append (renderers, renderer);
+
+ offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (widget),
+ -1, _("Prefix"), renderer,
+ "text", COL_PREFIX,
+ NULL);
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), offset - 1);
+ gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
+ gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+
+ /* Gateway column */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "editable", TRUE, NULL);
+ g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited), xml);
+ g_object_set_data (G_OBJECT (renderer), "column", GUINT_TO_POINTER (COL_NEXT_HOP));
+ g_signal_connect (renderer, "editing-started", G_CALLBACK (ip6_cell_editing_started), ok_button);
+ renderers = g_slist_append (renderers, renderer);
+
+ offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (widget),
+ -1, _("Gateway"), renderer,
+ "text", COL_NEXT_HOP,
+ NULL);
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), offset - 1);
+ gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
+ gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+
+ /* Metric column */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "editable", TRUE, NULL);
+ g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited), xml);
+ g_object_set_data (G_OBJECT (renderer), "column", GUINT_TO_POINTER (COL_METRIC));
+ g_signal_connect (renderer, "editing-started", G_CALLBACK (uint_cell_editing_started), ok_button);
+ renderers = g_slist_append (renderers, renderer);
+
+ offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (widget),
+ -1, _("Metric"), renderer,
+ "text", COL_METRIC,
+ NULL);
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), offset - 1);
+ gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
+ gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+
+ g_object_set_data_full (G_OBJECT (dialog), "renderers", renderers, (GDestroyNotify) g_slist_free);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (list_selection_changed),
+ glade_xml_get_widget (xml, "ip6_route_delete_button"));
+
+ widget = glade_xml_get_widget (xml, "ip6_route_add_button");
+ gtk_widget_set_sensitive (widget, TRUE);
+ g_signal_connect (widget, "clicked", G_CALLBACK (route_add_clicked), xml);
+
+ widget = glade_xml_get_widget (xml, "ip6_route_delete_button");
+ gtk_widget_set_sensitive (widget, FALSE);
+ g_signal_connect (widget, "clicked", G_CALLBACK (route_delete_clicked), xml);
+
+ widget = glade_xml_get_widget (xml, "ip6_ignore_auto_routes");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
+ nm_setting_ip6_config_get_ignore_auto_routes (s_ip6));
+ gtk_widget_set_sensitive (widget, automatic);
+
+ widget = glade_xml_get_widget (xml, "ip6_never_default");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
+ nm_setting_ip6_config_get_never_default (s_ip6));
+
+ /* Update initial validity */
+ validate (dialog);
+
+ return dialog;
+}
+
+void
+ip6_routes_dialog_update_setting (GtkWidget *dialog, NMSettingIP6Config *s_ip6)
+{
+ GladeXML *xml;
+ GtkWidget *widget;
+ GtkTreeModel *model;
+ GtkTreeIter tree_iter;
+ gboolean iter_valid;
+
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (s_ip6 != NULL);
+
+ xml = g_object_get_data (G_OBJECT (dialog), "glade-xml");
+ g_return_if_fail (xml != NULL);
+ g_return_if_fail (GLADE_IS_XML (xml));
+
+ widget = glade_xml_get_widget (xml, "ip6_routes");
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ iter_valid = gtk_tree_model_get_iter_first (model, &tree_iter);
+
+ nm_setting_ip6_config_clear_routes (s_ip6);
+
+ while (iter_valid) {
+ struct in6_addr dest, next_hop;
+ guint prefix = 0, metric = 0;
+ NMIP6Route *route;
+
+ /* Address */
+ if (!get_one_addr (model, &tree_iter, COL_ADDRESS, TRUE, &dest)) {
+ g_warning ("%s: IPv6 address missing or invalid!", __func__);
+ goto next;
+ }
+
+ /* Prefix */
+ if (!get_one_int (model, &tree_iter, COL_PREFIX, 128, TRUE, &prefix)) {
+ g_warning ("%s: IPv6 prefix missing or invalid!", __func__);
+ goto next;
+ }
+
+ /* Next hop (optional) */
+ if (!get_one_addr (model, &tree_iter, COL_NEXT_HOP, FALSE, &next_hop)) {
+ g_warning ("%s: IPv6 next hop invalid!", __func__);
+ goto next;
+ }
+
+ /* Metric (optional) */
+ if (!get_one_int (model, &tree_iter, COL_METRIC, G_MAXUINT32, FALSE, &metric)) {
+ g_warning ("%s: IPv6 metric invalid!", __func__);
+ goto next;
+ }
+
+ route = nm_ip6_route_new ();
+ nm_ip6_route_set_dest (route, &dest);
+ nm_ip6_route_set_prefix (route, prefix);
+ nm_ip6_route_set_next_hop (route, &next_hop);
+ nm_ip6_route_set_metric (route, metric);
+ nm_setting_ip6_config_add_route (s_ip6, route);
+ nm_ip6_route_unref (route);
+
+ next:
+ iter_valid = gtk_tree_model_iter_next (model, &tree_iter);
+ }
+
+ widget = glade_xml_get_widget (xml, "ip6_ignore_auto_routes");
+ g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES,
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)),
+ NULL);
+
+ widget = glade_xml_get_widget (xml, "ip6_never_default");
+ g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_NEVER_DEFAULT,
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)),
+ NULL);
+}
+
diff --git a/src/connection-editor/ip6-routes-dialog.h b/src/connection-editor/ip6-routes-dialog.h
new file mode 100644
index 0000000..e7d5d09
--- /dev/null
+++ b/src/connection-editor/ip6-routes-dialog.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Connection editor -- Connection editor for NetworkManager
+ *
+ * Dan Williams <dcbw redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2008 Red Hat, Inc.
+ */
+
+#ifndef IP6_ROUTES_DIALOG_H
+#define IP6_ROUTES_DIALOG_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "nm-setting-ip6-config.h"
+
+GtkWidget *ip6_routes_dialog_new (NMSettingIP6Config *s_ip6, gboolean automatic);
+
+void ip6_routes_dialog_update_setting (GtkWidget *dialog, NMSettingIP6Config *s_ip6);
+
+#endif /* IP6_ROUTES_DIALOG_H */
diff --git a/src/connection-editor/nm-connection-editor.c b/src/connection-editor/nm-connection-editor.c
index c695c03..90135b5 100644
--- a/src/connection-editor/nm-connection-editor.c
+++ b/src/connection-editor/nm-connection-editor.c
@@ -48,6 +48,7 @@
#include <nm-setting-connection.h>
#include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
#include <nm-setting-wired.h>
#include <nm-setting-8021x.h>
#include <nm-setting-wireless.h>
@@ -69,6 +70,7 @@
#include "page-wireless.h"
#include "page-wireless-security.h"
#include "page-ip4.h"
+#include "page-ip6.h"
#include "page-dsl.h"
#include "page-mobile.h"
#include "page-ppp.h"
@@ -604,6 +606,8 @@ nm_connection_editor_set_connection (NMConnectionEditor *editor,
goto out;
if (!add_page (editor, ce_page_ip4_new, connection, error))
goto out;
+ if (!add_page (editor, ce_page_ip6_new, connection, error))
+ goto out;
} else if (!strcmp (connection_type, NM_SETTING_WIRELESS_SETTING_NAME)) {
if (!add_page (editor, ce_page_wireless_new, connection, error))
goto out;
@@ -611,6 +615,8 @@ nm_connection_editor_set_connection (NMConnectionEditor *editor,
goto out;
if (!add_page (editor, ce_page_ip4_new, connection, error))
goto out;
+ if (!add_page (editor, ce_page_ip6_new, connection, error))
+ goto out;
} else if (!strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME)) {
if (!add_page (editor, ce_page_vpn_new, connection, error))
goto out;
diff --git a/src/connection-editor/page-ip6.c b/src/connection-editor/page-ip6.c
new file mode 100644
index 0000000..380cd32
--- /dev/null
+++ b/src/connection-editor/page-ip6.c
@@ -0,0 +1,880 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Connection editor -- Connection editor for NetworkManager
+ *
+ * Dan Williams <dcbw redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2008 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <nm-setting-connection.h>
+#include <nm-setting-ip6-config.h>
+#include <nm-setting-gsm.h>
+#include <nm-setting-cdma.h>
+#include <nm-setting-pppoe.h>
+#include <nm-setting-vpn.h>
+#include <nm-utils.h>
+
+#include "page-ip6.h"
+#include "ip6-routes-dialog.h"
+
+G_DEFINE_TYPE (CEPageIP6, ce_page_ip6, CE_TYPE_PAGE)
+
+#define CE_PAGE_IP6_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CE_TYPE_PAGE_IP6, CEPageIP6Private))
+
+#define COL_ADDRESS 0
+#define COL_PREFIX 1
+#define COL_LAST COL_PREFIX
+
+typedef struct {
+ NMSettingIP6Config *setting;
+ char *connection_id;
+ GType connection_type;
+
+ GtkComboBox *method;
+ GtkListStore *method_store;
+
+ /* Addresses */
+ GtkWidget *addr_label;
+ GtkButton *addr_add;
+ GtkButton *addr_delete;
+ GtkTreeView *addr_list;
+ GtkCellRenderer *addr_cells[COL_LAST + 1];
+
+ /* DNS servers */
+ GtkWidget *dns_servers_label;
+ GtkEntry *dns_servers;
+
+ /* Search domains */
+ GtkWidget *dns_searches_label;
+ GtkEntry *dns_searches;
+
+ GtkButton *routes_button;
+
+ GtkWindowGroup *window_group;
+ gboolean window_added;
+} CEPageIP6Private;
+
+#define METHOD_COL_NAME 0
+#define METHOD_COL_NUM 1
+#define METHOD_COL_ENABLED 2
+
+#define IP6_METHOD_IGNORE 0
+#define IP6_METHOD_AUTO 1
+#define IP6_METHOD_AUTO_ADDRESSES 2
+#define IP6_METHOD_MANUAL 3
+#define IP6_METHOD_LINK_LOCAL 4
+#define IP6_METHOD_SHARED 5
+
+static void
+ip6_private_init (CEPageIP6 *self, NMConnection *connection)
+{
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (self);
+ GladeXML *xml;
+ GtkTreeIter iter;
+ NMSettingConnection *s_con;
+ const char *connection_type;
+ char *str_auto = NULL, *str_auto_only = NULL;
+ GList *cells;
+
+ xml = CE_PAGE (self)->xml;
+
+ s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+ g_assert (s_con);
+ connection_type = nm_setting_connection_get_connection_type (s_con);
+ g_assert (connection_type);
+
+ priv->connection_type = nm_connection_lookup_setting_type (connection_type);
+
+ if (priv->connection_type == NM_TYPE_SETTING_VPN) {
+ str_auto = _("Automatic (VPN)");
+ str_auto_only = _("Automatic (VPN) addresses only");
+ } else if ( priv->connection_type == NM_TYPE_SETTING_GSM
+ || priv->connection_type == NM_TYPE_SETTING_CDMA) {
+ str_auto = _("Automatic (PPP)");
+ str_auto_only = _("Automatic (PPP) addresses only");
+ } else if (priv->connection_type == NM_TYPE_SETTING_PPPOE) {
+ str_auto = _("Automatic (PPPoE)");
+ str_auto_only = _("Automatic (PPPoE) addresses only");
+ } else {
+ str_auto = _("Automatic");
+ str_auto_only = _("Automatic, addresses only");
+ }
+
+ priv->method = GTK_COMBO_BOX (glade_xml_get_widget (xml, "ip6_method"));
+ cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->method));
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->method), cells->data,
+ "sensitive", METHOD_COL_ENABLED);
+
+ priv->method_store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_BOOLEAN);
+
+ gtk_list_store_append (priv->method_store, &iter);
+ gtk_list_store_set (priv->method_store, &iter,
+ METHOD_COL_NAME, _("Ignore"),
+ METHOD_COL_NUM, IP6_METHOD_IGNORE,
+ METHOD_COL_ENABLED, TRUE,
+ -1);
+
+ gtk_list_store_append (priv->method_store, &iter);
+ gtk_list_store_set (priv->method_store, &iter,
+ METHOD_COL_NAME, str_auto,
+ METHOD_COL_NUM, IP6_METHOD_AUTO,
+ METHOD_COL_ENABLED, FALSE,
+ -1);
+
+ gtk_list_store_append (priv->method_store, &iter);
+ gtk_list_store_set (priv->method_store, &iter,
+ METHOD_COL_NAME, str_auto_only,
+ METHOD_COL_NUM, IP6_METHOD_AUTO_ADDRESSES,
+ METHOD_COL_ENABLED, FALSE,
+ -1);
+
+ /* Manual is pointless for Mobile Broadband */
+ if ( priv->connection_type != NM_TYPE_SETTING_GSM
+ && priv->connection_type != NM_TYPE_SETTING_CDMA
+ && priv->connection_type != NM_TYPE_SETTING_VPN) {
+ gtk_list_store_append (priv->method_store, &iter);
+ gtk_list_store_set (priv->method_store, &iter,
+ METHOD_COL_NAME, _("Manual"),
+ METHOD_COL_NUM, IP6_METHOD_MANUAL,
+ METHOD_COL_ENABLED, TRUE,
+ -1);
+ }
+
+ /* Link-local is pointless for VPNs, Mobile Broadband, and PPPoE */
+ if ( priv->connection_type != NM_TYPE_SETTING_VPN
+ && priv->connection_type != NM_TYPE_SETTING_PPPOE
+ && priv->connection_type != NM_TYPE_SETTING_GSM
+ && priv->connection_type != NM_TYPE_SETTING_CDMA) {
+ gtk_list_store_append (priv->method_store, &iter);
+ gtk_list_store_set (priv->method_store, &iter,
+ METHOD_COL_NAME, _("Link-Local Only"),
+ METHOD_COL_NUM, IP6_METHOD_LINK_LOCAL,
+ METHOD_COL_ENABLED, FALSE,
+ -1);
+
+ gtk_list_store_append (priv->method_store, &iter);
+ gtk_list_store_set (priv->method_store, &iter,
+ METHOD_COL_NAME, _("Shared to other computers"),
+ METHOD_COL_NUM, IP6_METHOD_SHARED,
+ METHOD_COL_ENABLED, FALSE,
+ -1);
+ }
+
+ gtk_combo_box_set_model (priv->method, GTK_TREE_MODEL (priv->method_store));
+
+ priv->addr_label = glade_xml_get_widget (xml, "ip6_addr_label");
+ priv->addr_add = GTK_BUTTON (glade_xml_get_widget (xml, "ip6_addr_add_button"));
+ priv->addr_delete = GTK_BUTTON (glade_xml_get_widget (xml, "ip6_addr_delete_button"));
+ priv->addr_list = GTK_TREE_VIEW (glade_xml_get_widget (xml, "ip6_addresses"));
+
+ priv->dns_servers_label = glade_xml_get_widget (xml, "ip6_dns_servers_label");
+ priv->dns_servers = GTK_ENTRY (glade_xml_get_widget (xml, "ip6_dns_servers_entry"));
+
+ priv->dns_searches_label = glade_xml_get_widget (xml, "ip6_dns_searches_label");
+ priv->dns_searches = GTK_ENTRY (glade_xml_get_widget (xml, "ip6_dns_searches_entry"));
+
+ priv->routes_button = GTK_BUTTON (glade_xml_get_widget (xml, "ip6_routes_button"));
+}
+
+static void
+method_changed (GtkComboBox *combo, gpointer user_data)
+{
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (user_data);
+ guint32 method = IP6_METHOD_AUTO;
+ gboolean addr_enabled = FALSE;
+ gboolean dns_enabled = FALSE;
+ gboolean routes_enabled = FALSE;
+ GtkTreeIter iter;
+
+ if (gtk_combo_box_get_active_iter (priv->method, &iter)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->method_store), &iter,
+ METHOD_COL_NUM, &method, -1);
+ }
+
+ switch (method) {
+ case IP6_METHOD_AUTO:
+ addr_enabled = FALSE;
+ routes_enabled = TRUE;
+ break;
+ case IP6_METHOD_AUTO_ADDRESSES:
+ addr_enabled = FALSE;
+ dns_enabled = routes_enabled = TRUE;
+ break;
+ case IP6_METHOD_MANUAL:
+ addr_enabled = dns_enabled = routes_enabled = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ gtk_widget_set_sensitive (priv->addr_label, addr_enabled);
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->addr_add), addr_enabled);
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->addr_delete), addr_enabled);
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->addr_list), addr_enabled);
+ if (!addr_enabled) {
+ GtkListStore *store;
+
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (priv->addr_list));
+ gtk_list_store_clear (store);
+ }
+
+ gtk_widget_set_sensitive (priv->dns_servers_label, dns_enabled);
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->dns_servers), dns_enabled);
+ if (!dns_enabled)
+ gtk_entry_set_text (priv->dns_servers, "");
+
+ gtk_widget_set_sensitive (priv->dns_searches_label, dns_enabled);
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->dns_searches), dns_enabled);
+ if (!dns_enabled)
+ gtk_entry_set_text (priv->dns_searches, "");
+
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->routes_button), routes_enabled);
+
+ ce_page_changed (CE_PAGE (user_data));
+}
+
+typedef struct {
+ int method;
+ GtkComboBox *combo;
+} SetMethodInfo;
+
+static gboolean
+set_method (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
+{
+ SetMethodInfo *info = (SetMethodInfo *) user_data;
+ int method = 0;
+
+ gtk_tree_model_get (model, iter, METHOD_COL_NUM, &method, -1);
+ if (method == info->method) {
+ gtk_combo_box_set_active_iter (info->combo, iter);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+populate_ui (CEPageIP6 *self)
+{
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (self);
+ NMSettingIP6Config *setting = priv->setting;
+ GtkListStore *store;
+ GtkTreeIter model_iter;
+ int method = IP6_METHOD_IGNORE;
+ GString *string = NULL;
+ SetMethodInfo info;
+ const char *str_method;
+ int i;
+
+ /* Method */
+ gtk_combo_box_set_active (priv->method, 0);
+ str_method = nm_setting_ip6_config_get_method (setting);
+ if (str_method) {
+ if (!strcmp (str_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO))
+ method = IP6_METHOD_AUTO;
+ else if (!strcmp (str_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL))
+ method = IP6_METHOD_LINK_LOCAL;
+ else if (!strcmp (str_method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL))
+ method = IP6_METHOD_MANUAL;
+ else if (!strcmp (str_method, NM_SETTING_IP6_CONFIG_METHOD_SHARED))
+ method = IP6_METHOD_SHARED;
+ }
+
+ if (method == IP6_METHOD_AUTO && nm_setting_ip6_config_get_ignore_auto_dns (setting))
+ method = IP6_METHOD_AUTO_ADDRESSES;
+
+ info.method = method;
+ info.combo = priv->method;
+ gtk_tree_model_foreach (GTK_TREE_MODEL (priv->method_store), set_method, &info);
+
+ /* Addresses */
+ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+ for (i = 0; i < nm_setting_ip6_config_get_num_addresses (setting); i++) {
+ NMIP6Address *addr = nm_setting_ip6_config_get_address (setting, i);
+ const struct in6_addr *tmp_addr;
+ char buf[INET6_ADDRSTRLEN + 1];
+ const char *ignored;
+
+ if (!addr) {
+ g_warning ("%s: empty IP6 Address structure!", __func__);
+ continue;
+ }
+
+ gtk_list_store_append (store, &model_iter);
+
+ tmp_addr = nm_ip6_address_get_address (addr);
+ ignored = inet_ntop (AF_INET6, tmp_addr, &buf[0], sizeof (buf));
+ gtk_list_store_set (store, &model_iter, COL_ADDRESS, buf, -1);
+ snprintf (buf, sizeof (buf), "%u", nm_ip6_address_get_prefix (addr));
+ gtk_list_store_set (store, &model_iter, COL_PREFIX, buf, -1);
+ }
+
+ gtk_tree_view_set_model (priv->addr_list, GTK_TREE_MODEL (store));
+ g_signal_connect_swapped (store, "row-inserted", G_CALLBACK (ce_page_changed), self);
+ g_signal_connect_swapped (store, "row-deleted", G_CALLBACK (ce_page_changed), self);
+ g_object_unref (store);
+
+ /* DNS servers */
+ string = g_string_new ("");
+ for (i = 0; i < nm_setting_ip6_config_get_num_dns (setting); i++) {
+ const struct in6_addr *tmp_addr;
+ char buf[INET6_ADDRSTRLEN + 1];
+ const char *ignored;
+
+ tmp_addr = nm_setting_ip6_config_get_dns (setting, i);
+ if (!tmp_addr)
+ continue;
+
+ ignored = inet_ntop (AF_INET6, tmp_addr, &buf[0], sizeof (buf));
+ if (string->len)
+ g_string_append (string, ", ");
+ g_string_append (string, buf);
+ }
+ gtk_entry_set_text (priv->dns_servers, string->str);
+ g_string_free (string, TRUE);
+
+ /* DNS searches */
+ string = g_string_new ("");
+ for (i = 0; i < nm_setting_ip6_config_get_num_dns_searches (setting); i++) {
+ if (string->len)
+ g_string_append (string, ", ");
+ g_string_append (string, nm_setting_ip6_config_get_dns_search (setting, i));
+ }
+ gtk_entry_set_text (priv->dns_searches, string->str);
+ g_string_free (string, TRUE);
+}
+
+static void
+addr_add_clicked (GtkButton *button, gpointer user_data)
+{
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (user_data);
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+ GtkTreePath *path;
+ GList *cells;
+
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (priv->addr_list));
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, COL_ADDRESS, "", -1);
+
+ selection = gtk_tree_view_get_selection (priv->addr_list);
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
+ column = gtk_tree_view_get_column (priv->addr_list, COL_ADDRESS);
+
+ /* FIXME: using cells->data is pretty fragile but GTK apparently doesn't
+ * have a way to get a cell renderer from a column based on path or iter
+ * or whatever.
+ */
+ cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ gtk_tree_view_set_cursor_on_cell (priv->addr_list, path, column, cells->data, TRUE);
+
+ g_list_free (cells);
+ gtk_tree_path_free (path);
+}
+
+static void
+addr_delete_clicked (GtkButton *button, gpointer user_data)
+{
+ GtkTreeView *treeview = GTK_TREE_VIEW (user_data);
+ GtkTreeSelection *selection;
+ GList *selected_rows;
+ GtkTreeModel *model = NULL;
+ GtkTreeIter iter;
+ int num_rows;
+
+ selection = gtk_tree_view_get_selection (treeview);
+ if (gtk_tree_selection_count_selected_rows (selection) != 1)
+ return;
+
+ selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+ if (!selected_rows)
+ return;
+
+ if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) selected_rows->data))
+ gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+
+ g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected_rows);
+
+ num_rows = gtk_tree_model_iter_n_children (model, NULL);
+ if (num_rows && gtk_tree_model_iter_nth_child (model, &iter, NULL, num_rows - 1)) {
+ selection = gtk_tree_view_get_selection (treeview);
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+}
+
+static void
+list_selection_changed (GtkTreeSelection *selection, gpointer user_data)
+{
+ GtkWidget *button = GTK_WIDGET (user_data);
+ GtkTreeIter iter;
+ GtkTreeModel *model = NULL;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ gtk_widget_set_sensitive (button, TRUE);
+ else
+ gtk_widget_set_sensitive (button, FALSE);
+}
+
+static void
+cell_edited (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer user_data)
+{
+ CEPageIP6 *self = CE_PAGE_IP6 (user_data);
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (self);
+ GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (priv->addr_list));
+ GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+ GtkTreeIter iter;
+ guint32 column;
+ GtkTreeViewColumn *next_col;
+
+ column = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (cell), "column"));
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
+ gtk_list_store_set (store, &iter, column, new_text, -1);
+
+ /* Move focus to the next column */
+ column = (column >= COL_LAST) ? 0 : column + 1;
+ next_col = gtk_tree_view_get_column (priv->addr_list, column);
+ gtk_tree_view_set_cursor_on_cell (priv->addr_list, path, next_col, priv->addr_cells[column], TRUE);
+ gtk_widget_grab_focus (GTK_WIDGET (priv->addr_list));
+
+ gtk_tree_path_free (path);
+ ce_page_changed (CE_PAGE (self));
+}
+
+static void
+ip_address_filter_cb (GtkEntry * entry,
+ const gchar *text,
+ gint length,
+ gint * position,
+ gpointer data)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gboolean numeric = GPOINTER_TO_INT (data);
+ int i, count = 0;
+ gchar *result = g_new (gchar, length);
+
+ for (i = 0; i < length; i++) {
+ if ((numeric && g_ascii_isdigit (text[i])) ||
+ (!numeric && (g_ascii_isxdigit(text[i]) || (text[i] == ':'))))
+ result[count++] = text[i];
+ }
+
+ if (count > 0) {
+ g_signal_handlers_block_by_func (G_OBJECT (editable),
+ G_CALLBACK (ip_address_filter_cb),
+ data);
+ gtk_editable_insert_text (editable, result, count, position);
+ g_signal_handlers_unblock_by_func (G_OBJECT (editable),
+ G_CALLBACK (ip_address_filter_cb),
+ data);
+ }
+
+ g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
+ g_free (result);
+}
+
+static void
+cell_editing_started (GtkCellRenderer *cell,
+ GtkCellEditable *editable,
+ const gchar *path,
+ gpointer data)
+{
+ if (!GTK_IS_ENTRY (editable)) {
+ g_warning ("%s: Unexpected cell editable type.", __func__);
+ return;
+ }
+
+ /* Set up the entry filter */
+ g_signal_connect (G_OBJECT (editable), "insert-text",
+ (GCallback) ip_address_filter_cb,
+ data);
+}
+
+static void
+routes_dialog_close_cb (GtkWidget *dialog, gpointer user_data)
+{
+ gtk_widget_hide (dialog);
+ /* gtk_widget_destroy() will remove the window from the window group */
+ gtk_widget_destroy (dialog);
+}
+
+static void
+routes_dialog_response_cb (GtkWidget *dialog, gint response, gpointer user_data)
+{
+ CEPageIP6 *self = CE_PAGE_IP6 (user_data);
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (self);
+
+ if (response == GTK_RESPONSE_OK)
+ ip6_routes_dialog_update_setting (dialog, priv->setting);
+
+ routes_dialog_close_cb (dialog, NULL);
+}
+
+static void
+routes_button_clicked_cb (GtkWidget *button, gpointer user_data)
+{
+ CEPageIP6 *self = CE_PAGE_IP6 (user_data);
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (self);
+ GtkWidget *dialog, *toplevel;
+ gboolean automatic = FALSE;
+ const char *method;
+ char *tmp;
+
+ toplevel = gtk_widget_get_toplevel (CE_PAGE (self)->page);
+ g_return_if_fail (GTK_WIDGET_TOPLEVEL (toplevel));
+
+ method = nm_setting_ip6_config_get_method (priv->setting);
+ if (!method || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO))
+ automatic = TRUE;
+
+ dialog = ip6_routes_dialog_new (priv->setting, automatic);
+ if (!dialog) {
+ g_warning ("%s: failed to create the routes dialog!", __func__);
+ return;
+ }
+
+ gtk_window_group_add_window (priv->window_group, GTK_WINDOW (dialog));
+ if (!priv->window_added) {
+ gtk_window_group_add_window (priv->window_group, GTK_WINDOW (toplevel));
+ priv->window_added = TRUE;
+ }
+
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (toplevel));
+ tmp = g_strdup_printf (_("Editing IPv6 routes for %s"), priv->connection_id);
+ gtk_window_set_title (GTK_WINDOW (dialog), tmp);
+ g_free (tmp);
+
+ g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (routes_dialog_response_cb), self);
+ g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (routes_dialog_close_cb), self);
+
+ gtk_widget_show_all (dialog);
+}
+
+static void
+finish_setup (CEPageIP6 *self, gpointer unused, GError *error, gpointer user_data)
+{
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (self);
+ GtkTreeSelection *selection;
+ gint offset;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkListStore *store;
+
+ if (error)
+ return;
+
+ populate_ui (self);
+
+ /* Address column */
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (priv->addr_list));
+
+ /* IP Address column */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "editable", TRUE, NULL);
+ g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited), self);
+ g_object_set_data (G_OBJECT (renderer), "column", GUINT_TO_POINTER (COL_ADDRESS));
+ g_signal_connect (renderer, "editing-started", G_CALLBACK (cell_editing_started), GINT_TO_POINTER (FALSE));
+ priv->addr_cells[COL_ADDRESS] = GTK_CELL_RENDERER (renderer);
+
+ offset = gtk_tree_view_insert_column_with_attributes (priv->addr_list,
+ -1, _("Address"), renderer,
+ "text", COL_ADDRESS,
+ NULL);
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->addr_list), offset - 1);
+ gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
+ gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+
+ /* Prefix column */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "editable", TRUE, NULL);
+ g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited), self);
+ g_object_set_data (G_OBJECT (renderer), "column", GUINT_TO_POINTER (COL_PREFIX));
+ g_signal_connect (renderer, "editing-started", G_CALLBACK (cell_editing_started), GINT_TO_POINTER (TRUE));
+ priv->addr_cells[COL_PREFIX] = GTK_CELL_RENDERER (renderer);
+
+ offset = gtk_tree_view_insert_column_with_attributes (priv->addr_list,
+ -1, _("Prefix"), renderer,
+ "text", COL_PREFIX,
+ NULL);
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->addr_list), offset - 1);
+ gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
+ gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->addr_add), TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->addr_delete), FALSE);
+
+ g_signal_connect (priv->addr_add, "clicked", G_CALLBACK (addr_add_clicked), self);
+ g_signal_connect (priv->addr_delete, "clicked", G_CALLBACK (addr_delete_clicked), priv->addr_list);
+ selection = gtk_tree_view_get_selection (priv->addr_list);
+ g_signal_connect (selection, "changed", G_CALLBACK (list_selection_changed), priv->addr_delete);
+
+ g_signal_connect_swapped (priv->dns_servers, "changed", G_CALLBACK (ce_page_changed), self);
+ g_signal_connect_swapped (priv->dns_searches, "changed", G_CALLBACK (ce_page_changed), self);
+
+ method_changed (priv->method, self);
+ g_signal_connect (priv->method, "changed", G_CALLBACK (method_changed), self);
+
+ g_signal_connect (priv->routes_button, "clicked", G_CALLBACK (routes_button_clicked_cb), self);
+}
+
+CEPage *
+ce_page_ip6_new (NMConnection *connection, GtkWindow *parent_window, GError **error)
+{
+ CEPageIP6 *self;
+ CEPageIP6Private *priv;
+ CEPage *parent;
+ NMSettingConnection *s_con;
+
+ self = CE_PAGE_IP6 (g_object_new (CE_TYPE_PAGE_IP6,
+ CE_PAGE_CONNECTION, connection,
+ CE_PAGE_PARENT_WINDOW, parent_window,
+ NULL));
+ parent = CE_PAGE (self);
+
+ parent->xml = glade_xml_new (GLADEDIR "/ce-page-ip6.glade", "IP6Page", NULL);
+ if (!parent->xml) {
+ g_set_error (error, 0, 0, "%s", _("Could not load IPv6 user interface."));
+ g_object_unref (self);
+ return NULL;
+ }
+
+ parent->page = glade_xml_get_widget (parent->xml, "IP6Page");
+ if (!parent->page) {
+ g_set_error (error, 0, 0, "%s", _("Could not load IPv6 user interface."));
+ g_object_unref (self);
+ return NULL;
+ }
+ g_object_ref_sink (parent->page);
+
+ parent->title = g_strdup (_("IPv6 Settings"));
+
+ ip6_private_init (self, connection);
+ priv = CE_PAGE_IP6_GET_PRIVATE (self);
+
+ priv->window_group = gtk_window_group_new ();
+
+ s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+ g_assert (s_con);
+ priv->connection_id = g_strdup (nm_setting_connection_get_id (s_con));
+
+ priv->setting = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+ if (!priv->setting) {
+ priv->setting = NM_SETTING_IP6_CONFIG (nm_setting_ip6_config_new ());
+ nm_connection_add_setting (connection, NM_SETTING (priv->setting));
+ }
+
+ g_signal_connect (self, "initialized", G_CALLBACK (finish_setup), NULL);
+ if (!ce_page_initialize (parent, NULL, error)) {
+ g_object_unref (self);
+ return NULL;
+ }
+
+ return CE_PAGE (self);
+}
+
+static gboolean
+ui_to_setting (CEPageIP6 *self)
+{
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (self);
+ GtkTreeModel *model;
+ GtkTreeIter tree_iter;
+ int int_method = IP6_METHOD_AUTO;
+ const char *method;
+ gboolean valid = FALSE, iter_valid;
+ const char *text;
+ gboolean ignore_auto_dns = FALSE;
+ char **items = NULL, **iter;
+
+ /* Method */
+ if (gtk_combo_box_get_active_iter (priv->method, &tree_iter)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->method_store), &tree_iter,
+ METHOD_COL_NUM, &int_method, -1);
+ }
+
+ switch (int_method) {
+ case IP6_METHOD_LINK_LOCAL:
+ method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
+ break;
+ case IP6_METHOD_MANUAL:
+ method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
+ break;
+ case IP6_METHOD_SHARED:
+ method = NM_SETTING_IP6_CONFIG_METHOD_SHARED;
+ break;
+ case IP6_METHOD_AUTO_ADDRESSES:
+ ignore_auto_dns = TRUE;
+ /* fall through */
+ default:
+ method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+ break;
+ }
+
+ g_object_freeze_notify (G_OBJECT (priv->setting));
+ g_object_set (priv->setting,
+ NM_SETTING_IP6_CONFIG_METHOD, method,
+ NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS, ignore_auto_dns,
+ NULL);
+
+ /* IP addresses */
+ nm_setting_ip6_config_clear_addresses (priv->setting);
+ model = gtk_tree_view_get_model (priv->addr_list);
+ iter_valid = gtk_tree_model_get_iter_first (model, &tree_iter);
+ while (iter_valid) {
+ char *item = NULL, *end;
+ struct in6_addr tmp_addr;
+ NMIP6Address *addr;
+ guint32 prefix;
+
+ gtk_tree_model_get (model, &tree_iter, COL_ADDRESS, &item, -1);
+ if (!item || !inet_pton (AF_INET6, item, &tmp_addr)) {
+ g_warning ("%s: IPv6 address '%s' missing or invalid!",
+ __func__, item ? item : "<none>");
+ g_free (item);
+ goto out;
+ }
+ g_free (item);
+
+ gtk_tree_model_get (model, &tree_iter, COL_PREFIX, &item, -1);
+ if (!item) {
+ g_warning ("%s: IPv6 prefix '%s' missing!",
+ __func__, item ? item : "<none>");
+ goto out;
+ }
+
+ prefix = strtoul (item, &end, 10);
+ if (!end || *end || prefix == 0 || prefix > 128) {
+ g_warning ("%s: IPv6 prefix '%s' invalid!",
+ __func__, item ? item : "<none>");
+ g_free (item);
+ goto out;
+ }
+ g_free (item);
+
+ addr = nm_ip6_address_new ();
+ nm_ip6_address_set_address (addr, &tmp_addr);
+ nm_ip6_address_set_prefix (addr, prefix);
+ nm_setting_ip6_config_add_address (priv->setting, addr);
+ nm_ip6_address_unref (addr);
+
+ iter_valid = gtk_tree_model_iter_next (model, &tree_iter);
+ }
+
+ /* DNS servers */
+ nm_setting_ip6_config_clear_dns (priv->setting);
+ text = gtk_entry_get_text (GTK_ENTRY (priv->dns_servers));
+ if (text && strlen (text)) {
+ items = g_strsplit_set (text, ", ;", 0);
+ for (iter = items; *iter; iter++) {
+ struct in6_addr tmp_addr;
+ char *stripped = g_strstrip (*iter);
+
+ if (!strlen (stripped))
+ continue;
+
+ if (inet_pton (AF_INET6, stripped, &tmp_addr)) {
+ nm_setting_ip6_config_add_dns (priv->setting, &tmp_addr);
+ } else {
+ g_strfreev (items);
+ goto out;
+ }
+ }
+ g_strfreev (items);
+ }
+
+ /* Search domains */
+ nm_setting_ip6_config_clear_dns_searches (priv->setting);
+ text = gtk_entry_get_text (GTK_ENTRY (priv->dns_searches));
+ if (text && strlen (text)) {
+ items = g_strsplit_set (text, ", ;:", 0);
+ for (iter = items; *iter; iter++) {
+ char *stripped = g_strstrip (*iter);
+
+ if (strlen (stripped))
+ nm_setting_ip6_config_add_dns_search (priv->setting, stripped);
+ }
+
+ if (items)
+ g_strfreev (items);
+ }
+
+ valid = TRUE;
+
+out:
+ g_object_thaw_notify (G_OBJECT (priv->setting));
+
+ return valid;
+}
+
+static gboolean
+validate (CEPage *page, NMConnection *connection, GError **error)
+{
+ CEPageIP6 *self = CE_PAGE_IP6 (page);
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (self);
+
+ if (!ui_to_setting (self))
+ return FALSE;
+ return nm_setting_verify (NM_SETTING (priv->setting), NULL, error);
+}
+
+static void
+ce_page_ip6_init (CEPageIP6 *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ CEPageIP6 *self = CE_PAGE_IP6 (object);
+ CEPageIP6Private *priv = CE_PAGE_IP6_GET_PRIVATE (self);
+
+ if (priv->window_group)
+ g_object_unref (priv->window_group);
+
+ g_free (priv->connection_id);
+
+ G_OBJECT_CLASS (ce_page_ip6_parent_class)->dispose (object);
+}
+
+static void
+ce_page_ip6_class_init (CEPageIP6Class *ip6_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (ip6_class);
+ CEPageClass *parent_class = CE_PAGE_CLASS (ip6_class);
+
+ g_type_class_add_private (object_class, sizeof (CEPageIP6Private));
+
+ /* virtual methods */
+ parent_class->validate = validate;
+ object_class->dispose = dispose;
+}
diff --git a/src/connection-editor/page-ip6.h b/src/connection-editor/page-ip6.h
new file mode 100644
index 0000000..081b1fc
--- /dev/null
+++ b/src/connection-editor/page-ip6.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Connection editor -- Connection editor for NetworkManager
+ *
+ * Dan Williams <dcbw redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2008 Red Hat, Inc.
+ */
+
+#ifndef __PAGE_IP6_H__
+#define __PAGE_IP6_H__
+
+#include <nm-connection.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "ce-page.h"
+
+#define CE_TYPE_PAGE_IP6 (ce_page_ip6_get_type ())
+#define CE_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CE_TYPE_PAGE_IP6, CEPageIP6))
+#define CE_PAGE_IP6_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CE_TYPE_PAGE_IP6, CEPageIP6Class))
+#define CE_IS_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CE_TYPE_PAGE_IP6))
+#define CE_IS_PAGE_IP6_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), CE_TYPE_PAGE_IP6))
+#define CE_PAGE_IP6_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CE_TYPE_PAGE_IP6, CEPageIP6Class))
+
+typedef struct {
+ CEPage parent;
+} CEPageIP6;
+
+typedef struct {
+ CEPageClass parent;
+} CEPageIP6Class;
+
+GType ce_page_ip6_get_type (void);
+
+CEPage *ce_page_ip6_new (NMConnection *connection, GtkWindow *parent, GError **error);
+
+#endif /* __PAGE_IP6_H__ */
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]