shill: vpn: Configure VPN services from newly-pushed profiles

When a new profile is pushed, look for VPN entries and configure
services for them.

BUG=chromium-os:28157
TEST=New unit test

Change-Id: I571c791710e172f1385842867342ee96d8eb4eb0
Reviewed-on: https://gerrit.chromium.org/gerrit/19871
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/vpn_provider.cc b/vpn_provider.cc
index 4f4ae2b..5387cc9 100644
--- a/vpn_provider.cc
+++ b/vpn_provider.cc
@@ -7,13 +7,17 @@
 #include <algorithm>
 
 #include <base/logging.h>
+#include <base/string_util.h>
 #include <chromeos/dbus/service_constants.h>
 
 #include "shill/error.h"
 #include "shill/manager.h"
 #include "shill/openvpn_driver.h"
+#include "shill/profile.h"
+#include "shill/store_interface.h"
 #include "shill/vpn_service.h"
 
+using std::set;
 using std::string;
 using std::vector;
 
@@ -50,36 +54,11 @@
   }
 
   // Find a service in the provider list which matches these parameters.
-  for (vector<VPNServiceRefPtr>::const_iterator it = services_.begin();
-       it != services_.end();
-       ++it) {
-    if (type == (*it)->driver()->GetProviderType() &&
-        (*it)->GetStorageIdentifier() == storage_id) {
-      return *it;
-    }
+  VPNServiceRefPtr service = FindService(type, storage_id);
+  if (service == NULL) {
+    service = CreateService(type, storage_id, args, error);
   }
 
-  scoped_ptr<VPNDriver> driver;
-  if (type == flimflam::kProviderOpenVpn) {
-    driver.reset(new OpenVPNDriver(
-        control_interface_, dispatcher_, metrics_, manager_,
-        manager_->device_info(), manager_->glib(), args));
-  } else {
-    Error::PopulateAndLog(
-        error, Error::kNotSupported, "Unsupported VPN type: " + type);
-    return NULL;
-  }
-
-  VPNServiceRefPtr service = new VPNService(
-      control_interface_, dispatcher_, metrics_, manager_, driver.release());
-  service->set_storage_id(storage_id);
-  service->InitDriverPropertyStore();
-  string name = args.LookupString(flimflam::kProviderNameProperty, "");
-  if (!name.empty()) {
-    service->set_friendly_name(name);
-  }
-  services_.push_back(service);
-  manager_->RegisterService(service);
   return service;
 }
 
@@ -104,4 +83,89 @@
   }
 }
 
+void VPNProvider::CreateServicesFromProfile(ProfileRefPtr profile) {
+  const StoreInterface *storage = profile->GetConstStorage();
+  set<string> groups =
+      storage->GetGroupsWithKey(flimflam::kProviderTypeProperty);
+  for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
+    if (!StartsWithASCII(*it, "vpn_", false)) {
+      continue;
+    }
+
+    string type;
+    if (!storage->GetString(*it, flimflam::kProviderTypeProperty, &type)) {
+      LOG(ERROR) << "Group " << *it << " is missing the "
+                 << flimflam::kProviderTypeProperty << " property.";
+      continue;
+    }
+
+    VPNServiceRefPtr service = FindService(type, *it);
+    if (service != NULL) {
+      // If the service already exists, it does not need to be configured,
+      // since PushProfile would have already called ConfigureService on it.
+      VLOG(2) << "Service already exists " << *it;
+      continue;
+    }
+
+    // Create a service with an empty |args| parameter.  We will populate
+    // the service with properties using ConfigureService() below.
+    KeyValueStore args;
+    Error error;
+    service = CreateService(type, *it, args, &error);
+
+    if (service == NULL) {
+      LOG(ERROR) << "Could not create service for " << *it;
+      continue;
+    }
+
+    if (!profile->ConfigureService(service)) {
+      LOG(ERROR) << "Could not configure service for " << *it;
+      continue;
+    }
+  }
+}
+
+VPNServiceRefPtr VPNProvider::CreateService(const string &type,
+                                            const string &storage_id,
+                                            const KeyValueStore &args,
+                                            Error *error) {
+  scoped_ptr<VPNDriver> driver;
+  if (type == flimflam::kProviderOpenVpn) {
+    driver.reset(new OpenVPNDriver(
+        control_interface_, dispatcher_, metrics_, manager_,
+        manager_->device_info(), manager_->glib(), args));
+  } else {
+    Error::PopulateAndLog(
+        error, Error::kNotSupported, "Unsupported VPN type: " + type);
+    return NULL;
+  }
+
+  VPNServiceRefPtr service = new VPNService(
+      control_interface_, dispatcher_, metrics_, manager_, driver.release());
+  service->set_storage_id(storage_id);
+  service->InitDriverPropertyStore();
+  string name = args.LookupString(flimflam::kProviderNameProperty, "");
+  if (!name.empty()) {
+    service->set_friendly_name(name);
+  }
+  services_.push_back(service);
+  manager_->RegisterService(service);
+
+  return service;
+}
+
+VPNServiceRefPtr VPNProvider::FindService(const std::string &type,
+                                          const std::string &storage_id) {
+  for (vector<VPNServiceRefPtr>::const_iterator it = services_.begin();
+       it != services_.end();
+       ++it) {
+    if (type == (*it)->driver()->GetProviderType() &&
+        storage_id == (*it)->GetStorageIdentifier()) {
+      return *it;
+    }
+  }
+
+  return NULL;
+}
+
 }  // namespace shill