Kaynağa Gözat

[add] Generate tree hash

isundil 7 yıl önce
ebeveyn
işleme
d8a17887b1

+ 1 - 1
app/src/main/AndroidManifest.xml

@@ -3,7 +3,7 @@
     package="info.knacki.pass">
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
 
     <application
         android:allowBackup="true"

+ 14 - 0
app/src/main/java/info/knacki/pass/git/GitInterface.java

@@ -1,5 +1,8 @@
 package info.knacki.pass.git;
 
+import java.io.File;
+import java.util.Map;
+
 import info.knacki.pass.git.entities.GitCommit;
 import info.knacki.pass.git.entities.GitObject;
 import info.knacki.pass.git.entities.GitRef;
@@ -7,6 +10,7 @@ import info.knacki.pass.git.entities.GitRef;
 public interface GitInterface {
     interface OnResponseListener<T> {
         void onResponse(T result);
+
         void onError(String msg, Throwable e);
     }
 
@@ -15,8 +19,18 @@ public interface GitInterface {
     }
 
     void GetRefs(OnResponseListener<GitRef[]> callback);
+
     void FetchCommit(GitRef ref, OnResponseListener<GitCommit> response);
+
     void FetchTree(GitCommit ci, OnStreamResponseListener<GitObject.GitTree> response);
+
     void FetchTree(OnStreamResponseListener<GitObject.GitTree> response);
+
     void FetchBlob(GitObject.GitBlob blob, OnResponseListener<byte[]> response);
+
+    void PushBlobs(String rootGit, GitObject.GitTree tree, Map<String, GitObject.GitBlob> blobs, File[] files, GitInterface.OnStreamResponseListener<Void> resp);
+
+
+
+    void debugTree(String rootGit, GitObject.GitTree tree); // FIXME
 }

+ 1 - 1
app/src/main/java/info/knacki/pass/git/GitInterfaceFactory.java

@@ -7,7 +7,7 @@ import info.knacki.pass.settings.SettingsManager;
 public class GitInterfaceFactory {
     public static GitInterface factory(Context ctx, SettingsManager.Git config) {
         if (config.GetUrl().startsWith("http://") || config.GetUrl().startsWith("https://"))
-            return new DumbGitInterface(config);
+            return new HttpGitProtocol(config);
         return null;
     }
 }

+ 13 - 32
app/src/main/java/info/knacki/pass/git/GitLocal.java

