summary refs log tree commit diff
path: root/cli/src
diff options
context:
space:
mode:
Diffstat (limited to 'cli/src')
-rw-r--r--cli/src/Makefile.in11
-rw-r--r--cli/src/common.c198
-rw-r--r--cli/src/common.h4
-rw-r--r--cli/src/connections.c260
-rw-r--r--cli/src/devices.c723
-rw-r--r--cli/src/network-manager.c158
-rw-r--r--cli/src/settings.c64
-rw-r--r--cli/src/settings.h2
-rw-r--r--cli/src/utils.c38
-rw-r--r--cli/src/utils.h2
10 files changed, 1186 insertions, 274 deletions
diff --git a/cli/src/Makefile.in b/cli/src/Makefile.in
index ea5214e5..854628c2 100644
--- a/cli/src/Makefile.in
+++ b/cli/src/Makefile.in
@@ -48,7 +48,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
 	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
 	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
 	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
-	$(top_srcdir)/configure.ac
+	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
@@ -172,6 +172,10 @@ INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
 INTLTOOL_MERGE = @INTLTOOL_MERGE@
 INTLTOOL_PERL = @INTLTOOL_PERL@
 INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
 INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
 INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
 INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
@@ -267,6 +271,9 @@ UDEV_BASE_DIR = @UDEV_BASE_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
 VERSION = @VERSION@
 XGETTEXT = @XGETTEXT@
 XGETTEXT_015 = @XGETTEXT_015@
@@ -305,6 +312,8 @@ htmldir = @htmldir@
 includedir = @includedir@
 infodir = @infodir@
 install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
 libdir = @libdir@
 libexecdir = @libexecdir@
 localedir = @localedir@
diff --git a/cli/src/common.c b/cli/src/common.c
index e9a5d760..193ea33f 100644
--- a/cli/src/common.c
+++ b/cli/src/common.c
@@ -358,3 +358,201 @@ print_dhcp6_config (NMDHCP6Config *dhcp6, NmCli *nmc, const char *group_prefix)
 	return FALSE;
 }
 
+const char *
+nmc_device_state_to_string (NMDeviceState state)
+{
+	switch (state) {
+	case NM_DEVICE_STATE_UNMANAGED:
+		return _("unmanaged");
+	case NM_DEVICE_STATE_UNAVAILABLE:
+		return _("unavailable");
+	case NM_DEVICE_STATE_DISCONNECTED:
+		return _("disconnected");
+	case NM_DEVICE_STATE_PREPARE:
+		return _("connecting (prepare)");
+	case NM_DEVICE_STATE_CONFIG:
+		return _("connecting (configuring)");
+	case NM_DEVICE_STATE_NEED_AUTH:
+		return _("connecting (need authentication)");
+	case NM_DEVICE_STATE_IP_CONFIG:
+		return _("connecting (getting IP configuration)");
+	case NM_DEVICE_STATE_IP_CHECK:
+		return _("connecting (checking IP connectivity)");
+	case NM_DEVICE_STATE_SECONDARIES:
+		return _("connecting (starting secondary connections)");
+	case NM_DEVICE_STATE_ACTIVATED:
+		return _("connected");
+	case NM_DEVICE_STATE_DEACTIVATING:
+		return _("deactivating");
+	case NM_DEVICE_STATE_FAILED:
+		return _("connection failed");
+	default:
+		return _("unknown");
+	}
+}
+
+const char *
+nmc_device_reason_to_string (NMDeviceStateReason reason)
+{
+	switch (reason) {
+	case NM_DEVICE_STATE_REASON_NONE:
+		return _("No reason given");
+
+	case NM_DEVICE_STATE_REASON_UNKNOWN:
+		return _("Unknown error");
+
+	case NM_DEVICE_STATE_REASON_NOW_MANAGED:
+		return _("Device is now managed");
+
+	case NM_DEVICE_STATE_REASON_NOW_UNMANAGED:
+		return _("Device is now unmanaged");
+
+	case NM_DEVICE_STATE_REASON_CONFIG_FAILED:
+		return _("The device could not be readied for configuration");
+
+	case NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE:
+		return _("IP configuration could not be reserved (no available address, timeout, etc)");
+
+	case NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED:
+		return _("The IP configuration is no longer valid");
+
+	case NM_DEVICE_STATE_REASON_NO_SECRETS:
+		return _("Secrets were required, but not provided");
+
+	case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT:
+		return _("802.1X supplicant disconnected");
+
+	case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED:
+		return _("802.1X supplicant configuration failed");
+
+	case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED:
+		return _("802.1X supplicant failed");
+
+	case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT:
+		return _("802.1X supplicant took too long to authenticate");
+
+	case NM_DEVICE_STATE_REASON_PPP_START_FAILED:
+		return _("PPP service failed to start");
+
+	case NM_DEVICE_STATE_REASON_PPP_DISCONNECT:
+		return _("PPP service disconnected");
+
+	case NM_DEVICE_STATE_REASON_PPP_FAILED:
+		return _("PPP failed");
+
+	case NM_DEVICE_STATE_REASON_DHCP_START_FAILED:
+		return _("DHCP client failed to start");
+
+	case NM_DEVICE_STATE_REASON_DHCP_ERROR:
+		return _("DHCP client error");
+
+	case NM_DEVICE_STATE_REASON_DHCP_FAILED:
+		return _("DHCP client failed");
+
+	case NM_DEVICE_STATE_REASON_SHARED_START_FAILED:
+		return _("Shared connection service failed to start");
+
+	case NM_DEVICE_STATE_REASON_SHARED_FAILED:
+		return _("Shared connection service failed");
+
+	case NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED:
+		return _("AutoIP service failed to start");
+
+	case NM_DEVICE_STATE_REASON_AUTOIP_ERROR:
+		return _("AutoIP service error");
+
+	case NM_DEVICE_STATE_REASON_AUTOIP_FAILED:
+		return _("AutoIP service failed");
+
+	case NM_DEVICE_STATE_REASON_MODEM_BUSY:
+		return _("The line is busy");
+
+	case NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE:
+		return _("No dial tone");
+
+	case NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER:
+		return _("No carrier could be established");
+
+	case NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT:
+		return _("The dialing request timed out");
+
+	case NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED:
+		return _("The dialing attempt failed");
+
+	case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED:
+		return _("Modem initialization failed");
+
+	case NM_DEVICE_STATE_REASON_GSM_APN_FAILED:
+		return _("Failed to select the specified APN");
+
+	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING:
+		return _("Not searching for networks");
+
+	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED:
+		return _("Network registration denied");
+
+	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT:
+		return _("Network registration timed out");
+
+	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED:
+		return _("Failed to register with the requested network");
+
+	case NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED:
+		return _("PIN check failed");
+
+	case NM_DEVICE_STATE_REASON_FIRMWARE_MISSING:
+		return _("Necessary firmware for the device may be missing");
+
+	case NM_DEVICE_STATE_REASON_REMOVED:
+		return _("The device was removed");
+
+	case NM_DEVICE_STATE_REASON_SLEEPING:
+		return _("NetworkManager went to sleep");
+
+	case NM_DEVICE_STATE_REASON_CONNECTION_REMOVED:
+		return _("The device's active connection disappeared");
+
+	case NM_DEVICE_STATE_REASON_USER_REQUESTED:
+		return _("Device disconnected by user or client");
+
+	case NM_DEVICE_STATE_REASON_CARRIER:
+		return _("Carrier/link changed");
+
+	case NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED:
+		return _("The device's existing connection was assumed");
+
+	case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE:
+		return _("The supplicant is now available");
+
+	case NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND:
+		return _("The modem could not be found");
+
+	case NM_DEVICE_STATE_REASON_BT_FAILED:
+		return _("The Bluetooth connection failed or timed out");
+
+	case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED:
+		return _("GSM Modem's SIM card not inserted");
+
+	case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED:
+		return _("GSM Modem's SIM PIN required");
+
+	case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED:
+		return _("GSM Modem's SIM PUK required");
+
+	case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG:
+		return _("GSM Modem's SIM wrong");
+
+	case NM_DEVICE_STATE_REASON_INFINIBAND_MODE:
+		return _("InfiniBand device does not support connected mode");
+
+        case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
+		return _("A dependency of the connection failed");
+
+	case NM_DEVICE_STATE_REASON_BR2684_FAILED:
+		return _("A problem with the RFC 2684 Ethernet over ADSL bridge");
+
+	default:
+		return _("Unknown");
+	}
+}
+
diff --git a/cli/src/common.h b/cli/src/common.h
index 85015a9f..89412d28 100644
--- a/cli/src/common.h
+++ b/cli/src/common.h
@@ -28,6 +28,7 @@
 #include <nm-ip6-config.h>
 #include <nm-dhcp4-config.h>
 #include <nm-dhcp6-config.h>
+#include <nm-device.h>
 
 #include "nmcli.h"
 
@@ -36,4 +37,7 @@ gboolean print_ip6_config (NMIP6Config *cfg6, NmCli *nmc, const char *group_pref
 gboolean print_dhcp4_config (NMDHCP4Config *dhcp4, NmCli *nmc, const char *group_prefix);
 gboolean print_dhcp6_config (NMDHCP6Config *dhcp6, NmCli *nmc, const char *group_prefix);
 
+const char * nmc_device_state_to_string (NMDeviceState state);
+const char * nmc_device_reason_to_string (NMDeviceStateReason reason);
+
 #endif /* NMC_COMMON_H */
diff --git a/cli/src/connections.c b/cli/src/connections.c
index ea5211f5..8e654864 100644
--- a/cli/src/connections.c
+++ b/cli/src/connections.c
@@ -33,6 +33,7 @@
 
 #include <nm-client.h>
 #include <nm-device-ethernet.h>
+#include <nm-device-adsl.h>
 #include <nm-device-wifi.h>
 #if WITH_WIMAX
 #include <nm-device-wimax.h>
@@ -92,6 +93,7 @@ static NmcOutputField nmc_fields_settings_names[] = {
 	SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME, 0),            /* 16 */
 	SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME, 0),                  /* 17 */
 	SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME, 0),                  /* 18 */
