shill: Copy set_apn_helper.c from flimflam to shill and build it.

This generated binary is still unused. The source was developed by
ers@chromium.org and is copied as-is with slight copyright change.  A
follow up ebuild change will install the shim and another one will
modify bin/set_apn to use it.

BUG=chromium-os:35363
TEST=build shill

Change-Id: I5cb4bddc763db28961632abd241197d4ff0a2483
Reviewed-on: https://gerrit.chromium.org/gerrit/36335
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Ready: Darin Petkov <petkov@chromium.org>
diff --git a/Makefile b/Makefile
index 47f409a..d2558b4 100644
--- a/Makefile
+++ b/Makefile
@@ -26,6 +26,7 @@
 NSS_GET_CERT_PC_DEPS = $(COMMON_PC_DEPS) nss
 OPENVPN_SCRIPT_PC_DEPS = $(COMMON_PC_DEPS) dbus-c++-1
 PPPD_PLUGIN_PC_DEPS = $(COMMON_PC_DEPS) dbus-c++-1
+SET_APN_HELPER_PC_DEPS = dbus-1
 INCLUDE_DIRS = \
 	-iquote.. \
 	-iquote $(BUILDDIR) \
@@ -33,6 +34,7 @@
 		$(NSS_GET_CERT_PC_DEPS) \
 		$(OPENVPN_SCRIPT_PC_DEPS) \
 		$(PPPD_PLUGIN_PC_DEPS) \
+		$(SET_APN_HELPER_PC_DEPS) \
 		$(SHILL_PC_DEPS))
 SHILL_LIBS = \
 	-lbootstat \
@@ -42,9 +44,10 @@
 	-lminijail \
 	-lnl \
 	$(shell $(PKG_CONFIG) --libs $(SHILL_PC_DEPS))
-PPPD_PLUGIN_LIBS = $(shell $(PKG_CONFIG) --libs $(PPPD_PLUGIN_PC_DEPS))
 NSS_GET_CERT_LIBS = $(shell $(PKG_CONFIG) --libs $(NSS_GET_CERT_PC_DEPS))
 OPENVPN_SCRIPT_LIBS = $(shell $(PKG_CONFIG) --libs $(OPENVPN_SCRIPT_PC_DEPS))
+PPPD_PLUGIN_LIBS = $(shell $(PKG_CONFIG) --libs $(PPPD_PLUGIN_PC_DEPS))
+SET_APN_HELPER_LIBS = $(shell $(PKG_CONFIG) --libs $(SET_APN_HELPER_PC_DEPS))
 TEST_LIBS = $(SHILL_LIBS) $(NSS_GET_CERT_LIBS) -lgmock -lgtest
 
 DBUS_BINDINGS_DIR = dbus_bindings
@@ -426,15 +429,6 @@
 	wimax_unittest.o \
 	)
 
-PPPD_PLUGIN_OBJS = $(addprefix $(BUILD_SHIMS_DIR)/, \
-	c_ppp.o \
-	environment.o \
-	ppp.o \
-	pppd_plugin.o \
-	task_proxy.o \
-	)
-PPPD_PLUGIN_SO = $(BUILD_SHIMS_DIR)/shill-pppd-plugin.so
-
 NSS_GET_CERT_OBJS = $(BUILD_SHIMS_DIR)/certificates.o
 NSS_GET_CERT_MAIN_OBJ = $(BUILD_SHIMS_DIR)/nss_get_cert.o
 NSS_GET_CERT_BIN = $(BUILD_SHIMS_DIR)/nss-get-cert
@@ -446,6 +440,18 @@
 OPENVPN_SCRIPT_MAIN_OBJ = $(BUILD_SHIMS_DIR)/openvpn_script.o
 OPENVPN_SCRIPT_BIN = $(BUILD_SHIMS_DIR)/openvpn-script
 
+PPPD_PLUGIN_OBJS = $(addprefix $(BUILD_SHIMS_DIR)/, \
+	c_ppp.o \
+	environment.o \
+	ppp.o \
+	pppd_plugin.o \
+	task_proxy.o \
+	)
+PPPD_PLUGIN_SO = $(BUILD_SHIMS_DIR)/shill-pppd-plugin.so
+
+SET_APN_HELPER_MAIN_OBJ = $(BUILD_SHIMS_DIR)/set_apn_helper.o
+SET_APN_HELPER_BIN = $(BUILD_SHIMS_DIR)/set-apn-helper
+
 WPA_SUPPLICANT_CONF = $(BUILD_SHIMS_DIR)/wpa_supplicant.conf
 
 OBJS = \
@@ -454,6 +460,7 @@
 	$(OPENVPN_SCRIPT_MAIN_OBJ) \
 	$(OPENVPN_SCRIPT_OBJS) \
 	$(PPPD_PLUGIN_OBJS) \
+	$(SET_APN_HELPER_MAIN_OBJ) \
 	$(SHILL_MAIN_OBJ) \
 	$(SHILL_OBJS) \
 	$(TEST_OBJS)
