From 3dbb55fe35b12e5fcbf38e7ecf8de043b16327de Mon Sep 17 00:00:00 2001
From: Arne Schwabe <arne@rfc2549.org>
Date: Mon, 24 Dec 2012 07:47:50 +0100
Subject: Make send mini dump work with non Gmail Mail app, make google sending
 better placed

---
 AndroidManifest.xml                                |   5 +-
 openvpn/Android.mk                                 |   2 +-
 openvpn/src/openvpn/breakpad.cpp                   |  29 +++
 openvpn/src/openvpn/breakpad.h                     |  13 ++
 openvpn/src/openvpn/error.c                        |   7 +
 openvpn/src/openvpn/openvpn.c                      |  10 +-
 openvpn/src/openvpn/socket.c                       |   2 +-
 res/menu/logmenu.xml                               |   6 -
 res/values/strings.xml                             |   3 +
 src/de/blinkt/openvpn/FileProvider.java            | 206 ++++++++++++---------
 src/de/blinkt/openvpn/LogWindow.java               |  70 -------
 src/de/blinkt/openvpn/MainActivity.java            |  14 ++
 src/de/blinkt/openvpn/OpenVPN.java                 |  58 +++---
 src/de/blinkt/openvpn/OpenVPNThread.java           |  21 ++-
 src/de/blinkt/openvpn/OpenVpnManagementThread.java |  10 +-
 src/de/blinkt/openvpn/SendDumpActivity.java        |  60 ++++++
 16 files changed, 314 insertions(+), 202 deletions(-)
 create mode 100644 openvpn/src/openvpn/breakpad.cpp
 create mode 100644 openvpn/src/openvpn/breakpad.h
 create mode 100644 src/de/blinkt/openvpn/SendDumpActivity.java

diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8ccd1e96..564d4396 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -43,6 +43,7 @@
         <activity
             android:name=".LogWindow"
             android:label="@string/openvpn_log" />
+        <activity android:name=".SendDumpActivity" />
         <activity android:name=".FileSelect" />
         <activity android:name=".MainActivity" >
             <intent-filter>
@@ -121,8 +122,8 @@
         <provider
             android:name=".FileProvider"
             android:authorities="de.blinkt.openvpn.FileProvider"
-            android:grantUriPermissions="true"
-            android:exported="true" />
+            android:exported="true"
+            android:grantUriPermissions="true" />
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/openvpn/Android.mk b/openvpn/Android.mk
index 6307e5e0..c68bcccf 100644
--- a/openvpn/Android.mk
+++ b/openvpn/Android.mk
@@ -91,7 +91,7 @@ LOCAL_SRC_FILES:= \
 	src/openvpn/status.c \
 	src/openvpn/tun.c  
 ifneq ($(TARGET_ARCH),mips)
-LOCAL_SRC_FILES+=../jni/icsandroid.cpp
+LOCAL_SRC_FILES+=src/openvpn/breakpad.cpp
 endif
 
 
