package com.amaze.filemanager.ui.dialogs; import android.app.Dialog; import android.app.DialogFragment; import android.content.Context; import android.content.SharedPreferences; import android.content.res.ColorStateList; import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.design.widget.TextInputLayout; import android.support.v7.widget.AppCompatCheckBox; import android.support.v7.widget.AppCompatEditText; import android.text.Editable; import android.text.TextWatcher; import android.view.View; import android.widget.EditText; import android.widget.TextView; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.amaze.filemanager.R; import com.amaze.filemanager.filesystem.HFile; import com.amaze.filemanager.utils.CryptUtil; import com.amaze.filemanager.utils.EditTextColorStateUtil; import com.amaze.filemanager.utils.OpenMode; import com.amaze.filemanager.utils.PreferenceUtils; import com.amaze.filemanager.utils.Utils; import com.amaze.filemanager.utils.color.ColorUsage; import com.amaze.filemanager.utils.provider.UtilitiesProviderInterface; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import jcifs.smb.SmbFile; /** * Created by arpitkh996 on 17-01-2016. */ public class SmbConnectDialog extends DialogFragment { private UtilitiesProviderInterface utilsProvider; private static final String TAG = "SmbConnectDialog"; public interface SmbConnectionListener{ /** * Callback denoting a new connection been added from dialog * @param edit whether we edit existing connection or not * @param name name of connection as appears in navigation drawer * @param path the full path to the server. Includes an un-encrypted password to support * runtime loading without reloading stuff from database. * @param encryptedPath the full path to the server. Includes encrypted password to save in * database. Later be decrypted at every boot when we read from db entry. * @param oldname the old name of connection if we're here to edit * @param oldPath the old full path (un-encrypted as we read from existing entry in db, which * we decrypted beforehand). */ void addConnection(boolean edit, String name, String path, String encryptedPath, String oldname, String oldPath); /** * Callback denoting a connection been deleted from dialog * @param name name of connection as in navigation drawer and in database entry * @param path the full path to server. Includes an un-encrypted password as we decrypted it * beforehand while reading from database before coming here to delete. * We'll later have to encrypt the password back again in order to match entry * from db and to successfully delete it. If we don't want this behaviour, * then we'll have to not allow duplicate connection name, and delete entry based * on the name only. But that is not supported as of now. * See {@link com.amaze.filemanager.utils.HistoryManager#removePath(String, String, String)} */ void deleteConnection(String name, String path); } Context context; SmbConnectionListener smbConnectionListener; String emptyAddress, emptyName,invalidDomain,invalidUsername; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); utilsProvider = (UtilitiesProviderInterface) getActivity(); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final boolean edit=getArguments().getBoolean("edit",false); final String path=getArguments().getString("path"); final String name=getArguments().getString("name"); context=getActivity(); emptyAddress = String.format(getString(R.string.cantbeempty),getString(R.string.ip) ); emptyName = String.format(getString(R.string.cantbeempty),getString(R.string.connectionname) ); invalidDomain = String.format(getString(R.string.invalid),getString(R.string.domain)); invalidUsername = String.format(getString(R.string.invalid),getString(R.string.username).toLowerCase()); if(getActivity() instanceof SmbConnectionListener){ smbConnectionListener=(SmbConnectionListener)getActivity(); } final SharedPreferences sharedPreferences= PreferenceManager.getDefaultSharedPreferences(context); final MaterialDialog.Builder ba3 = new MaterialDialog.Builder(context); ba3.title((R.string.smb_con)); ba3.autoDismiss(false); final View v2 = getActivity().getLayoutInflater().inflate(R.layout.smb_dialog, null); final TextInputLayout connectionTIL = (TextInputLayout)v2.findViewById(R.id.connectionTIL); final TextInputLayout ipTIL = (TextInputLayout)v2.findViewById(R.id.ipTIL); final TextInputLayout domainTIL = (TextInputLayout)v2.findViewById(R.id.domainTIL); final TextInputLayout usernameTIL = (TextInputLayout)v2.findViewById(R.id.usernameTIL); final AppCompatEditText conName = (AppCompatEditText) v2.findViewById(R.id.connectionET); conName.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if(conName.getText().toString().length()==0) connectionTIL.setError(emptyName); else connectionTIL.setError(""); } }); final AppCompatEditText ip = (AppCompatEditText) v2.findViewById(R.id.ipET); ip.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if(ip.getText().toString().length()==0) ipTIL.setError(emptyAddress); else ipTIL.setError(""); } }); final AppCompatEditText domain = (AppCompatEditText) v2.findViewById(R.id.domainET); domain.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if(domain.getText().toString().contains(";")) domainTIL.setError(invalidDomain); else domainTIL.setError(""); } }); final AppCompatEditText user = (AppCompatEditText) v2.findViewById(R.id.usernameET); user.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if(user.getText().toString().contains(":")) usernameTIL.setError(invalidUsername); else usernameTIL.setError(""); } }); int accentColor = utilsProvider.getColorPreference().getColor(ColorUsage.ACCENT); final AppCompatEditText pass = (AppCompatEditText) v2.findViewById(R.id.passwordET); final AppCompatCheckBox ch = (AppCompatCheckBox) v2.findViewById(R.id.checkBox2); TextView help = (TextView) v2.findViewById(R.id.wanthelp); EditTextColorStateUtil.setTint(conName, accentColor); EditTextColorStateUtil.setTint(user, accentColor); EditTextColorStateUtil.setTint(pass, accentColor); Utils.setTint(ch, accentColor); help.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { GeneralDialogCreation.showSMBHelpDialog(context,PreferenceUtils.getAccentString(sharedPreferences)); } }); ch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (ch.isChecked()) { user.setEnabled(false); pass.setEnabled(false); } else { user.setEnabled(true); pass.setEnabled(true); } } }); if (edit) { String userp = "", passp = "", ipp = "",domainp = ""; conName.setText(name); try { jcifs.Config.registerSmbURLHandler(); URL a = new URL(path); String userinfo = a.getUserInfo(); if (userinfo != null) { String inf = URLDecoder.decode(userinfo, "UTF-8"); int domainDelim = !inf.contains(";") ? 0 : inf.indexOf(';'); domainp = inf.substring(0,domainDelim); if(domainp!=null && domainp.length()>0) inf = inf.substring(domainDelim+1); userp = inf.substring(0, inf.indexOf(":")); passp = inf.substring(inf.indexOf(":") + 1, inf.length()); domain.setText(domainp); user.setText(userp); pass.setText(passp); } else ch.setChecked(true); ipp = a.getHost(); ip.setText(ipp); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } }else if(path!=null && path.length()>0){ conName.setText(name); ip.setText(path); user.requestFocus(); } else { conName.setText(R.string.smb_con); conName.requestFocus(); } ba3.customView(v2, true); ba3.theme(utilsProvider.getAppTheme().getMaterialDialogTheme()); ba3.neutralText(R.string.cancel); ba3.positiveText(R.string.create); if (edit) ba3.negativeText(R.string.delete); ba3.positiveColor(accentColor).negativeColor(accentColor).neutralColor(accentColor); ba3.onPositive(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { String s[]; String ipa = ip.getText().toString(); String con_nam=conName.getText().toString(); String sDomain = domain.getText().toString(); String username = user.getText().toString(); TextInputLayout firstInvalidField = null; if(con_nam==null || con_nam.length()==0){ connectionTIL.setError(emptyName); firstInvalidField = connectionTIL; } if(ipa==null || ipa.length()==0){ ipTIL.setError(emptyAddress); if(firstInvalidField == null) firstInvalidField = ipTIL; } if(sDomain.contains(";")) { domainTIL.setError(invalidDomain); if(firstInvalidField == null) firstInvalidField = domainTIL; } if(username.contains(":")) { usernameTIL.setError(invalidUsername); if(firstInvalidField == null) firstInvalidField = usernameTIL; } if(firstInvalidField != null) { firstInvalidField.requestFocus(); return; } SmbFile smbFile; String domaind = domain.getText().toString(); if (ch.isChecked()) smbFile = connectingWithSmbServer(new String[]{ipa, "", "",domaind}, true); else { String useraw = user.getText().toString(); String useru = useraw.replaceAll(" ", "\\ "); String passp = pass.getText().toString(); smbFile = connectingWithSmbServer(new String[]{ipa, useru, passp,domaind}, false); } if (smbFile == null) return; s = new String[]{conName.getText().toString(), smbFile.getPath()}; if(smbConnectionListener!=null){ // encrypted path means path with encrypted pass smbConnectionListener.addConnection(edit, s[0], createSMBPath(new String[]{ipa, user.getText().toString().replaceAll(" ", "\\ "), pass.getText().toString(), domaind}, false).getPath(), s[1], name, path); } dismiss(); } }); ba3.onNegative(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { if(smbConnectionListener!=null){ smbConnectionListener.deleteConnection(name,path); dismiss(); } } }); ba3.onNeutral(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { dismiss(); } }); return ba3.build(); } public SmbFile connectingWithSmbServer(String[] auth, boolean anonym) { try { String yourPeerIP = auth[0], domain = auth[3]; String path; path = "smb://"+(android.text.TextUtils.isEmpty(domain) ? "" :( URLEncoder.encode(domain + ";","UTF-8")) ) + (anonym ? "" : (URLEncoder.encode(auth[1], "UTF-8") + ":" + CryptUtil.encryptPassword(context, auth[2]) + "@")) + yourPeerIP + "/"; HFile hFile = new HFile(OpenMode.SMB, path); SmbFile smbFile = hFile.getSmbFile(5000); return smbFile; } catch (Exception e) { e.printStackTrace(); return null; } } private SmbFile createSMBPath(String[] auth, boolean anonym) { try { String yourPeerIP = auth[0], domain = auth[3]; String path = "smb://"+(android.text.TextUtils.isEmpty(domain) ? "" :( URLEncoder.encode(domain + ";","UTF-8")) )+ (anonym ? "" : (URLEncoder.encode(auth[1], "UTF-8") + ":" + URLEncoder.encode(auth[2], "UTF-8") + "@")) + yourPeerIP + "/"; SmbFile smbFile = new SmbFile(path); return smbFile; } catch (MalformedURLException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } private static ColorStateList createEditTextColorStateList(int color) { int[][] states = new int[3][]; int[] colors = new int[3]; int i = 0; states[i] = new int[]{-android.R.attr.state_enabled}; colors[i] = Color.parseColor("#f6f6f6"); i++; states[i] = new int[]{-android.R.attr.state_pressed, -android.R.attr.state_focused}; colors[i] = Color.parseColor("#666666"); i++; states[i] = new int[]{}; colors[i] = color; return new ColorStateList(states, colors); } private static void setTint(EditText editText, int color) { if (Build.VERSION.SDK_INT >= 21) return; ColorStateList editTextColorStateList = createEditTextColorStateList(color); if (editText instanceof AppCompatEditText) { ((AppCompatEditText) editText).setSupportBackgroundTintList(editTextColorStateList); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { editText.setBackgroundTintList(editTextColorStateList); } } /** * Parse path to decrypt smb password * @return */ public static String getSmbDecryptedPath(Context context, String path) { StringBuffer buffer = new StringBuffer(); buffer.append(path.substring(0, path.indexOf(":", 4)+1)); String encryptedPassword = path.substring(path.indexOf(":", 4)+1, path.lastIndexOf("@")); String decryptedPassword; try { decryptedPassword = CryptUtil.decryptPassword(context, encryptedPassword); } catch (Exception e) { e.printStackTrace(); decryptedPassword = encryptedPassword; } buffer.append(decryptedPassword); buffer.append(path.substring(path.lastIndexOf("@"), path.length())); return buffer.toString(); } public static String getSmbEncryptedPath(Context context, String path) { StringBuffer buffer = new StringBuffer(); buffer.append(path.substring(0, path.indexOf(":", 4)+1)); String decryptedPassword = path.substring(path.indexOf(":", 4)+1, path.lastIndexOf("@")); String encryptPassword; try { encryptPassword = CryptUtil.encryptPassword(context, decryptedPassword); } catch (Exception e) { e.printStackTrace(); encryptPassword = decryptedPassword; } buffer.append(encryptPassword); buffer.append(path.substring(path.lastIndexOf("@"), path.length())); return buffer.toString(); } }