+	SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME, 0),                  /* 19 */
 	{NULL, NULL, 0, NULL, 0}
 };
 #define NMC_FIELDS_SETTINGS_NAMES_ALL_X  NM_SETTING_CONNECTION_SETTING_NAME","\
@@ -104,6 +106,7 @@ static NmcOutputField nmc_fields_settings_names[] = {
                                          NM_SETTING_SERIAL_SETTING_NAME","\
                                          NM_SETTING_PPP_SETTING_NAME","\
                                          NM_SETTING_PPPOE_SETTING_NAME","\
+                                         NM_SETTING_ADSL_SETTING_NAME","\
                                          NM_SETTING_GSM_SETTING_NAME","\
                                          NM_SETTING_CDMA_SETTING_NAME","\
                                          NM_SETTING_BLUETOOTH_SETTING_NAME","\
@@ -178,6 +181,7 @@ typedef struct {
 extern GMainLoop *loop;
 
 static ArgsInfo args_info;
+static guint progress_id = 0;  /* ID of event source for displaying progress */
 
 static void
 usage (void)
@@ -210,6 +214,11 @@ static const char *real_con_commands[] = {
 static void
 quit (void)
 {
+	if (progress_id) {
+		g_source_remove (progress_id);
+		nmc_terminal_erase_line ();
+	}
+
 	g_main_loop_quit (loop);  /* quit main loop */
 }
 
@@ -432,6 +441,15 @@ nmc_connection_detail (NMConnection *connection, NmCli *nmc)
 				continue;
 			}
 		}
+
+		if (!strcasecmp (nmc_fields_settings_names[section_idx].name, nmc_fields_settings_names[19].name)) {
+			NMSettingAdsl *s_adsl = nm_connection_get_setting_adsl (connection);
+			if (s_adsl) {
+				setting_adsl_details (s_adsl, nmc);
+				was_output = TRUE;
+				continue;
+			}
+		}
 	}
 
 	if (print_settings_array)
@@ -687,6 +705,21 @@ fill_in_fields_con_status (NMActiveConnection *active, GSList *con_list)
 	if (dev_str->len > 0)
 		g_string_truncate (dev_str, dev_str->len - 1);  /* Cut off last ',' */
 
+	/* Fill field values */
+	nmc_fields_con_status[0].value = nmc_fields_status_details_groups[0].name;
+	nmc_fields_con_status[1].value = _("N/A");
+	nmc_fields_con_status[2].value = nm_active_connection_get_uuid (active);
+	nmc_fields_con_status[3].value = dev_str->str;
+	nmc_fields_con_status[4].value = active_connection_state_to_string (state);
+	nmc_fields_con_status[5].value = nm_active_connection_get_default (active) ? _("yes") : _("no");
+	nmc_fields_con_status[6].value = nm_active_connection_get_default6 (active) ? _("yes") : _("no");
+	nmc_fields_con_status[7].value = nm_active_connection_get_specific_object (active);
+	nmc_fields_con_status[8].value = NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no");
+	nmc_fields_con_status[9].value = nm_object_get_path (NM_OBJECT (active));
+	nmc_fields_con_status[10].value = nm_active_connection_get_connection (active);
+	nmc_fields_con_status[11].value = _("N/A");
+	nmc_fields_con_status[12].value = nm_active_connection_get_master (active);
+
 	for (iter = con_list; iter; iter = g_slist_next (iter)) {
 		NMConnection *connection = (NMConnection *) iter->data;
 		const char *con_path = nm_connection_get_path (connection);
@@ -696,20 +729,9 @@ fill_in_fields_con_status (NMActiveConnection *active, GSList *con_list)
 			s_con = nm_connection_get_setting_connection (connection);
 			g_assert (s_con != NULL);
 
-			/* Fill field values */
-			nmc_fields_con_status[0].value = nmc_fields_status_details_groups[0].name;
+			/* Fill field values that depend on NMConnection */
 			nmc_fields_con_status[1].value = nm_setting_connection_get_id (s_con);
-			nmc_fields_con_status[2].value = nm_setting_connection_get_uuid (s_con);
-			nmc_fields_con_status[3].value = dev_str->str;
-			nmc_fields_con_status[4].value = active_connection_state_to_string (state);
-			nmc_fields_con_status[5].value = nm_active_connection_get_default (active) ? _("yes") : _("no");
-			nmc_fields_con_status[6].value = nm_active_connection_get_default6 (active) ? _("yes") : _("no");
-			nmc_fields_con_status[7].value = nm_active_connection_get_specific_object (active);
-			nmc_fields_con_status[8].value = NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no");
-			nmc_fields_con_status[9].value = nm_object_get_path (NM_OBJECT (active));
-			nmc_fields_con_status[10].value = nm_active_connection_get_connection (active);
 			nmc_fields_con_status[11].value = nm_setting_connection_get_zone (s_con);
-			nmc_fields_con_status[12].value = nm_active_connection_get_master (active);
 
 			success = TRUE;
 			break;
@@ -1336,10 +1358,12 @@ active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpoin
 
 	state = nm_active_connection_get_state (active);
 
-	printf (_("state: %s\n"), active_connection_state_to_string (state));
-
 	if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-		printf (_("Connection activated\n"));
+		if (nmc->print_output == NMC_PRINT_PRETTY) {
+			nmc_terminal_erase_line ();
+			printf (_("Connection successfully activated (D-Bus active path: %s)\n"),
+			        nm_object_get_path (NM_OBJECT (active)));
+		}
 		quit ();
 	} else if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) {
 		g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
@@ -1361,17 +1385,22 @@ vpn_connection_state_cb (NMVPNConnection *vpn,
 	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
 	case NM_VPN_CONNECTION_STATE_CONNECT:
 	case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
-		printf (_("state: %s (%d)\n"), vpn_connection_state_to_string (state), state);
+		/* no operation */
 		break;
 
 	case NM_VPN_CONNECTION_STATE_ACTIVATED:
-		printf (_("Connection activated\n"));
+		if (nmc->print_output == NMC_PRINT_PRETTY) {
+			nmc_terminal_erase_line ();
+			printf (_("VPN connection successfully activated (D-Bus active path: %s)\n"),
+			        nm_object_get_path (NM_OBJECT (vpn)));
+		}
 		quit ();
 		break;
 
 	case NM_VPN_CONNECTION_STATE_FAILED:
 	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
-		g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."), vpn_connection_state_reason_to_string (reason));
+		g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."),
+		                 vpn_connection_state_reason_to_string (reason));
 		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
 		quit ();
 		break;
@@ -1381,6 +1410,85 @@ vpn_connection_state_cb (NMVPNConnection *vpn,
 	}
 }
 
+/* --- VPN state workaround BEGIN --- */
+static NMVPNConnectionState
+dbus_get_vpn_state (const char *path)
+{
+	DBusGConnection *connection;
+	DBusGProxy *proxy;
+	GError *error = NULL;
+	GValue value = {0,};
+	NMVPNConnectionState state = NM_VPN_CONNECTION_STATE_UNKNOWN;
+
+	connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL);
+	proxy = dbus_g_proxy_new_for_name (connection,
+	                                   NM_DBUS_SERVICE,
+	                                   path,
+	                                   "org.freedesktop.DBus.Properties");
+	if (!proxy) {
+		g_warning ("%s: couldn't create D-Bus object proxy.", __func__);
+		dbus_g_connection_unref (connection);
+		return NM_VPN_CONNECTION_STATE_UNKNOWN;
+	}
+
+	/* Get VpnState property for object 'path' through D-Bus Get() call. */
+	if (dbus_g_proxy_call (proxy,
+	                       "Get", &error,
+	                       G_TYPE_STRING, NM_DBUS_INTERFACE_VPN_CONNECTION,
+	                       G_TYPE_STRING, "VpnState",
+	                       G_TYPE_INVALID,
+	                       G_TYPE_VALUE, &value, G_TYPE_INVALID)) {
+
+		state = g_value_get_uint (&value);
+	} else {
+		g_warning ("Error in getting active connection 'Vpn' property: (%d) %s",
+		           error->code, error->message);
+		g_error_free (error);
+	}
+
+	g_object_unref (proxy);
+	dbus_g_connection_unref (connection);
+
+	return state;
+}
+
+typedef struct {
+	NmCli *nmc;
+	NMVPNConnection *vpn;
+} VpnGetStateInfo;
+
+static gboolean
+get_vpn_state_cb (gpointer user_data)
+{
+	VpnGetStateInfo *info = (VpnGetStateInfo *) user_data;
+	NmCli *nmc = info->nmc;
+	NMVPNConnection *vpn = info->vpn;
+	NMVPNConnectionState state;
+
+	if (!NM_IS_OBJECT (vpn)) {
+		/* Active connection failed and dissapeared, quit. */
+		g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
+		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+		quit ();
+		g_free (info);
+		return FALSE;
+	}
+
+	state = dbus_get_vpn_state (nm_object_get_path (NM_OBJECT (vpn)));
+
+	vpn_connection_state_cb (vpn, state, NM_VPN_CONNECTION_STATE_REASON_UNKNOWN, nmc);
+
+	if (   state == NM_VPN_CONNECTION_STATE_ACTIVATED
+	    || state == NM_VPN_CONNECTION_STATE_FAILED
+	    || state == NM_VPN_CONNECTION_STATE_DISCONNECTED) {
+
+		g_free (info);
+		return FALSE;
+	} else
+		return TRUE;
+}
+/* --- VPN state workaround END --- */
+
 static gboolean
 timeout_cb (gpointer user_data)
 {
@@ -1394,10 +1502,58 @@ timeout_cb (gpointer user_data)
 	return FALSE;
 }
 
