Commit beb0878d authored by Mike Miller's avatar Mike Miller

Imported Upstream version 0.9.0

parent 9ea1803f
Dan Williams <dcbw@redhat.com>
David Zeuthen <davidz@redhat.com>
David Woodhouse <dwmw2@infradead.org>
This diff is collapsed.
2009-03-30 David Woodhouse <dwmw2@infradead.org>
* src/nm-openconnect-service.c
src/nm-openconnect-service.h
- Add 'autoconnect' and 'lasthost' options to remember the last used
host, and to automatically connect to that host if desired
2009-02-13 Dan Williams <dcbw@redhat.com>
* nm-openconnect-service.conf
- Clean up dbus permissions; retain deny for compat with older dbus
2008-12-17 Dan Williams <dcbw@redhat.com>
Patch from David Woodhouse <dwmw2@infradead.org>
* COPYING
-add
2008-12-17 Dan Williams <dcbw@redhat.com>
Patch from David Woodhouse <dwmw2@infradead.org>
* configure.in
- Remove libgnomeui from configure.in. We don't need it.
2008-12-15 Dan Williams <dcbw@redhat.com>
* src/nm-openconnect-service-openconnect-helper.c
- (main): MTU entry in IP4 config hash is supposed to be a uint
2008-11-26 David Woodhouse <dwmw2@infradead.org>
* New files, copied from vpnc support.
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-openconnect-service.conf
nmvpnservicedir = $(sysconfdir)/NetworkManager/VPN
nmvpnservice_DATA = nm-openconnect-service.name
nm-openconnect-service.name: $(srcdir)/nm-openconnect-service.name.in
sed -e 's|[@]LIBEXECDIR[@]|$(libexecdir)|g' $< >$@
EXTRA_DIST = nm-openconnect-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
This diff is collapsed.
This diff is collapsed.
INCLUDES = -I${top_srcdir}
libexec_PROGRAMS = nm-openconnect-auth-dialog
nm_openconnect_auth_dialog_CPPFLAGS = \
$(NETWORKMANAGER_CFLAGS) \
$(GTHREAD_CFLAGS) \
$(GTK_CFLAGS) \
$(GCONF_CFLAGS) \
$(OPENCONNECT_CFLAGS) \
$(LIBXML_CFLAGS) \
-DICONDIR=\""$(datadir)/pixmaps"\" \
-DBINDIR=\""$(bindir)"\" \
-DG_DISABLE_DEPRECATED \
-DGDK_DISABLE_DEPRECATED \
-DGNOME_DISABLE_DEPRECATED \
-DGNOMELOCALEDIR=\"$(datadir)/locale\" \
-DVERSION=\"$(VERSION)\"
nm_openconnect_auth_dialog_SOURCES = \
main.c \
$(NULL)
nm_openconnect_auth_dialog_LDADD = \
$(GTK_LIBS) \
$(NETWORKMANAGER_LIBS) \
$(GCONF_LIBS) \
$(OPENCONNECT_LIBS) \
$(LIBXML_LIBS)
CLEANFILES = *~
This diff is collapsed.
<dcbw> dwmw2: that auth dialog does a shitload of work
Indeed. But it's quite simple really.
AnyConnect works over HTTPS; authentication is through HTTP forms and
POST responses. Once you've filled in the forms that the server demands,
you're rewarded with an HTTP cookie which is handed on to OpenConnect to
actually make the connection.
The auth-dialog handles the arbitrary forms as the server presents them,
and spits out the cookie after a successful authentication. It's just a
really simple web-browser, effectively. (It has its own HTTP client
implementation instead of using libsoup because it needs to be able to
support certificates from a TPM, and to work around Cisco bugs).
To make it slightly more fun, you have a *choice* of servers; an
AnyConnect VPN is provisioned with an XML file that gives various pieces
of configuration for the client. We ignore most of the XML file, except
the list of available VPN server addresses.
So this is a brief flow of what the auth-dialog does...
1. Choose a server to connect to.
If we already have the XML configuration file for this VPN, you get
to choose a server from the list. Otherwise, you only have the host
that you configured in the VPN setup.
The auth-dialog will give you the choice of automatically connecting
to the last server you used. It does so by storing the boolean
'autoconnect' option, as well as the address of the last successful
server, in "secrets" that NetworkManager stores for it, but which
aren't actually used by OpenConnect itself at all.
2. (Offer your SSL certificate and) fill in all the forms it presents.
The server will present a sequence of forms which are filled in just
like normal web forms. At this point, the auth-dialog is just acting
like a really simple web browser. It uses the same trick with fake
secrets to remember the answers for any multiple-choice selection,
or input elements of type 'text'. Input elements of type 'password'
are not currently saved, but probably should be.
The choices and input boxes that we fill in at this point may not be
limited to *just* authenticating ourselves. You may also get to make
choices which affect your resulting connectivity. Some networks
offer the choice of full-tunnel or split-tunnel routing, IPv6 or
Legacy-only connectivity, etc. (The routing configuration is not
handled by the auth-dialog; that just manifests itself in the IP
configuration which is given to OpenConnect by the server, much
later when the connection is actually made.)
(
2½. Run the "Cisco Secure Desktop"[sic] trojan.
In some cases you are required to download a strange executable from
the server and run it. It is supposed to perform various "checks" on
your system and report its results to the server. The authentication
sequence is kept in a holding pattern with HTTP refresh responses
until the "trojan" has done its job.
Most people seem to bypass this crap and run a local tool of their
own devising to report the "correct" results. It's just another
simple HTTP POST, although the exact results that are expected may
vary from one server/configuration to another.
Try not to think about it. It will only make you sad.
)
3. Note the 'webvpn' cookie.
Once authentication is complete, the server's HTTP response will
include a 'webvpn' cookie.
This cookie is one of the three *real* secrets which are actually
passed to OpenConnect to make the connection. The other two are
the address of the server we finally ended up talking to (after
the user's initial choice and any HTTP redirects), and a hash of
the server's SSL certificate (to prevent MiTM attacks).
4. Check the XML configuration file.
With a successful authentication, we are *also* given the SHA1 of
the current XML configuration for this VPN connection. If it differs
from what we have, we are expected to fetch the new one. We store
this, base64-encoded, in yet another fake "secret".
5. Dump all the "secrets" to NetworkManager.
Finally, we dump all the secrets to stdout so that NetworkManager can
store them. Note that even on an *unsuccessful* attempt, we still
output the secrets we do have. That includes the state of the
"autoconnect" boolean option, for example, which would otherwise
not get saved except on a successful connection.
This diff is collapsed.
#! /bin/sh
# Wrapper for compilers which do not understand `-c -o'.
scriptversion=2009-04-28.21; # UTC
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009 Free Software
# Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
case $1 in
'')
echo "$0: No command. Try \`$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand `-c -o'.
Remove `-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file `INSTALL'.
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
esac
ofile=
cfile=
eat=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as `compile cc -o foo foo.c'.
# So we strip `-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no `-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# `.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use `[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:
This diff is collapsed.
/* config.h.in. Generated from configure.ac by autoheader. */
/* always defined to indicate that i18n is enabled */
#undef ENABLE_NLS
/* Gettext package */
#undef GETTEXT_PACKAGE
/* Define to 1 if you have the `bind_textdomain_codeset' function. */
#undef HAVE_BIND_TEXTDOMAIN_CODESET
/* Define to 1 if you have the `dcgettext' function. */
#undef HAVE_DCGETTEXT
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define if the GNU gettext() function is already present or preinstalled. */
#undef HAVE_GETTEXT
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define if your <locale.h> file defines LC_MESSAGES. */
#undef HAVE_LC_MESSAGES
/* Define to 1 if you have the <locale.h> header file. */
#undef HAVE_LOCALE_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <paths.h> header file. */
#undef HAVE_PATHS_H
/* Define to 1 if you have the `select' function. */
#undef HAVE_SELECT
/* Define to 1 if you have the `socket' function. */
#undef HAVE_SOCKET
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <syslog.h> header file. */
#undef HAVE_SYSLOG_H
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#undef HAVE_SYS_IOCTL_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the `uname' function. */
#undef HAVE_UNAME
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
#undef NO_MINUS_C_MINUS_O
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Version number of package */
#undef VERSION
/* Define to `int' if <sys/types.h> does not define. */
#undef mode_t
/* Define to `int' if <sys/types.h> does not define. */
#undef pid_t
This diff is collapsed.
This diff is collapsed.
AC_PREREQ(2.52)
AC_INIT(NetworkManager-openconnect, 0.9.0, dcbw@redhat.com, NetworkManager-openconnect)
AM_INIT_AUTOMAKE([subdir-objects no-dist-gzip dist-bzip2])
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-openconnect 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-openconnect without authentication dialog]))
AM_CONDITIONAL(WITH_AUTHDLG, test x"$with_authdlg" != xno)
GETTEXT_PACKAGE=NetworkManager-openconnect
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(LIBXML, libxml-2.0)
AC_SUBST(LIBXML_CFLAGS)
AC_SUBST(LIBXML_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
AC_ARG_WITH([gtkver], AS_HELP_STRING([--with-gtkver], [The major version of GTK+ to build with]),
with_gtkver="$withval",with_gtkver=0)
gtk2_req=2.24
gtk3_req=2.91.4
case "${with_gtkver}" in
0) PKG_CHECK_MODULES(GTK, gtk+-3.0 > $gtk3_req, ,
[PKG_CHECK_MODULES(GTK, gtk+-2.0 > $gtk2_req)])
;;
2) PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $gtk2_req)
;;
3) PKG_CHECK_MODULES(GTK, gtk+-3.0 >= $gtk3_req)
;;
*) AC_MSG_ERROR(unknown GTK+ version $with_gtkver!)
;;
esac
AC_SUBST(GTK_CFLAGS)
AC_SUBST(GTK_LIBS)
PKG_CHECK_MODULES(GCONF, gconf-2.0)
AC_SUBST(GCONF_CFLAGS)
AC_SUBST(GCONF_LIBS)
if test x"$with_authdlg" != xno; then
PKG_CHECK_MODULES(OPENCONNECT, openconnect)
AC_SUBST(OPENCONNECT_CFLAGS)
AC_SUBST(OPENCONNECT_LIBS)
fi
fi
PKG_CHECK_MODULES(NETWORKMANAGER,
NetworkManager >= 0.8.998
libnm-util >= 0.8.998
libnm-glib >= 0.8.998
libnm-glib-vpn >= 0.8.998)
AC_SUBST(NETWORKMANAGER_CFLAGS)
AC_SUBST(NETWORKMANAGER_LIBS)
NM_COMPILER_WARNINGS
AC_CONFIG_FILES([
Makefile
src/Makefile
auth-dialog/Makefile
properties/Makefile
po/Makefile.in
])
AC_OUTPUT
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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
])
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
#
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 6 ltsugar.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
# lt_join(SEP, ARG1, [ARG2...])
# -----------------------------
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
# associated separator.
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
# versions in m4sugar had bugs.
m4_define([lt_join],
[m4_if([$#], [1], [],
[$#], [2], [[$2]],
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
m4_define([_lt_join],
[m4_if([$#$2], [2], [],
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
# lt_car(LIST)
# lt_cdr(LIST)
# ------------
# Manipulate m4 lists.
# These macros are necessary as long as will still need to support
# Autoconf-2.59 which quotes differently.
m4_define([lt_car], [[$1]])
m4_define([lt_cdr],
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
[$#], 1, [],
[m4_dquote(m4_shift($@))])])
m4_define([lt_unquote], $1)
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
# ------------------------------------------
# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
# Note that neither SEPARATOR nor STRING are expanded; they are appended
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
# than defined and empty).
#
# This macro is needed until we can rely on Autoconf 2.62, since earlier
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
m4_define([lt_append],
[m4_define([$1],
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
# ----------------------------------------------------------
# Produce a SEP delimited list of all paired combinations of elements of
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
# has the form PREFIXmINFIXSUFFIXn.
# Needed until we can rely on m4_combine added in Autoconf 2.62.
m4_define([lt_combine],
[m4_if(m4_eval([$# > 3]), [1],
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
[[m4_foreach([_Lt_prefix], [$2],
[m4_foreach([_Lt_suffix],
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
# -----------------------------------------------------------------------
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
m4_define([lt_if_append_uniq],
[m4_ifdef([$1],
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
[lt_append([$1], [$2], [$3])$4],
[$5])],
[lt_append([$1], [$2], [$3])$4])])
# lt_dict_add(DICT, KEY, VALUE)
# -----------------------------
m4_define([lt_dict_add],
[m4_define([$1($2)], [$3])])
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
# --------------------------------------------
m4_define([lt_dict_add_subkey],
[m4_define([$1($2:$3)], [$4])])
# lt_dict_fetch(DICT, KEY, [SUBKEY])
# ----------------------------------
m4_define([lt_dict_fetch],
[m4_ifval([$3],
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
# -----------------------------------------------------------------
m4_define([lt_if_dict_fetch],