@@ -466,6 +473,7 @@
 	$(NSS_GET_CERT_BIN) \
 	$(OPENVPN_SCRIPT_BIN) \
 	$(PPPD_PLUGIN_SO) \
+	$(SET_APN_HELPER_BIN) \
 	$(WPA_SUPPLICANT_CONF)
 
 $(BUILD_DBUS_BINDINGS_SHIMS_DIR)/flimflam-task.xml: \
@@ -495,11 +503,6 @@
 $(SHILL_BIN): $(SHILL_MAIN_OBJ) $(SHILL_LIB)
 	$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(SHILL_LIBS) -o $@
 
-$(PPPD_PLUGIN_OBJS): $(DBUS_PROXY_BINDINGS)
-
-$(PPPD_PLUGIN_SO): $(PPPD_PLUGIN_OBJS)
-	$(CXX) $(LDFLAGS) -shared $^ $(PPPD_PLUGIN_LIBS) -o $@
-
 $(NSS_GET_CERT_BIN): $(NSS_GET_CERT_MAIN_OBJ) $(NSS_GET_CERT_OBJS) $(SHILL_LIB)
 	$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(NSS_GET_CERT_LIBS) -o $@
 
@@ -509,6 +512,14 @@
 $(OPENVPN_SCRIPT_BIN): $(OPENVPN_SCRIPT_MAIN_OBJ) $(OPENVPN_SCRIPT_OBJS)
 	$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(OPENVPN_SCRIPT_LIBS) -o $@
 
+$(PPPD_PLUGIN_OBJS): $(DBUS_PROXY_BINDINGS)
+
+$(PPPD_PLUGIN_SO): $(PPPD_PLUGIN_OBJS)
+	$(CXX) $(LDFLAGS) -shared $^ $(PPPD_PLUGIN_LIBS) -o $@
+
+$(SET_APN_HELPER_BIN): $(SET_APN_HELPER_MAIN_OBJ)
+	$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(SET_APN_HELPER_LIBS) -o $@
+
 $(WPA_SUPPLICANT_CONF): shims/wpa_supplicant.conf.in
 	sed s,@libdir@,$(LIBDIR), $^ > $@
 
diff --git a/shims/set_apn_helper.c b/shims/set_apn_helper.c
new file mode 100644
index 0000000..53e4957
--- /dev/null
+++ b/shims/set_apn_helper.c
@@ -0,0 +1,132 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Shim to set Cellular.APN property for a service. This exists because
+// dbus-send isn't capable of sending anything with nested containers, such as a
+// variant that is a dict.
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+
+#define CONNMAN_SERVICE			"org.chromium.flimflam"
+
+#define CONNMAN_SERVICE_INTERFACE	CONNMAN_SERVICE ".Service"
+
+static void append(DBusMessageIter *dict,
+		   const char *key,
+		   const char *value)
+{
+	DBusMessageIter entry;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+							NULL, &entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+int main(int argc, char *argv[])
+{
+	DBusConnection *conn;
+	DBusError error;
+	DBusMessage *msg;
+	DBusMessageIter iter, value, dict;
+	const char *property;
+	char *args;
+	char *cp;
+	char *argname;
+	char *argvalue;
+	int done;
+
+
+	if (argc < 4) {
+		fprintf(stderr,
+	"Usage: %s <service-dbus-path> <property-name> <apn-args>\n",
+			argv[0]);
+		return 1;
+	}
+
+	dbus_error_init(&error);
+
+	conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	if (conn == NULL) {
+		if (dbus_error_is_set(&error) == TRUE) {
+			fprintf(stderr, "%s\n", error.message);
+			dbus_error_free(&error);
+		} else
+			fprintf(stderr, "Failed to get on system bus\n");
+		return 1;
+	}
+
+	msg = dbus_message_new_method_call(CONNMAN_SERVICE,
+					   argv[1],
+					   CONNMAN_SERVICE_INTERFACE,
+					   "SetProperty");
+	if (msg == NULL) {
+		dbus_connection_unref(conn);
+		fprintf(stderr, "Failed to allocate method call\n");
+		return 1;
+	}
+
+	dbus_message_set_no_reply(msg, TRUE);
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	property = argv[2];
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+			DBUS_TYPE_ARRAY_AS_STRING
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &value);
+
+	dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	args = argv[3];
+	cp = args;
+	done = 0;
+
+	while (!done) {
+		argname = cp;
+		cp = strchr(cp, ',');
+		if (cp == NULL) {
+			fprintf(stderr, "Badly formed argument string\n");
+			dbus_message_unref(msg);
+			dbus_connection_unref(conn);
+			return 1;
+		}
+		*cp++ = '\0';
+		argvalue = cp;
+		cp = strchr(cp, ',');
+		if (cp == NULL)
+			done = 1;
+		else
+			*cp++ = '\0';
+		append(&dict, argname, argvalue);
+	}
+
+	dbus_message_iter_close_container(&iter, &dict);
+	dbus_message_iter_close_container(&dict, &value);
+
+	dbus_connection_send(conn, msg, NULL);
+
+	dbus_message_unref(msg);
+	dbus_connection_unref(conn);
+
+	return 0;
+}