diff --git a/openvpn/src/openvpn/breakpad.cpp b/openvpn/src/openvpn/breakpad.cpp
new file mode 100644
index 00000000..cfcc10a8
--- /dev/null
+++ b/openvpn/src/openvpn/breakpad.cpp
@@ -0,0 +1,29 @@
+
+#include "breakpad.h"
+#include "client/linux/handler/exception_handler.h"
+
+
+static
+bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
+                  void* context,
+                  bool succeeded) {
+    printf("Dump path: %s\n", descriptor.path());
+    fflush(stdout);
+    fflush(stderr);
+    return succeeded;
+}
+
+static google_breakpad::MinidumpDescriptor* desc;
+static google_breakpad::ExceptionHandler* eh;
+
+void breakpad_setup(void)
+{
+    printf("Initializing Google Breakpad!\n");
+    desc = new google_breakpad::MinidumpDescriptor("/data/data/de.blinkt.openvpn/cache");
+    eh = new google_breakpad::ExceptionHandler(*desc, NULL, DumpCallback, NULL, true,-1);
+}
+
+void breakpad_dodump(void)
+{
+    eh->WriteMinidump();
+}
diff --git a/openvpn/src/openvpn/breakpad.h b/openvpn/src/openvpn/breakpad.h
new file mode 100644
index 00000000..84df62ab
--- /dev/null
+++ b/openvpn/src/openvpn/breakpad.h
@@ -0,0 +1,13 @@
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    void breakpad_setup(void);
+    void breakpad_dodump(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/openvpn/src/openvpn/error.c b/openvpn/src/openvpn/error.c
index ebe9a52d..98611a1b 100644
--- a/openvpn/src/openvpn/error.c
+++ b/openvpn/src/openvpn/error.c
@@ -57,6 +57,10 @@
 #endif
 #endif
 
+#ifdef GOOGLE_BREAKPAD
+#include "breakpad.h"
+#endif
+
 /* Globals */
 unsigned int x_debug_level; /* GLOBAL */
 
@@ -399,6 +403,9 @@ dont_mute (unsigned int flags)
 void
 assert_failed (const char *filename, int line)
 {
+#ifdef GOOGLE_BREAKPAD
+    breakpad_dodump();
+#endif
   msg (M_FATAL, "Assertion failed at %s:%d", filename, line);
 }
 
diff --git a/openvpn/src/openvpn/openvpn.c b/openvpn/src/openvpn/openvpn.c
index 4bae5274..a177d9e8 100644
--- a/openvpn/src/openvpn/openvpn.c
+++ b/openvpn/src/openvpn/openvpn.c
@@ -41,6 +41,10 @@
 
 #define P2P_CHECK_SIG() EVENT_LOOP_CHECK_SIGNAL (c, process_signal_p2p, c);
 
+#ifdef GOOGLE_BREAKPAD
+#include "breakpad.h"
+#endif
+
 static bool
 process_signal_p2p (struct context *c)
 {
@@ -321,12 +325,10 @@ wmain (int argc, wchar_t *wargv[]) {
 #else
 int
 main (int argc, char *argv[]) {
-#ifdef TARGET_ANDROID
 #ifdef GOOGLE_BREAKPAD
-#include "../jni/icsandroid.h"
-    setup_breakpad();
-#endif
+    breakpad_setup();
 #endif
+
 	return openvpn_main(argc, argv);
 }
 #endif
diff --git a/openvpn/src/openvpn/socket.c b/openvpn/src/openvpn/socket.c
index 29a15a63..9a33197c 100644
--- a/openvpn/src/openvpn/socket.c
+++ b/openvpn/src/openvpn/socket.c
@@ -1255,7 +1255,7 @@ resolve_remote (struct link_socket *sock,
 	  if (remote_dynamic)
 	    *remote_dynamic = NULL;
 	}
-      else
+      /*      else, quick hack to fix persistent-remote ....*/
 	{
           CLEAR (sock->info.lsa->actual);
           if(sock->info.lsa->current_remote)
diff --git a/res/menu/logmenu.xml b/res/menu/logmenu.xml
index ec2997b0..bb810df0 100644
--- a/res/menu/logmenu.xml
+++ b/res/menu/logmenu.xml
@@ -1,11 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <menu xmlns:android="http://schemas.android.com/apk/res/android" >
-    <item
-        android:id="@+id/minidump"
-        android:icon="@android:drawable/ic_menu_upload"
-        android:showAsAction="ifRoom|withText"
-        android:title="Send Minidump to developer"
-        android:titleCondensed="Send Minidump"/>
     <item
         android:id="@+id/clearlog"
         android:icon="@drawable/ic_menu_trash_holo_light"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index eea81d8b..3613c7d1 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -236,5 +236,8 @@
     <string name="reconnection_settings">Reconnection settings</string>
     <string name="connectretrymessage">Number of seconds to wait between connection attempts.</string>
     <string name="connectretrywait">Seconds between connections</string>
+    <string name="minidump_generated">OpenVPN crashed unexpectedly. Please consider using the send Minidump option in the main menu</string>
+    <string name="send_minidump">Send Minidump to developer</string>
+    <string name="send_minidump_summary">Send debugging information about last crash to developer</string>
 
 </resources>
diff --git a/src/de/blinkt/openvpn/FileProvider.java b/src/de/blinkt/openvpn/FileProvider.java
index 1147ccf0..e86b544f 100644
--- a/src/de/blinkt/openvpn/FileProvider.java
+++ b/src/de/blinkt/openvpn/FileProvider.java
@@ -24,13 +24,15 @@ import java.io.IOException;
 import java.io.InputStream;
 
 import android.content.ContentProvider;
-import android.content.ContentValues;
 import android.content.ContentProvider.PipeDataWriter;
+import android.content.ContentValues;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
+import android.database.MatrixCursor;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.provider.OpenableColumns;
 import android.util.Log;
 
 /**
@@ -38,92 +40,118 @@ import android.util.Log;
  * our .apk.
  */
 public class FileProvider extends ContentProvider
-        implements PipeDataWriter<InputStream> {
-    @Override
-    public boolean onCreate() {
-        return true;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        // Don't support queries.
-        return null;
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        // Don't support inserts.
-        return null;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        // Don't support deletes.
-        return 0;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        // Don't support updates.
-        return 0;
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        // For this sample, assume all files are .apks.
-        return "application/octet-stream";
-    }
-
-    @Override
-    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
-        // Try to open an asset with the given name.
-    	String path = uri.getPath();
-    	if(path.startsWith("/"))
-    		path = path.replaceFirst("/", "");       
-    	
-    	// I think this already random enough, no need for magic secure cookies
-    	// 1f9563a4-a1f5-2165-255f2219-111823ef.dmp
-    	if (!path.matches("^[0-9a-z-.]*$"))
-    		throw new FileNotFoundException("url not in expect format " + uri);
-
-        try {
-        	
-        	File cachedir = getContext().getCacheDir();
-        	File dumpfile = new File(cachedir,path);
-            InputStream is = new FileInputStream(dumpfile);
-            // Start a new thread that pipes the stream data back to the caller.
-            return new AssetFileDescriptor(
-                    openPipeHelper(uri, null, null, is, this), 0,
-                    dumpfile.length());
-        } catch (IOException e) {
-            FileNotFoundException fnf = new FileNotFoundException("Unable to open minidump " + uri);
-            throw fnf;
-        }
-    }
-
-    @Override
-    public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
-            Bundle opts, InputStream args) {
-        // Transfer data from the asset to the pipe the client is reading.
-        byte[] buffer = new byte[8192];
-        int n;
-        FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
-        try {
-            while ((n=args.read(buffer)) >= 0) {
-                fout.write(buffer, 0, n);
-            }
-        } catch (IOException e) {
-            Log.i("OpenVPNFileProvider", "Failed transferring", e);
-        } finally {
-            try {
-                args.close();
-            } catch (IOException e) {
-            }
-            try {
-                fout.close();
-            } catch (IOException e) {
-            }
-        }
-    }
+implements PipeDataWriter<InputStream> {
+	@Override
+	public boolean onCreate() {
+		return true;
+	}
+
+	@Override
+	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+			String sortOrder) {
+		try {
+			File dumpfile = getFileFromURI(uri);
+
+
+			MatrixCursor c = new MatrixCursor(projection);
+
+			Object[] row = new Object[projection.length];
+			int i=0;
+			for (String r:projection) {
+				if(r.equals(OpenableColumns.SIZE))
+					row[i] = dumpfile.length();
+				if(r.equals(OpenableColumns.DISPLAY_NAME))
+					row[i] = dumpfile.getName();
+				i++;
+			}
+			c.addRow(row);
+			return c;
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+			return null;
+		}
+
+
+	}
+
+	@Override
+	public Uri insert(Uri uri, ContentValues values) {
+		// Don't support inserts.
+		return null;
+	}
+
+	@Override
+	public int delete(Uri uri, String selection, String[] selectionArgs) {
+		// Don't support deletes.
+		return 0;
+	}
+
+	@Override
+	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+		// Don't support updates.
+		return 0;
+	}
+
+	@Override
+	public String getType(Uri uri) {
+		// For this sample, assume all files are .apks.
+		return "application/octet-stream";
+	}
+
+	@Override
+	public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
+		File dumpfile = getFileFromURI(uri);
+
+		try {
+
+			InputStream is = new FileInputStream(dumpfile);
+			// Start a new thread that pipes the stream data back to the caller.
+			return new AssetFileDescriptor(
+					openPipeHelper(uri, null, null, is, this), 0,
+					dumpfile.length());
+		} catch (IOException e) {
+			FileNotFoundException fnf = new FileNotFoundException("Unable to open minidump " + uri);
+			throw fnf;
+		}
+	}
+
+	private File getFileFromURI(Uri uri) throws FileNotFoundException {
+		// Try to open an asset with the given name.
+		String path = uri.getPath();
+		if(path.startsWith("/"))
+			path = path.replaceFirst("/", "");       
+
+		// I think this already random enough, no need for magic secure cookies
+		// 1f9563a4-a1f5-2165-255f2219-111823ef.dmp
+		if (!path.matches("^[0-9a-z-.]*(dmp|dmp.log)$"))
+			throw new FileNotFoundException("url not in expect format " + uri);
+		File cachedir = getContext().getCacheDir();
+		File dumpfile = new File(cachedir,path);
+		return dumpfile;
+	}
+
+	@Override
+	public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
+			Bundle opts, InputStream args) {
+		// Transfer data from the asset to the pipe the client is reading.
+		byte[] buffer = new byte[8192];
+		int n;
+		FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
+		try {
+			while ((n=args.read(buffer)) >= 0) {
+				fout.write(buffer, 0, n);
+			}
+		} catch (IOException e) {
+			Log.i("OpenVPNFileProvider", "Failed transferring", e);
+		} finally {
+			try {
+				args.close();
+			} catch (IOException e) {
+			}
+			try {
+				fout.close();
+			} catch (IOException e) {
+			}
+		}
+	}
 }