@@ -9,19 +9,12 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
 public class GitLocal {
-    class ControlSum {
-        public final String fSha1;
-        public final String fMd5;
-
-        ControlSum(String sha1, String md5) {
-            fSha1 = sha1;
-            fMd5 = md5;
-        }
-    }
-    final Map<String, ControlSum> cache;
+    final private static Logger log = Logger.getLogger(GitLocal.class.getName());
+    final private Map<String, String> cache;
 
     public GitLocal(File file) {
         cache = new HashMap<>();
@@ -37,35 +30,23 @@ public class GitLocal {
         try {
             while ((line = in.readLine()) != null) {
                 int lineSep = line.indexOf(' ');
-                String sha1;
                 if (lineSep > -1) {
-                    sha1 = line.substring(0, lineSep);
-                    line = line.substring(lineSep +1);
-                    lineSep = line.indexOf(' ');
-                    if (lineSep > -1) {
-                        String md5 = line.substring(0, lineSep);
-                        cache.put(line.substring(lineSep +1), new ControlSum(sha1, md5));
-                    }
+                    cache.put(line.substring(lineSep +1), line.substring(0, lineSep));
                 }
             }
             in.close();
         }
-        catch (IOException e) {}
-    }
-
-    public String GetLocalHash(String key) {
-        ControlSum sums = cache.get(key);
-        return sums == null ? null : sums.fMd5;
+        catch (IOException e) {
+            log.log(Level.WARNING, e.getMessage(), e);
+        }
     }
 
-    public String GetRemoteHash(String key) {
-        ControlSum sums = cache.get(key);
-        return sums == null ? null : sums.fSha1;
+    public String GetHash(String key) {
+        return cache.get(key);
     }
 
-    public GitLocal SetHashes(String filename, String gitSha, String localMd5) {
-        ControlSum sums = cache.put(filename, new ControlSum(gitSha, localMd5));
-        return this;
+    public void SetHash(String filename, String hash) {
+        cache.put(filename, hash);
     }
 
     public Set<String> FileNames() {
@@ -81,8 +62,8 @@ public class GitLocal {
         try {
             f.createNewFile();
             FileWriter writer = new FileWriter(f);
-            for (HashMap.Entry<String, ControlSum> i: cache.entrySet())
-                writer.write(i.getValue().fSha1 +" " +i.getValue().fMd5 +" " +i.getKey() +"\n");
+            for (HashMap.Entry<String, String> i: cache.entrySet())
+                writer.write(i.getValue() +" " +i.getKey() +"\n");
             writer.close();
         }
         catch (IOException e)

+ 103 - 0
app/src/main/java/info/knacki/pass/git/GitSha1.java

@@ -0,0 +1,103 @@
+package info.knacki.pass.git;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import info.knacki.pass.git.entities.GitObject;
+
+public class GitSha1 {
+    private static String getHash(byte[] sha1Bytes) {
+        StringBuilder sb = new StringBuilder();
+        for (byte i: sha1Bytes)
+            sb.append(Integer.toString((i & 0xff) + 0x100, 16).substring(1));
+        return sb.toString();
+    }
+
+    public static String getSha1OfFile(String rootPath, String filePath)
+    {
+        byte[] raw = getRawSha1OfFile(new File(rootPath +filePath));
+        return raw.length == 0 ? "" : getHash(raw);
+    }
+
+    public static byte[] getRawSha1OfFile(String rootPath, String filePath)
+    {
+        return getRawSha1OfFile(new File(rootPath +filePath));
+    }
+
+    public static byte[] getRawSha1OfFile(File f)
+    {
+        try
+        {
+            MessageDigest shaHash = MessageDigest.getInstance("SHA1");
+            shaHash.update(("blob " +f.length()).getBytes());
+            shaHash.update(new byte[] {0});
+            InputStream input = new FileInputStream(f);
+            int numRead = 0;
+            while (numRead != -1)
+            {
+                byte[] buffer = new byte[1024];
+                numRead = input.read(buffer);
+                if (numRead > 0)
+                    shaHash.update(buffer, 0, numRead);
+            }
+            input.close();
+
+            return shaHash.digest();
+        }
+        catch (FileNotFoundException e) {
+            return new byte[]{};
+        }
+        catch(Throwable t) {
+            t.printStackTrace();
+        }
+        return new byte[] {};
+    }
+
+    public static byte[] getTreeMsg(String rootPath, GitObject.GitTree tree) {
+        ByteArrayOutputStream str = new ByteArrayOutputStream();
+
+        try {
+            for (GitObject go : tree.GetObjects()) {
+                str.write((go.GetMode() + ' ').getBytes());
+                str.write(go.GetFilename().getBytes());
+                str.write(new byte[]{0});
+                str.write(getRawSha1OfObject(rootPath, go));
+            }
+        }
+        catch (IOException e) {}
+        return str.toByteArray();
+    }
+
+    public static String getSha1OfObject(String rootPath, GitObject obj) {
+        return getHash(getRawSha1OfObject(rootPath, obj));
+    }
+
+    private static byte[] getRawSha1OfTree(String rootPath, GitObject.GitTree tree) {
+        try {
+            MessageDigest sha1Builder = MessageDigest.getInstance("SHA1");
+            byte[] msg = getTreeMsg(rootPath, tree);
+            sha1Builder.update(("tree " +msg.length).getBytes());
+            sha1Builder.update(new byte[] { 0 });
+            sha1Builder.update(msg);
+            byte[] digest = sha1Builder.digest();
+            return digest;
+        }
+        catch (NoSuchAlgorithmException e) {
+            return new byte[]{};
+        }
+    }
+
+    private static byte[] getRawSha1OfBlob(String rootPath, GitObject.GitBlob blob) {
+        return getRawSha1OfFile(rootPath, blob.GetGitPath());
+    }
+
+    public static byte[] getRawSha1OfObject(String rootPath, GitObject obj) {
+        return obj instanceof GitObject.GitTree ? getRawSha1OfTree(rootPath, (GitObject.GitTree) obj) : getRawSha1OfBlob(rootPath, (GitObject.GitBlob) obj);
+    }
+}

+ 166 - 105
app/src/main/java/info/knacki/pass/git/DumbGitInterface.java → app/src/main/java/info/knacki/pass/git/HttpGitProtocol.java

@@ -2,9 +2,9 @@ package info.knacki.pass.git;
 
 import android.os.AsyncTask;
 
-import java.io.BufferedReader;
+import java.io.File;
 import java.io.IOException;
-import java.io.InputStreamReader;
+import java.io.InputStream;
 import java.math.BigInteger;
 import java.net.Authenticator;
 import java.net.MalformedURLException;
@@ -13,6 +13,7 @@ import java.net.URL;
 import java.net.URLConnection;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
+import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.zip.InflaterInputStream;
@@ -22,115 +23,140 @@ import info.knacki.pass.git.entities.GitObject;
 import info.knacki.pass.git.entities.GitRef;
 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());
+class HttpGitProtocol implements GitInterface {
+    private final SettingsManager.Git fConfig;
+    private GitRef[] fRefsCache = null;
+    private static final Logger log = Logger.getLogger(HttpGitProtocol.class.getName());
 
-    DumbGitInterface(SettingsManager.Git config) {
+    HttpGitProtocol(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;
-                    }
+    static class DownloaderTask extends AsyncTask<Void, Void, Integer> {
+        private final URL fUrl;
+        private OnResponseListener<byte[]> fResp;
+        private final SettingsManager.Git fConfig;
 
-                    for (i =0; i < totalRead && buffer[i] != 0; ++i);
-                    if (i != totalRead) {
-                        ++i;
-                        byte []arr = new byte[buffer.length -i];
-                        System.arraycopy(buffer, i, arr, 0, totalRead -i);
-                        callback.onResponse(arr);
-                    } else {
-                        callback.onResponse(buffer);
-                    }
+        DownloaderTask(URL url, SettingsManager.Git config, OnResponseListener<byte[]> resp) {
+            fUrl = url;
+            fResp = resp;
+            fConfig = config;
+        }
+
+        DownloaderTask(URL url, SettingsManager.Git config) {
+            fUrl = url;
+            fConfig = config;
+        }
+
+        void SetResultHandler(OnResponseListener<byte[]> handler) {
+            fResp = handler;
+        }
+
+        protected InputStream GetInputFilter(URLConnection in) throws IOException {
+            return in.getInputStream();
+        }
+
+        protected void ManageResponse(byte[] resp) {
+            fResp.onResponse(resp);
+        }
+
+        @Override
+        protected Integer doInBackground(Void... voids) {
+            try {
+                log.log(Level.INFO, "fetching " +fUrl.toString());
+                URLConnection httpClient = fUrl.openConnection();
+                if (fConfig.HasAuthentification()) {
+                    Authenticator.setDefault(new Authenticator() {
+                        @Override
+                        protected PasswordAuthentication getPasswordAuthentication() {
+                            return new PasswordAuthentication(fConfig.GetUser(), fConfig.GetPassword().toCharArray());
+                        }
+                    });
                 }
-                catch (MalformedURLException e) {
-                    log.log(Level.WARNING, e.getMessage(), e);
-                    callback.onError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
+                InputStream in = GetInputFilter(httpClient);
+                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;
                 }
-                catch (IOException e) {
-                    log.log(Level.WARNING, e.getMessage(), e);
-                    callback.onError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
+                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;
                 }
-                return 0;
+                ManageResponse(buffer);
+            }
+            catch (IOException e) {
+                log.log(Level.WARNING, e.getMessage(), e);
+                fResp.onError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
             }
-        };
-        task.execute();
+            return 0;
+        }
     }
 
-    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);
+    static class DownloaderWithInflaterTask extends DownloaderTask {
+        DownloaderWithInflaterTask(URL url, SettingsManager.Git config, OnResponseListener<byte[]> resp) {
+            super(url, config, resp);
+        }
+
+        @Override
+        protected InputStream GetInputFilter(URLConnection in) throws IOException {
+            return new InflaterInputStream(super.GetInputFilter(in));
+        }
+
+        @Override
+        protected void ManageResponse(byte[] result) {
+            int i =0;
+
+            while (i < result.length && result[i] != 0)
+                ++i;
+            if (i != result.length) {
+                ++i;
+                byte []arr = new byte[result.length -i];
+                System.arraycopy(result, i, arr, 0, result.length -i);
+                super.ManageResponse(arr);
+            } else {
+                super.ManageResponse(result);
+            }
+        }
+    }
+
+    static class StringDownloaderTask extends DownloaderTask {
+        private final OnResponseListener<String> onResp;
+
+        StringDownloaderTask(URL url, SettingsManager.Git config, OnResponseListener<String> resultHandler) {
+            super(url, config);
+            SetResultHandler(new OnResponseListener<byte[]>() {
+                @Override
+                public void onResponse(byte[] result) {
+                    StringDownloaderTask.this.onResp.onResponse(new String(result, Charset.defaultCharset()));
                 }
-                catch (IOException e) {
-                    log.log(Level.WARNING, e.getMessage(), e);
-                    callback.onError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
+
+                @Override
+                public void onError(String msg, Throwable e) {
+                    StringDownloaderTask.this.onResp.onError(msg, e);
                 }
-                return 0;
-            }
-        };
-        task.execute();
+            });
+            onResp = resultHandler;
+        }
+    }
+
+    private void protoInflateGet(final URL url, final OnResponseListener<byte[]> callback) {
+        new DownloaderWithInflaterTask(url, fConfig, callback).execute();
+    }
+
+    private void protoGet(final URL url, final OnResponseListener<String> callback) {
+        new StringDownloaderTask(url, fConfig, callback).execute();
     }
 
     public void GetRefs(final OnResponseListener<GitRef[]> callback) {
@@ -213,19 +239,25 @@ class DumbGitInterface implements GitInterface {
 
             private void PullHash(String hash) {
                 fResponseListener.onMsg("Reading tree " +hash);
-                DumbGitInterface.this.PullHash(hash, this);
+                HttpGitProtocol.this.PullHash(hash, this);
             }
 
             private void FillTree(final GitObject.GitTree tree, byte[] data) {
                 int i = 0;
 
                 while (i < data.length) {
-                    byte[] mode = new byte[6];
-                    if (i +6 >= data.length)
+                    byte[] mode = null;
+                    int len;
+                    for (len =0; i +len <data.length && len < 8; ++len)
+                        if (data[i +len] == ' ') {
+                            mode = new byte[len];
+                            System.arraycopy(data, i, mode, 0, len);
+                            i += len +1;
+                            break;
+                        }
+                    if (mode == null)
                         break;
-                    System.arraycopy(data, i, mode, 0, 6);
-                    i += 6;
-                    int len = 0;
+                    len = 0;
                     while (i +len < data.length && data[i +len] != 0)
                         ++len;
                     byte[] filenameBytes = new byte[len];
@@ -237,7 +269,7 @@ class DumbGitInterface implements GitInterface {
                     byte []sha1 = new byte[21];
                     System.arraycopy(data, i, sha1, 0, 21);
                     i += 21;
-                    tree.AddItem(GitObject.factory(tree, new String(mode, Charset.defaultCharset()).toCharArray(), fileName, sha1ToString(sha1)));
+                    tree.AddItem(GitObject.factory(tree, mode, fileName, sha1ToString(sha1)));
                 }
             }
 
@@ -326,4 +358,33 @@ class DumbGitInterface implements GitInterface {
         }
         protoInflateGet(url, response);
     }
+
+    public void debugTree(String rootGit, GitObject.GitTree tree) {
+        log.severe(">>> DEBUG >> Tree hash is " +GitSha1.getSha1OfObject(rootGit, tree));
+    }
+
+    public void PushBlobs(String rootGit, GitObject.GitTree tree, final Map<String, GitObject.GitBlob> blobs, final File[] files, final GitInterface.OnStreamResponseListener<Void> response) {
+        GetRefs(new GitInterface.OnResponseListener<GitRef[]>() {
+            @Override
+            public void onResponse(final GitRef[] result) {
+                GitRef myref = null;
+                for (GitRef ref: result) {
+                    if (ref.GetBranch().equals(fConfig.GetBranch()))
+                        myref = ref;
+                }
+                if (myref != null) {
+                    response.onMsg("Pushing over " +myref.GetBranchName() +" revision " +myref.GetHash());
+                    // FIXME
+                    response.onResponse(null);
+                } else {
+                    response.onError("Branch " +fConfig.GetBranch() + " not found on remote for pushing", null);
+                }
+            }
+
+            @Override
+            public void onError(final String msg, final Throwable e) {
+                response.onError(msg, e);
+            }
+        });
+    }
 }

+ 37 - 23
app/src/main/java/info/knacki/pass/git/entities/GitObject.java

@@ -5,26 +5,21 @@ import android.support.annotation.NonNull;
 import java.util.HashMap;
 import java.util.SortedSet;
 import java.util.TreeSet;
-import java.util.logging.Logger;
 
 public class GitObject implements Comparable<GitObject> {
-    public final GitTree fParent;
-    public final char[] fMode;
-    public final String fName;
-    public final String fSha1;
-
+    final GitTree fParent;
+    final byte[] fMode;
+    final String fName;
+    final String fSha1;
+    boolean dirty = false;
 
     @Override
     public int compareTo(@NonNull GitObject gitObject) {
-        if ((this instanceof GitTree) && !(gitObject instanceof GitBlob))
-            return -1;
-        if (!(this instanceof GitTree) && (gitObject instanceof GitBlob))
-            return 1;
-        return fName.compareTo(gitObject.fName);
+        return fName.toLowerCase().compareTo(gitObject.fName.toLowerCase());
     }
 
     public static class GitBlob extends GitObject {
-        private GitBlob(GitTree parent, char[] mode, String name, String sha1) {
+        private GitBlob(GitTree parent, byte[] mode, String name, String sha1) {
             super(parent, mode, name, sha1);
         }
     }
@@ -32,12 +27,12 @@ public class GitObject implements Comparable<GitObject> {
     public static class GitTree extends GitObject {
         protected HashMap<String, GitObject> fItems = null;
 
-        private GitTree(GitTree parent, char[] mode, String name, String sha1) {
+        private GitTree(GitTree parent, byte[] mode, String name, String sha1) {
             super(parent, mode, name, sha1);
         }
 
         public GitTree(String sha1) {
-            super(null, new char[] { '4', '0', '0', '0', '0' }, "", sha1);
+            super(null, new byte[] { '4', '0', '0', '0', '0' }, "", sha1);
         }
 
         public GitTree Initialize() {
@@ -54,16 +49,16 @@ public class GitObject implements Comparable<GitObject> {
         }
 
         public GitObject GetObjectFullPath(String path) {
-            if (path.charAt(0) == '/')
+            if ('/' == path.charAt(0))
                 path = path.substring(1);
-            int nextSlash = path.indexOf('/');
-            String filename = nextSlash == -1 ? path : path.substring(nextSlash);
-            GitObject obj = GetObject(filename);
+            final int nextSlash = path.indexOf('/');
+            final String filename = nextSlash == -1 ? path : (path.substring(0, nextSlash));
+            final GitObject obj = GetObject(filename);
             if (obj == null || (obj instanceof GitBlob && nextSlash != -1))
                 return null;
             if (nextSlash == -1)
                 return obj;
-            return ((GitTree) obj).GetObjectFullPath(path.substring(nextSlash));
+            return ((GitTree) obj).GetObjectFullPath(path.substring(nextSlash +1));
         }
 
         public GitTree AddItem(GitObject item) {
@@ -84,7 +79,7 @@ public class GitObject implements Comparable<GitObject> {
             return null;
         }
 
-        public Iterable<GitObject> objects() {
+        public Iterable<GitObject> GetObjects() {
             SortedSet<GitObject> result = new TreeSet<>(fItems.values());
             return result;
         }
@@ -126,18 +121,18 @@ public class GitObject implements Comparable<GitObject> {
         }
     }
 
-    private GitObject(GitTree parent, char[] mode, String name, String sha1) {
+    private GitObject(GitTree parent, byte[] mode, String name, String sha1) {
         fParent = parent;
         fMode = mode;
         fName = name;
         fSha1 = sha1;
     }
 
-    public static GitObject factory(GitTree parent, char[] mode, String name, String sha1) {
+    public static GitObject factory(GitTree parent, byte[] mode, String name, String sha1) {
         if (mode[0] == '4') {
             return new GitTree(parent, mode, name, sha1);
         }
-        return new GitBlob(parent, mode, name.substring(1), sha1);
+        return new GitBlob(parent, mode, name, sha1);
     }
 
     public String GetHash() {
@@ -147,4 +142,23 @@ public class GitObject implements Comparable<GitObject> {
     public String GetFilename() {
         return fName;
     }
+
+    public String GetGitPath() {
+        StringBuilder sb = new StringBuilder();
+        GitTree parent = fParent;
+        sb.insert(0, "/" +fName);
+        while (parent != null) {
+            sb.insert(0, "/" +parent.fName);
+            parent = parent.fParent;
+        }
+        return sb.toString();
+    }
+
+    public String GetMode() {
+        return new String(fMode);
+    }
+
+    public boolean IsDirty() {
+        return dirty;
+    }
 }

+ 65 - 132
app/src/main/java/info/knacki/pass/io/GPGFileInterface.java

@@ -3,7 +3,7 @@ package info.knacki.pass.io;
 import android.content.Context;
 
 import org.bouncycastle.bcpg.ArmoredOutputStream;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.bcpg.CompressionAlgorithmTags;
 import org.bouncycastle.openpgp.PGPCompressedData;
 import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
 import org.bouncycastle.openpgp.PGPEncryptedData;
@@ -15,24 +15,19 @@ import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
 import org.bouncycastle.openpgp.PGPObjectFactory;
 import org.bouncycastle.openpgp.PGPPBEEncryptedData;
 import org.bouncycastle.openpgp.PGPPrivateKey;
-import org.bouncycastle.openpgp.PGPPublicKey;
 import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
 import org.bouncycastle.openpgp.PGPSecretKey;
 import org.bouncycastle.openpgp.PGPSecretKeyRing;
 import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
-import org.bouncycastle.openpgp.PGPSignature;
-import org.bouncycastle.openpgp.PGPSignatureGenerator;
-import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
 import org.bouncycastle.openpgp.PGPUtil;
 import org.bouncycastle.openpgp.operator.bc.BcPBEDataDecryptorFactory;
-import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
-import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
 import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
 import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
-import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
 import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
 import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
 import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
 import org.bouncycastle.util.io.Streams;
 
 import java.io.ByteArrayOutputStream;
@@ -44,7 +39,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.Charset;
-import java.security.Security;
+import java.security.SecureRandom;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.logging.Level;
@@ -73,39 +68,38 @@ class GPGFileInterface implements IFileInterface {
             res.key = pgpPrivKey;
             res.pass = pass;
             onResponse.onResponse(res);
-            return true;
         }
         catch (PGPException e) {
             // Wrong password
             log.log(Level.INFO, e.getMessage() +" (wrong password ?)", e);
             return false;
         }
+        return true;
     }
 
-    protected boolean FindSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, final GitInterface.OnResponseListener<PGPPrivateKeyAndPass> onResponse) throws PGPException {
+    private boolean FindSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, final GitInterface.OnResponseListener<PGPPrivateKeyAndPass> onResponse) throws PGPException {
         final PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
 
         if (pgpSecKey != null) {
-            if (!TryPassword(pgpSecKey, "", onResponse)) {
-                GitInterface.OnResponseListener<String> onResp = new GitInterface.OnResponseListener<String>() {
-                    @Override
-                    public void onResponse(String result) {
-                        if (result == null) {
-                            onResponse.onError("Invalid password", null);
-                        } else if (!TryPassword(pgpSecKey, result, onResponse)) {
-                            fPasswordGetter.WrongPassword();
-                            fPasswordGetter.GetPassword(this);
-                        }
+            if (TryPassword(pgpSecKey, "", onResponse))
+                return true;
+            GitInterface.OnResponseListener<String> onResp = new GitInterface.OnResponseListener<String>() {
+                @Override
+                public void onResponse(String result) {
+                    if (result == null) {
+                        onResponse.onError("Invalid password", null);
+                    } else if (!TryPassword(pgpSecKey, result, onResponse)) {
+                        fPasswordGetter.WrongPassword();
+                        fPasswordGetter.GetPassword(this);
                     }
+                }
 
-                    @Override
-                    public void onError(String msg, Throwable e) {
-                        onResponse.onError(msg, e);
-                    }
-                };
-                fPasswordGetter.GetPassword(onResp);
-            }
-            return true;
+                @Override
+                public void onError(String msg, Throwable e) {
+                    onResponse.onError(msg, e);
+                }
+            };
+            fPasswordGetter.GetPassword(onResp);
         }
         return false;
     }
@@ -126,8 +120,8 @@ class GPGFileInterface implements IFileInterface {
             } else if (po instanceof PGPLiteralData) {
                 return ((PGPLiteralData) po).getDataStream();
             } else if (po instanceof PGPEncryptedDataList) {
-                for (Iterator<PGPEncryptedData> it = ((PGPEncryptedDataList) po).getEncryptedDataObjects(); it.hasNext();) {
-                    PGPEncryptedData ped = it.next();
+                for (Iterator it = ((PGPEncryptedDataList) po).getEncryptedDataObjects(); it.hasNext();) {
+                    PGPEncryptedData ped = (PGPEncryptedData) it.next();
 
                     if (ped instanceof PGPPublicKeyEncryptedData) {
                         PGPPublicKeyEncryptedData pked = (PGPPublicKeyEncryptedData) ped;
@@ -158,7 +152,7 @@ class GPGFileInterface implements IFileInterface {
         public String pass;
     }
 
-    public void DecryptFile(final GitInterface.OnResponseListener<byte[]> resp) throws IOException, PGPException {
+    private void DecryptFile(final GitInterface.OnResponseListener<byte[]> resp) throws IOException, PGPException {
         PGPObjectFactory pgpF = new PGPObjectFactory(PGPUtil.getDecoderStream(new FileInputStream(fFile)), new JcaKeyFingerprintCalculator());
         Object o = pgpF.nextObject();
         final Iterator<?> it = ((o instanceof PGPEncryptedDataList) ? (PGPEncryptedDataList) o : (PGPEncryptedDataList) pgpF.nextObject()).getEncryptedDataObjects();
@@ -248,118 +242,57 @@ class GPGFileInterface implements IFileInterface {
         return key;
     }
 
-    private static PGPPublicKey findPublicKey(InputStream in) throws IOException, PGPException {
-        return findSecretKey(in).getPublicKey();
-    }
-
     private void CryptFile(final OutputStream fileOutStream, final byte[] data, final GitInterface.OnResponseListener<Void> resp) {
-        Security.addProvider(new BouncyCastleProvider());
-        final PGPEncryptedDataGenerator pedg = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.AES_256));
-        final ArmoredOutputStream armoredOutput = new ArmoredOutputStream(fileOutStream);
-        final PGPSecretKey secretKey;
-
         try {
-            secretKey = findSecretKey(new FileInputStream(fKeyFile));
-        }
-        catch (Throwable e) {
-            resp.onError(e.getMessage(), e);
-            return;
-        }
-        FindPassword(secretKey, new GitInterface.OnResponseListener<PGPPrivateKeyAndPass>() {
-            @Override
-            public void onResponse(PGPPrivateKeyAndPass privateKey) {
-                try {
-                    PGPSignatureGenerator sg = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1));
-                    pedg.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(secretKey.getPublicKey()));
-                    OutputStream encryptdOutStream = pedg.open(armoredOutput, new byte[1 << 16]);
-                    PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator( PGPCompressedData.UNCOMPRESSED);
-                    OutputStream compressedOutStream = comData.open(encryptdOutStream);
-
-                    sg.init(PGPSignature.BINARY_DOCUMENT, privateKey.key);
-                    Iterator it = secretKey.getPublicKey().getUserIDs();
-                    if (it.hasNext()) {
-                        PGPSignatureSubpacketGenerator ssg = new PGPSignatureSubpacketGenerator();
-                        ssg.setSignerUserID(false, (String) it.next());
-                        sg.setHashedSubpackets(ssg.generate());
-                    }
-                    sg.generateOnePassVersion(false).encode(compressedOutStream);
-
-                    PGPLiteralDataGenerator lg = new PGPLiteralDataGenerator();
-                    OutputStream literalDataOutStream = lg.open(compressedOutStream, PGPLiteralData.BINARY, PGPLiteralDataGenerator.CONSOLE, new Date(), data);
-
-                    literalDataOutStream.write(data);
-                    //sg.update(data);
-                    sg.generate().encode(compressedOutStream);
-                    literalDataOutStream.close();
-                    lg.close();
-                    compressedOutStream.close();
-                    comData.close();
-                    pedg.close();
-                    armoredOutput.close();
-                    resp.onResponse(null);
-                }
-                catch (Throwable e) {
-                    resp.onError(e.getMessage(), e);
-                }
-            }
-
-            @Override
-            public void onError(String msg, Throwable e) {
-                resp.onError(msg, e);
-            }
-        });
-    }
-
-    /*
-    This one do write, but ???9 instead of 12359*
-    private void CryptFile(OutputStream fileOutStream, final byte[] data, final GitInterface.OnResponseListener<Void> resp) {
-        if (null == Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)) {
-            Security.addProvider(new BouncyCastleProvider());
-        }
+            ByteArrayOutputStream compressedDataStream = new ByteArrayOutputStream();
+            PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(CompressionAlgorithmTags.UNCOMPRESSED);
+            OutputStream cos = comData.open(compressedDataStream);
+            PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
 
-        final PGPEncryptedDataGenerator pedg = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.TWOFISH));
-        ArmoredOutputStream aos = new ArmoredOutputStream(fileOutStream);
+            OutputStream pOut = lData.open(cos, // the compressed output stream
+                    PGPLiteralData.BINARY,
+                    PGPLiteralData.CONSOLE,  // "filename" to store
+                    data.length, // length of clear data
+                    new Date()  // current time
+            );
 
-        try {
-            pedg.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(findPublicKey(new FileInputStream(fKeyFile))));
-            PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.UNCOMPRESSED);
-            OutputStream compressedOutStream = comData.open(pedg.open(aos, new byte[1 << 16]));
+            pOut.write(data);
+            pOut.close();
+            comData.close();
 
-            PGPLiteralDataGenerator lg = new PGPLiteralDataGenerator();
-            OutputStream literalDataOutStream = lg.open(compressedOutStream, PGPLiteralData.TEXT, "-", new Date(), data);
-            literalDataOutStream.write(data);
-            literalDataOutStream.close();
-            lg.close();
+            final byte[] compressedData = compressedDataStream.toByteArray();
+            final OutputStream out = new ArmoredOutputStream(fileOutStream);
 
-            compressedOutStream.close();
-            comData.close();
-            literalDataOutStream.close();
-            pedg.close();
-            aos.close();
-            fileOutStream.close();
-            resp.onResponse(null);
-        }
-        catch (Throwable e) {
-            resp.onError(e.getMessage(), e);
-        }
-    }
+            final PGPSecretKey sKey = findSecretKey(new FileInputStream(fKeyFile));
+            FindPassword(sKey, new GitInterface.OnResponseListener<PGPPrivateKeyAndPass>() {
+                @Override
+                public void onResponse(PGPPrivateKeyAndPass result) {
+                    PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(PGPEncryptedDataGenerator.AES_128).setWithIntegrityPacket(true).setSecureRandom(new SecureRandom()).setProvider("BC"));
+                    encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(sKey.getPublicKey()).setProvider("BC"));
+                    try {
+                        OutputStream encOut = encGen.open(fileOutStream, compressedData.length);
 
+                        encOut.write(compressedData);
+                        encOut.close();
+                        out.close();
+                        fileOutStream.close();
+                        resp.onResponse(null);
+                    }
+                    catch (IOException | PGPException e) {
+                        resp.onError(e.getMessage(), e);
+                    }
+                }
 
-    /*
-    private void CryptFile(final OutputStream fileOutStream, final byte[] data, final GitInterface.OnResponseListener<Void> resp) {
-        try {
-            PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
-            OutputStream pOut = lData.open(fileOutStream, PGPLiteralDataGenerator.TEXT, "-", new Date(), data);
-            pOut.write(data, 0, data.length);
-            lData.close();
-            fileOutStream.close();
-            resp.onResponse(null);
+                @Override
+                public void onError(String msg, Throwable e) {
+                    resp.onError(msg, e);
+                }
+            });
         }
-        catch (IOException e) {
+        catch (IOException | PGPException e) {
             resp.onError(e.getMessage(), e);
         }
     }
-    */
 
     @Override
     public void ReadFile(final GitInterface.OnResponseListener<String> resp) {

+ 1 - 3
app/src/main/java/info/knacki/pass/io/PasswordFileInterface.java

@@ -31,16 +31,14 @@ import java.nio.charset.Charset;
 import java.security.SecureRandom;
 import java.security.Security;
 import java.util.Date;
-import java.util.logging.Logger;
 
 import info.knacki.pass.git.GitInterface;
 
 class PasswordFileInterface implements IFileInterface {
-    private final static Logger log = Logger.getLogger(GPGFileInterface.class.getName());
     private final File fFile;
     private final FileInterfaceFactory.PasswordGetter fPasswordGetter;
 
-    private final String passwordWrite = "mpass";
+    private final String passwordWrite = "mpass"; // FIXME
 
     PasswordFileInterface(FileInterfaceFactory.PasswordGetter passwordGetter, File f) {
         fFile = f;

+ 1 - 4
app/src/main/java/info/knacki/pass/io/PathUtils.java

@@ -10,10 +10,7 @@ public class PathUtils {
     private static final String DATA_GIT_LOCAL = "gitfiles";
 
     private static String GetAppRootDir(Context ctx) {
-        File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/pass");
-        f.mkdir();
-        return f.getAbsolutePath();
-        //return ctx.getFilesDir().getAbsolutePath();
+        return ctx.getFilesDir().getAbsolutePath();
     }
 
     public static String GetPassDir(Context ctx) {

+ 68 - 65
app/src/main/java/info/knacki/pass/ui/GitPullActivity.java

@@ -2,16 +2,14 @@ package info.knacki.pass.ui;
 
 import android.os.Bundle;
 import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.AppCompatTextView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
-import java.security.MessageDigest;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Stack;
@@ -22,7 +20,9 @@ import info.knacki.pass.R;
 import info.knacki.pass.git.GitInterface;
 import info.knacki.pass.git.GitInterfaceFactory;
 import info.knacki.pass.git.GitLocal;
+import info.knacki.pass.git.GitSha1;
 import info.knacki.pass.git.entities.GitObject;
+import info.knacki.pass.git.entities.GitRef;
 import info.knacki.pass.io.PathUtils;
 import info.knacki.pass.settings.SettingsManager;
 
@@ -32,6 +32,16 @@ public class GitPullActivity extends AppCompatActivity {
     private GitInterface fGitInterfage;
     private GitObject.GitTree fTree;
 
+    private void onMsg(final String msg) {
+        GitPullActivity.this.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TextView logView = findViewById(R.id.logView);
+                logView.append(msg +"\n");
+            }
+        });
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -50,13 +60,7 @@ public class GitPullActivity extends AppCompatActivity {
             fGitInterfage.FetchTree(new GitInterface.OnStreamResponseListener<GitObject.GitTree>() {
                 @Override
                 public void onMsg(final String msg) {
-                    GitPullActivity.this.runOnUiThread(new Runnable() {
-                        @Override
-                        public void run() {
-                            TextView logView = findViewById(R.id.logView);
-                            logView.append(msg +"\n");
-                        }
-                    });
+                    GitPullActivity.this.onMsg(msg);
                 }
 
                 @Override
@@ -88,64 +92,29 @@ public class GitPullActivity extends AppCompatActivity {
         }
     }
 
-    public String getMd5OfFile(String filePath)
-    {
-        return getMd5OfFile(new File(PathUtils.GetPassDir(this) +filePath));
-    }
-
-    public String getMd5OfFile(File f)
-    {
-        try
-        {
-            InputStream input = new FileInputStream(f);
-            byte[] buffer = new byte[1024];
-            MessageDigest md5Hash = MessageDigest.getInstance("MD5");
-            int numRead = 0;
-            while (numRead != -1)
-            {
-                numRead = input.read(buffer);
-                if (numRead > 0)
-                    md5Hash.update(buffer, 0, numRead);
-            }
-            input.close();
-
-            byte [] md5Bytes = md5Hash.digest();
-            StringBuilder sb = new StringBuilder();
-            for (byte i: md5Bytes)
-                sb.append(Integer.toString((i & 0xff) + 0x100, 16).substring(1));
-            return sb.toString();
-        }
-        catch(Throwable t) {
-            t.printStackTrace();
-        }
-        return "";
-    }
-
     public static String LOCALGIT_HASH_VERSION_FILE;
 
     protected void OnTreeStructureFetched() {
         final GitLocal localVersion = new GitLocal(new File(LOCALGIT_HASH_VERSION_FILE));
-        HashMap<String, GitObject.GitBlob> filesToDownload = new HashMap<>();
+        HashMap<String, GitObject.GitBlob> filesToPull = new HashMap<>();
         HashMap<String, GitObject.GitBlob> filesToPush = new HashMap<>();
         HashMap<String, GitObject.GitBlob> conflictingFiles = new HashMap<>();
 
         for (Map.Entry<String, GitObject.GitBlob>i: fTree.FindAllBlobs().entrySet()) {
-            String localHash = localVersion.GetLocalHash(i.getKey());
-            String remoteHash = localVersion.GetRemoteHash(i.getKey());
-            String currentHash = getMd5OfFile(i.getKey());
+            String remoteLocalHash = localVersion.GetHash(i.getKey());
+            String currentHash = GitSha1.getSha1OfFile(PathUtils.GetPassDir(this), i.getKey());
 
-            if (localHash == null) localHash = "";
-            if (remoteHash == null) remoteHash = "";
+            if (remoteLocalHash == null) remoteLocalHash = "";
 
-            final boolean remoteChanged = !remoteHash.equals(i.getValue().fSha1);
-            final boolean localChanged = !localHash.equals(currentHash);
+            final boolean remoteChanged = !remoteLocalHash.equals(i.getValue().GetHash());
+            final boolean localChanged = !remoteLocalHash.equals(currentHash);
 
             if (remoteChanged && localChanged) {
                 // Conflict
                 conflictingFiles.put(i.getKey(), i.getValue());
             } else if (remoteChanged) {
                 // remote changed
-                filesToDownload.put(i.getKey(), i.getValue());
+                filesToPull.put(i.getKey(), i.getValue());
             } else if (localChanged) {
                 // local changed
                 filesToPush.put(i.getKey(), i.getValue());
@@ -153,26 +122,40 @@ public class GitPullActivity extends AppCompatActivity {
         }
         for (String i: localVersion.FileNames()) {
             if (fTree.GetObjectFullPath(i) == null) {
-                final boolean localChanged = !getMd5OfFile(i).equals(localVersion.GetLocalHash(i));
+                log.severe("Cannot find obj on remote tree " +i);
+                final boolean localChanged = !GitSha1.getSha1OfFile(PathUtils.GetPassDir(this), i).equals(localVersion.GetHash(i));
                 if (localChanged)
                     conflictingFiles.put(i, null);
                 else
-                    filesToDownload.put(i, null);
+                    filesToPull.put(i, null);
             }
         }
         // FIXME new file on local
 
-        AskForConflicts(localVersion, conflictingFiles, filesToDownload, filesToPush);
+        AskForConflicts(localVersion, conflictingFiles, filesToPull, filesToPush);
     }
 
-    void AskForConflicts(GitLocal localVersion, HashMap<String, GitObject.GitBlob> conflicts, HashMap<String, GitObject.GitBlob> filesToDownload, HashMap<String, GitObject.GitBlob> filesToPush) {
+    void AskForConflicts(GitLocal localVersion, HashMap<String, GitObject.GitBlob> conflicts, HashMap<String, GitObject.GitBlob> filesToPull, HashMap<String, GitObject.GitBlob> filesToPush) {
         // FIXME
-        filesToDownload.putAll(conflicts);
-        SyncFiles(localVersion, filesToDownload, filesToPush);
+        filesToPull.putAll(conflicts);
+        SyncFiles(localVersion, filesToPull, filesToPush);
+    }
+
+    void RmEmptyDirs(File dir, boolean isRoot) {
+        File[] content = dir.listFiles();
+
+        if (null == content || content.length == 0) {
+            if (!isRoot)
+                dir.delete();
+            return;
+        }
+        for (File i: content)
+            if (i.isDirectory())
+                RmEmptyDirs(i, false);
     }
 
-    void SyncFiles(final GitLocal localVersion, final HashMap<String, GitObject.GitBlob> filesToDownload, final HashMap<String, GitObject.GitBlob> filesToPush) {
-        final GitInterface.OnResponseListener<Void> allDone = new GitInterface.OnResponseListener<Void>() {
+    void SyncFiles(final GitLocal localVersion, final HashMap<String, GitObject.GitBlob> filesToPull, final HashMap<String, GitObject.GitBlob> filesToPush) {
+        final GitInterface.OnStreamResponseListener<Void> allDone = new GitInterface.OnStreamResponseListener<Void>() {
             @Override
             public void onResponse(Void result) {
                 GitPullActivity.this.runOnUiThread(new Runnable() {
@@ -183,6 +166,8 @@ public class GitPullActivity extends AppCompatActivity {
                         pg.setMax(1);
                         pg.setProgress(1);
                         localVersion.Write(new File(LOCALGIT_HASH_VERSION_FILE));
+                        RmEmptyDirs(new File(PathUtils.GetPassDir(GitPullActivity.this)), true);
+                        fGitInterfage.debugTree(PathUtils.GetPassDir(GitPullActivity.this) +"/", fTree);
                     }
                 });
             }
@@ -199,9 +184,14 @@ public class GitPullActivity extends AppCompatActivity {
                     }
                 });
             }
+
+            @Override
+            public void onMsg(String message) {
+                GitPullActivity.this.onMsg(message);
+            }
         };
 
-        DownloadBlobs(filesToDownload, localVersion, new GitInterface.OnResponseListener<Void>(){
+        DownloadBlobs(filesToPull, localVersion, new GitInterface.OnResponseListener<Void>(){
             @Override
             public void onResponse(Void result) {
                 if (filesToPush.size() > 0)
@@ -223,7 +213,7 @@ public class GitPullActivity extends AppCompatActivity {
             return;
         }
         final TextView logView = findViewById(R.id.logView);
-        logView.append(blobs.size() +" files to Download\n");
+        logView.append(blobs.size() +" files to update locally\n");
         for (String i: blobs.keySet())
             logView.append(" > " +i +"\n");
         final Stack<Map.Entry<String, GitObject.GitBlob>> files = new Stack<>();
@@ -235,7 +225,6 @@ public class GitPullActivity extends AppCompatActivity {
                 final GitObject.GitBlob blob = files.peek().getValue();
                 final GitInterface.OnResponseListener<byte[]> _this = this;
 
-
                 GitPullActivity.this.runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
@@ -278,6 +267,7 @@ public class GitPullActivity extends AppCompatActivity {
                 new File(PathUtils.GetPassDir(this) +filename).delete();
                 localCache.remove(filename);
                 DownloadNext(files, localCache, downloader, resp);
+                log.info("Removed file " +filename);
             } else {
                 fGitInterfage.FetchBlob(files.peek().getValue(), downloader);
             }
@@ -302,9 +292,22 @@ public class GitPullActivity extends AppCompatActivity {
             log.log(Level.SEVERE, e.getMessage(), e);
             return;
         }
-        localVersion.SetHashes(filename, blob.GetHash(), getMd5OfFile(f));
+        localVersion.SetHash(filename, blob.GetHash());
     }
 
-    void PushBlobs(Map<String, GitObject.GitBlob> blobs, GitInterface.OnResponseListener<Void> resp) {
+    void PushBlobs(final Map<String, GitObject.GitBlob> blobs, final GitInterface.OnStreamResponseListener<Void> resp) {
+        GitPullActivity.this.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((AppCompatTextView)(findViewById(R.id.logView))).append("Committing " +blobs.size() +" files\n");
+                File[] files = new File[blobs.size()];
+                int i =0;
+                for (Map.Entry blob: blobs.entrySet()) {
+                    files[i++] = new File(PathUtils.GetPassDir(GitPullActivity.this) +blob.getKey());
+                }
+                //    public void PushBlobs(final GitRef prev, final Map<String, GitObject.GitBlob> blobs, final File[] files, final GitInterface.OnResponseListener<Void> resp);
+                fGitInterfage.PushBlobs(PathUtils.GetPassDir(GitPullActivity.this), fTree, blobs, files, resp);
+            }
+        });
     }
 }

+ 2 - 0
app/src/main/java/info/knacki/pass/ui/MainActivity.java

@@ -141,6 +141,8 @@ public class MainActivity extends AppCompatActivity implements PasswordClickList
         ((ScrollView)findViewById(R.id.passwordListContainer)).addView(vPasswordListView);
 
         requestPermissions();
+
+        startActivity(new Intent(this, GitPullActivity.class));
     }
 
     @Override