package esmska.persistence;
import com.csvreader.CsvReader;
import esmska.data.*;
import esmska.data.Config.GlobalConfig;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import esmska.data.Contact;
import esmska.data.SMS;
import esmska.data.Gateway;
import esmska.data.Tuple;
import esmska.update.VersionFile;
import java.beans.IntrospectionException;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.JarURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.io.FileUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/** Import program data
*
* @author ripper
*/
public class ImportManager {
private static final Logger logger = Logger.getLogger(ImportManager.class.getName());
/** Disabled constructor */
private ImportManager() {
}
/** Import contacts from file */
public static ArrayList<Contact> importContacts(File file, ContactParser.ContactType type)
throws Exception {
logger.finer("Importing contacts of type " + type + " from file: " + file.getAbsolutePath());
ContactParser parser = new ContactParser(file, type);
parser.execute();
ArrayList<Contact> contacts = parser.get();
logger.finer("Imported " + contacts.size() + " contacts");
return parser.get();
}
/** Import sms queue from file */
public static ArrayList<SMS> importQueue(File file) throws Exception {
logger.finer("Importing queue from file: " + file.getAbsolutePath());
ArrayList<SMS> queue = new ArrayList<SMS>();
CsvReader reader = null;
try {
reader = new CsvReader(file.getPath(), ',', Charset.forName("UTF-8"));
reader.setUseComments(true);
while (reader.readRecord()) {
try {
String name = reader.get(0);
String number = reader.get(1);
String gateway = reader.get(2);
String text = reader.get(3);
String id = reader.get(4);
SMS sms = new SMS(number, text, gateway, name, id);
queue.add(sms);
} catch (Exception e) {
logger.severe("Invalid queue record: " + reader.getRawRecord());
throw e;
}
}
} finally {
if (reader != null) {
reader.close();
}
}
logger.finer("Imported " + queue.size() + " SMSs to queue");
return queue;
}
/** Import sms history from file */
public static ArrayList<History.Record> importHistory(File file) throws Exception {
logger.finer("Importing history from file: " + file.getAbsolutePath());
ArrayList<History.Record> history = new ArrayList<History.Record>();
CsvReader reader = null;
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG,
DateFormat.LONG, Locale.ROOT);
try {
reader = new CsvReader(file.getPath(), ',', Charset.forName("UTF-8"));
reader.setUseComments(true);
while (reader.readRecord()) {
try {
String dateString = reader.get(0);
String name = reader.get(1);
String number = reader.get(2);
String gateway = reader.get(3);
String text = reader.get(4);
String senderName = reader.get(5);
String senderNumber = reader.get(6);
String id = reader.get(7);
Date date = df.parse(dateString);
History.Record record = new History.Record(number, text, gateway,
name, senderNumber, senderName, date, id);
history.add(record);
} catch (Exception e) {
logger.severe("Invalid history record: " + reader.getRawRecord());
throw e;
}
}
} finally {
if (reader != null) {
reader.close();
}
}
logger.finer("Imported " + history.size() + " history records");
return history;
}
/** Import all gateways from jar resource
* @param resource jar absolute resource path where to look for gateways
* @throws IOException When there is problem accessing gateway directory or files
* @throws IntrospectionException When current JRE does not support JavaScript execution
*/
public static TreeSet<Gateway> importGateways(String resource) throws
IOException, IntrospectionException {
logger.finer("Importing gateways from resource: " + resource);
URL gatewayBase = ImportManager.class.getResource(resource);
if (gatewayBase == null || //resource doesn't exist
!gatewayBase.getProtocol().equals("jar")) { //resource not packed in jar
throw new IOException("Could not find jar gateway resource: " + resource);
}
HashSet<URL> gatewayURLs = new HashSet<URL>();
JarURLConnection con = (JarURLConnection) gatewayBase.openConnection();
for (Enumeration entries = con.getJarFile().entries(); entries.hasMoreElements();) {
JarEntry entry = (JarEntry) entries.nextElement();
String name = entry.getName();
String absoluteName = name.startsWith("/") ? name : ("/" + name);
if (absoluteName.startsWith(resource) && absoluteName.endsWith(".gateway")) {
gatewayURLs.add(new URL("jar:" + con.getJarFileURL() + "!/" + name));
}
}
return importGateways(gatewayURLs, false);
}
/** Import all gateways from directory
* @param directory directory where to look for gateways
* @param deleteOnFail whether to delete gateway files that fail to be loaded
* @throws IOException When there is problem accessing gateway directory or files
* @throws IntrospectionException When current JRE does not support JavaScript execution
*/
public static TreeSet<Gateway> importGateways(File directory, boolean deleteOnFail) throws
IOException, IntrospectionException {
logger.finer("Importing gateways from directory: " + directory.getAbsolutePath());
if (!directory.canRead() || !directory.isDirectory()) {
throw new IOException("Invalid gateway directory: " + directory.getAbsolutePath());
}
HashSet<URL> gatewayURLs = new HashSet<URL>();
File[] files = directory.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getAbsolutePath().endsWith(".gateway") && pathname.canRead();
}
});
for (File f : files) {
gatewayURLs.add(f.toURI().toURL());
}
return importGateways(gatewayURLs, deleteOnFail);
}
/** Get set of gateways from set of gateway URLs
* @param gatewayURLs set of gateway URLs (file or jar URLs)
* @param deleteOnFail whether to delete gateway files that fail to be loaded
* @return set of gateways
* @throws java.beans.IntrospectionException When current JRE does not support JavaScript execution
*/
private static TreeSet<Gateway> importGateways(Set<URL> gatewayURLs, boolean deleteOnFail) throws
IntrospectionException {
logger.log(Level.FINER, "Importing gateways from set of {0} URLs", gatewayURLs.size());
TreeSet<Gateway> gateways = new TreeSet<Gateway>();
for (URL gatewayURL : gatewayURLs) {
try {
Gateway gateway = new Gateway(gatewayURL);
//check that this gateway can be used in this program
if (Config.compareProgramVersions(Config.getLatestVersion(),
gateway.getMinProgramVersion()) < 0) {
logger.log(Level.INFO, "Gateway {0} requires program of version at least {1}, skipping.",
new Object[]{gateway.getName(), gateway.getMinProgramVersion()});
continue;
}
//check that some older version of the same gateway is not
//already present (can happen when renaming gateway file,
//see http://code.google.com/p/esmska/issues/detail?id=235)
if (gateways.contains(gateway)) {
for (Iterator<Gateway> it = gateways.iterator(); it.hasNext(); ) {
Gateway op = it.next();
if (op.equals(gateway) &&
op.getVersion().compareTo(gateway.getVersion()) < 0) {
it.remove();
}
}
}
gateways.add(gateway);
} catch (IOException ex) {
logger.log(Level.WARNING, "Problem accessing gateway resource: " +
gatewayURL.toExternalForm(), ex);
} catch (IntrospectionException ex) {
throw ex;
} catch (Exception ex) {
logger.log(Level.WARNING, "Ivalid gateway resource: " +
gatewayURL.toExternalForm(), ex);
if (deleteOnFail) {
try {
logger.log(Level.FINE, "Deleting invalid gateway file: {0}", gatewayURL);
File gwFile = new File(gatewayURL.toURI());
gwFile.delete();
} catch (Exception exc) {
logger.log(Level.WARNING, "Can't delete gateway: " + gatewayURL, exc);
}
}
}
}
logger.log(Level.FINER, "Imported {0} gateways", gateways.size());
return gateways;
}
/** Get set of deprecated gateways from jar resource
* @param resource jar absolute resource path with xml file
* @see #importDeprecatedGateways(java.net.URL)
*/
public static HashSet<DeprecatedGateway> importDeprecatedGateways(String resource) throws
IOException, SAXException {
logger.finer("Importing deprecated gateways from resource: " + resource);
URL deprecFile = ImportManager.class.getResource(resource);
if (deprecFile == null || //resource doesn't exist
!deprecFile.getProtocol().equals("jar")) { //resource not packed in jar
throw new IOException("Could not find jar gateway resource: " + resource);
}
return importDeprecatedGateways(deprecFile);
}
/** Get set of deprecated gateways from a file
* @param file xml containing description of deprecated gateways
* @see #importDeprecatedGateways(java.net.URL)
*/
public static HashSet<DeprecatedGateway> importDeprecatedGateways(File file)
throws IOException, SAXException {
logger.finer("Importing deprecated gateways from file: " + file.getAbsolutePath());
if (!file.canRead() || !file.isFile()) {
throw new IOException("Invalid deprecated gateway file: " + file.getAbsolutePath());
}
return importDeprecatedGateways(file.toURI().toURL());
}
/** Get set of deprecated gateways from URL
* @param url url to xml file (file or jar url) containing description
* of deprecated gateways
* @return set of deprecated gateways
* @throws IOException problem accessing jar resource
* @throws SAXException problem parsing the file
*/
public static HashSet<DeprecatedGateway> importDeprecatedGateways(URL url) throws
IOException, SAXException {
logger.finer("Importing deprecated gateways from URL: " + url);
HashSet<DeprecatedGateway> deprecated = new HashSet<DeprecatedGateway>();
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
Document doc = db.parse(url.openStream());
NodeList gateways = doc.getElementsByTagName(VersionFile.TAG_DEPRECATED_GATEWAY);
for (int i = 0; i < gateways.getLength(); i++) {
Node gateway = gateways.item(i);
String name = xpath.evaluate(VersionFile.TAG_NAME + "/text()", gateway);
String version = xpath.evaluate(VersionFile.TAG_VERSION + "/text()", gateway);
String reason = xpath.evaluate(VersionFile.TAG_REASON + "/text()", gateway);
DeprecatedGateway depr = new DeprecatedGateway(name, version, reason);
deprecated.add(depr);
}
} catch (ParserConfigurationException ex) {
throw new SAXException(ex);
} catch (XPathExpressionException ex) {
throw new SAXException(ex);
}
return deprecated;
}
/** Import keyring data from file.
* @param file File to import from.
* @throws Exception When some error occur during file processing.
*/
public static void importKeyring(File file) throws Exception {
logger.finer("Importing keyring from file: " + file.getAbsolutePath());
Keyring keyring = Keyring.getInstance();
CsvReader reader = null;
try {
reader = new CsvReader(file.getPath(), ',', Charset.forName("UTF-8"));
reader.setUseComments(true);
while (reader.readRecord()) {
try {
String gatewayName = reader.get(0);
String login = reader.get(1);
String password = Keyring.decrypt(reader.get(2));
Tuple<String, String> key = new Tuple<String, String>(login, password);
keyring.putKey(gatewayName, key);
} catch (Exception e) {
logger.severe("Invalid keyring record: " + reader.getRawRecord());
throw e;
}
}
} finally {
if (reader != null) {
reader.close();
}
}
logger.finer("Imported keyring");
}
/** Import configuration from system-wide config into Config defaults */
public static void importGlobalConfig(File file) throws IOException {
logger.finer("Importing global configuration from file: " + file.getAbsolutePath());
GlobalConfig globalConfig = GlobalConfig.getInstance();
Reader reader = null;
try {
reader = new InputStreamReader(new FileInputStream(file), "UTF-8");
Properties props = new Properties();
props.load(reader);
//checkProgramUpdates
String prop = props.getProperty("announceProgramUpdates");
if ("yes".equals(prop)) {
globalConfig.setAnnounceProgramUpdates(true);
} else if ("no".equals(prop)) {
globalConfig.setAnnounceProgramUpdates(false);
}
// else config default will aply
} finally {
if (reader != null) {
reader.close();
}
}
logger.finer("Imported global configuration");
}
/** Import all gateway properties from file. */
public static void importGatewayProperties(File file) throws IOException {
logger.log(Level.FINER, "Importing gateway properties from file: {0}", file.getAbsolutePath());
String jsonString = FileUtils.readFileToString(file, "UTF-8");
JSONObject obj = JSONObject.fromObject(jsonString);
// default signature
JSONObject jsonDefSig = (JSONObject) obj.get("default signature");
if (jsonDefSig != null) {
try {
Signature defSig = (Signature) JSONObject.toBean(jsonDefSig, Signature.class);
Signature.DEFAULT.setUserName(defSig.getUserName());
Signature.DEFAULT.setUserNumber(defSig.getUserNumber());
} catch (Exception ex) {
logger.log(Level.SEVERE, "Could not load default signature", ex);
}
}
// custom signatures
JSONArray jsonSignatures = (JSONArray) obj.get("signatures");
if (jsonSignatures != null) {
try {
for (int i = 0; i < jsonSignatures.size(); i++) {
JSONObject sigObj = jsonSignatures.getJSONObject(i);
Signature signature = (Signature) JSONObject.toBean(sigObj, Signature.class);
Signatures.getInstance().add(signature);
}
} catch (Exception ex) {
logger.log(Level.SEVERE, "Could not load user signatures", ex);
}
}
// GatewayConfig objects
JSONObject jsonGwConfigs = (JSONObject) obj.get("configs");
if (jsonGwConfigs != null) {
try {
for (Iterator it = jsonGwConfigs.keys(); it.hasNext(); ) {
String gwName = (String) it.next();
GatewayConfig gwConfig = (GatewayConfig) JSONObject.toBean(jsonGwConfigs.getJSONObject(gwName), GatewayConfig.class);
Gateway gateway = Gateways.getInstance().get(gwName);
if (gateway != null) {
gateway.setConfig(gwConfig);
}
}
} catch (Exception ex) {
logger.log(Level.SEVERE, "Could not load gateway configs", ex);
}
}
logger.finer("Imported gateway properties");
}
}