diff --git a/src/de/blinkt/openvpn/LogWindow.java b/src/de/blinkt/openvpn/LogWindow.java
index 3c0f7246..790e143a 100644
--- a/src/de/blinkt/openvpn/LogWindow.java
+++ b/src/de/blinkt/openvpn/LogWindow.java
@@ -1,9 +1,5 @@
 package de.blinkt.openvpn;
 
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Vector;
 
 import android.app.AlertDialog;
@@ -16,7 +12,6 @@ import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.database.DataSetObserver;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Handler.Callback;
@@ -227,8 +222,6 @@ public class LogWindow extends ListActivity implements StateListener  {
 		} else if(item.getItemId()==R.id.info) {
 			if(mBconfig==null)
 				OpenVPN.triggerLogBuilderConfig();
-		} else if(item.getItemId()==R.id.minidump) {
-			emailMiniDumps();
 
 		} else if(item.getItemId()==R.id.send) {
 			ladapter.shareLog();
@@ -258,72 +251,9 @@ public class LogWindow extends ListActivity implements StateListener  {
 	public boolean onCreateOptionsMenu(Menu menu) {
 		MenuInflater inflater = getMenuInflater();
 		inflater.inflate(R.menu.logmenu, menu);
-		
-		
-		
-		if(getLastestDump()==null)
-			menu.removeItem(R.id.minidump);
-		
 		return true;
 	}
 
-	private File getLastestDump() {
-		long newestDumpTime=0;
-		File newestDumpFile=null;
-
-		for(File f:getCacheDir().listFiles()) {
-			if(!f.getName().endsWith(".dmp"))
-				continue;
-
-			if (newestDumpTime < f.lastModified()) {
-				newestDumpTime = f.lastModified();
-				newestDumpFile=f;
-			}
-		}
-		return newestDumpFile;
-	}
-
-
-	public void emailMiniDumps()
-	{
-		//need to "send multiple" to get more than one attachment
-		final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE);
-		emailIntent.setType("*/*");
-		emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, 
-				new String[]{"Arne Schwabe <arne@rfc2549.org>"});
-		emailIntent.putExtra(Intent.EXTRA_SUBJECT, "OpenVPN Minidump");
-
-		
-		ArrayList<CharSequence> textarraylist = new ArrayList<CharSequence>();
-		textarraylist.add("Please describe the issue you have experienced");
-		emailIntent.putExtra(Intent.EXTRA_TEXT, textarraylist);
-
-
-		ArrayList<Uri> uris = new ArrayList<Uri>();
-
-		File ldump = getLastestDump();
-		if(ldump==null) {
-			OpenVPN.logError("No Minidump found!");
-		}
-		
-		uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/" + ldump.getName()));
-		uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/openvpn.log"));
-		
-		
-		try {
-			FileWriter logout = new FileWriter(new File(getCacheDir(),"openvpn.log"));
-			logout.write(ladapter.getLogStr());
-			logout.close();
-
-		} catch (IOException e1) {
-			OpenVPN.logError("Error writing log: " + e1.getLocalizedMessage());
-		}
-		
-		//emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-		emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
-		startActivity(emailIntent);
-	}
-
 
 	@Override
 	protected void onResume() {
diff --git a/src/de/blinkt/openvpn/MainActivity.java b/src/de/blinkt/openvpn/MainActivity.java
index 9b329817..32cf575b 100644
--- a/src/de/blinkt/openvpn/MainActivity.java
+++ b/src/de/blinkt/openvpn/MainActivity.java
@@ -1,8 +1,11 @@
 package de.blinkt.openvpn;
 
+import java.io.File;
+import java.util.ArrayList;
 import java.util.List;
 
 import android.content.Intent;
+import android.net.Uri;
 import android.preference.PreferenceActivity;
 
 public class MainActivity extends PreferenceActivity {
@@ -19,6 +22,15 @@ public class MainActivity extends PreferenceActivity {
 			translation.summary = translatedby;
 			target.add(translation);
 		}
+		
+		if(SendDumpActivity.getLastestDump(this)!=null) {
+			Header sendDump = new Header();
+			sendDump.titleRes = R.string.send_minidump;
+			sendDump.summaryRes = R.string.send_minidump_summary;
+			sendDump.intent = new Intent(this,SendDumpActivity.class);
+			target.add(sendDump);
+		}
+		
 	}
 	
 	@Override
@@ -29,4 +41,6 @@ public class MainActivity extends PreferenceActivity {
 		
 		
 	}
+
+
 }
