Explorar o código

Refs #47 git fetch refs

isundil %!s(int64=7) %!d(string=hai) anos
pai
achega
8f69bc693f

+ 79 - 10
app/src/main/java/info/knacki/pass/git/SSHGitProtocol.java

@@ -1,7 +1,14 @@
 package info.knacki.pass.git;
 
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import info.knacki.pass.git.entities.GitCommit;
@@ -9,6 +16,7 @@ import info.knacki.pass.git.entities.GitObject;
 import info.knacki.pass.git.entities.GitRef;
 import info.knacki.pass.io.AppendableInputStream;
 import info.knacki.pass.io.CharsetHelper;
+import info.knacki.pass.io.OnErrorListener;
 import info.knacki.pass.io.OnResponseListener;
 import info.knacki.pass.io.OnStreamResponseListener;
 import info.knacki.pass.io.ssh.SSHConnection;
@@ -23,24 +31,85 @@ public class SSHGitProtocol implements GitInterface {
         fConfig = config;
     }
 
-    private void GetRefs(SSHConnection con, OutputStream stdin, InputStream stdout) {
+    private Collection<GitRef> GetRefs(SSHConnection con, OutputStream stdin, InputStream stdout) {
+        ArrayDeque<GitRef> references = new ArrayDeque<>();
+
+        try {
+            while (true) {
+                byte[] hash = new byte[44];
+                if (stdout.read(hash, 0, 4) < 4) {
+                    break;
+                }
+                if (CharsetHelper.ByteArrayToString(hash, 0, 4).equals("0000") && stdout.available() == 0) {
+                    break; // End Hash
+                }
+                // Read the remaining of the hash
+                if (stdout.read(hash, 4, 40) < 40) {
+                    break;
+                }
+
+                ByteArrayOutputStream buffer = null;
+                boolean ignoreToEol = false;
+                // read the ref name
+                while (true) {
+                    byte ch = (byte) stdout.read();
+                    if (ch == 0)
+                        ignoreToEol = true; // ignore server caps
+                    if (ch == '\n')
+                        break;
+                    if (buffer == null)
+                        buffer = new ByteArrayOutputStream();
+                    else if (!ignoreToEol)
+                        buffer.write(ch);
+                }
+                String branchRef;
+                if (buffer != null && (branchRef = CharsetHelper.ByteArrayToString(buffer.toByteArray())).startsWith("refs/heads/")){
+                    GitRef ref = new GitRef(CharsetHelper.ByteArrayToString(hash), branchRef);
+                    references.add(ref);
+                    log.info("Found ref: " + ref);
+                }
+            }
+        }
+        catch (IOException e) {
+            log.log(Level.SEVERE, "Cannot read from ssh command: " +e.getMessage(), e);
+        }
+        return references;
     }
 
-    private void WriteStdErrToLogger(byte[] stderr) {
-        String err = CharsetHelper.ByteArrayToString(stderr);
-        if (!err.isEmpty())
-            log.severe(err);
+    public void ListenForErrors(InputStream stderr, OnErrorListener OnError) {
+        (new Thread() {
+            @Override
+            public void run() {
+                BufferedReader reader = new BufferedReader(new InputStreamReader(stderr));
+                String line;
+
+                try {
+                    while ((line = reader.readLine()) != null) {
+                        OnError.OnError(line, null);
+                    }
+                }
+                catch (IOException e) {
+                    log.log(Level.SEVERE, "Cannot read from stderr", e);
+                }
+            }
+        }).start();
     }
 
     @Override
     public void GetRefs(OnResponseListener<GitRef[]> callback) {
-        AppendableInputStream stdin_in = new AppendableInputStream();
-        OutputStream stdin = stdin_in.GetWriter();
+        AppendableInputStream stdin = new AppendableInputStream();
 
         SSHFactory
-            .createInstance(fConfig, "git-upload-pack isundil/config") // FIXME tmp
-            .SetOnConnectionReadyListener((connection, stdout, stderr) -> GetRefs(connection, stdin, stdout))
-            .connect(stdin_in);
+            .createInstance(fConfig, "git-upload-pack isundil/pass") // FIXME tmp
+            .SetOnConnectionReadyListener((connection, stdout, stderr) -> {
+                ListenForErrors(stderr, (msg, exc) -> log.severe(msg));
+                Collection<GitRef> references = GetRefs(connection, stdin.GetWriter(), stdout);
+                stdin.closeUnsafe();
+                GitRef[] refs = new GitRef[references.size()];
+                callback.OnResponse(references.toArray(refs));
+                connection.disconnect();
+            })
+            .connect();
     }
 
     @Override

+ 22 - 1
app/src/main/java/info/knacki/pass/io/AppendableInputStream.java

@@ -1,10 +1,14 @@
 package info.knacki.pass.io;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayDeque;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 public class AppendableInputStream extends InputStream {
+    private final static Logger log = Logger.getLogger(AppendableInputStream.class.getName());
     private ArrayDeque<Integer> buffer;
     private OutputStream fWriter = new OutputStream() {
         @Override
@@ -23,11 +27,28 @@ public class AppendableInputStream extends InputStream {
 
     @Override
     public int read() {
-        return buffer.isEmpty() ? 0 : buffer.pop();
+        while (buffer.isEmpty()) {
+            try {
+                Thread.sleep(500);
+            }
+            catch (InterruptedException e) {
+                log.log(Level.WARNING, "Interrupted while waiting for available bytes", e);
+                return 0;
+            }
+        }
+        return buffer.pop();
     }
 
     @Override
     public int available() {
         return buffer.size();
     }
+
+    public void closeUnsafe() {
+        try {
+            close();
+        } catch (IOException e) {
+            log.log(Level.WARNING, "Cannot close stream", e);
+        }
+    }
 }

+ 6 - 2
app/src/main/java/info/knacki/pass/io/CharsetHelper.java

@@ -9,13 +9,17 @@ public class CharsetHelper {
     public final static String DEFAULT_CHARSET = "UTF-8";
 
     public static String ByteArrayToString(byte[] arr) {
+        return ByteArrayToString(arr, 0, arr.length);
+    }
+
+    public static String ByteArrayToString(byte[] arr, int offset, int len) {
         try {
-            return new String(arr, DEFAULT_CHARSET);
+            return new String(arr, offset, len, DEFAULT_CHARSET);
         }
         catch (UnsupportedEncodingException e) {
             log.severe("Unsupported charset " +DEFAULT_CHARSET);
         }
-        return new String(arr, Charset.defaultCharset());
+        return new String(arr, offset, len, Charset.defaultCharset());
     }
 
     public static byte[] StringToByteArray(String str) {

+ 32 - 24
app/src/main/java/info/knacki/pass/io/ssh/JSchWrapper.java

@@ -11,6 +11,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
+import java.security.InvalidParameterException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -27,6 +28,8 @@ public class JSchWrapper extends AsyncTask<InputStream, Void, Void> implements S
     private final PipedInputStream stdout = new PipedInputStream();
     private final PipedInputStream stderr = new PipedInputStream();
 
+    private ChannelExec fChannel = null;
+
     JSchWrapper(ConnectionParams config, String command) {
         fCommand = command;
         fAuth = config;
@@ -50,23 +53,27 @@ public class JSchWrapper extends AsyncTask<InputStream, Void, Void> implements S
 
     @Override
     public SSHConnection connect() {
+        if (fChannel != null)
+            throw new InvalidParameterException("JSch Already connected");
         execute();
         return this;
     }
 
     @Override
     public SSHConnection connect(InputStream stdin) {
+        if (fChannel != null)
+            throw new InvalidParameterException("JSch Already connected");
         execute(stdin);
         return this;
     }
 
     @Override
     protected Void doInBackground(InputStream... in) {
-        ChannelExec chan = InitConnection(in == null && in.length > 0 ? null : in[0]);
-        if (chan != null) {
+        InitConnection(in == null || in.length == 0 ? null : in[0]);
+        if (fChannel != null) {
             try {
-                stderr.connect(stdout_out);
-                stdout.connect(stderr_out);
+                stderr.connect(stderr_out);
+                stdout.connect(stdout_out);
             }
             catch (IOException e) {
                 log.log(Level.SEVERE, "Cannot read from output: " +e.getMessage(), e);
@@ -77,39 +84,40 @@ public class JSchWrapper extends AsyncTask<InputStream, Void, Void> implements S
         return null;
     }
 
-    private ChannelExec InitConnection(InputStream in) {
-        ChannelExec chan;
-
+    private void InitConnection(InputStream in) {
         try {
+            // Connection settings
             JSch connection = new JSch();
-            String privKey = fAuth.GetPrivateKey();
+            String privateKey = fAuth.GetPrivateKey();
             Session session = connection.getSession(fAuth.GetUser(), fAuth.GetUrl()); // FIXME only host !
             session.setConfig("StrictHostKeyChecking", "no"); // FIXME
-            if (privKey != null && !privKey.isEmpty()) {
-                connection.addIdentity(privKey); // FIXME passphrase
+            if (privateKey != null && !privateKey.isEmpty()) {
+                connection.addIdentity(privateKey); // FIXME key passphrase
             } else {
                 session.setPassword(fAuth.GetPassword());
             }
             session.connect();
-            chan = (ChannelExec) session.openChannel("exec");
+            fChannel = (ChannelExec) session.openChannel("exec");
+
+            // Stream settings
             if (in != null)
-                chan.setInputStream(in);
-        }
-        catch (JSchException e) {
-            log.log(Level.SEVERE, "Cannot Initiate ssh command: " +e.getMessage(), e);
-            return null;
-        }
-        try {
-            chan.setErrStream(stderr_out);
-            chan.setOutputStream(stdout_out);
+                fChannel.setInputStream(in);
+            fChannel.setErrStream(stderr_out);
+            fChannel.setOutputStream(stdout_out);
 
-            chan.setCommand(fCommand);
-            chan.connect();
+            // do connect
+            fChannel.setCommand(fCommand);
+            fChannel.connect();
         }
         catch (JSchException e) {
             log.log(Level.SEVERE, "Cannot Initiate ssh command: " +e.getMessage(), e);
-            return null;
+            fChannel = null;
         }
-        return chan;
+    }
+
+    @Override
+    public void disconnect() {
+        fChannel.disconnect();
+        fChannel = null;
     }
 }

+ 1 - 0
app/src/main/java/info/knacki/pass/io/ssh/SSHConnection.java

@@ -19,4 +19,5 @@ public interface SSHConnection {
     byte[] GetOutputErr() throws IOException;
     SSHConnection connect();
     SSHConnection connect(InputStream input);
+    void disconnect();
 }