+static gboolean
+progress_cb (gpointer user_data)
+{
+	const char *str = (const char *) user_data;
+
+	nmc_terminal_show_progress (str);
+
+	return TRUE;
+}
+
+static gboolean
+progress_device_cb (gpointer user_data)
+{
+	NMDevice *device = (NMDevice *) user_data;
+
+	nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : "");
+
+	return TRUE;
+}
+
+static gboolean
+progress_vpn_cb (gpointer user_data)
+{
+	NMVPNConnection *vpn = (NMVPNConnection *) user_data;
+	const char *str;
+
+	/* VPN state workaround */
+#if 0
+	str = NM_IS_VPN_CONNECTION (vpn) ?
+	        vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) :
+	        "";
+#endif
+	str = NM_IS_OBJECT (vpn) ?
+	        vpn_connection_state_to_string (dbus_get_vpn_state (nm_object_get_path (NM_OBJECT (vpn)))) :
+	        "";
+
+	nmc_terminal_show_progress (str);
+
+	return TRUE;
+}
+
+typedef struct {
+	NmCli *nmc;
+	NMDevice *device;
+} ActivateConnectionInfo;
+
 static void
 activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *error, gpointer user_data)
 {
-	NmCli *nmc = (NmCli *) user_data;
+	ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
+	NmCli *nmc = info->nmc;
+	NMDevice *device = info->device;
 	NMActiveConnectionState state;
 
 	if (error) {
@@ -1407,27 +1563,61 @@ activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *er
 	} else {
 		state = nm_active_connection_get_state (active);
 
-		printf (_("Active connection state: %s\n"), active_connection_state_to_string (state));
-		printf (_("Active connection path: %s\n"), nm_object_get_path (NM_OBJECT (active)));
-
 		if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-			/* don't want to wait or already activated */
+			/* User doesn't want to wait or already activated */
+			if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED && nmc->print_output == NMC_PRINT_PRETTY) {
+				printf (_("Connection successfully activated (D-Bus active path: %s)\n"),
+				        nm_object_get_path (NM_OBJECT (active)));
+			}
 			quit ();
 		} else {
-			if (NM_IS_VPN_CONNECTION (active))
-				g_signal_connect (NM_VPN_CONNECTION (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc);
-			else
+			if (NM_IS_VPN_CONNECTION (active)) {
+				VpnGetStateInfo *vpn_info;
+
+				/* Monitor VPN state */
+				g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc);
+
+				/* Start progress indication showing VPN states */
+				if (nmc->print_output == NMC_PRINT_PRETTY) {
+					if (progress_id)
+						g_source_remove (progress_id);
+					progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active));
+				}
+
+				/* --- workaround BEGIN --- */
+				/* There is a bug in libnm-glib or dbus-glib - 'vpn-state-changed' signal
+				 * is never issued. After inspection it appears that PropertiesChanged and
+				 * VpnStateChanged D-Bus signals are not processed, even if they are seen
+				 * on D-Bus (checked via dbus-monitor). Until the bug is identified and
+				 * fixed, we check the VPN state every second.
+				 */
+				vpn_info = g_malloc0 (sizeof (VpnGetStateInfo));
+				vpn_info->nmc = nmc;
+				vpn_info->vpn = NM_VPN_CONNECTION (active);
+				g_timeout_add_seconds (1, get_vpn_state_cb, vpn_info);
+				/* --- workaround END --- */
+			} else {
 				g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc);
 
+				/* Start progress indication showing device states */
+				if (nmc->print_output == NMC_PRINT_PRETTY) {
+					if (progress_id)
+						g_source_remove (progress_id);
+					progress_id = g_timeout_add (120, progress_device_cb, device);
+				}
+			}
+
 			/* Start timer not to loop forever when signals are not emitted */
 			g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);
 		}
 	}
+	g_free (info);
 }
 
 static NMCResultCode
 do_connection_up (NmCli *nmc, int argc, char **argv)
 {
+	ActivateConnectionInfo *info;
 	NMDevice *device = NULL;
 	const char *spec_object = NULL;
 	gboolean device_found;
@@ -1566,12 +1756,21 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 	 */
 	nmc->nowait_flag = !wait;
 	nmc->should_wait = TRUE;
+
+	info = g_malloc0 (sizeof (ActivateConnectionInfo));
+	info->nmc = nmc;
+	info->device = device;
+
 	nm_client_activate_connection (nmc->client,
 	                               connection,
 	                               device,
 	                               spec_object,
 	                               activate_connection_cb,
-	                               nmc);
+	                               info);
+
+	/* Start progress indication */
+	if (nmc->print_output == NMC_PRINT_PRETTY)
+		progress_id = g_timeout_add (120, progress_cb, "preparing");
 
 	return nmc->return_value;
 error:
