aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.idea/vcs.xml2
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/GitClone.java325
-rw-r--r--app/src/main/res/layout/activity_git_clone.xml22
-rw-r--r--app/src/main/res/layout/activity_pwdstore.xml6
-rw-r--r--app/src/main/res/menu/pwdstore.xml7
-rw-r--r--app/src/main/res/values/strings.xml10
6 files changed, 240 insertions, 132 deletions
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index def6a6a1..c80f2198 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
- <mapping directory="" vcs="" />
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
diff --git a/app/src/main/java/com/zeapo/pwdstore/GitClone.java b/app/src/main/java/com/zeapo/pwdstore/GitClone.java
index 7e2102a6..c7b67812 100644
--- a/app/src/main/java/com/zeapo/pwdstore/GitClone.java
+++ b/app/src/main/java/com/zeapo/pwdstore/GitClone.java
@@ -24,78 +24,118 @@ import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.UserInfo;
import com.zeapo.pwdstore.R;
+import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.apache.commons.io.FileUtils;
+import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.diff.Edit;
+import org.eclipse.jgit.errors.NoRemoteRepositoryException;
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.CredentialsProviderUserInfo;
+import org.eclipse.jgit.transport.JschConfigSessionFactory;
+import org.eclipse.jgit.transport.OpenSshConfig;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.StringUtils;
import java.io.File;
import java.io.IOException;
+import java.security.GeneralSecurityException;
import java.util.ArrayList;
+import java.util.Hashtable;
import java.util.List;
+import java.util.Properties;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
-public class GitClone extends Activity implements AdapterView.OnItemSelectedListener {
+public class GitClone extends Activity {
- /* The clone process has to be on a different thread than the main one */
- private class CloneTask extends AsyncTask<File, Integer, Long> {
- private ProgressDialog dialog;
- private Activity activity;
- private Context context;
-
- public CloneTask(Activity activity) {
- this.activity = activity;
- context = activity;
- dialog = new ProgressDialog(context);
- }
-
- protected void onPreExecute() {
- this.dialog.setMessage("Cloning...");
- this.dialog.setCancelable(false);
- this.dialog.show();
- }
+ private Activity activity;
+ private Context context;
- protected void onPostExecute(Long result) {
- this.dialog.dismiss();
- }
+ private String protocol;
+ private String connectionMode;
-
- protected Long doInBackground(File... remote) {
- int count = remote.length;
- long totalSize = 0;
- for (int i = 0; i < count; i++) {
- try {
- Git.cloneRepository().
- setCloneAllBranches(true).
- setDirectory(remote[i]).
- setURI(((TextView) findViewById(R.id.clone_uri)).getText().toString())
- .call();
- totalSize++;
- } catch (Exception e) {
- e.printStackTrace();
- totalSize++;
- }
- }
- return totalSize;
- }
- }
+ private File localDir;
+ private String hostname;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_git_clone);
- // init the spinner
+ context = getApplicationContext();
+ activity = this;
+
+ // init the spinner for protocols
+ Spinner protcol_spinner = (Spinner) findViewById(R.id.clone_protocol);
+ ArrayAdapter<CharSequence> protocol_adapter = ArrayAdapter.createFromResource(this,
+ R.array.clone_protocols, android.R.layout.simple_spinner_item);
+ protocol_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ protcol_spinner.setAdapter(protocol_adapter);
+ protcol_spinner.setOnItemSelectedListener(
+ new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
+ protocol = ((Spinner)findViewById(R.id.clone_protocol)).getSelectedItem().toString();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> adapterView) {
+
+ }
+ }
+ );
+
+ // init the spinner for connection modes
Spinner connection_mode_spinner = (Spinner) findViewById(R.id.connection_mode);
ArrayAdapter<CharSequence> connection_mode_adapter = ArrayAdapter.createFromResource(this,
R.array.connection_modes, android.R.layout.simple_spinner_item);
connection_mode_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
connection_mode_spinner.setAdapter(connection_mode_adapter);
- connection_mode_spinner.setOnItemSelectedListener(this);
+ connection_mode_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
+ String selection = ((Spinner) findViewById(R.id.connection_mode)).getSelectedItem().toString();
+
+ if (selection.equalsIgnoreCase("ssh-key")) {
+ new AlertDialog.Builder(activity)
+ .setMessage("Authentication method not implemented yet")
+ .setPositiveButton("OK",
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ }
+ ).show();
+ ((Button) findViewById(R.id.clone_button)).setEnabled(false);
+ } else {
+ ((Button) findViewById(R.id.clone_button)).setEnabled(true);
+ }
+ connectionMode = selection;
+ }
+ @Override
+ public void onNothingSelected(AdapterView<?> adapterView) {
+
+ }
+ });
}
@Override
@@ -117,54 +157,140 @@ public class GitClone extends Activity implements AdapterView.OnItemSelectedList
return super.onOptionsItemSelected(item);
}
- public void cloneRepository(View view) {
+ /* The clone process has to be on a different thread than the main one */
+ private class CloneTask extends AsyncTask<CloneCommand, Integer, Long> {
+ private ProgressDialog dialog;
- final File localDir = new File(getApplicationContext().getCacheDir().getAbsoluteFile() + "/store");
+ public CloneTask(Activity activity) {
+ context = activity;
+ dialog = new ProgressDialog(context);
+ }
+
+ protected void onPreExecute() {
+ this.dialog.setMessage("Cloning...");
+ this.dialog.setCancelable(false);
+ this.dialog.show();
+ }
+
+ protected void onPostExecute(Long result) {
+ if (result < 0) {
+ new AlertDialog.Builder(activity).
+ setTitle("Invalid remote repository path").
+ setMessage("Please check that the repository path is correct.\nDid you forget to specify the path after the hostname?").
+ setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
- if (localDir.exists()) {
- AlertDialog.Builder builder1 = new AlertDialog.Builder(this);
- builder1.setMessage(R.string.dialog_delete_msg);
- builder1.setCancelable(true);
- builder1.setPositiveButton(R.string.dialog_delete,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- try {
- FileUtils.deleteDirectory(localDir);
- } catch (IOException e) {
- //TODO Handle the exception correctly
- e.printStackTrace();
}
+ }).show();
+ }
+ this.dialog.dismiss();
+ }
- dialog.cancel();
- }
- }
- );
- builder1.setNegativeButton(R.string.dialog_do_not_delete,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- }
- );
- AlertDialog alert11 = builder1.create();
- alert11.show();
+ protected Long doInBackground(CloneCommand... cmd) {
+ int count = cmd.length;
+ long totalSize = 0;
+ for (int i = 0; i < count; i++) {
+ try {
+ cmd[i].call();
+ } catch (InvalidRemoteException e) {
+ return new Long(-1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ totalSize++;
+ }
+ return totalSize;
+ }
+ }
+
+ protected class GitConfigSessionFactory extends JschConfigSessionFactory {
+
+ public void configure(OpenSshConfig.Host hc, Session session) {
+ session.setConfig("StrictHostKeyChecking", "no");
+ }
+
+ @Override
+ protected JSch
+ getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
+ JSch jsch = super.getJSch(hc, fs);
+ jsch.removeAllIdentity();
+ return jsch;
+ }
+ }
+
+
+ public void cloneRepository(View view) {
+ localDir = new File(getApplicationContext().getCacheDir().getAbsoluteFile() + "/store");
+
+ hostname = ((TextView) findViewById(R.id.clone_uri)).getText().toString();
+ // don't ask the user, take off the protocol that he puts in
+ hostname = hostname.replaceFirst("^.+://", "");
+ ((TextView) findViewById(R.id.clone_uri)).setText(hostname);
+
+ // now cheat a little and prepend the real protocol
+ // jGit does not accept a ssh:// but requires https://
+ if (!protocol.equals("ssh://")) hostname = new String(protocol + hostname);
+
+ if (localDir.exists()) {
+ new AlertDialog.Builder(this).
+ setTitle(R.string.dialog_delete_title).
+ setMessage(R.string.dialog_delete_msg).
+ setCancelable(false).
+ setPositiveButton(R.string.dialog_delete,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ try {
+ FileUtils.deleteDirectory(localDir);
+ authenticateThenClone(localDir);
+ } catch (IOException e) {
+ //TODO Handle the exception correctly if we are unable to delete the directory...
+ e.printStackTrace();
+ } catch (Exception e) {
+ //This is what happens when jgit fails :(
+ //TODO Handle the diffent cases of exceptions
+ }
+
+ dialog.cancel();
+ }
+ }
+ ).
+ setNegativeButton(R.string.dialog_do_not_delete,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ }
+ ).
+ show();
+ } else {
+ try {
+ authenticateThenClone(localDir);
+ } catch (Exception e) {
+ //This is what happens when jgit fails :(
+ //TODO Handle the diffent cases of exceptions
+ e.printStackTrace();
+ }
}
+ }
+ private void authenticateThenClone(final File localDir) {
String connectionMode = ((Spinner) findViewById(R.id.connection_mode)).getSelectedItem().toString();
+
if (connectionMode.equalsIgnoreCase("ssh-key")) {
} else {
// Set an EditText view to get user input
- final LinearLayout layout = new LinearLayout(this);
+ final LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.VERTICAL);
- final EditText username = new EditText(this);
+ final EditText username = new EditText(activity);
username.setHint("Username");
username.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
- final EditText password = new EditText(this);
+ final EditText password = new EditText(activity);
password.setHint("Password");
password.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
@@ -172,54 +298,31 @@ public class GitClone extends Activity implements AdapterView.OnItemSelectedList
layout.addView(username);
layout.addView(password);
-
- new AlertDialog.Builder(this)
+ new AlertDialog.Builder(activity)
.setTitle("Authenticate")
.setMessage("Please provide your usename and password for this repository")
.setView(layout)
- .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
+ .setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
- //TODO use Jsch to set the authentication method
+ SshSessionFactory.setInstance(new GitConfigSessionFactory());
+
+ CloneCommand cmd = Git.cloneRepository().
+ setCredentialsProvider(new UsernamePasswordCredentialsProvider("git", "nicomint")).
+ setCloneAllBranches(true).
+ setDirectory(localDir).
+ setURI(hostname);
+
+ new CloneTask(activity).execute(cmd);
}
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- // Do nothing.
- }
- }).show();
+ public void onClick(DialogInterface dialog, int whichButton) {
+ // Do nothing.
+ }
+ }).show();
}
-
- new CloneTask(this).execute(localDir);
- }
-
-
- public void selectConnectionMode(View view) {
-
}
- /* when the connection mode is selected */
- @Override
- public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
- String selection = ((Spinner) findViewById(R.id.connection_mode)).getSelectedItem().toString();
- if (selection.equalsIgnoreCase("ssh-key")) {
- new AlertDialog.Builder(this)
- .setMessage("Authentication method not implemented yet")
- .setPositiveButton("OK",
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- }
- ).show();
- ((Button) findViewById(R.id.clone_button)).setEnabled(false);
- } else {
- ((Button) findViewById(R.id.clone_button)).setEnabled(true);
- }
- }
- @Override
- public void onNothingSelected(AdapterView<?> adapterView) {
-
- }
}
diff --git a/app/src/main/res/layout/activity_git_clone.xml b/app/src/main/res/layout/activity_git_clone.xml
index 01b18d26..6587524b 100644
--- a/app/src/main/res/layout/activity_git_clone.xml
+++ b/app/src/main/res/layout/activity_git_clone.xml
@@ -13,21 +13,25 @@
android:layoutDirection="ltr"
android:layout_width="fill_parent"
android:layout_height="match_parent">
- <EditText
- android:hint="Repository"
- android:id="@+id/clone_uri"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content">
+ <Spinner
+ android:id="@+id/clone_protocol"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"></Spinner>
+ <EditText
+ android:hint="Repository URI"
+ android:id="@+id/clone_uri"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/connection_mode"></Spinner>
- <LinearLayout
- android:id="@+id/config_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"></LinearLayout>
-
<Button
android:id="@+id/clone_button"
android:text="Clone!"
diff --git a/app/src/main/res/layout/activity_pwdstore.xml b/app/src/main/res/layout/activity_pwdstore.xml
index 1420c906..a59ed394 100644
--- a/app/src/main/res/layout/activity_pwdstore.xml
+++ b/app/src/main/res/layout/activity_pwdstore.xml
@@ -8,10 +8,4 @@
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".pwdstore">
- <Button
- android:text="@string/clone"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="getClone"/>
-
</RelativeLayout>
diff --git a/app/src/main/res/menu/pwdstore.xml b/app/src/main/res/menu/pwdstore.xml
index db164db6..97d4df5b 100644
--- a/app/src/main/res/menu/pwdstore.xml
+++ b/app/src/main/res/menu/pwdstore.xml
@@ -1,8 +1,9 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".pwdstore" >
- <item android:id="@+id/action_settings"
- android:title="@string/action_settings"
+ <item android:id="@+id/clone_setting"
+ android:title="@string/clone_setting"
android:orderInCategory="100"
- android:showAsAction="never" />
+ android:showAsAction="ifRoom"
+ android:onClick="getClone"/>
</menu>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 43246df8..7b9de7d3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -3,10 +3,10 @@
<string name="app_name">PwdStore</string>
<string name="clone">Clone!</string>
- <string name="action_settings">Settings</string>
+ <string name="clone_setting">Clone</string>
<string name="hello_world">Hello world!</string>
- <string name="dialog_delete_title">Remove dir</string>
+ <string name="dialog_delete_title">Directory already exist</string>
<string name="dialog_delete_msg">Target directory already exist. Current version support only a single store. Do you want to delete the current password store directory?</string>
<string name="dialog_delete">Delete directory</string>
<string name="dialog_do_not_delete">Cancel</string>
@@ -16,4 +16,10 @@
<item>ssh-key</item>
</string-array>
+ <string-array name="clone_protocols">
+ <item>ssh://</item>
+ <item>https://</item>
+ <item>http://</item>
+ </string-array>
+
</resources>