|
|
@@ -0,0 +1,296 @@
|
|
|
+package info.knacki.pass.git;
|
|
|
+
|
|
|
+import android.arch.core.util.Function;
|
|
|
+import android.os.AsyncTask;
|
|
|
+
|
|
|
+import java.io.BufferedReader;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStreamReader;
|
|
|
+import java.io.StringReader;
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
+import java.net.Authenticator;
|
|
|
+import java.net.MalformedURLException;
|
|
|
+import java.net.PasswordAuthentication;
|
|
|
+import java.net.URL;
|
|
|
+import java.net.URLConnection;
|
|
|
+import java.nio.charset.Charset;
|
|
|
+import java.security.InvalidKeyException;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.InvalidPropertiesFormatException;
|
|
|
+import java.util.logging.Level;
|
|
|
+import java.util.logging.Logger;
|
|
|
+import java.util.zip.InflaterInputStream;
|
|
|
+
|
|
|
+import info.knacki.pass.git.entities.GitCommit;
|
|
|
+import info.knacki.pass.git.entities.GitObject;
|
|
|
+import info.knacki.pass.git.entities.GitRef;
|
|
|
+import info.knacki.pass.git.entities.Util;
|
|
|
+import info.knacki.pass.settings.SettingsManager;
|
|
|
+
|
|
|
+class DumbGitInterface implements GitInterface {
|
|
|
+ protected final SettingsManager.Git fConfig;
|
|
|
+ protected GitRef[] fRefsCache = null;
|
|
|
+ private static final Logger log = Logger.getLogger(DumbGitInterface.class.getName());
|
|
|
+
|
|
|
+ DumbGitInterface(SettingsManager.Git config) {
|
|
|
+ fConfig = config;
|
|
|
+ }
|
|
|
+
|
|
|
+ void protoInflateGet(final URL url, final OnResponseListener<byte[]> callback) {
|
|
|
+ AsyncTask<Void, Void, Integer> task = new AsyncTask<Void, Void, Integer>() {
|
|
|
+ @Override
|
|
|
+ protected Integer doInBackground(Void... voids) {
|
|
|
+ try {
|
|
|
+ log.log(Level.INFO, "fetching " +url.toString());
|
|
|
+ URLConnection httpClient = url.openConnection();
|
|
|
+ if (fConfig.HasAuthentification()) {
|
|
|
+ Authenticator.setDefault(new Authenticator() {
|
|
|
+ @Override
|
|
|
+ protected PasswordAuthentication getPasswordAuthentication() {
|
|
|
+ return new PasswordAuthentication(fConfig.GetUser(), fConfig.GetPassword().toCharArray());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ InflaterInputStream in = new InflaterInputStream(httpClient.getInputStream());
|
|
|
+ ArrayList<byte[]> fullBuffer = new ArrayList<>();
|
|
|
+ int totalRead = 0;
|
|
|
+ byte[] buffer = new byte[1024];
|
|
|
+ int currentRead;
|
|
|
+ while ((currentRead = in.read(buffer, 0, 1024)) != 0) {
|
|
|
+ fullBuffer.add(buffer);
|
|
|
+ buffer = new byte[1024];
|
|
|
+ totalRead += currentRead;
|
|
|
+ if (currentRead < 1024)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ in.close();
|
|
|
+ buffer = new byte[totalRead];
|
|
|
+ int i =0;
|
|
|
+ for (byte []currentBuf: fullBuffer) {
|
|
|
+ for (int j =0; j < currentBuf.length && i*1024+j < totalRead; ++j) {
|
|
|
+ buffer[i*1024 +j] = currentBuf[j];
|
|
|
+ }
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ callback.onResponse(buffer);
|
|
|
+ }
|
|
|
+ catch (MalformedURLException e) {
|
|
|
+ log.log(Level.WARNING, e.getMessage(), e);
|
|
|
+ callback.onError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
|
|
|
+ }
|
|
|
+ catch (IOException e) {
|
|
|
+ log.log(Level.WARNING, e.getMessage(), e);
|
|
|
+ callback.onError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ task.execute();
|
|
|
+ }
|
|
|
+
|
|
|
+ void protoGet(final URL url, final OnResponseListener<String> callback) {
|
|
|
+ AsyncTask<Void, Void, Integer> task = new AsyncTask<Void, Void, Integer>() {
|
|
|
+ @Override
|
|
|
+ protected Integer doInBackground(Void... voids) {
|
|
|
+ try {
|
|
|
+ log.log(Level.INFO, "fetching " +url.toString());
|
|
|
+ URLConnection httpClient = url.openConnection();
|
|
|
+ if (fConfig.HasAuthentification()) {
|
|
|
+ Authenticator.setDefault(new Authenticator() {
|
|
|
+ @Override
|
|
|
+ protected PasswordAuthentication getPasswordAuthentication() {
|
|
|
+ return new PasswordAuthentication(fConfig.GetUser(), fConfig.GetPassword().toCharArray());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ BufferedReader in = new BufferedReader(new InputStreamReader(httpClient.getInputStream()));
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ String buf;
|
|
|
+ while((buf = in.readLine()) != null)
|
|
|
+ {
|
|
|
+ if (sb.length() != 0)
|
|
|
+ sb.append("\n");
|
|
|
+ sb.append(buf);
|
|
|
+ }
|
|
|
+ in.close();
|
|
|
+ callback.onResponse(sb.toString());
|
|
|
+ }
|
|
|
+ catch (MalformedURLException e) {
|
|
|
+ log.log(Level.WARNING, e.getMessage(), e);
|
|
|
+ callback.onError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
|
|
|
+ }
|
|
|
+ catch (IOException e) {
|
|
|
+ log.log(Level.WARNING, e.getMessage(), e);
|
|
|
+ callback.onError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ task.execute();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void GetRefs(final OnResponseListener<GitRef[]> callback) {
|
|
|
+ if (fRefsCache != null) {
|
|
|
+ callback.onResponse(fRefsCache);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ URL url = new URL(fConfig.GetUrl() + "/info/REFS");
|
|
|
+ protoGet(url, new OnResponseListener<String>() {
|
|
|
+ @Override
|
|
|
+ public void onResponse(String result) {
|
|
|
+ String [] refStrings = result.split("\n");
|
|
|
+ fRefsCache = new GitRef[refStrings.length];
|
|
|
+ for (int i =0; i < refStrings.length; ++i) {
|
|
|
+ String refLine = refStrings[i];
|
|
|
+ for (int j =0; j < refLine.length(); ++j) {
|
|
|
+ if (refLine.charAt(j) == '\t') {
|
|
|
+ fRefsCache[i] = new GitRef(refLine.substring(0, j).trim(), refLine.substring(j +1).trim());
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback.onResponse(fRefsCache);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onError(String msg, Throwable e) {
|
|
|
+ callback.onError(msg, e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (MalformedURLException e) {
|
|
|
+ callback.onError(e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void FetchCommit(GitRef ref, final OnResponseListener<GitCommit> response) {
|
|
|
+ PullHash(ref.GetHash(), new OnResponseListener<byte[]>() {
|
|
|
+ @Override
|
|
|
+ public void onResponse(byte[] result) {
|
|
|
+ response.onResponse(new GitCommit(new String(result, Charset.defaultCharset())));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onError(String msg, Throwable e) {
|
|
|
+ response.onError(msg, e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // FIXME recursive strategy
|
|
|
+
|
|
|
+ public void RecursiveFetchTree(final GitObject.GitTree rootTree, final OnResponseListener<GitObject.GitTree> response) {
|
|
|
+ response.onResponse(rootTree);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void FillTree(final GitObject.GitTree tree, byte[] data) {
|
|
|
+ int i = 3;
|
|
|
+
|
|
|
+ while (data[i -1] != 0 && i < data.length)
|
|
|
+ ++i;
|
|
|
+ while (i < data.length) {
|
|
|
+ byte[] mode = new byte[6];
|
|
|
+ System.arraycopy(data, i, mode, 0, 6);
|
|
|
+ i += 6;
|
|
|
+ int len = 0;
|
|
|
+ while (i +len < data.length && data[i +len] != 0)
|
|
|
+ ++len;
|
|
|
+ byte[] filenameBytes = new byte[len];
|
|
|
+ System.arraycopy(data, i, filenameBytes, 0, len);
|
|
|
+ i += len +1;
|
|
|
+ String fileName = new String(filenameBytes, Charset.defaultCharset());
|
|
|
+ byte []sha1 = new byte[20];
|
|
|
+ System.arraycopy(data, i, sha1, 0, 20);
|
|
|
+ i += 20;
|
|
|
+ tree.AddItem(GitObject.factory(new String(mode, Charset.defaultCharset()).toCharArray(), fileName, sha1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void FetchTree(final GitCommit ci, final OnResponseListener<GitObject.GitTree> response) {
|
|
|
+ PullHash(ci.GetTree(), new OnResponseListener<byte[]>() {
|
|
|
+ @Override
|
|
|
+ public void onResponse(byte[] result) {
|
|
|
+ GitObject.GitTree tree = new GitObject.GitTree(ci.GetTree()).Initialize();
|
|
|
+ FillTree(tree, result);
|
|
|
+ RecursiveFetchTree(tree, response);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onError(String msg, Throwable e) {
|
|
|
+ response.onError(msg, e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Pull(final OnStreamResponseListener<Void> response) {
|
|
|
+ GetRefs(new GitInterface.OnResponseListener<GitRef[]>() {
|
|
|
+ @Override
|
|
|
+ public void onResponse(final GitRef[] result) {
|
|
|
+ GitRef myref = null;
|
|
|
+ response.onMsg("Found refs: ");
|
|
|
+ for (GitRef ref: result) {
|
|
|
+ response.onMsg("\t> "+ref.GetBranch());
|
|
|
+ if (ref.GetBranch().equals(fConfig.GetBranch()))
|
|
|
+ myref = ref;
|
|
|
+ }
|
|
|
+ if (myref != null) {
|
|
|
+ response.onMsg("Checking out branch " +myref.GetBranchName() +" revision " +myref.GetHash());
|
|
|
+ FetchCommit(myref, new OnResponseListener<GitCommit>() {
|
|
|
+ @Override
|
|
|
+ public void onResponse(GitCommit result) {
|
|
|
+ response.onMsg("Finished read commit");
|
|
|
+ response.onMsg(result.GetMessage());
|
|
|
+ response.onMsg("Reading tree #" +result.GetTree());
|
|
|
+ FetchTree(result, new OnResponseListener<GitObject.GitTree>() {
|
|
|
+ @Override
|
|
|
+ public void onResponse(GitObject.GitTree result) {
|
|
|
+ response.onMsg("Finished read tree");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onError(String msg, Throwable e) {
|
|
|
+ response.onError(msg, e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onError(String msg, Throwable e) {
|
|
|
+ response.onError(msg, e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ response.onError("Branch " +fConfig.GetBranch() + " not found on remote", null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onError(final String msg, final Throwable e) {
|
|
|
+ response.onError(msg, e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public void PullHash(String hash, final OnResponseListener<byte[]> response) {
|
|
|
+ URL url;
|
|
|
+ try {
|
|
|
+ url = new URL(fConfig.GetUrl() + "/objects/" + hash.substring(0, 2) + "/" + hash.substring(2));
|
|
|
+ }
|
|
|
+ catch (MalformedURLException e) {
|
|
|
+ response.onError(e.getClass().getName() +": " +e.getMessage(), e);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ protoInflateGet(url, new OnResponseListener<byte[]>() {
|
|
|
+ @Override
|
|
|
+ public void onResponse(byte []result) {
|
|
|
+ response.onResponse(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onError(String msg, Throwable e) {
|
|
|
+ response.onError(msg, e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|