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:
Rick Brown
2023-07-12 16:51:17 -04:00
parent c16eeff130
commit 464fe43323
7 changed files with 136 additions and 10 deletions

View File

@@ -58,12 +58,12 @@ namespace keepass2android
}
private static string LogFilename
public static string LogFilename
{
get { return Application.Context.FilesDir.CanonicalPath +"/keepass2android.log"; }
}
private static bool LogToFile
public static bool LogToFile
{
get
{

View File

@@ -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;
}
}
}

View File

@@ -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 {
String public_key_filename = key_filename + ".pub";
File file = new File(key_filename);

View File

@@ -98,6 +98,7 @@
<string name="TrayTotp_SeedField_key">TrayTotp_SeedField_key</string>
<string name="TrayTotp_prefs_key">TrayTotp_prefs_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_send_key">DebugLog_send</string>
<string name="AutofillDisabledQueriesPreference_key">AutofillDisabledQueriesPreference_key</string>

View File

@@ -680,6 +680,7 @@
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Log-File for Debugging</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_send">Send debug log...</string>

View File

@@ -670,10 +670,17 @@
android:defaultValue="false"
android:title="@string/DebugLog_title"
android:key="@string/DebugLog_key" />
<Preference
android:enabled="true"
android:title="@string/DebugLog_send"
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>

View File

@@ -175,6 +175,7 @@ namespace keepass2android
FindPreference(GetString(Resource.String.DebugLog_key)).PreferenceChange += OnDebugLogChanged;
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" };
@@ -417,16 +418,31 @@ namespace keepass2android
private void OnDebugLogChanged(object sender, Preference.PreferenceChangeEventArgs e)
{
if ((bool)e.NewValue)
{
Kp2aLog.CreateLogFile();
}
if ((bool)e.NewValue)
Kp2aLog.CreateLogFile();
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)
{