@@ -1769,7 +1968,7 @@ parse_cmd (NmCli *nmc, int argc, char **argv)
 		nmc->return_value = do_connections_list (nmc, argc, argv);
 	} else {
 
-	 	if (matches (*argv, "list") == 0) {
+		if (matches (*argv, "list") == 0) {
 			nmc->return_value = do_connections_list (nmc, argc-1, argv+1);
 		}
 		else if (matches(*argv, "status") == 0) {
@@ -1836,7 +2035,7 @@ do_connections (NmCli *nmc, int argc, char **argv)
 	else {
 		while (real_con_commands[i] && matches (*argv, real_con_commands[i]) != 0)
 			i++;
- 		if (real_con_commands[i] != NULL)
+		if (real_con_commands[i] != NULL)
 			real_cmd = TRUE;
 	}
 
@@ -1847,6 +2046,9 @@ do_connections (NmCli *nmc, int argc, char **argv)
 		if (!nmc_versions_match (nmc))
 			return nmc->return_value;
 
+		/* Get NMClient object early */
+		nmc->get_client (nmc);
+
 		nmc->should_wait = TRUE;
 
 		args_info.nmc = nmc;
diff --git a/cli/src/devices.c b/cli/src/devices.c
index 0b1b6d6e..6b9367b4 100644
--- a/cli/src/devices.c
+++ b/cli/src/devices.c
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <netinet/ether.h>
 
 #include <glib.h>
 #include <glib/gi18n.h>
@@ -30,6 +31,7 @@
 #include <nm-client.h>
 #include <nm-device.h>
 #include <nm-device-ethernet.h>
+#include <nm-device-adsl.h>
 #include <nm-device-wifi.h>
 #include <nm-device-modem.h>
 #include <nm-device-bt.h>
@@ -46,6 +48,7 @@
 #include <nm-vpn-connection.h>
 #include <nm-setting-connection.h>
 #include <nm-setting-wired.h>
+#include <nm-setting-adsl.h>
 #include <nm-setting-pppoe.h>
 #include <nm-setting-wireless.h>
 #include <nm-setting-gsm.h>
@@ -105,17 +108,20 @@ static NmcOutputField nmc_fields_dev_list_general[] = {
 	{"VENDOR",            N_("VENDOR"),            20, NULL, 0},  /* 3 */
 	{"PRODUCT",           N_("PRODUCT"),           50, NULL, 0},  /* 4 */
 	{"DRIVER",            N_("DRIVER"),             9, NULL, 0},  /* 5 */
-	{"HWADDR",            N_("HWADDR"),            19, NULL, 0},  /* 6 */
-	{"STATE",             N_("STATE"),             14, NULL, 0},  /* 7 */
-	{"REASON",            N_("REASON"),            25, NULL, 0},  /* 8 */
-	{"UDI",               N_("UDI"),               64, NULL, 0},  /* 9 */
-	{"IP-IFACE",          N_("IP-IFACE"),          10, NULL, 0},  /* 10 */
-	{"NM-MANAGED",        N_("NM-MANAGED"),        15, NULL, 0},  /* 11 */
-	{"FIRMWARE-MISSING",  N_("FIRMWARE-MISSING"),  18, NULL, 0},  /* 12 */
-	{"CONNECTION",        N_("CONNECTION"),        51, NULL, 0},  /* 13 */
+	{"DRIVER-VERSION",    N_("DRIVER-VERSION"),    18, NULL, 0},  /* 6 */
+	{"FIRMWARE-VERSION",  N_("FIRMWARE-VERSION"),  18, NULL, 0},  /* 7 */
+	{"HWADDR",            N_("HWADDR"),            19, NULL, 0},  /* 8 */
+	{"STATE",             N_("STATE"),             14, NULL, 0},  /* 9 */
+	{"REASON",            N_("REASON"),            25, NULL, 0},  /* 10 */
+	{"UDI",               N_("UDI"),               64, NULL, 0},  /* 11 */
+	{"IP-IFACE",          N_("IP-IFACE"),          10, NULL, 0},  /* 12 */
+	{"NM-MANAGED",        N_("NM-MANAGED"),        15, NULL, 0},  /* 13 */
+	{"AUTOCONNECT",       N_("AUTOCONNECT"),       15, NULL, 0},  /* 14 */
+	{"FIRMWARE-MISSING",  N_("FIRMWARE-MISSING"),  18, NULL, 0},  /* 15 */
+	{"CONNECTION",        N_("CONNECTION"),        51, NULL, 0},  /* 16 */
 	{NULL, NULL, 0, NULL, 0}
 };
-#define NMC_FIELDS_DEV_LIST_GENERAL_ALL     "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,HWADDR,STATE,REASON,UDI,IP-IFACE,NM-MANAGED,FIRMWARE-MISSING,CONNECTION"
+#define NMC_FIELDS_DEV_LIST_GENERAL_ALL     "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,DRIVER-VERSION,FIRMWARE-VERSION,HWADDR,STATE,REASON,UDI,IP-IFACE,NM-MANAGED,AUTOCONNECT,FIRMWARE-MISSING,CONNECTION"
 #define NMC_FIELDS_DEV_LIST_GENERAL_COMMON  "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,HWADDR,STATE"
 
 /* Available fields for 'dev list' - CAPABILITIES part */
@@ -208,6 +214,8 @@ static NmcOutputField nmc_fields_dev_wimax_list[] = {
 /* glib main loop variable - defined in nmcli.c */
 extern GMainLoop *loop;
 
+static guint progress_id = 0;  /* ID of event source for displaying progress */
+
 static void
 usage (void)
 {
@@ -222,6 +230,8 @@ usage (void)
 	         "  list [iface <iface>]\n"
 	         "  disconnect iface <iface> [--nowait] [--timeout <timeout>]\n"
 	         "  wifi [list [iface <iface>] [bssid <BSSID>]]\n"
+	         "  wifi connect <(B)SSID> [password <password>] [wep-key-type key|phrase] [iface <iface>] [bssid <BSSID>] [name <name>]\n"
+	         "               [--private] [--nowait] [--timeout <timeout>]\n"
 #if WITH_WIMAX
 	         "  wimax [list [iface <iface>] [nsp <name>]]\n\n"
 #endif
@@ -232,201 +242,12 @@ usage (void)
 static void
 quit (void)
 {
-	g_main_loop_quit (loop);  /* quit main loop */
-}
-
-static const char *
-device_state_to_string (NMDeviceState state)
-{
-	switch (state) {
-	case NM_DEVICE_STATE_UNMANAGED:
-		return _("unmanaged");
-	case NM_DEVICE_STATE_UNAVAILABLE:
-		return _("unavailable");
-	case NM_DEVICE_STATE_DISCONNECTED:
-		return _("disconnected");
-	case NM_DEVICE_STATE_PREPARE:
-		return _("connecting (prepare)");
-	case NM_DEVICE_STATE_CONFIG:
-		return _("connecting (configuring)");
-	case NM_DEVICE_STATE_NEED_AUTH:
-		return _("connecting (need authentication)");
-	case NM_DEVICE_STATE_IP_CONFIG:
-		return _("connecting (getting IP configuration)");
-	case NM_DEVICE_STATE_IP_CHECK:
-		return _("connecting (checking IP connectivity)");
-	case NM_DEVICE_STATE_SECONDARIES:
-		return _("connecting (starting secondary connections)");
-	case NM_DEVICE_STATE_ACTIVATED:
-		return _("connected");
-	case NM_DEVICE_STATE_DEACTIVATING:
-		return _("deactivating");
-	case NM_DEVICE_STATE_FAILED:
-		return _("connection failed");
-	default:
-		return _("unknown");
+	if (progress_id) {
+		g_source_remove (progress_id);
+		nmc_terminal_erase_line ();
 	}
-}
-
-static const char *
-device_reason_to_string (NMDeviceStateReason reason)
-{
-	switch (reason) {
-	case NM_DEVICE_STATE_REASON_NONE:
-		return _("No reason given");
-
-	case NM_DEVICE_STATE_REASON_UNKNOWN:
-		return _("Unknown error");
-
-	case NM_DEVICE_STATE_REASON_NOW_MANAGED:
-		return _("Device is now managed");
-
-	case NM_DEVICE_STATE_REASON_NOW_UNMANAGED:
-		return _("Device is now unmanaged");
-
-	case NM_DEVICE_STATE_REASON_CONFIG_FAILED:
-		return _("The device could not be readied for configuration");
-
-	case NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE:
-		return _("IP configuration could not be reserved (no available address, timeout, etc)");
-
-	case NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED:
-		return _("The IP configuration is no longer valid");
-
-	case NM_DEVICE_STATE_REASON_NO_SECRETS:
-		return _("Secrets were required, but not provided");
-
-	case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT:
-		return _("802.1X supplicant disconnected");
-
-	case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED:
-		return _("802.1X supplicant configuration failed");
-
-	case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED:
-		return _("802.1X supplicant failed");
-
-	case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT:
-		return _("802.1X supplicant took too long to authenticate");
-
-	case NM_DEVICE_STATE_REASON_PPP_START_FAILED:
-		return _("PPP service failed to start");
-
-	case NM_DEVICE_STATE_REASON_PPP_DISCONNECT:
-		return _("PPP service disconnected");
-
-	case NM_DEVICE_STATE_REASON_PPP_FAILED:
-		return _("PPP failed");
-
-	case NM_DEVICE_STATE_REASON_DHCP_START_FAILED:
-		return _("DHCP client failed to start");
-
-	case NM_DEVICE_STATE_REASON_DHCP_ERROR:
-		return _("DHCP client error");
-
-	case NM_DEVICE_STATE_REASON_DHCP_FAILED:
-		return _("DHCP client failed");
-
-	case NM_DEVICE_STATE_REASON_SHARED_START_FAILED:
-		return _("Shared connection service failed to start");
-
-	case NM_DEVICE_STATE_REASON_SHARED_FAILED:
-		return _("Shared connection service failed");
-
-	case NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED:
-		return _("AutoIP service failed to start");
-
-	case NM_DEVICE_STATE_REASON_AUTOIP_ERROR:
-		return _("AutoIP service error");
-
-	case NM_DEVICE_STATE_REASON_AUTOIP_FAILED:
-		return _("AutoIP service failed");
-
-	case NM_DEVICE_STATE_REASON_MODEM_BUSY:
-		return _("The line is busy");
-
-	case NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE:
-		return _("No dial tone");
-
-	case NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER:
-		return _("No carrier could be established");
 
-	case NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT:
-		return _("The dialing request timed out");
-
-	case NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED:
-		return _("The dialing attempt failed");
-
-	case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED:
-		return _("Modem initialization failed");
-
-	case NM_DEVICE_STATE_REASON_GSM_APN_FAILED:
-		return _("Failed to select the specified APN");
-
-	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING:
-		return _("Not searching for networks");
-
-	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED:
-		return _("Network registration denied");
-
-	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT:
-		return _("Network registration timed out");
-
-	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED:
-		return _("Failed to register with the requested network");
-
-	case NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED:
-		return _("PIN check failed");
-
-	case NM_DEVICE_STATE_REASON_FIRMWARE_MISSING:
-		return _("Necessary firmware for the device may be missing");
-
-	case NM_DEVICE_STATE_REASON_REMOVED:
-		return _("The device was removed");
-
-	case NM_DEVICE_STATE_REASON_SLEEPING:
-		return _("NetworkManager went to sleep");
-
-	case NM_DEVICE_STATE_REASON_CONNECTION_REMOVED:
-		return _("The device's active connection disappeared");
-
-	case NM_DEVICE_STATE_REASON_USER_REQUESTED:
-		return _("Device disconnected by user or client");
-
-	case NM_DEVICE_STATE_REASON_CARRIER:
-		return _("Carrier/link changed");
-
-	case NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED:
-		return _("The device's existing connection was assumed");
-
-	case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE:
-		return _("The supplicant is now available");
-
-	case NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND:
-		return _("The modem could not be found");
-
-	case NM_DEVICE_STATE_REASON_BT_FAILED:
-		return _("The Bluetooth connection failed or timed out");
-
-	case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED:
-		return _("GSM Modem's SIM card not inserted");
-
-	case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED:
-		return _("GSM Modem's SIM PIN required");
-
-	case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED:
-		return _("GSM Modem's SIM PUK required");
-
-	case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG:
-		return _("GSM Modem's SIM wrong");
-
-	case NM_DEVICE_STATE_REASON_INFINIBAND_MODE:
-		return _("InfiniBand device does not support connected mode");
-
-        case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
-		return _("A dependency of the connection failed");
-	default:
-		return _("Unknown");
-	}
+	g_main_loop_quit (loop);  /* quit main loop */
 }
 
 /* Convert device type to string. Use setting names strings to match with
@@ -440,6 +261,8 @@ device_type_to_string (NMDevice *device)
 	switch (nm_device_get_device_type (device)) {
 	case NM_DEVICE_TYPE_ETHERNET:
 		return NM_SETTING_WIRED_SETTING_NAME;
+	case NM_DEVICE_TYPE_ADSL:
+		return NM_SETTING_ADSL_SETTING_NAME;
 	case NM_DEVICE_TYPE_WIFI:
 		return NM_SETTING_WIRELESS_SETTING_NAME;
 	case NM_DEVICE_TYPE_MODEM:
@@ -505,7 +328,7 @@ ap_wpa_rsn_flags_to_string (NM80211ApSecurityFlags flags)
 
 	i = 0;
 	while (flags_str[i])
-		 g_free (flags_str[i++]);
+		g_free (flags_str[i++]);
 
 	return ret_str;
 }
@@ -745,8 +568,8 @@ show_device_info (gpointer data, gpointer user_data)
 			else if (NM_IS_DEVICE_VLAN (device))
 				hwaddr = nm_device_vlan_get_hw_address (NM_DEVICE_VLAN (device));
 
-			state_str = g_strdup_printf ("%d (%s)", state, device_state_to_string (state));
-			reason_str = g_strdup_printf ("%d (%s)", reason, device_reason_to_string (reason));
+			state_str = g_strdup_printf ("%d (%s)", state, nmc_device_state_to_string (state));
+			reason_str = g_strdup_printf ("%d (%s)", reason, nmc_device_reason_to_string (reason));
 
 			nmc->allowed_fields[0].value = nmc_fields_dev_list_sections[0].name;  /* "GENERAL"*/
 			nmc->allowed_fields[1].value = nm_device_get_iface (device);
@@ -754,14 +577,17 @@ show_device_info (gpointer data, gpointer user_data)
 			nmc->allowed_fields[3].value = nm_device_get_vendor (device);
 			nmc->allowed_fields[4].value = nm_device_get_product (device);
 			nmc->allowed_fields[5].value = nm_device_get_driver (device) ? nm_device_get_driver (device) : _("(unknown)");
-			nmc->allowed_fields[6].value = hwaddr ? hwaddr : _("(unknown)");
-			nmc->allowed_fields[7].value = state_str;
-			nmc->allowed_fields[8].value = reason_str;
-			nmc->allowed_fields[9].value = nm_device_get_udi (device);
-			nmc->allowed_fields[10].value = nm_device_get_ip_iface (device);
-			nmc->allowed_fields[11].value = nm_device_get_managed (device) ? _("yes") : _("no");
-			nmc->allowed_fields[12].value = nm_device_get_firmware_missing (device) ? _("yes") : _("no");
-			nmc->allowed_fields[13].value = (acon = nm_device_get_active_connection (device)) ?
+			nmc->allowed_fields[6].value = nm_device_get_driver_version (device);
+			nmc->allowed_fields[7].value = nm_device_get_firmware_version (device);
+			nmc->allowed_fields[8].value = hwaddr ? hwaddr : _("(unknown)");
+			nmc->allowed_fields[9].value = state_str;
+			nmc->allowed_fields[10].value = reason_str;
+			nmc->allowed_fields[11].value = nm_device_get_udi (device);
+			nmc->allowed_fields[12].value = nm_device_get_ip_iface (device);
+			nmc->allowed_fields[13].value = nm_device_get_managed (device) ? _("yes") : _("no");
+			nmc->allowed_fields[14].value = nm_device_get_autoconnect (device) ? _("yes") : _("no");
+			nmc->allowed_fields[15].value = nm_device_get_firmware_missing (device) ? _("yes") : _("no");
+			nmc->allowed_fields[16].value = (acon = nm_device_get_active_connection (device)) ?
 			                                   nm_object_get_path (NM_OBJECT (acon)) : _("not connected");
 
 			nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX;
@@ -975,7 +801,7 @@ show_device_status (NMDevice *device, NmCli *nmc)
 {
 	nmc->allowed_fields[0].value = nm_device_get_iface (device);
 	nmc->allowed_fields[1].value = device_type_to_string (device);
-	nmc->allowed_fields[2].value = device_state_to_string (nm_device_get_state (device));
+	nmc->allowed_fields[2].value = nmc_device_state_to_string (nm_device_get_state (device));
 	nmc->allowed_fields[3].value = nm_object_get_path (NM_OBJECT (device));
 
 	nmc->print_fields.flags &= ~NMC_PF_FLAG_MAIN_HEADER_ADD & ~NMC_PF_FLAG_MAIN_HEADER_ONLY & ~NMC_PF_FLAG_FIELD_NAMES; /* Clear header flags */
@@ -1110,7 +936,7 @@ do_devices_list (NmCli *nmc, int argc, char **argv)
 				device = candidate;
 		}
 		if (!device) {
-		 	g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), iface);
+			g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), iface);
 			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 			goto error;
 		}
@@ -1151,6 +977,16 @@ timeout_cb (gpointer user_data)
 	return FALSE;
 }
 
+static gboolean
+progress_cb (gpointer user_data)
+{
+	NMDevice *device = (NMDevice *) user_data;
+
+	nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : "");
+
+	return TRUE;
+}
+
 static void
 disconnect_device_cb (NMDevice *device, GError *error, gpointer user_data)
 {
@@ -1166,10 +1002,13 @@ disconnect_device_cb (NMDevice *device, GError *error, gpointer user_data)
 		quit ();
 	} else {
 		state = nm_device_get_state (device);
-		printf (_("Device state: %d (%s)\n"), state, device_state_to_string (state));
 
 		if (nmc->nowait_flag || state == NM_DEVICE_STATE_DISCONNECTED) {
 			/* Don't want to wait or device already disconnected */
+			if (state == NM_DEVICE_STATE_DISCONNECTED && nmc->print_output == NMC_PRINT_PRETTY) {
+				nmc_terminal_erase_line ();
+				printf (_("Device '%s' has been disconnected.\n"), nm_device_get_iface (device));
+			}
 			quit ();
 		} else {
 			g_signal_connect (device, "notify::state", G_CALLBACK (device_state_cb), nmc);
@@ -1273,6 +1112,10 @@ do_device_disconnect (NmCli *nmc, int argc, char **argv)
 	nmc->should_wait = TRUE;
 	nm_device_disconnect (device, disconnect_device_cb, nmc);
 
+	/* Start progress indication */
+	if (nmc->print_output == NMC_PRINT_PRETTY)
+		progress_id = g_timeout_add (120, progress_cb, device);
+
 error:
 	return nmc->return_value;
 }
@@ -1399,7 +1242,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 		}
 
 		if (!device) {
-		 	g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), iface);
+			g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), iface);
 			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 			goto error;
 		}