diff --git a/src/de/blinkt/openvpn/OpenVPN.java b/src/de/blinkt/openvpn/OpenVPN.java
index 48623865..0ae681bc 100644
--- a/src/de/blinkt/openvpn/OpenVPN.java
+++ b/src/de/blinkt/openvpn/OpenVPN.java
@@ -7,10 +7,10 @@ import android.content.Context;
 import android.os.Build;
 
 public class OpenVPN {
-	
+
 
 	public static LinkedList<LogItem> logbuffer;
-	
+
 	private static Vector<LogListener> logListener;
 	private static Vector<StateListener> stateListener;
 	private static String[] mBconfig;
@@ -18,14 +18,14 @@ public class OpenVPN {
 	private static String mLaststatemsg;
 
 	private static String mLaststate;
-	
+
 	static {
 		logbuffer  = new LinkedList<LogItem>();
 		logListener = new Vector<OpenVPN.LogListener>();
 		stateListener = new Vector<OpenVPN.StateListener>();
 		logInformation();
 	}
-	
+
 	static class LogItem {
 		public static final int ERROR = 1;
 		public static final int INFO = 2;
@@ -36,20 +36,20 @@ public class OpenVPN {
 		private int mRessourceId;
 		// Default log priority
 		int mLevel = INFO;
-		
+
 		public LogItem(int ressourceId, Object[] args) {
-		 mRessourceId = ressourceId;
-		 mArgs = args;
+			mRessourceId = ressourceId;
+			mArgs = args;
 		}
 
-		
+
 		public LogItem(int loglevel,int ressourceId, Object[] args) {
-			 mRessourceId = ressourceId;
-			 mArgs = args;
-			 mLevel = loglevel;
-			}
+			mRessourceId = ressourceId;
+			mArgs = args;
+			mLevel = loglevel;
+		}
+
 
-		
 		public LogItem(String message) {
 			mMessage = message;
 		}
@@ -70,28 +70,32 @@ public class OpenVPN {
 			if(mMessage !=null) {
 				return mMessage;
 			} else {
-				if(mArgs == null)
-					return c.getString(mRessourceId);
-				else
-					return c.getString(mRessourceId,mArgs);
+				if(c!=null) {
+					if(mArgs == null)
+						return c.getString(mRessourceId);
+					else
+						return c.getString(mRessourceId,mArgs);
+				} else {
+					return String.format("Log (no context) resid %d", mRessourceId);
+				}
 			}
 		}
 	}
-	
+
 	private static final int MAXLOGENTRIES = 200;
 
 
 	public static final String MANAGMENT_PREFIX = "M:";
 
 
-	
+
 
 
 
 	public interface LogListener {
 		void newLog(LogItem logItem);
 	}
-	
+
 	public interface StateListener {
 		void updateState(String state, String logmessage);
 	}
@@ -108,7 +112,7 @@ public class OpenVPN {
 	}
 
 	private static void logInformation() {
-		
+
 		logInfo(R.string.mobile_info,Build.MODEL, Build.BOARD,Build.BRAND,Build.VERSION.SDK_INT);
 	}
 
@@ -120,7 +124,7 @@ public class OpenVPN {
 		logListener.remove(ll);
 	}
 
-	
+
 	synchronized static void addStateListener(StateListener sl){
 		stateListener.add(sl);
 		if(mLaststate!=null)
@@ -156,7 +160,7 @@ public class OpenVPN {
 	public synchronized static void updateStateString(String state, String msg) {
 		mLaststate= state;
 		mLaststatemsg = msg;
-		
+
 		for (StateListener sl : stateListener) {
 			sl.updateState(state,msg);
 		}
@@ -174,7 +178,7 @@ public class OpenVPN {
 		logbuffer.addLast(logItem);
 		if(logbuffer.size()>MAXLOGENTRIES)
 			logbuffer.removeFirst();
-		
+
 		for (LogListener ll : logListener) {
 			ll.newLog(logItem);
 		}
@@ -182,7 +186,7 @@ public class OpenVPN {
 
 	public static void logError(String msg) {
 		newlogItem(new LogItem(LogItem.ERROR, msg));
-		
+
 	}
 
 	public static void logError(int ressourceId) {
@@ -191,6 +195,6 @@ public class OpenVPN {
 	public static void logError(int ressourceId, Object... args) {
 		newlogItem(new LogItem(LogItem.ERROR, ressourceId,args));
 	}
-	
-	
+
+
 }
diff --git a/src/de/blinkt/openvpn/OpenVPNThread.java b/src/de/blinkt/openvpn/OpenVPNThread.java
index 13b8688b..7d58552a 100644
--- a/src/de/blinkt/openvpn/OpenVPNThread.java
+++ b/src/de/blinkt/openvpn/OpenVPNThread.java
@@ -1,19 +1,24 @@
 package de.blinkt.openvpn;
 
 import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.LinkedList;
 
 import android.util.Log;
+import de.blinkt.openvpn.OpenVPN.LogItem;
 
 public class OpenVPNThread implements Runnable {
+	private static final String DUMP_PATH_STRING = "Dump path: ";
 	private static final String TAG = "OpenVPN";
 	private String[] mArgv;
 	private Process mProcess;
 	private String mNativeDir;
 	private OpenVpnService mService;
+	private String mDumpPath;
 
 	public OpenVPNThread(OpenVpnService service,String[] argv, String nativelibdir)
 	{
@@ -49,7 +54,18 @@ public class OpenVPNThread implements Runnable {
 				OpenVPN.logError("Process exited with exit value " + exitvalue);
 			
 			OpenVPN.updateStateString("NOPROCESS","No process running.");
-
+			if(mDumpPath!=null) {
+				try {
+					BufferedWriter logout = new BufferedWriter(new FileWriter(mDumpPath + ".log"));
+					for(LogItem li :OpenVPN.getlogbuffer()){
+						logout.write(li.getString(null) + "\n");
+					}
+					logout.close();
+					OpenVPN.logError(R.string.minidump_generated);
+				} catch (IOException e) {
+					OpenVPN.logError("Writing minidump log: " +e.getLocalizedMessage());
+				}
+			}
 
 			mService.processDied();
 			Log.i(TAG, "Exiting");
@@ -89,6 +105,9 @@ public class OpenVPNThread implements Runnable {
 				
 			while(true) {
 				String logline = br.readLine();
+				if (logline.startsWith(DUMP_PATH_STRING))
+					mDumpPath = logline.substring(DUMP_PATH_STRING.length());
+					
 				if(logline==null) {
 					return;
 				}
diff --git a/src/de/blinkt/openvpn/OpenVpnManagementThread.java b/src/de/blinkt/openvpn/OpenVpnManagementThread.java
index d15e6a33..9926ba13 100644
--- a/src/de/blinkt/openvpn/OpenVpnManagementThread.java
+++ b/src/de/blinkt/openvpn/OpenVpnManagementThread.java
@@ -39,7 +39,8 @@ public class OpenVpnManagementThread implements Runnable {
 	private long mLastOut=0;
 	private LocalServerSocket mServerSocket;
 	private boolean mReleaseHold=true;
-	private boolean mWaitingForRelease=false; 
+	private boolean mWaitingForRelease=false;
+	private long mLastHoldRelease=0; 
 
 	private static Vector<OpenVpnManagementThread> active=new Vector<OpenVpnManagementThread>();
 
@@ -223,8 +224,15 @@ public class OpenVpnManagementThread implements Runnable {
 		}
 	}
 	private void releaseHoldCmd() {
+		if ((System.currentTimeMillis()- mLastHoldRelease) < 5000) {
+			try {
+				Thread.sleep(3000);
+			} catch (InterruptedException e) {}
+			
+		}
 		mWaitingForRelease=false;
 		mReleaseHold=true;
+		mLastHoldRelease  = System.currentTimeMillis();
 		managmentCommand("hold release\n");
 		managmentCommand("bytecount " + mBytecountinterval + "\n");
 		managmentCommand("state on\n");
diff --git a/src/de/blinkt/openvpn/SendDumpActivity.java b/src/de/blinkt/openvpn/SendDumpActivity.java
new file mode 100644
index 00000000..8a09b535
--- /dev/null
+++ b/src/de/blinkt/openvpn/SendDumpActivity.java
@@ -0,0 +1,60 @@
+package de.blinkt.openvpn;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+public class SendDumpActivity extends Activity {
+	
+	protected void onStart() {
+		super.onStart();
+		emailMiniDumps();
+		finish();
+	};
+	
+	public void emailMiniDumps()
+	{
+		//need to "send multiple" to get more than one attachment
+		final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE);
+		emailIntent.setType("*/*");
+		emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, 
+				new String[]{"Arne Schwabe <arne@rfc2549.org>"});
+		emailIntent.putExtra(Intent.EXTRA_SUBJECT, "OpenVPN Minidump");
+
+		emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe the issue you have experienced");
+
+		ArrayList<Uri> uris = new ArrayList<Uri>();
+
+		File ldump = getLastestDump(this);
+		if(ldump==null) {
+			OpenVPN.logError("No Minidump found!");
+		}
+		
+		uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/" + ldump.getName()));
+		uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/" + ldump.getName() + ".log"));
+
+		emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+		emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
+		startActivity(emailIntent);
+	}
+
+	static public File getLastestDump(Context c) {
+		long newestDumpTime=0;
+		File newestDumpFile=null;
+
+		for(File f:c.getCacheDir().listFiles()) {
+			if(!f.getName().endsWith(".dmp"))
+				continue;
+
+			if (newestDumpTime < f.lastModified()) {
+				newestDumpTime = f.lastModified();
+				newestDumpFile=f;
+			}
+		}
+		return newestDumpFile;
+	}
+}
-- 
cgit v1.2.3