OneDrive: handle null modification time

WebDav: use custom HostnameVerifier to allow to ignore certificate problems
File chooser: improve error reporting in C# app
FileSelectHelper: fix issue with incorrect handling of default scheme in webdav dialog
release 1.01-c
This commit is contained in:
Philipp Crocoll 2017-01-27 21:03:53 +01:00
parent 7ccf55f84f
commit 14f7167340
12 changed files with 129 additions and 38 deletions

View File

@ -289,7 +289,8 @@ public class OneDriveStorage extends JavaFileStorageBase
e.displayName = i.name; e.displayName = i.name;
e.canRead = e.canWrite = true; e.canRead = e.canWrite = true;
e.path = getProtocolId() +"://"+path; e.path = getProtocolId() +"://"+path;
e.lastModifiedTime = i.lastModifiedDateTime.getTimeInMillis(); if (i.lastModifiedDateTime != null)
e.lastModifiedTime = i.lastModifiedDateTime.getTimeInMillis();
e.isDirectory = i.folder != null; e.isDirectory = i.folder != null;
return e; return e;
} }

View File

@ -37,6 +37,7 @@ import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import keepass2android.javafilestorage.webdav.ConnectionInfo; import keepass2android.javafilestorage.webdav.ConnectionInfo;
import keepass2android.javafilestorage.webdav.DecoratedHostnameVerifier;
import keepass2android.javafilestorage.webdav.DecoratedTrustManager; import keepass2android.javafilestorage.webdav.DecoratedTrustManager;
import keepass2android.javafilestorage.webdav.PropfindXmlParser; import keepass2android.javafilestorage.webdav.PropfindXmlParser;
import keepass2android.javafilestorage.webdav.WebDavUtil; import keepass2android.javafilestorage.webdav.WebDavUtil;
@ -45,6 +46,7 @@ import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
import okhttp3.internal.tls.OkHostnameVerifier;
public class WebDavStorage extends JavaFileStorageBase { public class WebDavStorage extends JavaFileStorageBase {
@ -131,6 +133,8 @@ public class WebDavStorage extends JavaFileStorageBase {
builder = builder.authenticator(new CachingAuthenticatorDecorator(authenticator, authCache)) builder = builder.authenticator(new CachingAuthenticatorDecorator(authenticator, authCache))
.addInterceptor(new AuthenticationCacheInterceptor(authCache)); .addInterceptor(new AuthenticationCacheInterceptor(authCache));
if ((mCertificateErrorHandler != null) && (!mCertificateErrorHandler.alwaysFailOnValidationError())) { if ((mCertificateErrorHandler != null) && (!mCertificateErrorHandler.alwaysFailOnValidationError())) {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm()); TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null); trustManagerFactory.init((KeyStore) null);
@ -145,7 +149,10 @@ public class WebDavStorage extends JavaFileStorageBase {
sslContext.init(null, new TrustManager[] { trustManager }, null); sslContext.init(null, new TrustManager[] { trustManager }, null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
builder = builder.sslSocketFactory(sslSocketFactory, trustManager); builder = builder.sslSocketFactory(sslSocketFactory, trustManager)
.hostnameVerifier(new DecoratedHostnameVerifier(OkHostnameVerifier.INSTANCE, mCertificateErrorHandler));
} }
OkHttpClient client = builder.build(); OkHttpClient client = builder.build();

View File

@ -0,0 +1,41 @@
package keepass2android.javafilestorage.webdav;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import keepass2android.javafilestorage.ICertificateErrorHandler;
import okhttp3.internal.tls.OkHostnameVerifier;
/**
* Created by Philipp on 27.01.2017.
*/
public class DecoratedHostnameVerifier implements HostnameVerifier
{
public DecoratedHostnameVerifier(HostnameVerifier hostnameVerifier, ICertificateErrorHandler mCertificateErrorHandler) {
this.hostnameVerifier = hostnameVerifier;
certificateErrorHandler = mCertificateErrorHandler;
}
@Override
public boolean verify(String host, SSLSession sslSession) {
boolean baseResult = hostnameVerifier.verify(host, sslSession);
if (baseResult)
return true; //verification ok
if ((certificateErrorHandler == null) || (!certificateErrorHandler.onValidationError("Failed to verify host " + host))) {
//certificate error handler does not allow to ignore the error
return false;
}
//certificate error handler did display a warning probably and allowed to ignore the error.
return true;
}
private final HostnameVerifier hostnameVerifier;
private final ICertificateErrorHandler certificateErrorHandler;
}

View File

@ -1024,6 +1024,8 @@ public class FragmentFiles extends Fragment implements
String errMsg = null; String errMsg = null;
boolean errorMessageInDialog = false; boolean errorMessageInDialog = false;
String log = "";
@Override @Override
protected Bundle doInBackground(Void... params) { protected Bundle doInBackground(Void... params) {
/* /*
@ -1032,6 +1034,12 @@ public class FragmentFiles extends Fragment implements
Uri path = (Uri) (savedInstanceState != null ? savedInstanceState Uri path = (Uri) (savedInstanceState != null ? savedInstanceState
.getParcelable(CURRENT_LOCATION) : null); .getParcelable(CURRENT_LOCATION) : null);
log += "savedInstanceState != null ? " + (savedInstanceState != null);
log += "\npath != null ? " + (path != null);
if (path != null) log += path;
log += "\nmRoot != null ? " + (mRoot != null);
if (mRoot != null) log += mRoot;
if (mRoot != null) { if (mRoot != null) {
Uri queryUri = BaseFile.genContentUriApi(mRoot.getAuthority()) Uri queryUri = BaseFile.genContentUriApi(mRoot.getAuthority())
.buildUpon() .buildUpon()
@ -1042,10 +1050,11 @@ public class FragmentFiles extends Fragment implements
Cursor cursor = getActivity().getContentResolver().query( Cursor cursor = getActivity().getContentResolver().query(
queryUri, queryUri,
null, null, null, null); null, null, null, null);
log += "\ncursor != null ? " + (cursor != null);
if (cursor != null) { if (cursor != null) {
cursor.moveToFirst(); cursor.moveToFirst();
errMsg = getString(R.string.afc_msg_cannot_connect_to_file_provider_service) + " " + cursor.getString(0); errMsg = getString(R.string.afc_msg_cannot_connect_to_file_provider_service) + " " + " " + cursor.getString(0);
errorMessageInDialog = true; errorMessageInDialog = true;
return null; return null;
} }
@ -1057,6 +1066,7 @@ public class FragmentFiles extends Fragment implements
/* /*
* Selected file * Selected file
*/ */
log += "try selected file ";
if (path == null) { if (path == null) {
path = (Uri) getArguments().getParcelable( path = (Uri) getArguments().getParcelable(
FileChooserActivity.EXTRA_SELECT_FILE); FileChooserActivity.EXTRA_SELECT_FILE);
@ -1067,15 +1077,23 @@ public class FragmentFiles extends Fragment implements
path = BaseFileProviderUtils.getParentFile( path = BaseFileProviderUtils.getParentFile(
getActivity(), path); getActivity(), path);
} }
log += "success ? " + (path != null);
/* /*
* Rootpath * Rootpath
*/ */
if (path == null log += "rootpath";
|| !BaseFileProviderUtils.isDirectory(getActivity(), if ((path == null)
path)) { || (!BaseFileProviderUtils.isDirectory(getActivity(),
path))) {
log += " assign";
path = mRoot; path = mRoot;
} }
if (path != null) {
log += " path=" + path;
log += " isdir?" + BaseFileProviderUtils.isDirectory(getActivity(),
path);
}
/* /*
* Last location * Last location
@ -1084,29 +1102,35 @@ public class FragmentFiles extends Fragment implements
&& DisplayPrefs.isRememberLastLocation(getActivity())) { && DisplayPrefs.isRememberLastLocation(getActivity())) {
String lastLocation = DisplayPrefs String lastLocation = DisplayPrefs
.getLastLocation(getActivity()); .getLastLocation(getActivity());
if (lastLocation != null) if (lastLocation != null) {
path = Uri.parse(lastLocation); path = Uri.parse(lastLocation);
log += " from last loc";
}
} }
if (path == null if (path == null
|| !BaseFileProviderUtils.isDirectory(getActivity(), || !BaseFileProviderUtils.isDirectory(getActivity(),
path)) path)) {
path = BaseFileProviderUtils.getDefaultPath( path = BaseFileProviderUtils.getDefaultPath(
getActivity(), getActivity(),
path == null ? mFileProviderAuthority : path path == null ? mFileProviderAuthority : path
.getAuthority()); .getAuthority());
log += " getDefault. path==null?" + (path == null);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
errMsg = getString(R.string.afc_msg_cannot_connect_to_file_provider_service) + " " + ex.toString(); errMsg = getString(R.string.afc_msg_cannot_connect_to_file_provider_service) + " " + ex.toString() ;
errorMessageInDialog = true; errorMessageInDialog = true;
return null; return null;
} }
if (path == null) if (path == null) {
errMsg = "Did not find initial path.";
errorMessageInDialog = true;
return null; return null;
}
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
Log.d(CLASSNAME, "loadInitialPath() >> " + path); Log.d(CLASSNAME, "loadInitialPath() >> " + path);
@ -1117,6 +1141,7 @@ public class FragmentFiles extends Fragment implements
result.putParcelable(PATH, path); result.putParcelable(PATH, path);
return result; return result;
} else { } else {
errorMessageInDialog = true;
errMsg = getString(R.string.afc_pmsg_cannot_access_dir, errMsg = getString(R.string.afc_pmsg_cannot_access_dir,
BaseFileProviderUtils.getFileName(getActivity(), BaseFileProviderUtils.getFileName(getActivity(),
path)); path));
@ -1162,7 +1187,7 @@ public class FragmentFiles extends Fragment implements
} }
} else } else
showCannotConnectToServiceAndWaitForTheUserToFinish(); showCannotConnectToServiceAndWaitForTheUserToFinish("loadInitialPath");
}// onPostExecute() }// onPostExecute()
}.execute(); }.execute();
@ -1195,9 +1220,9 @@ public class FragmentFiles extends Fragment implements
/** /**
* As the name means... * As the name means...
*/ */
private void showCannotConnectToServiceAndWaitForTheUserToFinish() { private void showCannotConnectToServiceAndWaitForTheUserToFinish(String info) {
Dlg.showError(getActivity(), Dlg.showError(getActivity(),
R.string.afc_msg_cannot_connect_to_file_provider_service, getActivity().getString(R.string.afc_msg_cannot_connect_to_file_provider_service) + " " + info,
new DialogInterface.OnCancelListener() { new DialogInterface.OnCancelListener() {
@Override @Override
@ -1808,7 +1833,7 @@ public class FragmentFiles extends Fragment implements
* a directory, of course. * a directory, of course.
* @since v4.3 beta * @since v4.3 beta
*/ */
private void goTo(Uri dir) { private void goTo(final Uri dir) {
new LoadingDialog<Uri, String, Bundle>(getActivity(), false) { new LoadingDialog<Uri, String, Bundle>(getActivity(), false) {
/** /**
@ -1866,7 +1891,7 @@ public class FragmentFiles extends Fragment implements
Dlg.toast(getActivity(), errMsg, Dlg.LENGTH_SHORT); Dlg.toast(getActivity(), errMsg, Dlg.LENGTH_SHORT);
} }
else else
showCannotConnectToServiceAndWaitForTheUserToFinish(); showCannotConnectToServiceAndWaitForTheUserToFinish("goTo: " + dir.toString());
}// onPostExecute() }// onPostExecute()
}.execute(dir); }.execute(dir);

View File

@ -66,6 +66,9 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
@Override @Override
public boolean onCreate() { public boolean onCreate() {
Log.d("KP2A_FC_P", "onCreate");
BaseFileProviderUtils.registerProviderInfo(_ID, BaseFileProviderUtils.registerProviderInfo(_ID,
getAuthority()); getAuthority());
@ -84,7 +87,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
@Override @Override
public int delete(Uri uri, String selection, String[] selectionArgs) { public int delete(Uri uri, String selection, String[] selectionArgs) {
if (Utils.doLog()) if (Utils.doLog())
Log.d(CLASSNAME, "delete() >> " + uri); Log.d("KP2A_FC_P", "delete() >> " + uri);
int count = 0; int count = 0;
@ -136,7 +139,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
@Override @Override
public Uri insert(Uri uri, ContentValues values) { public Uri insert(Uri uri, ContentValues values) {
if (Utils.doLog()) if (Utils.doLog())
Log.d(CLASSNAME, "insert() >> " + uri); Log.d("KP2A_FC_P", "insert() >> " + uri);
switch (URI_MATCHER.match(uri)) { switch (URI_MATCHER.match(uri)) {
case URI_DIRECTORY: case URI_DIRECTORY:
@ -184,7 +187,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
public Cursor query(Uri uri, String[] projection, String selection, public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) { String[] selectionArgs, String sortOrder) {
if (Utils.doLog()) if (Utils.doLog())
Log.d(CLASSNAME, String.format( Log.d("KP2A_FC_P", String.format(
"query() >> uri = %s (%s) >> match = %s", uri, "query() >> uri = %s (%s) >> match = %s", uri,
uri.getLastPathSegment(), URI_MATCHER.match(uri))); uri.getLastPathSegment(), URI_MATCHER.match(uri)));
@ -223,10 +226,13 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
try try
{ {
checkConnection(uri); checkConnection(uri);
Log.d("KP2A_FC_P", "checking connection for " + uri + " ok.");
return null; return null;
} }
catch (Exception e) catch (Exception e)
{ {
Log.d("KP2A_FC_P","Check connection failed with: " + e.toString());
MatrixCursor matrixCursor = new MatrixCursor(BaseFileProviderUtils.CONNECTION_CHECK_CURSOR_COLUMNS); MatrixCursor matrixCursor = new MatrixCursor(BaseFileProviderUtils.CONNECTION_CHECK_CURSOR_COLUMNS);
RowBuilder newRow = matrixCursor.newRow(); RowBuilder newRow = matrixCursor.newRow();
String message = e.getLocalizedMessage(); String message = e.getLocalizedMessage();
@ -244,11 +250,17 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
{ {
String path = Uri.parse( String path = Uri.parse(
uri.getQueryParameter(BaseFile.PARAM_SOURCE)).toString(); uri.getQueryParameter(BaseFile.PARAM_SOURCE)).toString();
getFileEntry(path); StringBuilder sb = new StringBuilder();
FileEntry result = getFileEntry(path, sb);
if (result == null)
throw new Exception(sb.toString());
} }
catch (FileNotFoundException ex) catch (FileNotFoundException ex)
{ {
Log.d("KP2A_FC_P","File not found. Ignore.");
return; return;
} }
} }
@ -269,7 +281,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
String lastPathSegment = uri.getLastPathSegment(); String lastPathSegment = uri.getLastPathSegment();
//Log.d(CLASSNAME, "lastPathSegment:" + lastPathSegment); Log.d("KP2A_FC_P", "lastPathSegment:" + lastPathSegment);
if (BaseFile.CMD_CANCEL.equals(lastPathSegment)) { if (BaseFile.CMD_CANCEL.equals(lastPathSegment)) {
int taskId = ProviderUtils.getIntQueryParam(uri, int taskId = ProviderUtils.getIntQueryParam(uri,
@ -354,6 +366,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
} else if (BaseFile.CMD_CHECK_CONNECTION.equals(lastPathSegment)) } else if (BaseFile.CMD_CHECK_CONNECTION.equals(lastPathSegment))
{ {
Log.d("KP2A_FC_P","Check connection...");
return getCheckConnectionCursor(uri); return getCheckConnectionCursor(uri);
} }
@ -591,7 +604,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
FileEntry newEntry ; FileEntry newEntry ;
try { try {
//it's not -> query the information. //it's not -> query the information.
newEntry = getFileEntry(filename); newEntry = getFileEntry(filename, null);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;
@ -680,11 +693,11 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
}); });
} catch (CancellationException e) { } catch (CancellationException e) {
if (Utils.doLog()) if (Utils.doLog())
Log.d(CLASSNAME, "sortFiles() >> cancelled..."); Log.d("KP2A_FC_P", "sortFiles() >> cancelled...");
} }
catch (Exception e) catch (Exception e)
{ {
Log.d(CLASSNAME, "sortFiles() >> "+e); Log.d("KP2A_FC_P", "sortFiles() >> "+e);
throw e; throw e;
} }
}// sortFiles() }// sortFiles()
@ -721,11 +734,11 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
if (targetParent != null && targetParent.startsWith(source)) if (targetParent != null && targetParent.startsWith(source))
{ {
if (Utils.doLog()) if (Utils.doLog())
Log.d(CLASSNAME, source+" is parent of "+target); Log.d("KP2A_FC_P", source+" is parent of "+target);
return BaseFileProviderUtils.newClosedCursor(); return BaseFileProviderUtils.newClosedCursor();
} }
if (Utils.doLog()) if (Utils.doLog())
Log.d(CLASSNAME, source+" is no parent of "+target); Log.d("KP2A_FC_P", source+" is no parent of "+target);
return null; return null;
}// doCheckAncestor() }// doCheckAncestor()
@ -764,27 +777,27 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
path = removeTrailingSlash(path); path = removeTrailingSlash(path);
if (path.indexOf("://") == -1) if (path.indexOf("://") == -1)
{ {
Log.d(CLASSNAME, "invalid path: " + path); Log.d("KP2A_FC_P", "invalid path: " + path);
return null; return null;
} }
String pathWithoutProtocol = path.substring(path.indexOf("://")+3); String pathWithoutProtocol = path.substring(path.indexOf("://")+3);
int lastSlashPos = path.lastIndexOf("/"); int lastSlashPos = path.lastIndexOf("/");
if (pathWithoutProtocol.indexOf("/") == -1) if (pathWithoutProtocol.indexOf("/") == -1)
{ {
Log.d(CLASSNAME, "parent of " + path +" is null"); Log.d("KP2A_FC_P", "parent of " + path +" is null");
return null; return null;
} }
else else
{ {
String parent = path.substring(0, lastSlashPos)+"/"; String parent = path.substring(0, lastSlashPos)+"/";
Log.d(CLASSNAME, "parent of " + path +" is "+parent); Log.d("KP2A_FC_P", "parent of " + path +" is "+parent);
return parent; return parent;
} }
} }
protected abstract FileEntry getFileEntry(String path) throws Exception; protected abstract FileEntry getFileEntry(String path, StringBuilder errorMessageBuilder) throws Exception;
/** /**
* Lists all file inside {@code dirName}. * Lists all file inside {@code dirName}.

View File

@ -26,7 +26,7 @@ namespace keepass2android
AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(ctx, Android.Resource.Style.ThemeHoloLightDialog)); AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(ctx, Android.Resource.Style.ThemeHoloLightDialog));
builder.SetTitle(ctx.GetString(Resource.String.ChangeLog_title)); builder.SetTitle(ctx.GetString(Resource.String.ChangeLog_title));
List<string> changeLog = new List<string>{ List<string> changeLog = new List<string>{
ctx.GetString(Resource.String.ChangeLog_1_01b), ctx.GetString(Resource.String.ChangeLog_1_01c),
ctx.GetString(Resource.String.ChangeLog_1_01), ctx.GetString(Resource.String.ChangeLog_1_01),
ctx.GetString(Resource.String.ChangeLog_1_0_0e), ctx.GetString(Resource.String.ChangeLog_1_0_0e),
ctx.GetString(Resource.String.ChangeLog_1_0_0), ctx.GetString(Resource.String.ChangeLog_1_0_0),

View File

@ -77,7 +77,7 @@ namespace keepass2android
string user = dlgContents.FindViewById<EditText>(Resource.Id.http_user).Text; string user = dlgContents.FindViewById<EditText>(Resource.Id.http_user).Text;
string password = dlgContents.FindViewById<EditText>(Resource.Id.http_password).Text; string password = dlgContents.FindViewById<EditText>(Resource.Id.http_password).Text;
string scheme = defaultPath.Substring(defaultPath.IndexOf("://", StringComparison.Ordinal)); string scheme = defaultPath.Substring(0, defaultPath.IndexOf("://", StringComparison.Ordinal));
if (host.Contains("://") == false) if (host.Contains("://") == false)
host = scheme + "://" + host; host = scheme + "://" + host;
string httpPath = new Keepass2android.Javafilestorage.WebDavStorage(null).BuildFullPath(host, user, string httpPath = new Keepass2android.Javafilestorage.WebDavStorage(null).BuildFullPath(host, user,

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="88" android:versionCode="89"
android:versionName="1.01-b" android:versionName="1.01-c"
package="keepass2android.keepass2android" package="keepass2android.keepass2android"
android:installLocation="auto"> android:installLocation="auto">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" /> <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" />

View File

@ -680,9 +680,11 @@
<string name="ActivateAutoFillService_btnAutoFill">Use AutoFill service</string> <string name="ActivateAutoFillService_btnAutoFill">Use AutoFill service</string>
<string name="ActivateAutoFillService_toast">Please enable the Keepass2Android service.</string> <string name="ActivateAutoFillService_toast">Please enable the Keepass2Android service.</string>
<string name="ShowKeyboardDuringFingerprintAuth">Show soft keyboard for password input when fingerprint scan is active.</string> <string name="ShowKeyboardDuringFingerprintAuth">Show soft keyboard for password input when fingerprint scan is active.</string>
<string name="ChangeLog_1_01b"> <string name="ChangeLog_1_01c">
Version 1.01-b\n Version 1.01-c\n
* Fix for QuickUnlock sometimes failing despite correct code.\n * Fix for OneDrive file listing\n
* Allow to ignore certificate errors also when host name verification fails (not recommended for production use)\n
* Fix for QuickUnlock sometimes failing despite correct unlock code\n
</string> </string>
<string name="ChangeLog_0_9_8c"> <string name="ChangeLog_0_9_8c">

View File

@ -62,7 +62,7 @@ namespace keepass2android
} }
} }
protected override FileEntry GetFileEntry(string filename) protected override FileEntry GetFileEntry(string filename, Java.Lang.StringBuilder errorMessageBuilder)
{ {
try try
{ {
@ -70,6 +70,8 @@ namespace keepass2android
} }
catch (Exception e) catch (Exception e)
{ {
if (errorMessageBuilder != null)
errorMessageBuilder.Append(e.Message);
Kp2aLog.Log(e.ToString()); Kp2aLog.Log(e.ToString());
return null; return null;
} }