@@ -1419,7 +1262,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 					g_free (bssid_up);
 				}
 				if (!ap) {
-				 	g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), bssid_user);
+					g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), bssid_user);
 					nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 					goto error;
 				}
@@ -1436,7 +1279,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 				show_acces_point_info (device, nmc);
 			}
 		} else {
-		 	g_string_printf (nmc->return_text, _("Error: Device '%s' is not a WiFi device."), iface);
+			g_string_printf (nmc->return_text, _("Error: Device '%s' is not a WiFi device."), iface);
 			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 			goto error;
 		}
@@ -1473,7 +1316,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 				}
 			}
 			if (!ap) {
-			 	g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), bssid_user);
+				g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), bssid_user);
 				nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 				goto error;
 			}
@@ -1490,6 +1333,433 @@ error:
 	return nmc->return_value;
 }
 
+static void
+monitor_device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data)
+{
+	NmCli *nmc = (NmCli *) user_data;
+	NMDeviceState state;
+	NMDeviceStateReason reason;
+
+	state = nm_device_get_state_reason (device, &reason);
+
+	if (state == NM_DEVICE_STATE_ACTIVATED) {
+		if (nmc->print_output == NMC_PRINT_PRETTY) {
+			NMActiveConnection *active = nm_device_get_active_connection (device);
+
+			nmc_terminal_erase_line ();
+			printf (_("Connection with UUID '%s' created and activated on device '%s'\n"),
+			        nm_active_connection_get_uuid (active), nm_device_get_iface (device));
+		}
+		quit ();
+	} else if (state == NM_DEVICE_STATE_FAILED) {
+		g_string_printf (nmc->return_text, _("Error: Connection activation failed: (%d) %s."),
+		                 reason, nmc_device_reason_to_string (reason));
+		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+		quit ();
+	}
+}
+
+typedef struct {
+	NmCli *nmc;
+	NMDevice *device;
+} AddAndActivateInfo;
+
+static void
+add_and_activate_cb (NMClient *client,
+                     NMActiveConnection *active,
+                     const char *connection_path,
+                     GError *error,
+                     gpointer user_data)
+{
+	AddAndActivateInfo *info = (AddAndActivateInfo *) user_data;
+	NmCli *nmc = info->nmc;
+	NMDevice *device = info->device;
+	NMActiveConnectionState state;
+
+        if (error) {
+		g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: (%d) %s"),
+		                 error->code, error->message);
+		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+		quit ();
+	} else {
+		state = nm_active_connection_get_state (active);
+
+		if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) {
+			g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: Unknown error"));
+			nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+			quit ();
+		}
+
+		if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
+			/* User doesn't want to wait or already activated */
+			if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED && nmc->print_output == NMC_PRINT_PRETTY) {
+				printf (_("Connection with UUID '%s' created and activated on device '%s'\n"),
+				        nm_active_connection_get_uuid (active), nm_device_get_iface (device));
+			}
+			quit ();
+		} else {
+			g_signal_connect (device, "notify::state", G_CALLBACK (monitor_device_state_cb), nmc);
+			g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);  /* Exit if timeout expires */
+
+			if (nmc->print_output == NMC_PRINT_PRETTY)
+				progress_id = g_timeout_add (120, progress_cb, device);
+		}
+	}
+
+	g_free (info);
+}
+
+/*
+ * Find a Wi-Fi device with 'iface' in 'devices' array. If 'iface' is NULL,
+ * the first Wi-Fi device is returned. 'idx' parameter is updated to the point
+ * where the function finished so that the function can be called repeatedly
+ * to get next matching device.
+ * Returns: found device or NULL
+ */
+static NMDevice *
+find_wifi_device_by_iface (const GPtrArray *devices, const char *iface, int *idx)
+{
+	NMDevice *device = NULL;
+	int i;
+
+	for (i = *idx; devices && (i < devices->len); i++) {
+		NMDevice *candidate = g_ptr_array_index (devices, i);
+		const char *dev_iface = nm_device_get_iface (candidate);
+
+		if (!NM_IS_DEVICE_WIFI (candidate))
+			continue;
+
+		if (iface) {
+			/* If a iface was specified then use it. */
+			if (strcmp (dev_iface, iface) == 0) {
+				device = candidate;
+				break;
+			}
+		} else {
+			/* Else return the first Wi-Fi device. */
+			device = candidate;
+			break;
+		}
+	}
+
+	*idx = i + 1;
+	return device;
+}
+
+/*
+ * Find AP on 'device' according to 'bssid' or 'ssid' parameter.
+ * Returns: found AP or NULL
+ */
+static NMAccessPoint *
+find_ap_on_device (NMDevice *device, GByteArray *bssid, const char *ssid)
+{
+	const GPtrArray *aps;
+	NMAccessPoint *ap = NULL;
+	int i;
+
+	g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL);
+	g_return_val_if_fail ((bssid && !ssid) || (!bssid && ssid), NULL);
+
+	aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device));
+	for (i = 0; aps && (i < aps->len); i++) {
+		NMAccessPoint *candidate_ap = g_ptr_array_index (aps, i);
+
+		if (ssid) {
+			/* Parameter is SSID */
+			const GByteArray *candidate_ssid = nm_access_point_get_ssid (candidate_ap);
+			char *ssid_tmp = nm_utils_ssid_to_utf8 (candidate_ssid);
+
+			/* Compare SSIDs */
+			if (strcmp (ssid, ssid_tmp) == 0) {
+				ap = candidate_ap;
+				g_free (ssid_tmp);
+				break;
+			}
+			g_free (ssid_tmp);
+		} else if (bssid) {
+			/* Parameter is BSSID */
+			const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap);
+			char *bssid_up = nm_utils_hwaddr_ntoa (bssid->data, ARPHRD_ETHER);
+
+			/* Compare BSSIDs */
+			if (strcmp (bssid_up, candidate_bssid) == 0) {
+				ap = candidate_ap;
+				g_free (bssid_up);
+				break;
+			}
+			g_free (bssid_up);
+		}
+	}
+
+	return ap;
+}
+
+static NMCResultCode
+do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
+{
+	NMDevice *device = NULL;
+	NMAccessPoint *ap = NULL;
+	NMConnection *connection = NULL;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wifi;
+	NMSettingWirelessSecurity *s_wsec;
+	AddAndActivateInfo *info;
+	const char *param_user = NULL;
+	const char *iface = NULL;
+	const char *bssid = NULL;
+	const char *password = NULL;
+	const char *con_name = NULL;
+	gboolean private = FALSE;
+	gboolean wait = TRUE;
+	gboolean wep_passphrase = FALSE;
+	GByteArray *bssid1_arr = NULL;
+	GByteArray *bssid2_arr = NULL;
+	const GPtrArray *devices;
+	int devices_idx;
+	GError *error = NULL;
+
+	/* Default timeout for waiting for operation completion */
+	nmc->timeout = 90;
+
+	/* Get the first compulsory argument (SSID or BSSID) */
+	if (argc > 0) {
+		param_user = *argv;
+		bssid1_arr = nm_utils_hwaddr_atoba (param_user, ARPHRD_ETHER);
+
+		argc--;
+		argv++;
+	} else {
+		g_string_printf (nmc->return_text, _("Error: SSID or BSSID are missing."));
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		goto error;
+	}
+
+	/* Get the rest of the parameters */
+	while (argc > 0) {
+		if (strcmp (*argv, "iface") == 0) {
+			if (next_arg (&argc, &argv) != 0) {
+				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				goto error;
+			}
+			iface = *argv;
+		} else if (strcmp (*argv, "bssid") == 0) {
+			if (next_arg (&argc, &argv) != 0) {
+				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				goto error;
+			}
+			bssid = *argv;
+			bssid2_arr = nm_utils_hwaddr_atoba (bssid, ARPHRD_ETHER);
+			if (!bssid2_arr) {
+				g_string_printf (nmc->return_text, _("Error: bssid argument value '%s' is not a valid BSSID."),
+				                 bssid);
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				goto error;
+			}
+		} else if (strcmp (*argv, "password") == 0) {
+			if (next_arg (&argc, &argv) != 0) {
+				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				goto error;
+			}
+			password = *argv;
+		} else if (strcmp (*argv, "wep-key-type") == 0) {
+			if (next_arg (&argc, &argv) != 0) {
+				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				goto error;
+			}
+			if (strcmp (*argv, "key") == 0)
+				wep_passphrase = FALSE;
+			else if (strcmp (*argv, "phrase") == 0)
+				wep_passphrase = TRUE;
+			else {
+				g_string_printf (nmc->return_text,
+				                 _("Error: wep-key-type argument value '%s' is invalid, use 'key' or 'phrase'."),
+				                 *argv);
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				goto error;
+			}
+		} else if (strcmp (*argv, "name") == 0) {
+			if (next_arg (&argc, &argv) != 0) {
+				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				goto error;
+			}
+			con_name = *argv;
+		} else if (strcmp (*argv, "--private") == 0) {
+			private = TRUE;
+		} else if (strcmp (*argv, "--nowait") == 0) {
+			wait = FALSE;
+		} else if (strcmp (*argv, "--timeout") == 0) {
+			if (next_arg (&argc, &argv) != 0) {
+				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				goto error;
+			}
+
+			errno = 0;
+			nmc->timeout = strtol (*argv, NULL, 10);
+			if (errno || nmc->timeout < 0) {
+				g_string_printf (nmc->return_text, _("Error: timeout value '%s' is not valid."), *argv);
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				goto error;
+			}
+		} else {
+			fprintf (stderr, _("Unknown parameter: %s\n"), *argv);
+		}
+
+		argc--;
+		argv++;
+	}
+
+	/* Verify SSID/BSSID parameters */
+	if (bssid1_arr && bssid2_arr && memcmp (bssid1_arr->data, bssid2_arr->data, ETH_ALEN)) {
+		g_string_printf (nmc->return_text, _("Error: BSSID to connect to (%s) differs from bssid argument (%s)."),
+		                 param_user, bssid);
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		goto error;
+	}
+	if (!bssid1_arr && strlen (param_user) > 32) {
+		g_string_printf (nmc->return_text, _("Error: Parameter '%s' is neither SSID nor BSSID."), param_user);
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		goto error;
+	}
+
+	if (!nmc_is_nm_running (nmc, &error)) {
+		if (error) {
+			g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message);
+			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+			g_error_free (error);
+		} else {
+			g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
+			nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
+		}
+		goto error;
+	}
+
+	if (!nmc_versions_match (nmc))
+		goto error;
+
+	nmc->get_client (nmc);
+	devices = nm_client_get_devices (nmc->client);
+
+	/* Find a device to activate the connection on */
+	devices_idx = 0;
+	device = find_wifi_device_by_iface (devices, iface, &devices_idx);
+
+	if (!device) {
+		if (iface)
+			g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), iface);
+		else
+			g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found."));
+		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+		goto error;
+	}
+
+	/* Find an AP to connect to */
+	ap = find_ap_on_device (device, bssid1_arr, bssid1_arr ? NULL : param_user);
+	if (!ap && !iface) {
+		/* AP not found. iface was not specified, so try finding the AP on another device. */
+		while ((device = find_wifi_device_by_iface (devices, NULL, &devices_idx)) != NULL) {
+			ap = find_ap_on_device (device, bssid1_arr, bssid1_arr ? NULL : param_user);
+			if (ap)
+				break;
+		}
+	}
+
+	if (!ap) {
+		if (!bssid1_arr)
+			g_string_printf (nmc->return_text, _("Error: No network with SSID '%s' found."), param_user);
+		else
+			g_string_printf (nmc->return_text, _("Error: No access point with BSSID '%s' found."), param_user);
+		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+		goto error;
+	}
+
+	/* If there are some connection data from user, create a connection and
+	 * fill them into proper settings. */
+	if (con_name || private || bssid2_arr || password)
+		connection = nm_connection_new ();
+
+	if (con_name || private) {
+		s_con =  (NMSettingConnection *) nm_setting_connection_new ();
+		nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+		/* Set user provided connection name */
+		if (con_name)
+			g_object_set (s_con, NM_SETTING_CONNECTION_ID, con_name, NULL);
+
+		/* Connection will only be visible to this user when '--private' is specified */
+		if (private)
+			nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL);
+	}
+	if (bssid2_arr) {
+		s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+		nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+		/* 'bssid' parameter is used to restrict the conenction only to the BSSID */
+		g_object_set (s_wifi, NM_SETTING_WIRELESS_BSSID, bssid2_arr, NULL);
+	}
+	if (password) {
+		NM80211ApFlags flags = nm_access_point_get_flags (ap);
+		NM80211ApSecurityFlags wpa_flags = nm_access_point_get_wpa_flags (ap);
+		NM80211ApSecurityFlags rsn_flags = nm_access_point_get_rsn_flags (ap);
+
+		s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+		nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+
+		/* Set password for WEP or WPA-PSK. */
+		if (flags & NM_802_11_AP_FLAGS_PRIVACY) {
+			if (wpa_flags == NM_802_11_AP_SEC_NONE && rsn_flags == NM_802_11_AP_SEC_NONE) {
+				/* WEP */
+				nm_setting_wireless_security_set_wep_key (s_wsec, 0, password);
+				g_object_set (G_OBJECT (s_wsec),
+				              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE,
+				              wep_passphrase ? NM_WEP_KEY_TYPE_PASSPHRASE: NM_WEP_KEY_TYPE_KEY,
+				              NULL);
+			} else if (   !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
+				   && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) {
+				/* WPA PSK */
+				g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_PSK, password, NULL);
+			}
+		}
+		// FIXME: WPA-Enterprise is not supported yet.
+		// We are not able to determine and fill all the parameters for
+		// 802.1X authentication automatically without user providing
+		// the data. Adding nmcli options for the 8021x setting would
+		// clutter the command. However, that could be solved later by
+		// implementing add/edit connections support for nmcli.
+	}
+
+	/* nowait_flag indicates user input. should_wait says whether quit in start().
+	 * We have to delay exit after add_and_activate_cb() is called, even if
+	 * the user doesn't want to wait, in order to give NM time to check our
+	 * permissions. */
+	nmc->nowait_flag = !wait;
+	nmc->should_wait = TRUE;
+
+	info = g_malloc0 (sizeof (AddAndActivateInfo));
+	info->nmc = nmc;
+	info->device = device;
+
+	nm_client_add_and_activate_connection (nmc->client,
+	                                       connection,
+	                                       device,
+	                                       nm_object_get_path (NM_OBJECT (ap)),
+	                                       add_and_activate_cb,
+	                                       info);
+
+error:
+	if (bssid1_arr)
+		g_byte_array_free (bssid1_arr, TRUE);
+	if (bssid2_arr)
+		g_byte_array_free (bssid2_arr, TRUE);
+
+	return nmc->return_value;
+}
+
 static NMCResultCode
 do_device_wifi (NmCli *nmc, int argc, char **argv)
 {
@@ -1498,8 +1768,9 @@ do_device_wifi (NmCli *nmc, int argc, char **argv)
 	else if (argc > 0) {
 		if (matches (*argv, "list") == 0) {
 			nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1);
-		}
-		else {
+		} else if (matches (*argv, "connect") == 0) {
+			nmc->return_value = do_device_wifi_connect_network (nmc, argc-1, argv+1);
+		} else {
 			g_string_printf (nmc->return_text, _("Error: 'dev wifi' command '%s' is not valid."), *argv);
 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
 		}
@@ -1618,7 +1889,7 @@ do_device_wimax_list (NmCli *nmc, int argc, char **argv)
 		}
 
 		if (!device) {
-		 	g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), iface);
+			g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), iface);
 			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 			goto error;
 		}
