From 85daca6eb73c67c7f9900f0818bd8399cc43620b Mon Sep 17 00:00:00 2001 From: Catfriend1 Date: Thu, 11 Oct 2018 22:42:34 +0200 Subject: [PATCH] Preserve xml encoding on read, parse and save (fixes #88) (#89) * Preserve xml encoding on read, parse and save (fixes #88) --- .../syncthingandroid/util/ConfigXml.java | 65 ++++++++++++++----- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java b/app/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java index f3eb9353..90086e33 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java @@ -12,16 +12,15 @@ import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.service.Constants; import com.nutomic.syncthingandroid.service.SyncthingRunnable; -import org.mindrot.jbcrypt.BCrypt; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; - -import java.io.File; +import java.io.ByteArrayOutputStream; import java.io.InputStreamReader; import java.io.IOException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.util.Locale; @@ -33,12 +32,21 @@ import javax.inject.Inject; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import org.mindrot.jbcrypt.BCrypt; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; +import org.xml.sax.InputSource; + /** * Provides direct access to the config.xml file in the file system. * This class should only be used if the syncthing API is not available (usually during startup). @@ -95,14 +103,19 @@ public class ConfigXml { throw new OpenConfigException(); } try { + FileInputStream inputStream = new FileInputStream(mConfigFile); + InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); + InputSource inputSource = new InputSource(inputStreamReader); + inputSource.setEncoding("UTF-8"); DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - Log.d(TAG, "Trying to read '" + mConfigFile + "'"); - mConfig = db.parse(mConfigFile); + Log.d(TAG, "Parsing config file '" + mConfigFile + "'"); + mConfig = db.parse(inputSource); + inputStream.close(); + Log.i(TAG, "Successfully parsed config file"); } catch (SAXException | ParserConfigurationException | IOException e) { - Log.w(TAG, "Cannot read '" + mConfigFile + "'", e); + Log.w(TAG, "Failed to parse config file '" + mConfigFile + "'", e); throw new OpenConfigException(); } - Log.i(TAG, "Loaded Syncthing config file"); } public URL getWebGuiUrl() { @@ -339,14 +352,34 @@ public class ConfigXml { Log.i(TAG, "Writing updated config file"); File mConfigTempFile = Constants.getConfigTempFile(mContext); try { + // Write XML header. + FileOutputStream fileOutputStream = new FileOutputStream(mConfigTempFile); + fileOutputStream.write("".getBytes("UTF-8")); + + // Prepare Object-to-XML transform. TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); - DOMSource domSource = new DOMSource(mConfig); - StreamResult streamResult = new StreamResult(mConfigTempFile); - transformer.transform(domSource, streamResult); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-16"); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.setOutputProperty(OutputKeys.INDENT, "no"); + + // Output XML body. + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + StreamResult streamResult = new StreamResult(new OutputStreamWriter(byteArrayOutputStream, "UTF-8")); + transformer.transform(new DOMSource(mConfig), streamResult); + byte[] outputBytes = byteArrayOutputStream.toByteArray(); + fileOutputStream.write(outputBytes); + fileOutputStream.close(); } catch (TransformerException e) { - Log.w(TAG, "Failed to save temporary config file", e); + Log.w(TAG, "Failed to transform object to xml and save temporary config file", e); return; + } catch (FileNotFoundException e) { + Log.w(TAG, "Failed to save temporary config file, FileNotFoundException", e); + } catch (UnsupportedEncodingException e) { + Log.w(TAG, "Failed to save temporary config file, UnsupportedEncodingException", e); + } catch (IOException e) { + Log.w(TAG, "Failed to save temporary config file, IOException", e); } try { mConfigTempFile.renameTo(mConfigFile);