summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore33
-rw-r--r--AUTHORS1
-rw-r--r--COPYING351
-rw-r--r--Makefile.am33
-rw-r--r--auth-dialog/Makefile.am30
-rw-r--r--auth-dialog/main.c246
-rw-r--r--auth-dialog/vpn-password-dialog.c461
-rw-r--r--auth-dialog/vpn-password-dialog.h79
-rwxr-xr-xautogen.sh21
-rw-r--r--configure.ac92
-rw-r--r--m4/compiler_warnings.m431
-rw-r--r--network-manager-iodine.doap23
-rw-r--r--nm-iodine-service.conf14
-rw-r--r--nm-iodine-service.name.in8
-rw-r--r--po/LINGUAS2
-rw-r--r--po/POTFILES.in7
-rw-r--r--properties/Makefile.am34
-rw-r--r--properties/nm-iodine-dialog.ui291
-rw-r--r--properties/nm-iodine.c768
-rw-r--r--properties/nm-iodine.h83
-rw-r--r--src/Makefile.am30
-rw-r--r--src/nm-iodine-service.c672
-rw-r--r--src/nm-iodine-service.h57
23 files changed, 3367 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1dc5c5b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,33 @@
+*~
+*.o
+*.lo
+*.la
+Makefile
+Makefile.in*
+configure
+compile
+config.*
+aclocal.m4
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+mkinstalldirs
+POTFILES
+stamp-*
+.deps
+.libs
+autom4te.cache
+intltool-*
+po/*.gmo
+po/.intltool-merge-cache
+m4/gtk-doc.m4
+m4/intltool.m4
+m4/libtool.m4
+m4/lt*.m4
+
+nm-iodine-service.name
+src/nm-iodine-service
+auth-dialog/nm-iodine-auth-dialog
+
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..080f177
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Guido Günther <agx@sigxcpu.org>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..fde38aa
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,351 @@
+Unless a COPYING file in a subdirectory or file-specific license headers
+specify a different license, the following applies to all files in this
+directory and all subdirectories.
+
+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.
+
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..794cc01
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,33 @@
+AUTOMAKE_OPTIONS = foreign
+
+SUBDIRS = src
+
+if WITH_GNOME
+SUBDIRS += properties po
+
+if WITH_AUTHDLG
+SUBDIRS += auth-dialog
+endif
+endif
+
+dbusservicedir = $(sysconfdir)/dbus-1/system.d
+dbusservice_DATA = nm-iodine-service.conf
+
+nmvpnservicedir = $(sysconfdir)/NetworkManager/VPN
+nmvpnservice_DATA = nm-iodine-service.name
+
+nm-iodine-service.name: $(srcdir)/nm-iodine-service.name.in
+ sed -e 's|[@]LIBEXECDIR[@]|$(libexecdir)|g' $< >$@
+
+EXTRA_DIST = nm-iodine-service.name.in \
+ $(dbusservice_DATA) \
+ $(desktop_in_files) \
+ $(icon_DATA) \
+ intltool-extract.in \
+ intltool-merge.in \
+ intltool-update.in
+
+CLEANFILES = $(nmvpnservice_DATA) $(desktop_DATA) *~
+DISTCLEANFILES = intltool-extract intltool-merge intltool-update
+
+ACLOCAL_AMFLAGS = -I m4
diff --git a/auth-dialog/Makefile.am b/auth-dialog/Makefile.am
new file mode 100644
index 0000000..fe45a82
--- /dev/null
+++ b/auth-dialog/Makefile.am
@@ -0,0 +1,30 @@
+INCLUDES = -I${top_srcdir}
+
+libexec_PROGRAMS = nm-iodine-auth-dialog
+
+nm_iodine_auth_dialog_CPPFLAGS = \
+ $(NM_CFLAGS) \
+ $(GTHREAD_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(GNOMEKEYRING_CFLAGS) \
+ -DICONDIR=\""$(datadir)/pixmaps"\" \
+ -DBINDIR=\""$(bindir)"\" \
+ -DG_DISABLE_DEPRECATED \
+ -DGDK_DISABLE_DEPRECATED \
+ -DGNOME_DISABLE_DEPRECATED \
+ -DGNOMELOCALEDIR=\"$(datadir)/locale\" \
+ -DVERSION=\"$(VERSION)\"
+
+nm_iodine_auth_dialog_SOURCES = \
+ main.c \
+ vpn-password-dialog.c \
+ vpn-password-dialog.h \
+ $(NULL)
+
+nm_iodine_auth_dialog_LDADD = \
+ $(GTK_LIBS) \
+ $(NM_LIBS) \
+ $(GNOMEKEYRING_LIBS)
+
+CLEANFILES = *~
+
diff --git a/auth-dialog/main.c b/auth-dialog/main.c
new file mode 100644
index 0000000..a7f127b
--- /dev/null
+++ b/auth-dialog/main.c
@@ -0,0 +1,246 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Authentication dialog for NetworkManager iodine VPN connections
+ *
+ * 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.
+ *
+ * Copyright © 2012 Guido Günther <agx@sigxcpu.org>
+ *
+ * Heavily based on network-manager-pptp by Dan Williams <dcbw@redhat.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <gnome-keyring.h>
+#include <gnome-keyring-memory.h>
+
+#include <nm-setting-vpn.h>
+#include <nm-vpn-plugin-utils.h>
+
+#include "src/nm-iodine-service.h"
+
+#include "vpn-password-dialog.h"
+
+#define KEYRING_UUID_TAG "connection-uuid"
+#define KEYRING_SN_TAG "setting-name"
+#define KEYRING_SK_TAG "setting-key"
+
+static char *
+keyring_lookup_secret (const char *uuid, const char *secret_name)
+{
+ GList *found_list = NULL;
+ GnomeKeyringResult ret;
+ GnomeKeyringFound *found;
+ char *secret = NULL;
+
+ ret = gnome_keyring_find_itemsv_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ &found_list,
+ KEYRING_UUID_TAG,
+ GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ uuid,
+ KEYRING_SN_TAG,
+ GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ NM_SETTING_VPN_SETTING_NAME,
+ KEYRING_SK_TAG,
+ GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ secret_name,
+ NULL);
+ if (ret == GNOME_KEYRING_RESULT_OK && found_list) {
+ found = g_list_nth_data (found_list, 0);
+ secret = gnome_keyring_memory_strdup (found->secret);
+ }
+
+ gnome_keyring_found_list_free (found_list);
+ return secret;
+}
+
+static gboolean
+get_secrets (const char *vpn_uuid,
+ const char *vpn_name,
+ gboolean retry,
+ gboolean allow_interaction,
+ const char *in_pw,
+ char **out_pw,
+ NMSettingSecretFlags pw_flags)
+{
+ VpnPasswordDialog *dialog;
+ char *prompt, *pw = NULL;
+ const char *new_password = NULL;
+
+ g_return_val_if_fail (vpn_uuid != NULL, FALSE);
+ g_return_val_if_fail (vpn_name != NULL, FALSE);
+ g_return_val_if_fail (out_pw != NULL, FALSE);
+ g_return_val_if_fail (*out_pw == NULL, FALSE);
+
+ /* Get the existing secret, if any */
+ if ( !(pw_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+ && !(pw_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
+ if (in_pw)
+ pw = gnome_keyring_memory_strdup (in_pw);
+ else
+ pw = keyring_lookup_secret (vpn_uuid, NM_IODINE_KEY_PASSWORD);
+ }
+
+ /* Don't ask if the passwords is unused */
+ if (pw_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) {
+ gnome_keyring_memory_free (pw);
+ return TRUE;
+ }
+
+ if (!retry) {
+ /* Don't ask the user if we don't need a new password (ie, !retry),
+ * we have an existing PW, and the password is saved.
+ */
+ if (pw && !(pw_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
+ *out_pw = pw;
+ return TRUE;
+ }
+ }
+
+ /* If interaction isn't allowed, just return existing secrets */
+ if (allow_interaction == FALSE) {
+ *out_pw = pw;
+ return TRUE;
+ }
+
+ /* Otherwise, we have no saved password, or the password flags indicated
+ * that the password should never be saved.
+ */
+ prompt = g_strdup_printf (_("You need to authenticate to access the "
+ "Virtual Private Network '%s'."), vpn_name);
+ dialog = (VpnPasswordDialog *) \
+ vpn_password_dialog_new (_("Authenticate VPN"), prompt, NULL);
+ g_free (prompt);
+
+ vpn_password_dialog_set_show_password_secondary (dialog, FALSE);
+
+ /* pre-fill dialog with the password */
+ if (pw && !(pw_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
+ vpn_password_dialog_set_password (dialog, pw);
+
+ gtk_widget_show (GTK_WIDGET (dialog));
+
+ if (vpn_password_dialog_run_and_block (dialog)) {
+
+ new_password = vpn_password_dialog_get_password (dialog);
+ if (new_password)
+ *out_pw = gnome_keyring_memory_strdup (new_password);
+ }
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ return TRUE;
+}
+
+
+static void
+wait_for_quit (void)
+{
+ GString *str;
+ char c;
+ ssize_t n;
+ time_t start;
+
+ str = g_string_sized_new (10);
+ start = time (NULL);
+ do {
+ errno = 0;
+ n = read (0, &c, 1);
+ if (n == 0 || (n < 0 && errno == EAGAIN))
+ g_usleep (G_USEC_PER_SEC / 10);
+ else if (n == 1) {
+ g_string_append_c (str, c);
+ if (strstr (str->str, "QUIT") || (str->len > 10))
+ break;
+ } else
+ break;
+ } while (time (NULL) < start + 20);
+ g_string_free (str, TRUE);
+}
+
+int
+main (int argc, char *argv[])
+{
+ gboolean retry = FALSE, allow_interaction = FALSE;
+ char *vpn_name = NULL, *vpn_uuid = NULL, *vpn_service = NULL;
+ char *password = NULL;
+ GHashTable *data = NULL, *secrets = NULL;
+ NMSettingSecretFlags pw_flags = NM_SETTING_SECRET_FLAG_NONE;
+ GOptionContext *context;
+ GOptionEntry entries[] = {
+ { "reprompt", 'r', 0, G_OPTION_ARG_NONE, &retry,
+ "Reprompt for passwords", NULL},
+ { "uuid", 'u', 0, G_OPTION_ARG_STRING, &vpn_uuid,
+ "UUID of VPN connection", NULL},
+ { "name", 'n', 0, G_OPTION_ARG_STRING, &vpn_name,
+ "Name of VPN connection", NULL},
+ { "service", 's', 0, G_OPTION_ARG_STRING, &vpn_service,
+ "VPN service type", NULL},
+ { "allow-interaction", 'i', 0, G_OPTION_ARG_NONE,
+ &allow_interaction, "Allow user interaction", NULL},
+ { NULL }
+ };
+
+ bindtextdomain (GETTEXT_PACKAGE, NULL);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ gtk_init (&argc, &argv);
+
+ context = g_option_context_new ("- iodine auth dialog");
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ if (!nm_vpn_plugin_utils_read_vpn_details (0, &data, &secrets)) {
+ fprintf (stderr, "Failed to read '%s' (%s) data and secrets from stdin.\n",
+ vpn_name, vpn_uuid);
+ return 1;
+ }
+
+ nm_vpn_plugin_utils_get_secret_flags (secrets, NM_IODINE_KEY_PASSWORD, &pw_flags);
+
+ if (!get_secrets (vpn_uuid, vpn_name, retry, allow_interaction,
+ g_hash_table_lookup (secrets, NM_IODINE_KEY_PASSWORD),
+ &password,
+ pw_flags))
+ return 1;
+
+ /* dump the passwords to stdout */
+ if (password)
+ printf ("%s\n%s\n", NM_IODINE_KEY_PASSWORD, password);
+ printf ("\n\n");
+
+ /* for good measure, flush stdout since Kansas is going Bye-Bye */
+ fflush (stdout);
+
+ /* Wait for quit signal */
+ wait_for_quit ();
+
+ if (data)
+ g_hash_table_unref (data);
+ if (secrets)
+ g_hash_table_unref (secrets);
+ return 0;
+}
diff --git a/auth-dialog/vpn-password-dialog.c b/auth-dialog/vpn-password-dialog.c
new file mode 100644
index 0000000..6c976a3
--- /dev/null
+++ b/auth-dialog/vpn-password-dialog.c
@@ -0,0 +1,461 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* vpn-password-dialog.c - A use password prompting dialog widget.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the ree Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library 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.
+ *
+ * Copyright (C) 1999, 2000 Eazel, Inc.
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * Authors: Ramiro Estrugo <ramiro@eazel.com>
+ * Dan Williams <dcbw@redhat.com>
+ */
+
+#include <config.h>
+#include <gnome-keyring-memory.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "vpn-password-dialog.h"
+
+G_DEFINE_TYPE (VpnPasswordDialog, vpn_password_dialog, GTK_TYPE_DIALOG)
+
+#define VPN_PASSWORD_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ VPN_TYPE_PASSWORD_DIALOG, \
+ VpnPasswordDialogPrivate))
+
+typedef struct {
+ /* Attributes */
+ gboolean show_password;
+ gboolean show_password_secondary;
+
+ /* Internal widgetry and flags */
+ GtkWidget *password_entry;
+ GtkWidget *password_entry_secondary;
+ GtkWidget *show_passwords_checkbox;
+
+ GtkWidget *table_alignment;
+ GtkWidget *table;
+ GtkSizeGroup *group;
+
+ char *primary_password_label;
+ char *secondary_password_label;
+} VpnPasswordDialogPrivate;
+
+/* VpnPasswordDialogClass methods */
+static void vpn_password_dialog_class_init (VpnPasswordDialogClass *password_dialog_class);
+static void vpn_password_dialog_init (VpnPasswordDialog *password_dialog);
+
+/* GtkDialog callbacks */
+static void dialog_show_callback (GtkWidget *widget, gpointer callback_data);
+static void dialog_close_callback (GtkWidget *widget, gpointer callback_data);
+
+static void
+finalize (GObject *object)
+{
+ VpnPasswordDialogPrivate *priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (object);
+
+ g_object_unref (priv->password_entry);
+ g_object_unref (priv->password_entry_secondary);
+ g_object_unref (priv->group);
+
+ g_free (priv->primary_password_label);
+ g_free (priv->secondary_password_label);
+
+ G_OBJECT_CLASS (vpn_password_dialog_parent_class)->finalize (object);
+}
+
+static void
+vpn_password_dialog_class_init (VpnPasswordDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (VpnPasswordDialogPrivate));
+
+ object_class->finalize = finalize;
+}
+
+static void
+vpn_password_dialog_init (VpnPasswordDialog *dialog)
+{
+ VpnPasswordDialogPrivate *priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ priv->show_password = TRUE;
+ priv->primary_password_label = g_strdup ( _("_Password:") );
+ priv->show_password_secondary = TRUE;
+ priv->secondary_password_label = g_strdup ( _("_Secondary Password:") );
+}
+
+/* GtkDialog callbacks */
+static void
+dialog_show_callback (GtkWidget *widget, gpointer callback_data)
+{
+ VpnPasswordDialog *dialog = VPN_PASSWORD_DIALOG (callback_data);
+ VpnPasswordDialogPrivate *priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ if (gtk_widget_get_visible (priv->password_entry))
+ gtk_widget_grab_focus (priv->password_entry);
+ else if (gtk_widget_get_visible (priv->password_entry_secondary))
+ gtk_widget_grab_focus (priv->password_entry_secondary);
+}
+
+static void
+dialog_close_callback (GtkWidget *widget, gpointer callback_data)
+{
+ gtk_widget_hide (widget);
+}
+
+static void
+add_row (GtkWidget *table, int row, const char *label_text, GtkWidget *entry)
+{
+ GtkWidget *label;
+
+ label = gtk_label_new_with_mnemonic (label_text);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, row, row + 1);
+ gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, row, row + 1);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+}
+
+static void
+remove_child (GtkWidget *child, GtkWidget *table)
+{
+ gtk_container_remove (GTK_CONTAINER (table), child);
+}
+
+static void
+add_table_rows (VpnPasswordDialog *dialog)
+{
+ VpnPasswordDialogPrivate *priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+ int row;
+ int offset = 0;
+
+ gtk_alignment_set_padding (GTK_ALIGNMENT (priv->table_alignment), 0, 0, offset, 0);
+
+ /* This will not kill the entries, since they are ref:ed */
+ gtk_container_foreach (GTK_CONTAINER (priv->table), (GtkCallback) remove_child, priv->table);
+
+ row = 0;
+ if (priv->show_password)
+ add_row (priv->table, row++, priv->primary_password_label, priv->password_entry);
+ if (priv->show_password_secondary)
+ add_row (priv->table, row++, priv->secondary_password_label, priv->password_entry_secondary);
+
+ gtk_table_attach_defaults (GTK_TABLE (priv->table), priv->show_passwords_checkbox, 1, 2, row, row + 1);
+
+ gtk_widget_show_all (priv->table);
+}
+
+static void
+show_passwords_toggled_cb (GtkWidget *widget, gpointer user_data)
+{
+ VpnPasswordDialog *dialog = VPN_PASSWORD_DIALOG (user_data);
+ VpnPasswordDialogPrivate *priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+ gboolean visible;
+
+ visible = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry), visible);
+ gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry_secondary), visible);
+}
+
+/* Public VpnPasswordDialog methods */
+GtkWidget *
+vpn_password_dialog_new (const char *title,
+ const char *message,
+ const char *password)
+{
+ GtkWidget *dialog;
+ VpnPasswordDialogPrivate *priv;
+ GtkLabel *message_label;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *main_vbox;
+ GtkWidget *dialog_icon;
+ GtkBox *content, *action_area;
+
+ dialog = gtk_widget_new (VPN_TYPE_PASSWORD_DIALOG, NULL);
+ if (!dialog)
+ return NULL;
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), title);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ content = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog)));
+ action_area = GTK_BOX (gtk_dialog_get_action_area (GTK_DIALOG (dialog)));
+
+ /* Setup the dialog */
+#if !GTK_CHECK_VERSION (2,22,0)
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+#endif
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (content, 2); /* 2 * 5 + 2 = 12 */
+ gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
+ gtk_box_set_spacing (action_area, 6);
+
+ gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+ g_signal_connect (dialog, "show",
+ G_CALLBACK (dialog_show_callback),
+ dialog);
+ g_signal_connect (dialog, "close",
+ G_CALLBACK (dialog_close_callback),
+ dialog);
+
+ /* The table that holds the captions */
+ priv->table_alignment = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
+
+ priv->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ priv->table = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (priv->table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE (priv->table), 6);
+ gtk_container_add (GTK_CONTAINER (priv->table_alignment), priv->table);
+
+ priv->password_entry = gtk_entry_new ();
+ priv->password_entry_secondary = gtk_entry_new ();
+
+ priv->show_passwords_checkbox = gtk_check_button_new_with_mnemonic (_("Sh_ow passwords"));
+
+ /* We want to hold on to these during the table rearrangement */
+ g_object_ref_sink (priv->password_entry);
+ g_object_ref_sink (priv->password_entry_secondary);
+ g_object_ref_sink (priv->show_passwords_checkbox);
+
+ gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry), FALSE);
+ gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry_secondary), FALSE);
+
+ g_signal_connect_swapped (priv->password_entry, "activate",
+ G_CALLBACK (gtk_window_activate_default),
+ dialog);
+ g_signal_connect_swapped (priv->password_entry_secondary, "activate",
+ G_CALLBACK (gtk_window_activate_default),
+ dialog);
+
+ g_signal_connect (priv->show_passwords_checkbox, "toggled",
+ G_CALLBACK (show_passwords_toggled_cb),
+ dialog);
+
+ add_table_rows (VPN_PASSWORD_DIALOG (dialog));
+
+ /* Adds some eye-candy to the dialog */
+#if GTK_CHECK_VERSION (3,1,6)
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+#else
+ hbox = gtk_hbox_new (FALSE, 12);
+#endif
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+ dialog_icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION, GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (dialog_icon), 0.5, 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), dialog_icon, FALSE, FALSE, 0);
+
+ /* Fills the vbox */
+#if GTK_CHECK_VERSION (3,1,6)
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 18);
+#else
+ main_vbox = gtk_vbox_new (FALSE, 18);
+#endif
+
+ if (message) {
+ message_label = GTK_LABEL (gtk_label_new (message));
+ gtk_label_set_justify (message_label, GTK_JUSTIFY_LEFT);
+ gtk_label_set_line_wrap (message_label, TRUE);
+ gtk_label_set_max_width_chars (message_label, 35);
+ gtk_size_group_add_widget (priv->group, GTK_WIDGET (message_label));
+ gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (message_label), FALSE, FALSE, 0);
+ gtk_size_group_add_widget (priv->group, priv->table_alignment);
+ }
+
+#if GTK_CHECK_VERSION (3,1,6)
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+#else
+ vbox = gtk_vbox_new (FALSE, 6);
+#endif
+ gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), priv->table_alignment, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), main_vbox, FALSE, FALSE, 0);
+ gtk_box_pack_start (content, hbox, FALSE, FALSE, 0);
+ gtk_widget_show_all (GTK_WIDGET (content));
+
+ vpn_password_dialog_set_password (VPN_PASSWORD_DIALOG (dialog), password);
+
+ return GTK_WIDGET (dialog);
+}
+
+gboolean
+vpn_password_dialog_run_and_block (VpnPasswordDialog *dialog)
+{
+ gint button_clicked;
+
+ g_return_val_if_fail (dialog != NULL, FALSE);
+ g_return_val_if_fail (VPN_IS_PASSWORD_DIALOG (dialog), FALSE);
+
+ button_clicked = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_hide (GTK_WIDGET (dialog));
+
+ return button_clicked == GTK_RESPONSE_OK;
+}
+
+void
+vpn_password_dialog_set_password (VpnPasswordDialog *dialog,
+ const char *password)
+{
+ VpnPasswordDialogPrivate *priv;
+
+ g_return_if_fail (VPN_IS_PASSWORD_DIALOG (dialog));
+
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+ gtk_entry_set_text (GTK_ENTRY (priv->password_entry), password ? password : "");
+}
+
+void
+vpn_password_dialog_set_password_secondary (VpnPasswordDialog *dialog,
+ const char *password_secondary)
+{
+ VpnPasswordDialogPrivate *priv;
+
+ g_return_if_fail (VPN_IS_PASSWORD_DIALOG (dialog));
+
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+ gtk_entry_set_text (GTK_ENTRY (priv->password_entry_secondary),
+ password_secondary ? password_secondary : "");
+}
+
+void
+vpn_password_dialog_set_show_password (VpnPasswordDialog *dialog, gboolean show)
+{
+ VpnPasswordDialogPrivate *priv;
+
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (VPN_IS_PASSWORD_DIALOG (dialog));
+
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ show = !!show;
+ if (priv->show_password != show) {
+ priv->show_password = show;
+ add_table_rows (dialog);
+ }
+}
+
+void
+vpn_password_dialog_set_show_password_secondary (VpnPasswordDialog *dialog,
+ gboolean show)
+{
+ VpnPasswordDialogPrivate *priv;
+
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (VPN_IS_PASSWORD_DIALOG (dialog));
+
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ show = !!show;
+ if (priv->show_password_secondary != show) {
+ priv->show_password_secondary = show;
+ add_table_rows (dialog);
+ }
+}
+
+void
+vpn_password_dialog_focus_password (VpnPasswordDialog *dialog)
+{
+ VpnPasswordDialogPrivate *priv;
+
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (VPN_IS_PASSWORD_DIALOG (dialog));
+
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+ if (priv->show_password)
+ gtk_widget_grab_focus (priv->password_entry);
+}
+
+void
+vpn_password_dialog_focus_password_secondary (VpnPasswordDialog *dialog)
+{
+ VpnPasswordDialogPrivate *priv;
+
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (VPN_IS_PASSWORD_DIALOG (dialog));
+
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+ if (priv->show_password_secondary)
+ gtk_widget_grab_focus (priv->password_entry_secondary);
+}
+
+const char *
+vpn_password_dialog_get_password (VpnPasswordDialog *dialog)
+{
+ VpnPasswordDialogPrivate *priv;
+
+ g_return_val_if_fail (VPN_IS_PASSWORD_DIALOG (dialog), NULL);
+
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+ return gtk_entry_get_text (GTK_ENTRY (priv->password_entry));
+}
+
+const char *
+vpn_password_dialog_get_password_secondary (VpnPasswordDialog *dialog)
+{
+ VpnPasswordDialogPrivate *priv;
+
+ g_return_val_if_fail (VPN_IS_PASSWORD_DIALOG (dialog), NULL);
+
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+ return gtk_entry_get_text (GTK_ENTRY (priv->password_entry_secondary));
+}
+
+void vpn_password_dialog_set_password_label (VpnPasswordDialog *dialog,
+ const char *label)
+{
+ VpnPasswordDialogPrivate *priv;
+
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (VPN_IS_PASSWORD_DIALOG (dialog));
+
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ g_free (priv->primary_password_label);
+ priv->primary_password_label = g_strdup (label);
+
+ if (priv->show_password)
+ add_table_rows (dialog);
+}
+
+void vpn_password_dialog_set_password_secondary_label (VpnPasswordDialog *dialog,
+ const char *label)
+{
+ VpnPasswordDialogPrivate *priv;
+
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (VPN_IS_PASSWORD_DIALOG (dialog));
+
+ priv = VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ g_free (priv->secondary_password_label);
+ priv->secondary_password_label = g_strdup (label);
+
+ if (priv->show_password_secondary)
+ add_table_rows (dialog);
+}
+
diff --git a/auth-dialog/vpn-password-dialog.h b/auth-dialog/vpn-password-dialog.h
new file mode 100644
index 0000000..7d69320
--- /dev/null
+++ b/auth-dialog/vpn-password-dialog.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* vpn-password-dialog.c - A use password prompting dialog widget.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the ree Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library 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.
+ *
+ * Copyright (C) 1999, 2000 Eazel, Inc.
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * Authors: Ramiro Estrugo <ramiro@eazel.com>
+ * Dan Williams <dcbw@redhat.com>
+ */
+
+#ifndef VPN_PASSWORD_DIALOG_H
+#define VPN_PASSWORD_DIALOG_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define VPN_TYPE_PASSWORD_DIALOG (vpn_password_dialog_get_type ())
+#define VPN_PASSWORD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VPN_TYPE_PASSWORD_DIALOG, VpnPasswordDialog))
+#define VPN_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VPN_TYPE_PASSWORD_DIALOG, VpnPasswordDialogClass))
+#define VPN_IS_PASSWORD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VPN_TYPE_PASSWORD_DIALOG))
+#define VPN_IS_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VPN_TYPE_PASSWORD_DIALOG))
+
+typedef struct VpnPasswordDialog VpnPasswordDialog;
+typedef struct VpnPasswordDialogClass VpnPasswordDialogClass;
+
+struct VpnPasswordDialog {
+ GtkDialog parent;
+};
+
+struct VpnPasswordDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType vpn_password_dialog_get_type (void);
+GtkWidget* vpn_password_dialog_new (const char *title,
+ const char *message,
+ const char *password);
+
+gboolean vpn_password_dialog_run_and_block (VpnPasswordDialog *dialog);
+
+/* Attribute mutators */
+void vpn_password_dialog_set_show_password (VpnPasswordDialog *dialog,
+ gboolean show);
+void vpn_password_dialog_focus_password (VpnPasswordDialog *dialog);
+void vpn_password_dialog_set_password (VpnPasswordDialog *dialog,
+ const char *password);
+void vpn_password_dialog_set_password_label (VpnPasswordDialog *dialog,
+ const char *label);
+
+void vpn_password_dialog_set_show_password_secondary (VpnPasswordDialog *dialog,
+ gboolean show);
+void vpn_password_dialog_focus_password_secondary (VpnPasswordDialog *dialog);
+void vpn_password_dialog_set_password_secondary (VpnPasswordDialog *dialog,
+ const char *password_secondary);
+void vpn_password_dialog_set_password_secondary_label (VpnPasswordDialog *dialog,
+ const char *label);
+/* Attribute accessors */
+const char *vpn_password_dialog_get_password (VpnPasswordDialog *dialog);
+
+const char *vpn_password_dialog_get_password_secondary (VpnPasswordDialog *dialog);
+
+G_END_DECLS
+
+#endif /* VPN_PASSWORD_DIALOG_H */
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..1a112a6
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+REQUIRED_AUTOMAKE_VERSION=1.7
+PKG_NAME=NetworkManager-iodine
+
+(test -f $srcdir/configure.ac) || {
+ echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+ echo " top-level $PKG_NAME directory"
+ exit 1
+}
+
+(cd $srcdir;
+ autoreconf --install &&
+ intltoolize --force &&
+ autoreconf &&
+ ./configure --enable-maintainer-mode $@
+)
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..0961d0b
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,92 @@
+AC_PREREQ(2.52)
+
+AC_INIT(NetworkManager-iodine, 0.0.1, agx@sigxcpu.org, NetworkManager-iodine)
+AM_INIT_AUTOMAKE([subdir-objects no-dist-gzip dist-xz])
+AM_MAINTAINER_MODE
+
+AC_CONFIG_MACRO_DIR([m4])
+
+AC_CONFIG_HEADERS([config.h])
+
+dnl
+dnl Require programs
+dnl
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+dnl
+dnl Required headers
+dnl
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h paths.h sys/ioctl.h sys/time.h syslog.h unistd.h)
+
+dnl
+dnl Checks for typedefs, structures, and compiler characteristics.
+dnl
+AC_TYPE_MODE_T
+AC_TYPE_PID_T
+AC_HEADER_TIME
+
+dnl
+dnl Checks for library functions.
+dnl
+AC_PROG_GCC_TRADITIONAL
+AC_FUNC_MEMCMP
+AC_CHECK_FUNCS(select socket uname)
+
+dnl
+dnl GNOME support
+dnl
+AC_ARG_WITH(gnome, AS_HELP_STRING([--without-gnome], [Build NetworkManager-iodine without GNOME support, e.g. vpn service only]))
+AM_CONDITIONAL(WITH_GNOME, test x"$with_gnome" != xno)
+
+AC_ARG_WITH(authdlg, AS_HELP_STRING([--without-authdlg], [Build NetworkManager-iodine without authentication dialog]))
+AM_CONDITIONAL(WITH_AUTHDLG, test x"$with_authdlg" != xno)
+
+GETTEXT_PACKAGE=NetworkManager-iodine
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package])
+
+IT_PROG_INTLTOOL([0.35])
+AM_GLIB_GNU_GETTEXT
+
+PKG_CHECK_MODULES(GTHREAD, gthread-2.0)
+AC_SUBST(GTHREAD_CFLAGS)
+AC_SUBST(GTHREAD_LIBS)
+
+PKG_CHECK_MODULES(DBUS, dbus-glib-1 >= 0.74)
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+if test x"$with_gnome" != xno; then
+ gtk3_req=2.91.4
+ PKG_CHECK_MODULES(GTK, gtk+-3.0 > $gtk3_req, ,
+ [PKG_CHECK_MODULES(GTK, gtk+-2.0 > $gtk2_req)])
+ AC_SUBST(GTK_CFLAGS)
+ AC_SUBST(GTK_LIBS)
+
+ PKG_CHECK_MODULES(GNOMEKEYRING, gnome-keyring-1)
+ AC_SUBST(GNOMEKEYRING_CFLAGS)
+ AC_SUBST(GNOMEKEYRING_LIBS)
+fi
+
+PKG_CHECK_MODULES(NM,
+ NetworkManager >= 0.9.2
+ libnm-util >= 0.9.2
+ libnm-glib >= 0.9.2
+ libnm-glib-vpn >= 0.9.2)
+AC_SUBST(NM_CFLAGS)
+AC_SUBST(NM_LIBS)
+
+NM_COMPILER_WARNINGS
+
+AC_CONFIG_FILES([
+Makefile
+src/Makefile
+auth-dialog/Makefile
+properties/Makefile
+po/Makefile.in
+])
+AC_OUTPUT
diff --git a/m4/compiler_warnings.m4 b/m4/compiler_warnings.m4
new file mode 100644
index 0000000..6cea2f7
--- /dev/null
+++ b/m4/compiler_warnings.m4
@@ -0,0 +1,31 @@
+AC_DEFUN([NM_COMPILER_WARNINGS],
+[AC_ARG_ENABLE(more-warnings,
+ AS_HELP_STRING([--enable-more-warnings], [Maximum compiler warnings]),
+ set_more_warnings="$enableval",set_more_warnings=yes)
+AC_MSG_CHECKING(for more warnings, including -Werror)
+if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
+ AC_MSG_RESULT(yes)
+ CFLAGS="-Wall -Werror -std=gnu89 $CFLAGS"
+
+ for option in -Wshadow -Wmissing-declarations -Wmissing-prototypes \
+ -Wdeclaration-after-statement -Wstrict-prototypes \
+ -Wfloat-equal -Wno-unused-parameter -Wno-sign-compare \
+ -fno-strict-aliasing; do
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $option"
+ AC_MSG_CHECKING([whether gcc understands $option])
+ AC_TRY_COMPILE([], [],
+ has_option=yes,
+ has_option=no,)
+ if test $has_option = no; then
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ AC_MSG_RESULT($has_option)
+ unset has_option
+ unset SAVE_CFLAGS
+ done
+ unset option
+else
+ AC_MSG_RESULT(no)
+fi
+])
diff --git a/network-manager-iodine.doap b/network-manager-iodine.doap
new file mode 100644
index 0000000..a24d8b9
--- /dev/null
+++ b/network-manager-iodine.doap
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
+ xmlns:gnome="http://api.gnome.org/doap-extensions#"
+ xmlns="http://usefulinc.com/ns/doap#">
+
+ <name xml:lang="en">network-manager-iodine</name>
+ <shortdesc xml:lang="en">Iodine support for NetworkManager</shortdesc>
+
+ <homepage rdf:resource="http://honk.sigxcpu.org/piki/projects//network-manager-iodine/" />
+ <category rdf:resource="http://api.gnome.org/doap-extensions#development" />
+
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Guido Günther</foaf:name>
+ <foaf:mbox rdf:resource="mailto:agx@sigxcpu.org" />
+ <gnome:userid>guidog</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+
+</Project>
+
diff --git a/nm-iodine-service.conf b/nm-iodine-service.conf
new file mode 100644
index 0000000..8c00cc1
--- /dev/null
+++ b/nm-iodine-service.conf
@@ -0,0 +1,14 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy user="root">
+ <allow own="org.freedesktop.NetworkManager.iodine"/>
+ <allow send_destination="org.freedesktop.NetworkManager.iodine"/>
+ </policy>
+ <policy context="default">
+ <deny own="org.freedesktop.NetworkManager.iodine"/>
+ <deny send_destination="org.freedesktop.NetworkManager.iodine"/>
+ </policy>
+</busconfig>
+
diff --git a/nm-iodine-service.name.in b/nm-iodine-service.name.in
new file mode 100644
index 0000000..4a3d471
--- /dev/null
+++ b/nm-iodine-service.name.in
@@ -0,0 +1,8 @@
+[VPN Connection]
+name=iodine
+service=org.freedesktop.NetworkManager.iodine
+program=@LIBEXECDIR@/nm-iodine-service
+
+[GNOME]
+auth-dialog=nm-iodine-auth-dialog
+properties=libnm-iodine-properties
diff --git a/po/LINGUAS b/po/LINGUAS
new file mode 100644
index 0000000..bc8cbb0
--- /dev/null
+++ b/po/LINGUAS
@@ -0,0 +1,2 @@
+# please keep this list sorted alphabetically
+#
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644
index 0000000..2aad63d
--- /dev/null
+++ b/po/POTFILES.in
@@ -0,0 +1,7 @@
+# List of source files containing translatable strings.
+# Please keep this file sorted alphabetically.
+auth-dialog/main.c
+auth-dialog/vpn-password-dialog.c
+properties/nm-iodine.c
+[type: gettext/glade]properties/nm-iodine-dialog.ui
+src/nm-iodine-service.c
diff --git a/properties/Makefile.am b/properties/Makefile.am
new file mode 100644
index 0000000..30e7529
--- /dev/null
+++ b/properties/Makefile.am
@@ -0,0 +1,34 @@
+plugindir = $(libdir)/NetworkManager
+plugin_LTLIBRARIES = libnm-iodine-properties.la
+
+libnm_iodine_properties_la_SOURCES = \
+ nm-iodine.c \
+ nm-iodine.h
+
+uidir = $(datadir)/gnome-vpn-properties/iodine
+ui_DATA = nm-iodine-dialog.ui
+
+libnm_iodine_properties_la_CFLAGS = \
+ $(GTK_CFLAGS) \
+ $(GCONF_CFLAGS) \
+ $(NM_CFLAGS) \
+ -DICONDIR=\""$(datadir)/pixmaps"\" \
+ -DUIDIR=\""$(uidir)"\" \
+ -DG_DISABLE_DEPRECATED \
+ -DGDK_DISABLE_DEPRECATED \
+ -DGNOME_DISABLE_DEPRECATED \
+ -DGNOMELOCALEDIR=\"$(datadir)/locale\" \
+ -DVERSION=\"$(VERSION)\"
+
+libnm_iodine_properties_la_LIBADD = \
+ $(GTK_LIBS) \
+ $(GCONF_LIBS) \
+ $(NM_LIBS)
+
+libnm_iodine_properties_la_LDFLAGS = \
+ -avoid-version
+
+CLEANFILES = *.bak *~
+
+EXTRA_DIST = \
+ $(ui_DATA)
diff --git a/properties/nm-iodine-dialog.ui b/properties/nm-iodine-dialog.ui
new file mode 100644
index 0000000..f362c6c
--- /dev/null
+++ b/properties/nm-iodine-dialog.ui
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 2.6 -->
+ <object class="GtkListStore" id="pass_type_model">
+ <columns>
+ <!-- column-name gchararray -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">Saved</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Always ask</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Not required</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkVBox" id="iodine-vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="spacing">16</property>
+ <child>
+ <object class="GtkVBox" id="vbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label22">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;General&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="xscale">0</property>
+ <child>
+ <object class="GtkEntry" id="topdomain_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="invisible_char">•</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label23">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Toplevel Domain:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">topdomain_entry</property>
+ </object>
+ <packing>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Optional&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label28">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Nameserver:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">nameserver_entry</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Password:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">password_entry</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="password_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Fragment Size:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">fragsize_entry</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="fragsize_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">•</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="nameserver_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">•</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show_passwords_checkbutton">
+ <property name="label" translatable="yes">Show password</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="pass_type_combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">pass_type_model</property>
+ <child>
+ <object class="GtkCellRendererText" id="renderer2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/properties/nm-iodine.c b/properties/nm-iodine.c
new file mode 100644
index 0000000..b1e7537
--- /dev/null
+++ b/properties/nm-iodine.c
@@ -0,0 +1,768 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * GNOME UI dialogs for configuring iodine VPN connections
+ *
+ * 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.
+ *
+ * Copyright © 2012 Guido Günther <agx@sigxcpu.org>
+ *
+ * Based on network-manager-{openconnect,pptp}
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#define NM_VPN_API_SUBJECT_TO_CHANGE
+
+#include <nm-vpn-plugin-ui-interface.h>
+#include <nm-setting-vpn.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-ip4-config.h>
+
+#include "../src/nm-iodine-service.h"
+#include "nm-iodine.h"
+
+#define IODINE_PLUGIN_NAME _("Iodine DNS Tunnel")
+#define IODINE_PLUGIN_DESC _("Tunnel connections via DNS.")
+#define IODINE_PLUGIN_SERVICE NM_DBUS_SERVICE_IODINE
+
+#define PW_TYPE_SAVE 0
+#define PW_TYPE_ASK 1
+#define PW_TYPE_UNUSED 2
+
+/************** plugin class **************/
+
+static void iodine_plugin_ui_interface_init (NMVpnPluginUiInterface
+ *iface_class);
+
+G_DEFINE_TYPE_EXTENDED (IodinePluginUi, iodine_plugin_ui, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (NM_TYPE_VPN_PLUGIN_UI_INTERFACE,
+ iodine_plugin_ui_interface_init))
+
+/************** UI widget class **************/
+
+static void iodine_plugin_ui_widget_interface_init (NMVpnPluginUiWidgetInterface
+ *iface_class);
+
+G_DEFINE_TYPE_EXTENDED (IodinePluginUiWidget, iodine_plugin_ui_widget,G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE,
+ iodine_plugin_ui_widget_interface_init))
+
+#define IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IODINE_TYPE_PLUGIN_UI_WIDGET, IodinePluginUiWidgetPrivate))
+
+typedef struct {
+ GtkBuilder *builder;
+ GtkWidget *widget;
+ GtkSizeGroup *group;
+ gboolean window_added;
+} IodinePluginUiWidgetPrivate;
+
+static NMConnection *
+import (NMVpnPluginUiInterface *iface, const char *path, GError **error)
+{
+ NMConnection *connection;
+ NMSettingConnection *s_con;
+ NMSettingVPN *s_vpn;
+ NMSettingIP4Config *s_ip4;
+ GKeyFile *keyfile;
+ GKeyFileFlags flags;
+ const char *buf;
+
+ keyfile = g_key_file_new ();
+ flags = G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS;
+
+ if (!g_key_file_load_from_file (keyfile, path, flags, error)) {
+ g_set_error (error,
+ 0,
+ 0,
+ "does not look like a %s VPN connection (parse failed)",
+ IODINE_PLUGIN_NAME);
+ return NULL;
+ }
+
+ connection = nm_connection_new ();
+ s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+ s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ());
+ g_object_set (s_vpn,
+ NM_SETTING_VPN_SERVICE_TYPE,
+ NM_DBUS_SERVICE_IODINE,
+ NULL);
+ nm_connection_add_setting (connection, NM_SETTING (s_vpn));
+
+ s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
+ nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+ /* top level domain */
+ buf = g_key_file_get_string (keyfile, "iodine", "topdomain", NULL);
+ if (buf) {
+ nm_setting_vpn_add_data_item (s_vpn, NM_IODINE_KEY_TOPDOMAIN, buf);
+ } else {
+ g_set_error (error,
+ 0,
+ 0,
+ "does not look like a %s VPN connection "
+ "(no top level domain)",
+ IODINE_PLUGIN_NAME);
+ g_object_unref (connection);
+ return NULL;
+ }
+
+ /* Optional Settings */
+ /* Description */
+ buf = g_key_file_get_string (keyfile, "iodine", "Description", NULL);
+ if (buf)
+ g_object_set (s_con, NM_SETTING_CONNECTION_ID, buf, NULL);
+
+ /* Name server */
+ buf = g_key_file_get_string (keyfile, "iodine", "Nameserver", NULL);
+ if (buf)
+ nm_setting_vpn_add_data_item (s_vpn, NM_IODINE_KEY_NAMESERVER, buf);
+
+ /* Fragment size */
+ buf = g_key_file_get_string (keyfile, "iodine", "Fragsize", NULL);
+ if (buf)
+ nm_setting_vpn_add_data_item (s_vpn, NM_IODINE_KEY_FRAGSIZE, "yes");
+
+ return connection;
+}
+
+static gboolean
+export (NMVpnPluginUiInterface *iface,
+ const char *path,
+ NMConnection *connection,
+ GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingVPN *s_vpn;
+ const char *value;
+ const char *topdomain = NULL;
+ const char *nameserver = NULL;
+ const char *fragsize = NULL;
+ gboolean success = FALSE;
+ FILE *f;
+
+ f = fopen (path, "w");
+ if (!f) {
+ g_set_error (error, 0, 0, "could not open file for writing");
+ return FALSE;
+ }
+
+ s_con = NM_SETTING_CONNECTION (nm_connection_get_setting
+ (connection, NM_TYPE_SETTING_CONNECTION));
+
+ s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection,
+ NM_TYPE_SETTING_VPN);
+
+ value = nm_setting_vpn_get_data_item (s_vpn, NM_IODINE_KEY_TOPDOMAIN);
+ if (value && strlen (value))
+ topdomain = value;
+ else {
+ g_set_error (error, 0, 0,
+ "connection was incomplete (missing top level domain)");
+ goto done;
+ }
+
+ value = nm_setting_vpn_get_data_item (s_vpn, NM_IODINE_KEY_NAMESERVER);
+ if (value && strlen (value))
+ nameserver = value;
+
+ value = nm_setting_vpn_get_data_item (s_vpn, NM_IODINE_KEY_FRAGSIZE);
+ if (value && strlen (value))
+ fragsize = value;
+
+ fprintf (f,
+ "[iodine]\n"
+ "Description=%s\n"
+ "Topdomain=%s\n"
+ "Nameserver=%s\n"
+ "Fragsize=%s\n",
+ /* Description */ nm_setting_connection_get_id (s_con),
+ /* Topdomain */ topdomain,
+ /* Nameserver */ nameserver,
+ /* Fragsize */ fragsize);
+
+ success = TRUE;
+
+done:
+ fclose (f);
+ return success;
+}
+
+GQuark
+iodine_plugin_ui_error_quark (void)
+{
+ static GQuark error_quark = 0;
+
+ if (G_UNLIKELY (error_quark == 0))
+ error_quark = g_quark_from_static_string ("iodine-plugin-ui-error-quark");
+
+ return error_quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+iodine_plugin_ui_error_get_type (void)
+{
+ static GType etype = 0;
+
+ if (etype == 0) {
+ static const GEnumValue values[] = {
+ /* Unknown error. */
+ ENUM_ENTRY (IODINE_PLUGIN_UI_ERROR_UNKNOWN,
+ "UnknownError"),
+ /* The specified property was invalid. */
+ ENUM_ENTRY (IODINE_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+ "InvalidProperty"),
+ /* The specified property was missing and is required. */
+ ENUM_ENTRY (IODINE_PLUGIN_UI_ERROR_MISSING_PROPERTY,
+ "MissingProperty"),
+ { 0, 0, 0 }
+ };
+ etype = g_enum_register_static ("IodinePluginUiError", values);
+ }
+ return etype;
+}
+
+static gboolean
+check_validity (IodinePluginUiWidget *self, GError **error)
+{
+ IodinePluginUiWidgetPrivate *priv = \
+ IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+ GtkWidget *widget;
+ const char *str;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "topdomain_entry"));
+ str = gtk_entry_get_text (GTK_ENTRY (widget));
+ if (!str || !strlen (str)) {
+ g_set_error (error,
+ IODINE_PLUGIN_UI_ERROR,
+ IODINE_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+ NM_IODINE_KEY_TOPDOMAIN);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+stuff_changed_cb (GtkWidget *widget, gpointer user_data)
+{
+ g_signal_emit_by_name (IODINE_PLUGIN_UI_WIDGET (user_data), "changed");
+}
+
+static void
+setup_password_widget (IodinePluginUiWidget *self,
+ const char *entry_name,
+ NMSettingVPN *s_vpn,
+ const char *secret_name)
+{
+ IodinePluginUiWidgetPrivate *priv = \
+ IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+
+ NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+ GtkWidget *widget;
+ const char *value;
+
+ /* Default to agent-owned for new connections */
+ if (s_vpn == NULL)
+ secret_flags = NM_SETTING_SECRET_FLAG_AGENT_OWNED;
+
+ widget = (GtkWidget *) gtk_builder_get_object (priv->builder, entry_name);
+ g_assert (widget);
+ gtk_size_group_add_widget (priv->group, widget);
+
+ if (s_vpn) {
+ value = nm_setting_vpn_get_secret (s_vpn, secret_name);
+ gtk_entry_set_text (GTK_ENTRY (widget), value ? value : "");
+ nm_setting_get_secret_flags (NM_SETTING (s_vpn),
+ secret_name,
+ &secret_flags,
+ NULL);
+ }
+
+ secret_flags &= ~(NM_SETTING_SECRET_FLAG_NOT_SAVED |
+ NM_SETTING_SECRET_FLAG_NOT_REQUIRED);
+ g_object_set_data (G_OBJECT (widget),
+ "flags",
+ GUINT_TO_POINTER (secret_flags));
+
+ g_signal_connect (widget, "changed", G_CALLBACK (stuff_changed_cb), self);
+}
+
+static void
+show_toggled_cb (GtkCheckButton *button, IodinePluginUiWidget *self)
+{
+ IodinePluginUiWidgetPrivate *priv = \
+ IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+ GtkWidget *widget;
+ gboolean visible;
+
+ visible = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "password_entry"));
+ g_assert (widget);
+ gtk_entry_set_visibility (GTK_ENTRY (widget), visible);
+}
+
+static void
+pw_type_combo_changed_cb (GtkWidget *combo, gpointer user_data)
+{
+ IodinePluginUiWidget *self = IODINE_PLUGIN_UI_WIDGET (user_data);
+ IodinePluginUiWidgetPrivate *priv = \
+ IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+ GtkWidget *entry;
+
+ entry = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "password_entry"));
+ g_assert (entry);
+
+ /* If the user chose "Not required", desensitize and clear the correct
+ * password entry.
+ */
+ switch (gtk_combo_box_get_active (GTK_COMBO_BOX (combo))) {
+ case PW_TYPE_ASK:
+ case PW_TYPE_UNUSED:
+ gtk_entry_set_text (GTK_ENTRY (entry), "");
+ gtk_widget_set_sensitive (entry, FALSE);
+ break;
+ default:
+ gtk_widget_set_sensitive (entry, TRUE);
+ break;
+ }
+
+ stuff_changed_cb (combo, self);
+}
+
+static void
+init_one_pw_combo (IodinePluginUiWidget *self,
+ NMSettingVPN *s_vpn,
+ const char *combo_name,
+ const char *secret_key,
+ const char *entry_name)
+{
+ IodinePluginUiWidgetPrivate *priv = \
+ IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+ int active = -1;
+ GtkWidget *widget;
+ GtkListStore *store;
+ const char *value = NULL;
+ guint32 default_idx = 1;
+ NMSettingSecretFlags pw_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+ /* If there's already a password and the password type can't be found in
+ * the VPN settings, default to saving it. Otherwise, always ask for it.
+ */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, entry_name));
+ g_assert (widget);
+ value = gtk_entry_get_text (GTK_ENTRY (widget));
+ if (value && strlen (value))
+ default_idx = 0;
+
+ store = GTK_LIST_STORE(gtk_builder_get_object (priv->builder, "pass_type_model"));
+ g_assert (store);
+
+ if (s_vpn)
+ nm_setting_get_secret_flags (NM_SETTING (s_vpn),
+ secret_key,
+ &pw_flags,
+ NULL);
+ if ((active < 0)
+ && !(pw_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+ && !(pw_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
+ active = PW_TYPE_SAVE;
+ }
+
+ if ((active < 0) && (pw_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
+ active = PW_TYPE_ASK;
+
+ if ((active < 0) && (pw_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
+ active = PW_TYPE_UNUSED;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, combo_name));
+ g_assert (widget);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget),
+ active < 0 ? default_idx : active);
+
+ pw_type_combo_changed_cb (widget, self);
+ g_signal_connect (G_OBJECT (widget),
+ "changed",
+ G_CALLBACK (pw_type_combo_changed_cb), self);
+}
+
+static gboolean
+init_plugin_ui (IodinePluginUiWidget *self,
+ NMConnection *connection,
+ GError **error)
+{
+ IodinePluginUiWidgetPrivate *priv = \
+ IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+ NMSettingVPN *s_vpn;
+ GtkWidget *widget;
+ const char *value;
+
+ s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection,
+ NM_TYPE_SETTING_VPN);
+
+ priv->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "topdomain_entry"));
+ if (!widget)
+ return FALSE;
+ gtk_size_group_add_widget (priv->group, widget);
+ if (s_vpn) {
+ value = nm_setting_vpn_get_data_item (s_vpn, NM_IODINE_KEY_TOPDOMAIN);
+ if (value)
+ gtk_entry_set_text (GTK_ENTRY (widget), value);
+ }
+ g_signal_connect (G_OBJECT (widget),
+ "changed",
+ G_CALLBACK (stuff_changed_cb), self);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "nameserver_entry"));
+ if (!widget)
+ return FALSE;
+ gtk_size_group_add_widget (priv->group, widget);
+ if (s_vpn) {
+ value = nm_setting_vpn_get_data_item (s_vpn, NM_IODINE_KEY_NAMESERVER);
+ if (value)
+ gtk_entry_set_text (GTK_ENTRY (widget), value);
+ }
+ g_signal_connect (G_OBJECT (widget),
+ "changed",
+ G_CALLBACK (stuff_changed_cb), self);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "fragsize_entry"));
+ if (!widget)
+ return FALSE;
+ if (s_vpn) {
+ value = nm_setting_vpn_get_data_item (s_vpn, NM_IODINE_KEY_FRAGSIZE);
+ if (value)
+ gtk_entry_set_text (GTK_ENTRY (widget), value);
+ }
+ g_signal_connect (G_OBJECT (widget),
+ "changed",
+ G_CALLBACK (stuff_changed_cb), self);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "show_passwords_checkbutton"));
+ g_signal_connect (G_OBJECT (widget), "toggled",
+ (GCallback) show_toggled_cb,
+ self);
+
+ setup_password_widget (self,
+ "password_entry",
+ s_vpn,
+ NM_IODINE_KEY_PASSWORD);
+
+ init_one_pw_combo (self,
+ s_vpn,
+ "pass_type_combo",
+ NM_IODINE_KEY_PASSWORD,
+ "password_entry");
+ return TRUE;
+}
+
+static GObject *
+get_widget (NMVpnPluginUiWidgetInterface *iface)
+{
+ IodinePluginUiWidget *self = IODINE_PLUGIN_UI_WIDGET (iface);
+ IodinePluginUiWidgetPrivate *priv = IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+
+ return G_OBJECT (priv->widget);
+}
+
+static void
+save_password_and_flags (NMSettingVPN *s_vpn,
+ GtkBuilder *builder,
+ const char *entry_name,
+ const char *combo_name,
+ const char *secret_key)
+{
+ NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+ const char *password;
+ GtkWidget *entry;
+ GtkWidget *combo;
+
+ /* Grab original password flags */
+ entry = GTK_WIDGET (gtk_builder_get_object (builder, entry_name));
+ flags = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (entry), "flags"));
+
+ /* And set new ones based on the type combo */
+ combo = GTK_WIDGET (gtk_builder_get_object (builder, combo_name));
+
+ switch (gtk_combo_box_get_active (GTK_COMBO_BOX (combo))) {
+ case PW_TYPE_SAVE:
+ password = gtk_entry_get_text (GTK_ENTRY (entry));
+ if (password && strlen (password))
+ nm_setting_vpn_add_secret (s_vpn, secret_key, password);
+ break;
+ case PW_TYPE_UNUSED:
+ flags |= NM_SETTING_SECRET_FLAG_NOT_REQUIRED;
+ break;
+ case PW_TYPE_ASK:
+ default:
+ flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED;
+ break;
+ }
+
+ /* Set new secret flags */
+ nm_setting_set_secret_flags (NM_SETTING (s_vpn), secret_key, flags, NULL);
+}
+
+static gboolean
+update_connection (NMVpnPluginUiWidgetInterface *iface,
+ NMConnection *connection,
+ GError **error)
+{
+ IodinePluginUiWidget *self = IODINE_PLUGIN_UI_WIDGET (iface);
+ IodinePluginUiWidgetPrivate *priv = \
+ IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+ NMSettingVPN *s_vpn;
+ GtkWidget *widget;
+ char *str;
+
+ if (!check_validity (self, error))
+ return FALSE;
+
+ s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ());
+ g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE,
+ NM_DBUS_SERVICE_IODINE,
+ NULL);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "topdomain_entry"));
+ g_assert(widget);
+ str = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
+ if (str && strlen (str))
+ nm_setting_vpn_add_data_item (s_vpn, NM_IODINE_KEY_TOPDOMAIN, str);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "nameserver_entry"));
+ g_assert(widget);
+ str = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
+ if (str && strlen (str))
+ nm_setting_vpn_add_data_item (s_vpn, NM_IODINE_KEY_NAMESERVER, str);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "fragsize_entry"));
+ g_assert(widget);
+ str = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
+ if (str && strlen (str))
+ nm_setting_vpn_add_data_item (s_vpn, NM_IODINE_KEY_FRAGSIZE, str);
+
+ /* User password and flags */
+ save_password_and_flags (s_vpn,
+ priv->builder,
+ "password_entry",
+ "pass_type_combo",
+ NM_IODINE_KEY_PASSWORD);
+
+ nm_connection_add_setting (connection, NM_SETTING (s_vpn));
+ return TRUE;
+}
+
+static NMVpnPluginUiWidgetInterface *
+nm_vpn_plugin_ui_widget_interface_new (NMConnection *connection, GError **error)
+{
+ NMVpnPluginUiWidgetInterface *object;
+ IodinePluginUiWidgetPrivate *priv;
+ char *ui_file;
+
+ if (error)
+ g_return_val_if_fail (*error == NULL, NULL);
+
+ object = NM_VPN_PLUGIN_UI_WIDGET_INTERFACE \
+ (g_object_new (IODINE_TYPE_PLUGIN_UI_WIDGET, NULL));
+
+ if (!object) {
+ g_set_error (error, IODINE_PLUGIN_UI_ERROR, 0,
+ "could not create iodine object");
+ return NULL;
+ }
+
+ priv = IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE (object);
+ ui_file = g_strdup_printf ("%s/%s", UIDIR, "nm-iodine-dialog.ui");
+ priv->builder = gtk_builder_new ();
+
+ gtk_builder_set_translation_domain (priv->builder, GETTEXT_PACKAGE);
+ if (!gtk_builder_add_from_file (priv->builder, ui_file, error)) {
+ g_warning ("Couldn't load builder file: %s",
+ error && *error ? (*error)->message : "(unknown)");
+ g_clear_error (error);
+ g_set_error (error, IODINE_PLUGIN_UI_ERROR, 0,
+ "could not load required resources at %s", ui_file);
+ g_free (ui_file);
+ g_object_unref (object);
+ return NULL;
+ }
+ g_free (ui_file);
+
+ priv->widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+ "iodine-vbox"));
+ if (!priv->widget) {
+ g_set_error (error, IODINE_PLUGIN_UI_ERROR, 0,
+ "could not load UI widget");
+ g_object_unref (object);
+ return NULL;
+ }
+ g_object_ref_sink (priv->widget);
+
+ if (!init_plugin_ui (IODINE_PLUGIN_UI_WIDGET (object), connection, error)) {
+ g_object_unref (object);
+ return NULL;
+ }
+
+ return object;
+}
+
+static void
+dispose (GObject *object)
+{
+ IodinePluginUiWidget *plugin = IODINE_PLUGIN_UI_WIDGET (object);
+ IodinePluginUiWidgetPrivate *priv = \
+ IODINE_PLUGIN_UI_WIDGET_GET_PRIVATE (plugin);
+
+ if (priv->group)
+ g_object_unref (priv->group);
+
+ if (priv->widget)
+ g_object_unref (priv->widget);
+
+ if (priv->builder)
+ g_object_unref (priv->builder);
+
+ G_OBJECT_CLASS (iodine_plugin_ui_widget_parent_class)->dispose (object);
+}
+
+static void
+iodine_plugin_ui_widget_class_init (IodinePluginUiWidgetClass *req_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+
+ g_type_class_add_private (req_class, sizeof (IodinePluginUiWidgetPrivate));
+
+ object_class->dispose = dispose;
+}
+
+static void
+iodine_plugin_ui_widget_init (IodinePluginUiWidget *plugin)
+{
+}
+
+static void
+iodine_plugin_ui_widget_interface_init (NMVpnPluginUiWidgetInterface *iface_class)
+{
+ /* interface implementation */
+ iface_class->get_widget = get_widget;
+ iface_class->update_connection = update_connection;
+}
+
+static guint32
+get_capabilities (NMVpnPluginUiInterface *iface)
+{
+ return (NM_VPN_PLUGIN_UI_CAPABILITY_IMPORT |
+ NM_VPN_PLUGIN_UI_CAPABILITY_EXPORT);
+}
+
+static NMVpnPluginUiWidgetInterface *
+ui_factory (NMVpnPluginUiInterface *iface,
+ NMConnection *connection,
+ GError **error)
+{
+ return nm_vpn_plugin_ui_widget_interface_new (connection, error);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME:
+ g_value_set_string (value, IODINE_PLUGIN_NAME);
+ break;
+ case NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC:
+ g_value_set_string (value, IODINE_PLUGIN_DESC);
+ break;
+ case NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE:
+ g_value_set_string (value, IODINE_PLUGIN_SERVICE);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+iodine_plugin_ui_class_init (IodinePluginUiClass *req_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+
+ object_class->get_property = get_property;
+
+ g_object_class_override_property (object_class,
+ NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME,
+ NM_VPN_PLUGIN_UI_INTERFACE_NAME);
+
+ g_object_class_override_property (object_class,
+ NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC,
+ NM_VPN_PLUGIN_UI_INTERFACE_DESC);
+
+ g_object_class_override_property (object_class,
+ NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE,
+ NM_VPN_PLUGIN_UI_INTERFACE_SERVICE);
+}
+
+static void
+iodine_plugin_ui_init (IodinePluginUi *plugin)
+{
+}
+
+static void
+iodine_plugin_ui_interface_init (NMVpnPluginUiInterface *iface_class)
+{
+ /* interface implementation */
+ iface_class->ui_factory = ui_factory;
+ iface_class->get_capabilities = get_capabilities;
+ iface_class->import_from_file = import;
+ iface_class->export_to_file = export;
+}
+
+G_MODULE_EXPORT NMVpnPluginUiInterface *
+nm_vpn_plugin_ui_factory (GError **error)
+{
+ if (error)
+ g_return_val_if_fail (*error == NULL, NULL);
+
+ return NM_VPN_PLUGIN_UI_INTERFACE (g_object_new (IODINE_TYPE_PLUGIN_UI,
+ NULL));
+}
diff --git a/properties/nm-iodine.h b/properties/nm-iodine.h
new file mode 100644
index 0000000..1769fff
--- /dev/null
+++ b/properties/nm-iodine.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * GNOME UI dialogs for configuring iodine VPN connections
+ *
+ * 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.
+ *
+ * Copyright © 2012 Guido Günther <agx@sigxcpu.org>
+ */
+
+#ifndef _NM_IODINE_H_
+#define _NM_IODINE_H_
+
+#include <glib-object.h>
+
+typedef enum
+{
+ IODINE_PLUGIN_UI_ERROR_UNKNOWN = 0,
+ IODINE_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+ IODINE_PLUGIN_UI_ERROR_MISSING_PROPERTY
+} IodinePluginUiError;
+
+
+GQuark iodine_plugin_ui_error_quark (void);
+#define IODINE_PLUGIN_UI_ERROR iodine_plugin_ui_error_quark ()
+
+#define IODINE_TYPE_PLUGIN_UI_ERROR (iodine_plugin_ui_error_get_type ())
+GType iodine_plugin_ui_error_get_type (void);
+
+#define IODINE_TYPE_PLUGIN_UI (iodine_plugin_ui_get_type ())
+#define IODINE_PLUGIN_UI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IODINE_TYPE_PLUGIN_UI, IodinePluginUi))
+#define IODINE_PLUGIN_UI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IODINE_TYPE_PLUGIN_UI, IodinePluginUiClass))
+#define IODINE_IS_PLUGIN_UI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IODINE_TYPE_PLUGIN_UI))
+#define IODINE_IS_PLUGIN_UI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), IODINE_TYPE_PLUGIN_UI))
+#define IODINE_PLUGIN_UI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IODINE_TYPE_PLUGIN_UI, IodinePluginUiClass))
+
+typedef struct _IodinePluginUi IodinePluginUi;
+typedef struct _IodinePluginUiClass IodinePluginUiClass;
+
+struct _IodinePluginUi {
+ GObject parent;
+};
+
+struct _IodinePluginUiClass {
+ GObjectClass parent;
+};
+
+GType iodine_plugin_ui_get_type (void);
+
+
+#define IODINE_TYPE_PLUGIN_UI_WIDGET (iodine_plugin_ui_widget_get_type ())
+#define IODINE_PLUGIN_UI_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IODINE_TYPE_PLUGIN_UI_WIDGET, IodinePluginUiWidget))
+#define IODINE_PLUGIN_UI_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IODINE_TYPE_PLUGIN_UI_WIDGET, IodinePluginUiWidgetClass))
+#define IODINE_IS_PLUGIN_UI_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IODINE_TYPE_PLUGIN_UI_WIDGET))
+#define IODINE_IS_PLUGIN_UI_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), IODINE_TYPE_PLUGIN_UI_WIDGET))
+#define IODINE_PLUGIN_UI_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IODINE_TYPE_PLUGIN_UI_WIDGET, IodinePluginUiWidgetClass))
+
+typedef struct _IodinePluginUiWidget IodinePluginUiWidget;
+typedef struct _IodinePluginUiWidgetClass IodinePluginUiWidgetClass;
+
+struct _IodinePluginUiWidget {
+ GObject parent;
+};
+
+struct _IodinePluginUiWidgetClass {
+ GObjectClass parent;
+};
+
+GType iodine_plugin_ui_widget_get_type (void);
+
+#endif /* _NM_IODINE_H_ */
+
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..c7f3a1e
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,30 @@
+INCLUDES = -I${top_srcdir}
+
+AM_CPPFLAGS = \
+ $(DBUS_CFLAGS) \
+ $(GTHREAD_CFLAGS) \
+ $(NM_CFLAGS) \
+ -DG_DISABLE_DEPRECATED \
+ -DBINDIR=\"$(bindir)\" \
+ -DPREFIX=\""$(prefix)"\" \
+ -DSYSCONFDIR=\""$(sysconfdir)"\" \
+ -DVERSION="\"$(VERSION)\"" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DLOCALSTATEDIR=\""$(localstatedir)"\" \
+ -DDATADIR=\"$(datadir)\"
+
+libexec_PROGRAMS = \
+ nm-iodine-service
+
+nm_iodine_service_SOURCES = \
+ nm-iodine-service.c \
+ nm-iodine-service.h
+
+
+nm_iodine_service_LDADD = \
+ $(DBUS_LIBS) \
+ $(GTHREAD_LIBS) \
+ $(NM_LIBS)
+
+CLEANFILES = *~
diff --git a/src/nm-iodine-service.c b/src/nm-iodine-service.c
new file mode 100644
index 0000000..4c97581
--- /dev/null
+++ b/src/nm-iodine-service.c
@@ -0,0 +1,672 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * NetworkManager iodine VPN connections
+ *
+ * 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.
+ *
+ * Copyright © 2012 Guido Günther <agx@sigxcpu.org>
+ *
+ * Based on network-manager-{openconnect,pptp}
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/if_tun.h>
+#include <net/if.h>
+#include <pwd.h>
+#include <grp.h>
+#include <glib/gi18n.h>
+
+#include <nm-setting-vpn.h>
+#include "nm-iodine-service.h"
+#include "nm-utils.h"
+
+G_DEFINE_TYPE (NMIODINEPlugin, nm_iodine_plugin, NM_TYPE_VPN_PLUGIN)
+
+typedef struct {
+ GPid pid;
+ GHashTable *ip4config;
+} NMIODINEPluginPrivate;
+
+#define NM_IODINE_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IODINE_PLUGIN, NMIODINEPluginPrivate))
+
+static const char *iodine_binary_paths[] =
+{
+ "/usr/bin/iodine",
+ "/usr/sbin/iodine",
+ "/usr/local/bin/iodine",
+ "/usr/local/sbin/iodine",
+ "/opt/bin/iodine",
+ "/opt/sbin/iodine",
+ NULL
+};
+
+#define NM_IODINE_HELPER_PATH LIBEXECDIR"/nm-iodine-service-iodine-helper"
+
+typedef struct {
+ const char *name;
+ GType type;
+ gint int_min;
+ gint int_max;
+} ValidProperty;
+
+static ValidProperty valid_properties[] = {
+ { NM_IODINE_KEY_TOPDOMAIN, G_TYPE_STRING, 0, 0 },
+ { NM_IODINE_KEY_NAMESERVER, G_TYPE_STRING, 0, 0 },
+ { NM_IODINE_KEY_FRAGSIZE, G_TYPE_STRING, 0, 0 },
+ { NULL, G_TYPE_NONE, 0, 0 }
+};
+
+static ValidProperty valid_secrets[] = {
+ { NM_IODINE_KEY_PASSWORD, G_TYPE_STRING, 0, 0 },
+ { NULL, G_TYPE_NONE, 0, 0 }
+};
+
+typedef struct ValidateInfo {
+ ValidProperty *table;
+ GError **error;
+ gboolean have_items;
+} ValidateInfo;
+
+static void
+validate_one_property (const char *key, const char *value, gpointer user_data)
+{
+ ValidateInfo *info = (ValidateInfo *) user_data;
+ int i;
+
+ if (*(info->error))
+ return;
+
+ info->have_items = TRUE;
+
+ /* 'name' is the setting name; always allowed but unused */
+ if (!strcmp (key, NM_SETTING_NAME))
+ return;
+
+ for (i = 0; info->table[i].name; i++) {
+ ValidProperty prop = info->table[i];
+ long int tmp;
+
+ if (strcmp (prop.name, key))
+ continue;
+
+ switch (prop.type) {
+ case G_TYPE_STRING:
+ return; /* valid */
+ case G_TYPE_INT:
+ errno = 0;
+ tmp = strtol (value, NULL, 10);
+ if (errno == 0 && tmp >= prop.int_min && tmp <= prop.int_max)
+ return; /* valid */
+
+ g_set_error (info->error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ _("invalid integer property '%s' or out of range "
+ "[%d -> %d]"),
+ key, prop.int_min, prop.int_max);
+ break;
+ case G_TYPE_BOOLEAN:
+ if (!strcmp (value, "yes") || !strcmp (value, "no"))
+ return; /* valid */
+
+ g_set_error (info->error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ _("invalid boolean property '%s' (not yes or no)"),
+ key);
+ break;
+ default:
+ g_set_error (info->error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ _("unhandled property '%s' type %s"),
+ key, g_type_name (prop.type));
+ break;
+ }
+ }
+
+ /* Did not find the property from valid_properties or the type did not
+ match */
+ if (!info->table[i].name && strncmp(key, "form:", 5)) {
+ g_warning ("property '%s' unknown", key);
+ if (0)
+ g_set_error (info->error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ _("property '%s' invalid or not supported"),
+ key);
+ }
+}
+
+static gboolean
+nm_iodine_properties_validate (NMSettingVPN *s_vpn, GError **error)
+{
+ ValidateInfo info = { &valid_properties[0], error, FALSE };
+
+ nm_setting_vpn_foreach_data_item (s_vpn, validate_one_property, &info);
+ if (!info.have_items) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "%s",
+ _("No VPN configuration options."));
+ return FALSE;
+ }
+
+ return *error ? FALSE : TRUE;
+}
+
+
+static gboolean
+nm_iodine_secrets_validate (NMSettingVPN *s_vpn, GError **error)
+{
+ ValidateInfo info = { &valid_secrets[0], error, FALSE };
+
+ nm_setting_vpn_foreach_secret (s_vpn, validate_one_property, &info);
+ if (!info.have_items) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "%s",
+ _("No VPN secrets!"));
+ return FALSE;
+ }
+
+ return *error ? FALSE : TRUE;
+}
+
+static GValue *
+str_to_gvalue (const char *str, gboolean try_convert)
+{
+ GValue *val;
+
+ /* Empty */
+ if (!str || strlen (str) < 1)
+ return NULL;
+
+ if (!g_utf8_validate (str, -1, NULL)) {
+ if (try_convert && !(str = g_convert (str,
+ -1,
+ "ISO-8859-1",
+ "UTF-8",
+ NULL,
+ NULL,
+ NULL)))
+ str = g_convert (str, -1, "C", "UTF-8", NULL, NULL, NULL);
+ if (!str)
+ /* Invalid */
+ return NULL;
+ }
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_STRING);
+ g_value_set_string (val, str);
+ return val;
+}
+
+static GValue *
+uint_to_gvalue (guint32 num)
+{
+ GValue *val;
+
+ if (num == 0)
+ return NULL;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UINT);
+ g_value_set_uint (val, num);
+
+ return val;
+}
+
+static GValue *
+addr_to_gvalue (const char *str)
+{
+ struct in_addr temp_addr;
+
+ /* Empty */
+ if (!str || strlen (str) < 1)
+ return NULL;
+
+ if (inet_pton (AF_INET, str, &temp_addr) <= 0)
+ return NULL;
+
+ return uint_to_gvalue (temp_addr.s_addr);
+}
+
+static void
+value_destroy (gpointer data)
+{
+ GValue *val = (GValue *) data;
+
+ g_value_unset (val);
+ g_slice_free (GValue, val);
+}
+
+static gboolean
+iodine_parse_stderr_line (const char* line, GHashTable *ip4config)
+{
+ gchar **split;
+ GValue *val;
+ gint len;
+ gint ret = TRUE;
+
+ split = g_strsplit (line, " ", 0);
+ len = g_strv_length (split);
+ if (len < 2)
+ goto out;
+
+ if (g_str_has_prefix(line, "Server tunnel IP is ")) {
+ g_debug("PTP address: %s", split[len-1]);
+ val = addr_to_gvalue (split[len-1]);
+ if (val)
+ g_hash_table_insert (ip4config,
+ NM_VPN_PLUGIN_IP4_CONFIG_PTP,
+ val);
+ val = addr_to_gvalue (split[len-1]);
+ if (val)
+ g_hash_table_insert (ip4config,
+ NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY,
+ val);
+ } else if (g_str_has_prefix(line, "Sending DNS queries for ")) {
+ g_debug("External gw: %s", split[len-1]);
+ val = addr_to_gvalue (split[len-1]);
+ if (val)
+ g_hash_table_insert (ip4config,
+ NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY,
+ val);
+ } else if (g_str_has_prefix(line, "Sending raw traffic directly to ")) {
+ /* If the DNS server is directly reachable we need to set it
+ as external gateway overwriting the above valus */
+ g_debug("Overwrite ext. gw. address: %s", split[len-1]);
+ val = addr_to_gvalue (split[len-1]);
+ if (val)
+ g_hash_table_insert (ip4config,
+ NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY,
+ val);
+ } else if (g_str_has_prefix(line, "Setting IP of dns")) {
+ g_debug("Address: %s", split[len-1]);
+ val = addr_to_gvalue (split[len-1]);
+ if (val)
+ g_hash_table_insert (ip4config,
+ NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS,
+ val);
+ } else if (g_str_has_prefix(line, "Setting MTU of ")) {
+ g_debug("MTU: %s", split[len-1]);
+ val = addr_to_gvalue (split[len-1]);
+ if (val)
+ g_hash_table_insert (ip4config,
+ NM_VPN_PLUGIN_IP4_CONFIG_MTU,
+ val);
+ } else if (g_str_has_prefix(line, "Opened dns")) {
+ g_debug("Interface: %s", split[len-1]);
+ val = str_to_gvalue (split[len-1], FALSE);
+ if (val)
+ g_hash_table_insert (ip4config,
+ NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV,
+ val);
+ } else if (g_str_has_prefix(line,
+ "Connection setup complete, "
+ "transmitting data.")) {
+ val = uint_to_gvalue(27);
+ g_hash_table_insert (ip4config,
+ NM_VPN_PLUGIN_IP4_CONFIG_PREFIX,
+ val);
+ ret = FALSE;
+ } else
+ g_debug("%s", line);
+
+out:
+ g_strfreev(split);
+ return ret;
+}
+
+
+static gboolean
+iodine_stderr_cb (GIOChannel *source, GIOCondition condition, gpointer plugin)
+{
+ GIOStatus status;
+ GError *err = NULL;
+ gchar *line;
+ gint ret, l;
+ NMIODINEPluginPrivate *priv = NM_IODINE_PLUGIN_GET_PRIVATE (plugin);
+
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &err);
+ if (status != G_IO_STATUS_NORMAL) {
+ g_warning ("Fetching data failed: %s", err->message);
+ return FALSE;
+ }
+
+ l = strlen(line);
+ if (l)
+ line[l-1] = '\0';
+
+ ret = iodine_parse_stderr_line(line, priv->ip4config);
+ if (!ret) {
+ g_debug("Parsing done, sending IP4 config");
+ nm_vpn_plugin_set_ip4_config(plugin, priv->ip4config);
+
+ g_hash_table_destroy (priv->ip4config);
+ priv->ip4config = NULL;
+ }
+ g_free (line);
+ return TRUE;
+}
+
+
+static void
+iodine_watch_cb (GPid pid, gint status, gpointer user_data)
+{
+ NMIODINEPlugin *plugin = NM_IODINE_PLUGIN (user_data);
+ NMIODINEPluginPrivate *priv = NM_IODINE_PLUGIN_GET_PRIVATE (plugin);
+ guint error = 0;
+
+ if (WIFEXITED (status)) {
+ error = WEXITSTATUS (status);
+ if (error != 0)
+ g_warning ("iodine exited with error code %d", error);
+ }
+ else if (WIFSTOPPED (status))
+ g_warning ("iodine stopped unexpectedly with signal %d",
+ WSTOPSIG (status));
+ else if (WIFSIGNALED (status))
+ g_warning ("iodine died with signal %d", WTERMSIG (status));
+ else
+ g_warning ("iodine died from an unknown cause");
+
+ /* Reap child if needed. */
+ waitpid (priv->pid, NULL, WNOHANG);
+ priv->pid = 0;
+
+ /* Must be after data->state is set since signals use data->state */
+ if (error) {
+ nm_vpn_plugin_failure (NM_VPN_PLUGIN (plugin),
+ NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED);
+ }
+
+ nm_vpn_plugin_set_state (NM_VPN_PLUGIN (plugin),
+ NM_VPN_SERVICE_STATE_STOPPED);
+}
+
+static gboolean
+has_user(const char* user)
+{
+ return (getpwnam(user) == NULL) ? FALSE : TRUE;
+}
+
+static gint
+nm_iodine_start_iodine_binary(NMIODINEPlugin *plugin,
+ NMSettingVPN *s_vpn,
+ GError **error)
+{
+ GPid pid;
+ const char **iodine_binary = NULL;
+ GPtrArray *iodine_argv;
+ GSource *iodine_watch;
+ GIOChannel *stderr_channel;
+ gint stdin_fd, stderr_fd;
+ const char *props_topdomain, *props_fragsize, *props_nameserver, *passwd;
+
+ /* Find iodine */
+ iodine_binary = iodine_binary_paths;
+ while (*iodine_binary != NULL) {
+ if (g_file_test (*iodine_binary, G_FILE_TEST_EXISTS))
+ break;
+ iodine_binary++;
+ }
+
+ if (!*iodine_binary) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
+ "%s",
+ _("Could not find iodine binary."));
+ return -1;
+ }
+
+ props_fragsize = nm_setting_vpn_get_data_item (s_vpn,
+ NM_IODINE_KEY_FRAGSIZE);
+ props_nameserver = nm_setting_vpn_get_data_item (s_vpn,
+ NM_IODINE_KEY_NAMESERVER);
+ props_topdomain = nm_setting_vpn_get_data_item (s_vpn,
+ NM_IODINE_KEY_TOPDOMAIN);
+
+ passwd = nm_setting_vpn_get_secret (s_vpn, NM_IODINE_KEY_PASSWORD);
+
+ iodine_argv = g_ptr_array_new ();
+ g_ptr_array_add (iodine_argv, (gpointer) (*iodine_binary));
+ /* Run in foreground */
+ g_ptr_array_add (iodine_argv, (gpointer) "-f");
+
+ if (props_fragsize && strlen(props_fragsize)) {
+ g_ptr_array_add (iodine_argv, (gpointer) "-m");
+ g_ptr_array_add (iodine_argv, (gpointer) props_fragsize);
+ }
+
+ if (passwd && strlen(passwd)) {
+ g_ptr_array_add (iodine_argv, (gpointer) "-P");
+ g_ptr_array_add (iodine_argv, (gpointer) passwd);
+ }
+
+ if (has_user(NM_IODINE_USER)) {
+ g_ptr_array_add (iodine_argv, (gpointer) "-u");
+ g_ptr_array_add (iodine_argv, (gpointer) NM_IODINE_USER);
+ } else
+ g_warning("Running as root user");
+
+ if (props_nameserver && strlen(props_nameserver))
+ g_ptr_array_add (iodine_argv, (gpointer) props_nameserver);
+
+ if (props_topdomain && strlen(props_topdomain))
+ g_ptr_array_add (iodine_argv, (gpointer) props_topdomain);
+
+ g_ptr_array_add (iodine_argv, NULL);
+
+ if (!g_spawn_async_with_pipes (NULL, (char **) iodine_argv->pdata, NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL,
+ &pid, &stdin_fd, NULL, &stderr_fd, error)) {
+ g_ptr_array_free (iodine_argv, TRUE);
+ g_warning ("iodine failed to start. error: '%s'", (*error)->message);
+ return -1;
+ }
+ g_ptr_array_free (iodine_argv, TRUE);
+
+ g_message ("iodine started with pid %d", pid);
+ close(stdin_fd);
+
+ stderr_channel = g_io_channel_unix_new (stderr_fd);
+ g_io_add_watch(stderr_channel,
+ G_IO_IN,
+ iodine_stderr_cb,
+ plugin);
+
+ NM_IODINE_PLUGIN_GET_PRIVATE (plugin)->pid = pid;
+ iodine_watch = g_child_watch_source_new (pid);
+ g_source_set_callback (iodine_watch,
+ (GSourceFunc) iodine_watch_cb,
+ plugin,
+ NULL);
+ g_source_attach (iodine_watch, NULL);
+ g_source_unref (iodine_watch);
+
+ return 0;
+}
+
+static gboolean
+real_connect (NMVPNPlugin *plugin,
+ NMConnection *connection,
+ GError **error)
+{
+ NMSettingVPN *s_vpn;
+ gint iodine_fd = -1;
+
+ s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection,
+ NM_TYPE_SETTING_VPN));
+ g_assert (s_vpn);
+ if (!nm_iodine_properties_validate (s_vpn, error))
+ goto out;
+
+ if (!nm_iodine_secrets_validate (s_vpn, error))
+ goto out;
+
+ iodine_fd = nm_iodine_start_iodine_binary (NM_IODINE_PLUGIN (plugin),
+ s_vpn, error);
+ if (!iodine_fd)
+ return TRUE;
+
+ out:
+ return FALSE;
+}
+
+static gboolean
+real_need_secrets (NMVPNPlugin *plugin,
+
+ NMConnection *connection,
+ char **setting_name,
+ GError **error)
+{
+ NMSettingVPN *s_vpn;
+
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+ s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection,
+ NM_TYPE_SETTING_VPN));
+ if (!s_vpn) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
+ "%s",
+ "Could not process the request because the VPN"
+ "connection settings were invalid.");
+ return FALSE;
+ }
+
+ if (!nm_setting_vpn_get_secret (s_vpn, NM_IODINE_KEY_PASSWORD)) {
+ *setting_name = NM_SETTING_VPN_SETTING_NAME;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ensure_killed (gpointer data)
+{
+ int pid = GPOINTER_TO_INT (data);
+
+ if (kill (pid, 0) == 0)
+ kill (pid, SIGKILL);
+
+ return FALSE;
+}
+
+static gboolean
+real_disconnect (NMVPNPlugin *plugin,
+ GError **err)
+{
+ NMIODINEPluginPrivate *priv = NM_IODINE_PLUGIN_GET_PRIVATE (plugin);
+
+ if (priv->pid) {
+ if (kill (priv->pid, SIGTERM) == 0)
+ g_timeout_add (2000, ensure_killed, GINT_TO_POINTER (priv->pid));
+ else
+ kill (priv->pid, SIGKILL);
+
+ g_message ("Terminated iodine daemon with PID %d.", priv->pid);
+ priv->pid = 0;
+ }
+
+ return TRUE;
+}
+
+static void
+nm_iodine_plugin_init (NMIODINEPlugin *plugin)
+{
+ NMIODINEPluginPrivate *priv = NM_IODINE_PLUGIN_GET_PRIVATE (plugin);
+
+ priv->ip4config = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ value_destroy);
+}
+
+static void
+nm_iodine_plugin_class_init (NMIODINEPluginClass *iodine_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (iodine_class);
+ NMVPNPluginClass *parent_class = NM_VPN_PLUGIN_CLASS (iodine_class);
+
+ g_type_class_add_private (object_class, sizeof (NMIODINEPluginPrivate));
+
+ /* virtual methods */
+ parent_class->connect = real_connect;
+ parent_class->need_secrets = real_need_secrets;
+ parent_class->disconnect = real_disconnect;
+}
+
+NMIODINEPlugin *
+nm_iodine_plugin_new (void)
+{
+ return (NMIODINEPlugin *) g_object_new (NM_TYPE_IODINE_PLUGIN,
+ NM_VPN_PLUGIN_DBUS_SERVICE_NAME,
+ NM_DBUS_SERVICE_IODINE,
+ NULL);
+}
+
+static void
+quit_mainloop (NMIODINEPlugin *plugin, gpointer user_data)
+{
+ g_main_loop_quit ((GMainLoop *) user_data);
+}
+
+int main (int argc, char *argv[])
+{
+ NMIODINEPlugin *plugin;
+ GMainLoop *main_loop;
+
+ g_type_init ();
+
+ plugin = nm_iodine_plugin_new ();
+ if (!plugin)
+ exit (EXIT_FAILURE);
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ g_signal_connect (plugin, "quit",
+ G_CALLBACK (quit_mainloop),
+ main_loop);
+
+ g_main_loop_run (main_loop);
+
+ g_main_loop_unref (main_loop);
+ g_object_unref (plugin);
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/src/nm-iodine-service.h b/src/nm-iodine-service.h
new file mode 100644
index 0000000..f82e282
--- /dev/null
+++ b/src/nm-iodine-service.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * 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.
+ *
+ * Copyright © 2012 Guido Günther <agx@sigxcpu.org>
+ */
+
+#ifndef NM_IODINE_PLUGIN_H
+#define NM_IODINE_PLUGIN_H
+
+#include <glib.h>
+#include <nm-vpn-plugin.h>
+
+#define NM_TYPE_IODINE_PLUGIN (nm_iodine_plugin_get_type ())
+#define NM_IODINE_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IODINE_PLUGIN, NMIODINEPlugin))
+#define NM_IODINE_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IODINE_PLUGIN, NMIODINEPluginClass))
+#define NM_IS_IODINE_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IODINE_PLUGIN))
+#define NM_IS_IODINE_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IODINE_PLUGIN))
+#define NM_IODINE_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IODINE_PLUGIN, NMIODINEPluginClass))
+
+#define NM_DBUS_SERVICE_IODINE "org.freedesktop.NetworkManager.iodine"
+#define NM_DBUS_INTERFACE_IODINE "org.freedesktop.NetworkManager.iodine"
+#define NM_DBUS_PATH_IODINE "/org/freedesktop/NetworkManager/iodine"
+
+#define NM_IODINE_KEY_TOPDOMAIN "topdomain"
+#define NM_IODINE_KEY_NAMESERVER "nameserver"
+#define NM_IODINE_KEY_FRAGSIZE "fragsize"
+#define NM_IODINE_KEY_PASSWORD "password"
+
+typedef struct {
+ NMVPNPlugin parent;
+} NMIODINEPlugin;
+
+typedef struct {
+ NMVPNPluginClass parent;
+} NMIODINEPluginClass;
+
+GType nm_iodine_plugin_get_type (void);
+
+NMIODINEPlugin *nm_iodine_plugin_new (void);
+
+#define NM_IODINE_USER "nm-iodine"
+
+#endif /* NM_IODINE_PLUGIN_H */