@@ -1638,7 +1909,7 @@ do_device_wimax_list (NmCli *nmc, int argc, char **argv)
 					g_free (nsp_up);
 				}
 				if (!nsp) {
-				 	g_string_printf (nmc->return_text, _("Error: NSP with name '%s' not found."), nsp_user);
+					g_string_printf (nmc->return_text, _("Error: NSP with name '%s' not found."), nsp_user);
 					nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 					goto error;
 				}
@@ -1649,7 +1920,7 @@ do_device_wimax_list (NmCli *nmc, int argc, char **argv)
 				show_nsp_info (device, nmc);
 			}
 		} else {
-		 	g_string_printf (nmc->return_text, _("Error: Device '%s' is not a WiMAX device."), iface);
+			g_string_printf (nmc->return_text, _("Error: Device '%s' is not a WiMAX device."), iface);
 			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 			goto error;
 		}
@@ -1680,7 +1951,7 @@ do_device_wimax_list (NmCli *nmc, int argc, char **argv)
 				}
 			}
 			if (!nsp) {
-			 	g_string_printf (nmc->return_text, _("Error: Access point with nsp '%s' not found."), nsp_user);
+				g_string_printf (nmc->return_text, _("Error: Access point with nsp '%s' not found."), nsp_user);
 				nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 				goto error;
 			}
