Add JSch (SFTP) debug logging
-App Settings->Log-File for Debugging->SFTP debug logging -Logs to android log (logcat) if log file is not enabled -Logs to Kp2a log file if it is enabled -Logs are tagged as "KP2AJFS[JSch]" -When enabled, logs ALL levels (DEBUG+). NOTE: Sensitive SSH connection information may be logged!!
This commit is contained in:
		@@ -58,12 +58,12 @@ namespace keepass2android
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private static string LogFilename
 | 
							public static string LogFilename
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			get { return Application.Context.FilesDir.CanonicalPath +"/keepass2android.log"; }
 | 
								get { return Application.Context.FilesDir.CanonicalPath +"/keepass2android.log"; }
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private static bool LogToFile
 | 
							public static bool LogToFile
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			get
 | 
								get
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					package keepass2android.javafilestorage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jcraft.jsch.Logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.FileWriter;
 | 
				
			||||||
 | 
					import java.io.PrintWriter;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class Kp2aJSchLogger implements Logger {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final String PREFIX = "KP2AJFS[JSch]";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private interface ILogger {
 | 
				
			||||||
 | 
					        void log(String message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final class LogEntry {
 | 
				
			||||||
 | 
					        private final String levelTag;
 | 
				
			||||||
 | 
					        private final ILogger logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LogEntry(String levelTag, ILogger logger) {
 | 
				
			||||||
 | 
					            this.levelTag = levelTag;
 | 
				
			||||||
 | 
					            this.logger = logger;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    private static final ILogger DEBUG = msg -> Log.d(PREFIX, msg);
 | 
				
			||||||
 | 
					    private static final LogEntry DEBUG_ENTRY = new LogEntry("D", DEBUG);
 | 
				
			||||||
 | 
					    private static final ILogger ERROR = msg -> Log.e(PREFIX, msg);
 | 
				
			||||||
 | 
					    private static final LogEntry DEFAULT_ENTRY = DEBUG_ENTRY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Map<Integer, LogEntry> loggers = Map.of(
 | 
				
			||||||
 | 
					            Logger.DEBUG, DEBUG_ENTRY,
 | 
				
			||||||
 | 
					            Logger.INFO, new LogEntry("I", msg -> Log.i(PREFIX, msg)),
 | 
				
			||||||
 | 
					            Logger.WARN, new LogEntry("W", msg -> Log.w(PREFIX, msg)),
 | 
				
			||||||
 | 
					            Logger.ERROR, new LogEntry("E", ERROR),
 | 
				
			||||||
 | 
					            Logger.FATAL, new LogEntry("F", msg -> Log.wtf(PREFIX, msg))
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final String logFilename;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Kp2aJSchLogger(String logFilename) {
 | 
				
			||||||
 | 
					        this.logFilename = logFilename;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean isEnabled(int level) {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void log(int level, String message) {
 | 
				
			||||||
 | 
					        if (isEnabled(level))
 | 
				
			||||||
 | 
					            getLogger(level).log(message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ILogger getLogger(int level) {
 | 
				
			||||||
 | 
					        LogEntry entry = loggers.get(level);
 | 
				
			||||||
 | 
					        if (entry == null)
 | 
				
			||||||
 | 
					            entry = DEFAULT_ENTRY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ILogger logger;
 | 
				
			||||||
 | 
					        if (logFilename != null) {
 | 
				
			||||||
 | 
					            logger = createFileLogger(entry);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            logger = entry.logger;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return logger;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ILogger createFileLogger(LogEntry entry) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            final PrintWriter p = new PrintWriter(new FileWriter(logFilename, true));
 | 
				
			||||||
 | 
					            return msg -> {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    String fullMsg = String.join(" ", entry.levelTag, PREFIX, msg);
 | 
				
			||||||
 | 
					                    p.println(fullMsg);
 | 
				
			||||||
 | 
					                } catch (Exception e) {
 | 
				
			||||||
 | 
					                    ERROR.log(e.getMessage());
 | 
				
			||||||
 | 
					                } finally {
 | 
				
			||||||
 | 
					                    p.close();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            ERROR.log(e.getMessage());
 | 
				
			||||||
 | 
					            return entry.logger;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -375,6 +375,14 @@ public class SftpStorage extends JavaFileStorageBase {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setJschLogging(boolean enabled, String logFilename) {
 | 
				
			||||||
 | 
							if (enabled) {
 | 
				
			||||||
 | 
								JSch.setLogger(new Kp2aJSchLogger(logFilename));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								JSch.setLogger(null);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private String createKeyPair(String key_filename) throws JSchException, IOException {
 | 
						private String createKeyPair(String key_filename) throws JSchException, IOException {
 | 
				
			||||||
		String public_key_filename = key_filename + ".pub";
 | 
							String public_key_filename = key_filename + ".pub";
 | 
				
			||||||
		File file = new File(key_filename);
 | 
							File file = new File(key_filename);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -98,6 +98,7 @@
 | 
				
			|||||||
	<string name="TrayTotp_SeedField_key">TrayTotp_SeedField_key</string>
 | 
						<string name="TrayTotp_SeedField_key">TrayTotp_SeedField_key</string>
 | 
				
			||||||
	<string name="TrayTotp_prefs_key">TrayTotp_prefs_key</string>
 | 
						<string name="TrayTotp_prefs_key">TrayTotp_prefs_key</string>
 | 
				
			||||||
  <string name="DebugLog_key">DebugLog_key</string>
 | 
					  <string name="DebugLog_key">DebugLog_key</string>
 | 
				
			||||||
 | 
					  <string name="JSchDebug_key">JSchDebug_key</string>
 | 
				
			||||||
  <string name="DebugLog_prefs_key">DebugLog_prefs_key</string>
 | 
					  <string name="DebugLog_prefs_key">DebugLog_prefs_key</string>
 | 
				
			||||||
  <string name="DebugLog_send_key">DebugLog_send</string>
 | 
					  <string name="DebugLog_send_key">DebugLog_send</string>
 | 
				
			||||||
  <string name="AutofillDisabledQueriesPreference_key">AutofillDisabledQueriesPreference_key</string>
 | 
					  <string name="AutofillDisabledQueriesPreference_key">AutofillDisabledQueriesPreference_key</string>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -680,6 +680,7 @@
 | 
				
			|||||||
	<string name="TrayTotp_prefs">TrayTotp</string>
 | 
						<string name="TrayTotp_prefs">TrayTotp</string>
 | 
				
			||||||
  <string name="DebugLog_prefs_prefs">Log-File for Debugging</string>
 | 
					  <string name="DebugLog_prefs_prefs">Log-File for Debugging</string>
 | 
				
			||||||
  <string name="DebugLog_title">Use log file</string>
 | 
					  <string name="DebugLog_title">Use log file</string>
 | 
				
			||||||
 | 
					  <string name="JSchDebug_title">SFTP debug logging</string>
 | 
				
			||||||
  <string name="DebugLog_summary">Write app output to a local log file</string>
 | 
					  <string name="DebugLog_summary">Write app output to a local log file</string>
 | 
				
			||||||
  <string name="DebugLog_send">Send debug log...</string>
 | 
					  <string name="DebugLog_send">Send debug log...</string>
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -670,10 +670,17 @@
 | 
				
			|||||||
							  android:defaultValue="false"
 | 
												  android:defaultValue="false"
 | 
				
			||||||
							  android:title="@string/DebugLog_title"
 | 
												  android:title="@string/DebugLog_title"
 | 
				
			||||||
							  android:key="@string/DebugLog_key" />
 | 
												  android:key="@string/DebugLog_key" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Preference
 | 
					      <Preference
 | 
				
			||||||
               android:enabled="true"
 | 
					               android:enabled="true"
 | 
				
			||||||
               android:title="@string/DebugLog_send"
 | 
					               android:title="@string/DebugLog_send"
 | 
				
			||||||
               android:key="@string/DebugLog_send_key" />
 | 
					               android:key="@string/DebugLog_send_key" />
 | 
				
			||||||
 | 
					      <CheckBoxPreference
 | 
				
			||||||
 | 
												android:enabled="true"
 | 
				
			||||||
 | 
												  android:persistent="true"
 | 
				
			||||||
 | 
												  android:defaultValue="false"
 | 
				
			||||||
 | 
												  android:title="@string/JSchDebug_title"
 | 
				
			||||||
 | 
												  android:key="@string/JSchDebug_key" />
 | 
				
			||||||
    </PreferenceScreen>
 | 
					    </PreferenceScreen>
 | 
				
			||||||
    </PreferenceScreen>
 | 
					    </PreferenceScreen>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -175,6 +175,7 @@ namespace keepass2android
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            FindPreference(GetString(Resource.String.DebugLog_key)).PreferenceChange += OnDebugLogChanged;
 | 
					            FindPreference(GetString(Resource.String.DebugLog_key)).PreferenceChange += OnDebugLogChanged;
 | 
				
			||||||
            FindPreference(GetString(Resource.String.DebugLog_send_key)).PreferenceClick += OnSendDebug;
 | 
					            FindPreference(GetString(Resource.String.DebugLog_send_key)).PreferenceClick += OnSendDebug;
 | 
				
			||||||
 | 
					            FindPreference(GetString(Resource.String.JSchDebug_key)).PreferenceChange += OnJSchDebugChanged;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            HashSet<string> supportedLocales = new HashSet<string>() { "en", "af", "ar", "az", "be", "bg", "ca", "cs", "da", "de", "el", "es", "eu", "fa", "fi", "fr", "gl", "he", "hr", "hu", "id", "in", "it", "iw", "ja", "ko", "ml", "nb", "nl", "nn", "no", "pl", "pt", "ro", "ru", "si", "sk", "sl", "sr", "sv", "tr", "uk", "vi", "zh" };
 | 
					            HashSet<string> supportedLocales = new HashSet<string>() { "en", "af", "ar", "az", "be", "bg", "ca", "cs", "da", "de", "el", "es", "eu", "fa", "fi", "fr", "gl", "he", "hr", "hu", "id", "in", "it", "iw", "ja", "ko", "ml", "nb", "nl", "nn", "no", "pl", "pt", "ro", "ru", "si", "sk", "sl", "sr", "sv", "tr", "uk", "vi", "zh" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -418,14 +419,29 @@ namespace keepass2android
 | 
				
			|||||||
	    private void OnDebugLogChanged(object sender, Preference.PreferenceChangeEventArgs e)
 | 
						    private void OnDebugLogChanged(object sender, Preference.PreferenceChangeEventArgs e)
 | 
				
			||||||
	    {
 | 
						    {
 | 
				
			||||||
            if ((bool)e.NewValue)
 | 
					            if ((bool)e.NewValue)
 | 
				
			||||||
		    {
 | 
					 | 
				
			||||||
                Kp2aLog.CreateLogFile();
 | 
					                Kp2aLog.CreateLogFile();
 | 
				
			||||||
		    }
 | 
					 | 
				
			||||||
		    else
 | 
							    else
 | 
				
			||||||
		    {
 | 
					 | 
				
			||||||
                Kp2aLog.FinishLogFile();
 | 
					                Kp2aLog.FinishLogFile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            bool jschLogEnable = PreferenceManager.GetDefaultSharedPreferences(Application.Context)
 | 
				
			||||||
 | 
					                .GetBoolean(Application.Context.GetString(Resource.String.JSchDebug_key), false);
 | 
				
			||||||
 | 
					            SetJSchLogging(jschLogEnable);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnJSchDebugChanged(object sender, Preference.PreferenceChangeEventArgs e)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetJSchLogging((bool)e.NewValue);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void SetJSchLogging(bool enabled)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var sftpStorage = new Keepass2android.Javafilestorage.SftpStorage(Context);
 | 
				
			||||||
 | 
					            string? logFilename = null;
 | 
				
			||||||
 | 
					            if (Kp2aLog.LogToFile)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                logFilename = Kp2aLog.LogFilename;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            sftpStorage.SetJschLogging(enabled, logFilename);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    private void AlgorithmPrefChange(object sender, Preference.PreferenceChangeEventArgs preferenceChangeEventArgs)
 | 
						    private void AlgorithmPrefChange(object sender, Preference.PreferenceChangeEventArgs preferenceChangeEventArgs)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user