/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.withings.internal.api;
import static org.apache.commons.lang.StringUtils.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.io.IOUtils;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import oauth.signpost.OAuthConsumer;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.http.HttpParameters;
import oauth.signpost.signature.AuthorizationHeaderSigningStrategy;
import oauth.signpost.signature.HmacSha1MessageSigner;
/**
* This class holds the Withings Account Credentials. It can also load
* and store the credentials the either the {@code openhab.cfg} (legacy)
* or the {@code services/withings-ouath.cfg} file. Where to store values
* is decided whether {@code openhab.cfg} contains already keys
* starting with {@code withings-oauth} or not.
*
* Note: We'd decided to reimplement {@code store} and {@code load} rather
* than using the {@link java.util.Properties} class because our keys can
* contain ":" which unfortunately is a reserved delimiter in
* {@link java.util.Properties}.
*
* @author Thomas.Eichstaedt-Engelen
* @since 1.7.0
*/
public final class WithingsAccount {
private static final Logger logger = LoggerFactory.getLogger(WithingsAccount.class);
private static final String SERVICE_NAME = "withings-oauth";
private static final String CONFIG_DIR = "." + File.separator + "configurations";
private static final String CONTENT_DIR = CONFIG_DIR + File.separator + "services";
private String accountId;
String userId;
String consumerKey;
String consumerSecret;
String token;
String tokenSecret;
OAuthConsumer consumer;
ServiceRegistration<?> clientServiceRegistration;
public WithingsAccount(String accountId, String consumerKey, String consumerSecret) {
this.accountId = accountId;
this.consumerKey = consumerKey;
this.consumerSecret = consumerSecret;
}
public boolean isValid() {
return isNotBlank(consumerKey) && isNotBlank(consumerSecret);
}
public void registerAccount(BundleContext bundleContext) {
Dictionary<String, Object> serviceProperties = new Hashtable<String, Object>();
serviceProperties.put("withings.accountid", accountId);
serviceProperties.put("withings.userid", userId);
if (this.clientServiceRegistration != null) {
this.clientServiceRegistration.unregister();
}
this.clientServiceRegistration = bundleContext.registerService(WithingsApiClient.class.getName(),
new WithingsApiClient(consumer, userId), serviceProperties);
}
public boolean isAuthenticated() {
return isNotBlank(userId) && isNotBlank(consumerKey) && isNotBlank(consumerSecret) && isNotBlank(token)
&& isNotBlank(tokenSecret);
}
public OAuthConsumer createConsumer() {
consumer = new DefaultOAuthConsumer(consumerKey, consumerSecret);
consumer.setSigningStrategy(new AuthorizationHeaderSigningStrategy());
consumer.setMessageSigner(new HmacSha1MessageSigner());
return consumer;
}
public void setOuathToken(String token, String tokenSecret) {
this.token = token;
this.tokenSecret = tokenSecret;
consumer.setTokenWithSecret(token, tokenSecret);
consumer.setAdditionalParameters(new HttpParameters());
}
public void unregisterAccount() {
if (this.clientServiceRegistration != null) {
this.clientServiceRegistration.unregister();
}
}
public void persist() {
File file = null;
String prefix = "";
try {
// store properties either to openhab.cfg (legacy) or
// services/withings-oauth.cfg
if (isLegacyConfiguration()) {
file = new File(CONFIG_DIR + File.separator + "openhab.cfg");
// in legacy case each property has to be prefixed with
prefix = SERVICE_NAME + ":";
} else {
file = new File(CONTENT_DIR + File.separator + SERVICE_NAME + ".cfg");
}
if (!file.exists()) {
file.getParentFile().mkdirs();
file.createNewFile();
}
// if an account different from the default account is used
// it get's prefixed separated by "."
if (!WithingsAuthenticator.DEFAULT_ACCOUNT_ID.equals(accountId)) {
prefix += accountId + ".";
}
Map<String, String> config = load(file);
config.put(prefix + "userid", userId);
config.put(prefix + "token", token);
config.put(prefix + "tokensecret", tokenSecret);
store(config, file);
logger.debug("Saved WithingsAccount to file '{}'.", file.getAbsolutePath());
} catch (IOException ioe) {
logger.error("Couldn't write WithingsAccount to file '{}'.", file.getAbsolutePath());
}
}
/**
* Checks whether the Binding configuration has been store to
* openhab.cfg rather than the /services/withings-oauth.cfg dir
*
* @return true, if there is a configuration key like "withings-oauth"
* in openhab.cfg and false in all other cases.
*/
private boolean isLegacyConfiguration() {
File file = new File(CONFIG_DIR + File.separator + "openhab.cfg");
try {
if (file.exists()) {
Map<String, String> config = load(file);
for (Object key : config.keySet()) {
if (key.toString().startsWith(SERVICE_NAME)) {
return true;
}
}
}
} catch (IOException e) {
logger.warn("Couldn't open Configuration File '{}'", file.getAbsolutePath());
}
return false;
}
private Map<String, String> load(File file) throws IOException {
Map<String, String> config = new LinkedHashMap<String, String>();
FileInputStream is = new FileInputStream(file);
List<String> lines = IOUtils.readLines(is);
for (String line : lines) {
String[] parameterPair = line.split("=");
if (parameterPair.length == 2) {
config.put(parameterPair[0].trim(), parameterPair[1].trim());
} else {
config.put(parameterPair[0], "");
}
}
IOUtils.closeQuietly(is);
return config;
}
private Map<String, String> store(Map<String, String> config, File file) throws IOException {
FileOutputStream os = new FileOutputStream(file);
List<String> lines = new ArrayList<String>();
for (Entry<String, String> line : config.entrySet()) {
String value = isBlank(line.getValue()) ? "" : "=" + line.getValue();
lines.add(line.getKey() + value);
}
IOUtils.writeLines(lines, System.getProperty("line.separator"), os);
IOUtils.closeQuietly(os);
return config;
}
@Override
public String toString() {
return "WithingsAccount [userId=" + userId + ", token=" + token + ", tokenSecret=" + tokenSecret + "]";
}
}