diff --git a/cli/src/network-manager.c b/cli/src/network-manager.c
index c23c9fae..927eadd1 100644
--- a/cli/src/network-manager.c
+++ b/cli/src/network-manager.c
@@ -14,7 +14,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2010 - 2011 Red Hat, Inc.
+ * (C) Copyright 2010 - 2012 Red Hat, Inc.
  */
 
 #include "config.h"
@@ -57,6 +57,16 @@ static NmcOutputField nmc_fields_nm_status[] = {
 #define NMC_FIELDS_NM_WWAN           "WWAN"
 #define NMC_FIELDS_NM_WIMAX          "WIMAX"
 
+/* Available fields for 'nm permissions' */
+static NmcOutputField nmc_fields_nm_permissions[] = {
+	{"PERMISSION",     N_("PERMISSION"),      57, NULL, 0},  /* 0 */
+	{"VALUE",          N_("VALUE"),           10, NULL, 0},  /* 1 */
+	{NULL,             NULL,                  0, NULL, 0}
+};
+#define NMC_FIELDS_NM_PERMISSIONS_ALL     "PERMISSION,VALUE"
+#define NMC_FIELDS_NM_PERMISSIONS_COMMON  "PERMISSION,VALUE"
+
+
 /* glib main loop variable - defined in nmcli.c */
 extern GMainLoop *loop;
 
@@ -66,18 +76,20 @@ usage (void)
 	fprintf (stderr,
 	         _("Usage: nmcli nm { COMMAND | help }\n\n"
 #if WITH_WIMAX
-	         "  COMMAND := { status | enable | sleep | wifi | wwan | wimax }\n\n"
+	         "  COMMAND := { status | permissions | enable | sleep | wifi | wwan | wimax }\n\n"
 #else
-	         "  COMMAND := { status | enable | sleep | wifi | wwan }\n\n"
+	         "  COMMAND := { status | permissions | enable | sleep | wifi | wwan }\n\n"
 #endif
 	         "  status\n"
+	         "  permissions\n"
 	         "  enable [true|false]\n"
 	         "  sleep [true|false]\n"
 	         "  wifi [on|off]\n"
 	         "  wwan [on|off]\n"
 #if WITH_WIMAX
-	         "  wimax [on|off]\n\n"
+	         "  wimax [on|off]\n"
 #endif
+	         "\n"
 	         ));
 }
 
@@ -135,7 +147,7 @@ show_nm_status (NmCli *nmc)
 		fields_str = fields_common;
 	else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
 		fields_str = fields_all;
-	else 
+	else
 		fields_str = nmc->required_fields;
 
 	nmc->allowed_fields = nmc_fields_nm_status;
@@ -203,6 +215,129 @@ error:
 	return nmc->return_value;
 }
 
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK     "org.freedesktop.NetworkManager.enable-disable-network"
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI        "org.freedesktop.NetworkManager.enable-disable-wifi"
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN        "org.freedesktop.NetworkManager.enable-disable-wwan"
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX       "org.freedesktop.NetworkManager.enable-disable-wimax"
+#define NM_AUTH_PERMISSION_SLEEP_WAKE                 "org.freedesktop.NetworkManager.sleep-wake"
+#define NM_AUTH_PERMISSION_NETWORK_CONTROL            "org.freedesktop.NetworkManager.network-control"
+#define NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED       "org.freedesktop.NetworkManager.wifi.share.protected"
+#define NM_AUTH_PERMISSION_WIFI_SHARE_OPEN            "org.freedesktop.NetworkManager.wifi.share.open"
+#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM     "org.freedesktop.NetworkManager.settings.modify.system"
+#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN        "org.freedesktop.NetworkManager.settings.modify.own"
+#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME   "org.freedesktop.NetworkManager.settings.modify.hostname"
+
+static const char *
+permission_to_string (NMClientPermission perm)
+{
+	switch (perm) {
+	case NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK:
+		return NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK;
+	case NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI:
+		return NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI;
+	case NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN:
+		return NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN;
+	case NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX:
+		return NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX;
+	case NM_CLIENT_PERMISSION_SLEEP_WAKE:
+		return NM_AUTH_PERMISSION_SLEEP_WAKE;
+	case NM_CLIENT_PERMISSION_NETWORK_CONTROL:
+		return NM_AUTH_PERMISSION_NETWORK_CONTROL;
+	case NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED:
+		return NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED;
+	case NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN:
+		return NM_AUTH_PERMISSION_WIFI_SHARE_OPEN;
+	case NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM:
+		return NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM;
+	case NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN:
+		return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
+	case NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME:
+		return NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME;
+	default:
+		return _("unknown");
+	}
+}
+
+static const char *
+permission_result_to_string (NMClientPermissionResult perm_result)
+{
+	
+	switch (perm_result) {
+	case NM_CLIENT_PERMISSION_RESULT_YES:
+		return _("yes");
+	case NM_CLIENT_PERMISSION_RESULT_NO:
+		return _("no");
+	case NM_CLIENT_PERMISSION_RESULT_AUTH:
+		return _("auth");
+	default:
+		return _("unknown");
+	}
+}
+
+static NMCResultCode
+show_nm_permissions (NmCli *nmc)
+{
+	NMClientPermission perm;
+	GError *error = NULL;
+	const char *fields_str;
+	const char *fields_all =    NMC_FIELDS_NM_PERMISSIONS_ALL;
+	const char *fields_common = NMC_FIELDS_NM_PERMISSIONS_COMMON;
+	guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0;
+	guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0;
+	guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0;
+
+	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
+		fields_str = fields_common;
+	else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
+		fields_str = fields_all;
+	else
+		fields_str = nmc->required_fields;
+
+	nmc->allowed_fields = nmc_fields_nm_permissions;
+	nmc->print_fields.indices = parse_output_fields (fields_str, nmc->allowed_fields, &error);
+
+	if (error) {
+		if (error->code == 0)
+			g_string_printf (nmc->return_text, _("Error: 'nm permissions': %s"), error->message);
+		else
+			g_string_printf (nmc->return_text, _("Error: 'nm permissions': %s; allowed fields: %s"), error->message, NMC_FIELDS_NM_PERMISSIONS_ALL);
+		g_error_free (error);
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return nmc->return_value;
+	}
+
+	if (!nmc_is_nm_running (nmc, &error)) {
+		if (error) {
+			g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message);
+			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+			g_error_free (error);
+		} else {
+			g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
+			nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
+		}
+		goto error;
+	}
+
+	nmc->get_client (nmc); /* create NMClient */
+
+	nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES;
+	nmc->print_fields.header_name = _("NetworkManager permissions");
+	print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */
+
+	for (perm = NM_CLIENT_PERMISSION_NONE + 1; perm <= NM_CLIENT_PERMISSION_LAST; perm++) {
+		NMClientPermissionResult perm_result = nm_client_get_permission_result (nmc->client, perm);
+
+		set_val_str (nmc->allowed_fields, 0, permission_to_string (perm));
+		set_val_str (nmc->allowed_fields, 1, permission_result_to_string (perm_result));
+		nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag;
+		print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */
+	}
+	return NMC_RESULT_SUCCESS;
+
+error:
+	return nmc->return_value;
+}
+
 /* libnm-glib doesn't provide API fro Sleep method - implement D-Bus call ourselves */
 static void networking_set_sleep (NmCli *nmc, gboolean in_sleep)
 {
@@ -214,8 +349,8 @@ static void networking_set_sleep (NmCli *nmc, gboolean in_sleep)
 	if (!connection) {
 		g_string_printf (nmc->return_text, _("Error: Couldn't connect to system bus: %s"), err->message);
 		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
-        	g_error_free (err);
-	        goto gone;
+		g_error_free (err);
+		goto gone;
 	}
 
 	proxy = dbus_g_proxy_new_for_name (connection,
@@ -226,8 +361,8 @@ static void networking_set_sleep (NmCli *nmc, gboolean in_sleep)
 		g_string_printf (nmc->return_text, _("Error: Couldn't create D-Bus object proxy."));
 		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 		goto gone;
-        }
- 
+	}
+
 	if (!dbus_g_proxy_call (proxy, "Sleep", &err, G_TYPE_BOOLEAN, in_sleep, G_TYPE_INVALID, G_TYPE_INVALID)) {
 		g_string_printf (nmc->return_text, _("Error in sleep: %s"), err->message);
 		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
@@ -267,6 +402,11 @@ do_network_manager (NmCli *nmc, int argc, char **argv)
 				goto opt_error;
 			nmc->return_value = show_nm_status (nmc);
 		}
