diff options
-rw-r--r-- | .idea/vcs.xml | 2 | ||||
-rw-r--r-- | app/src/main/java/com/zeapo/pwdstore/GitClone.java | 325 | ||||
-rw-r--r-- | app/src/main/res/layout/activity_git_clone.xml | 22 | ||||
-rw-r--r-- | app/src/main/res/layout/activity_pwdstore.xml | 6 | ||||
-rw-r--r-- | app/src/main/res/menu/pwdstore.xml | 7 | ||||
-rw-r--r-- | app/src/main/res/values/strings.xml | 10 |
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> |