diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java index 498b68ac..d5d34876 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java @@ -8,6 +8,7 @@ import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicHeader; import java.io.BufferedReader; import java.io.IOException; @@ -24,12 +25,18 @@ public class GetTask extends AsyncTask { public static final String URI_VERSION = "/rest/version"; + /** + * params[0] Syncthing hostname + * params[1] URI to call + * params[2] Syncthing API key + */ @Override - protected String doInBackground(String... uri) { - String fullUri = uri[0] + uri[1]; + protected String doInBackground(String... params) { + String fullUri = params[0] + params[1]; Log.i(TAG, "Sending GET request to " + fullUri); HttpClient httpclient = new DefaultHttpClient(); HttpGet get = new HttpGet(fullUri); + get.addHeader(new BasicHeader("X-API-Key", params[2])); String responseString = null; try { HttpResponse response = httpclient.execute(get); diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/PostTask.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/PostTask.java index 74743504..ce1534ce 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/PostTask.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/PostTask.java @@ -7,6 +7,7 @@ import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicHeader; import java.io.IOException; @@ -19,12 +20,18 @@ public class PostTask extends AsyncTask { public static final String URI_SHUTDOWN = "/rest/shutdown"; + /** + * params[0] Syncthing hostname + * params[1] URI to call + * params[2] Syncthing API key + */ @Override - protected Void doInBackground(String... uri) { - String fullUri = uri[0] + uri[1]; + protected Void doInBackground(String... params) { + String fullUri = params[0] + params[1]; Log.i(TAG, "Sending POST request to " + fullUri); HttpClient httpclient = new DefaultHttpClient(); HttpPost post = new HttpPost(fullUri); + post.addHeader(new BasicHeader("X-API-Key", params[2])); String responseString = null; try { HttpResponse response = httpclient.execute(post); diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java index 10acb232..8faaa77d 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java @@ -16,8 +16,11 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener { private String mUrl; - public RestApi(String url) { + private String mApiKey; + + public RestApi(String url, String apiKey) { mUrl = url; + mApiKey = apiKey; } /** @@ -35,7 +38,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener { mVersion = version; Log.i(TAG, "Syncthing version is " + mVersion); } - }.execute(mUrl, GetTask.URI_VERSION); + }.execute(mUrl, GetTask.URI_VERSION, mApiKey); } /** @@ -49,7 +52,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener { * Stops syncthing. You should probably use SyncthingService.stopService() instead. */ public void shutdown() { - new PostTask().execute(mUrl, PostTask.URI_SHUTDOWN); + new PostTask().execute(mUrl, PostTask.URI_SHUTDOWN, mApiKey); } } diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java index 473f13c5..c294baa8 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java @@ -33,11 +33,13 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.LinkedList; +import java.util.Random; import java.util.concurrent.locks.ReentrantLock; 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; @@ -301,12 +303,14 @@ public class SyncthingService extends Service { updateConfig(); String syncthingUrl = null; + String apiKey = null; try { DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document d = db.parse(getConfigFile()); - Element options = (Element) - d.getDocumentElement().getElementsByTagName("gui").item(0); - syncthingUrl = options.getElementsByTagName("address").item(0).getTextContent(); + Element gui = (Element) d.getDocumentElement() + .getElementsByTagName("gui").item(0); + syncthingUrl = gui.getElementsByTagName("address").item(0).getTextContent(); + apiKey = gui.getElementsByTagName("apikey").item(0).getTextContent(); } catch (SAXException e) { throw new RuntimeException("Failed to read gui url, aborting", e); @@ -318,7 +322,7 @@ public class SyncthingService extends Service { throw new RuntimeException("Failed to read gui url, aborting", e); } finally { - mApi = new RestApi("http://" + syncthingUrl); + mApi = new RestApi("http://" + syncthingUrl, apiKey); Log.i(TAG, "Web GUI will be available at " + mApi.getUrl()); registerOnWebGuiAvailableListener(mApi); } @@ -379,12 +383,28 @@ public class SyncthingService extends Service { Log.i(TAG, "Checking for needed config updates"); boolean changed = false; DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - Document d = db.parse(getConfigFile()); + Document doc = db.parse(getConfigFile()); + Element options = (Element) doc.getDocumentElement() + .getElementsByTagName("options").item(0); + Element gui = (Element) doc.getDocumentElement() + .getElementsByTagName("gui").item(0); + // Create an API key if it does not exist. + if (gui.getElementsByTagName("apikey").getLength() == 0) { + Log.i(TAG, "Initializing API key with random string"); + char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray(); + StringBuilder sb = new StringBuilder(); + Random random = new Random(); + for (int i = 0; i < 20; i++) { + sb.append(chars[random.nextInt(chars.length)]); + } + Element apiKey = doc.createElement("apikey"); + apiKey.setTextContent(sb.toString()); + gui.appendChild(apiKey); + changed = true; + } // Hardcode default globalAnnounceServer ip. - Element options = (Element) - d.getDocumentElement().getElementsByTagName("options").item(0); Element globalAnnounceServer = (Element) options.getElementsByTagName("globalAnnounceServer").item(0); if (globalAnnounceServer.getTextContent().equals("announce.syncthing.net:22025")) { @@ -394,7 +414,7 @@ public class SyncthingService extends Service { } // Set ignorePerms attribute. - NodeList repos = d.getDocumentElement().getElementsByTagName("repository"); + NodeList repos = doc.getDocumentElement().getElementsByTagName("repository"); for (int i = 0; i < repos.getLength(); i++) { Element r = (Element) repos.item(i); if (!r.hasAttribute("ignorePerms") || @@ -410,7 +430,7 @@ public class SyncthingService extends Service { Log.i(TAG, "Writing updated config back to file"); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); - DOMSource domSource = new DOMSource(d); + DOMSource domSource = new DOMSource(doc); StreamResult streamResult = new StreamResult(getConfigFile()); transformer.transform(domSource, streamResult); }