+		else if (matches (*argv, "permissions") == 0) {
+			if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
+				goto opt_error;
+			nmc->return_value = show_nm_permissions (nmc);
+		}
 		else if (matches (*argv, "enable") == 0) {
 			if (next_arg (&argc, &argv) != 0) {
 				/* no argument, show current state of networking */
diff --git a/cli/src/settings.c b/cli/src/settings.c
index bf422920..e67bd8f0 100644
--- a/cli/src/settings.c
+++ b/cli/src/settings.c
@@ -348,6 +348,26 @@ static NmcOutputField nmc_fields_setting_pppoe[] = {
                                          NM_SETTING_PPPOE_PASSWORD
 #define NMC_FIELDS_SETTING_PPPOE_COMMON  NMC_FIELDS_SETTING_PPP_ALL
 
+/* Available fields for NM_SETTING_ADSL_SETTING_NAME */
+static NmcOutputField nmc_fields_setting_adsl[] = {
+	SETTING_FIELD ("name", 10),                                /* 0 */
+	SETTING_FIELD (NM_SETTING_ADSL_USERNAME, 15),              /* 1 */
+	SETTING_FIELD (NM_SETTING_ADSL_PASSWORD, 15),              /* 2 */
+	SETTING_FIELD (NM_SETTING_ADSL_VPI, 10),                   /* 3 */
+	SETTING_FIELD (NM_SETTING_ADSL_VCI, 10),                   /* 4 */
+	SETTING_FIELD (NM_SETTING_ADSL_ENCAPSULATION, 10),         /* 5 */
+	SETTING_FIELD (NM_SETTING_ADSL_PROTOCOL, 10),              /* 6 */
+	{NULL, NULL, 0, NULL, 0}
+};
+#define NMC_FIELDS_SETTING_ADSL_ALL     "name"","\
+                                        NM_SETTING_ADSL_USERNAME","\
+                                        NM_SETTING_ADSL_PASSWORD","\
+                                        NM_SETTING_ADSL_VPI","\
+                                        NM_SETTING_ADSL_VCI","\
+                                        NM_SETTING_ADSL_ENCAPSULATION","\
+                                        NM_SETTING_ADSL_PROTOCOL
+#define NMC_FIELDS_SETTING_ADSL_COMMON  NMC_FIELDS_SETTING_ADSL_ALL
+
 /* Available fields for NM_SETTING_GSM_SETTING_NAME */
 static NmcOutputField nmc_fields_setting_gsm[] = {
 	SETTING_FIELD ("name", 10),                                        /* 0 */
@@ -607,10 +627,11 @@ vlan_priorities_to_string (NMSettingVlan *s_vlan, NMVlanPriorityMap map)
 	for (i = 0; i < nm_setting_vlan_get_num_priorities (s_vlan, map); i++) {
 		guint32 from, to;
 
-		nm_setting_vlan_get_priority (s_vlan, map, i, &from, &to);
-		g_string_append_printf (priorities, "%d:%d,", from, to);
+		if (nm_setting_vlan_get_priority (s_vlan, map, i, &from, &to))
+			g_string_append_printf (priorities, "%d:%d,", from, to);
 	}
-	g_string_truncate (priorities, priorities->len-1);  /* chop off trailing ',' */
+	if (priorities->len)
+		g_string_truncate (priorities, priorities->len-1);  /* chop off trailing ',' */
 
 	return g_string_free (priorities, FALSE);
 }
@@ -917,11 +938,11 @@ setting_wireless_details (NMSettingWireless *s_wireless, NmCli *nmc)
 	nmc->allowed_fields[2].value = nm_setting_wireless_get_mode (s_wireless);
 	nmc->allowed_fields[3].value = nm_setting_wireless_get_band (s_wireless);
 	nmc->allowed_fields[4].value = channel_str;
-	nmc->allowed_fields[5].value = bssid_str ? bssid_str : _("not set");
+	nmc->allowed_fields[5].value = bssid_str;
 	nmc->allowed_fields[6].value = rate_str;
 	nmc->allowed_fields[7].value = tx_power_str;
-	nmc->allowed_fields[8].value = device_mac_str ? device_mac_str : _("not set");
-	nmc->allowed_fields[9].value = cloned_mac_str ? cloned_mac_str : _("not set");
+	nmc->allowed_fields[8].value = device_mac_str;
+	nmc->allowed_fields[9].value = cloned_mac_str;
 	nmc->allowed_fields[10].value = mac_blacklist->str;
 	nmc->allowed_fields[11].value = strcmp (mtu_str, "0") ?  mtu_str : _("auto");
 	nmc->allowed_fields[12].value = seen_bssids->str;
@@ -1715,3 +1736,34 @@ setting_vlan_details (NMSettingVlan *s_vlan, NmCli *nmc)
 	return TRUE;
 }
 
+gboolean
+setting_adsl_details (NMSettingAdsl *s_adsl, NmCli *nmc)
+{
+	guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0;
+	guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0;
+	guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0;
+
+	g_return_val_if_fail (NM_IS_SETTING_ADSL (s_adsl), FALSE);
+
+	nmc->allowed_fields = nmc_fields_setting_adsl;
+	nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_ADSL_ALL, nmc->allowed_fields, NULL);
+	nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES;
+	print_fields (nmc->print_fields, nmc->allowed_fields);  /* Print field names */
+
+	nmc->allowed_fields[0].value = NM_SETTING_ADSL_SETTING_NAME;
+	nmc->allowed_fields[1].value = nm_setting_adsl_get_username (s_adsl);
+	nmc->allowed_fields[2].value = nm_setting_adsl_get_password (s_adsl);
+	nmc->allowed_fields[3].value = g_strdup_printf ("%d", nm_setting_adsl_get_vpi (s_adsl));
+	nmc->allowed_fields[4].value = g_strdup_printf ("%d", nm_setting_adsl_get_vci (s_adsl));
+	nmc->allowed_fields[5].value = nm_setting_adsl_get_encapsulation (s_adsl);
+	nmc->allowed_fields[6].value = nm_setting_adsl_get_protocol (s_adsl);
+
+	nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX;
+	print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */
+
+	g_free ((char*) nmc->allowed_fields[3].value);
+	g_free ((char*) nmc->allowed_fields[4].value);
+
+	return TRUE;
+}
+
diff --git a/cli/src/settings.h b/cli/src/settings.h
index 964d6648..3309bd0a 100644
--- a/cli/src/settings.h
+++ b/cli/src/settings.h
@@ -22,6 +22,7 @@
 
 #include <nm-setting-connection.h>
 #include <nm-setting-wired.h>
+#include <nm-setting-adsl.h>
 #include <nm-setting-8021x.h>
 #include <nm-setting-wireless.h>
 #include <nm-setting-wireless-security.h>
@@ -63,5 +64,6 @@ gboolean setting_wimax_details (NMSettingWimax *s_wimax, NmCli *nmc);
 gboolean setting_infiniband_details (NMSettingInfiniband *s_infiniband, NmCli *nmc);
 gboolean setting_bond_details (NMSettingBond *s_bond, NmCli *nmc);
 gboolean setting_vlan_details (NMSettingVlan *s_vlan, NmCli *nmc);
+gboolean setting_adsl_details (NMSettingAdsl *s_adsl, NmCli *nmc);
 
 #endif /* NMC_SETTINGS_H */
diff --git a/cli/src/utils.c b/cli/src/utils.c
index 24bc998c..8030d5e1 100644
--- a/cli/src/utils.c
+++ b/cli/src/utils.c
@@ -69,7 +69,7 @@ ssid_to_printable (const char *str, gsize len)
 	int i;
 
 	if (str == NULL || len == 0)
-		 return NULL;
+		return NULL;
 
 	if (g_utf8_validate (str, len, NULL))
 		return g_strdup_printf ("'%.*s'", (int) len, str);
@@ -137,6 +137,38 @@ nmc_ip6_address_as_string (const struct in6_addr *ip, GError **error)
 }
 
 /*
+ * Erase terminal line using ANSI escape sequences.
+ * It prints <ESC>[2K sequence to erase the line and then \r to return back
+ * to the beginning of the line.
+ *
+ * http://www.termsys.demon.co.uk/vtansi.htm
+ */
+void
+nmc_terminal_erase_line (void)
+{
+	printf ("\33[2K\r");
+	fflush (stdout);
+}
+
+/*
+ * Print animated progress for an operation.
+ * Repeated calls of the function will show rotating slash in terminal followed
+ * by the string passed in 'str' argument.
+ */
+void
+nmc_terminal_show_progress (const char *str)
+{
+	static int idx = 0;
+	const char slashes[4] = {'|', '/', '-', '\\'};
+
+	nmc_terminal_erase_line ();
+	printf ("%c %s", slashes[idx++], str ? str : "");
+	fflush (stdout);
+	if (idx == 4)
+		idx = 0;
+}
+
+/*
  * Find out how many columns an UTF-8 string occupies on the screen
  */
 int
@@ -244,7 +276,7 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[])
 	int table_width = 0;
 	char *line = NULL;
 	char *indent_str;
-	const char *not_set_str = _("not set");
+	const char *not_set_str = "--";
 	int i;
 	gboolean multiline = fields.flags & NMC_PF_FLAG_MULTILINE;
 	gboolean terse = fields.flags & NMC_PF_FLAG_TERSE;
@@ -439,7 +471,7 @@ nmc_is_nm_running (NmCli *nmc, GError **error)
 			g_set_error (error, 0, 0, "%s", nmc->return_text->str);
 		goto done;
 	}
- 
+
 	if (!org_freedesktop_DBus_name_has_owner (proxy, NM_DBUS_SERVICE, &has_owner, &err)) {
 		g_string_printf (nmc->return_text, _("Error: NameHasOwner request failed: %s"),
 		                 (err && err->message) ? err->message : _("(unknown)"));
diff --git a/cli/src/utils.h b/cli/src/utils.h
index a22ae144..81ce1633 100644
--- a/cli/src/utils.h
+++ b/cli/src/utils.h
@@ -30,6 +30,8 @@ int next_arg (int *argc, char ***argv);
 char *ssid_to_printable (const char *str, gsize len);
 char *nmc_ip4_address_as_string (guint32 ip, GError **error);
 char *nmc_ip6_address_as_string (const struct in6_addr *ip, GError **error);
+void nmc_terminal_erase_line (void);
+void nmc_terminal_show_progress (const char *str);
 int nmc_string_screen_width (const char *start, const char *end);
 void set_val_str (NmcOutputField fields_array[], guint32 index, const char *value);
 void set_val_arr (NmcOutputField fields_array[], guint32 index, const char **value);