source: branches/eraser6/CodeReview/Eraser.Util/BlackBox.cs @ 1622

Revision 1622, 23.1 KB checked in by lowjoel, 5 years ago (diff)

Combine the progress and upload steps to the Upload function since compression of the report is strictly the task of the Report Uploader. Addresses #307: Eraser Updater restructuring

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2009 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Text;
25
26using System.Windows.Forms;
27using System.IO;
28using System.Runtime.InteropServices;
29using System.Diagnostics;
30using System.Reflection;
31using Microsoft.Win32.SafeHandles;
32
33using System.Drawing;
34using System.Drawing.Imaging;
35using System.Collections.ObjectModel;
36using System.Globalization;
37
38using ICSharpCode.SharpZipLib.Tar;
39using ICSharpCode.SharpZipLib.BZip2;
40using System.Net;
41using System.Xml;
42
43namespace Eraser.Util
44{
45    /// <summary>
46    /// Handles application exceptions, stores minidumps and uploads them to the
47    /// Eraser server.
48    /// </summary>
49    public class BlackBox
50    {
51        /// <summary>
52        /// Initialises the BlackBox handler. Call this initialiser once throughout
53        /// the lifespan of the application.
54        /// </summary>
55        /// <returns>The global BlackBox instance.</returns>
56        public static BlackBox Get()
57        {
58            if (Instance == null)
59                Instance = new BlackBox();
60            return Instance;
61        }
62
63        /// <summary>
64        /// Creates a new BlackBox report based on the exception provided.
65        /// </summary>
66        /// <param name="e">The exception which triggered this dump.</param>
67        public void CreateReport(Exception e)
68        {
69            if (e == null)
70                throw new ArgumentNullException("e");
71
72            //Generate a unique identifier for this report.
73            string crashName = DateTime.Now.ToUniversalTime().ToString(
74                CrashReportName, CultureInfo.InvariantCulture);
75            string currentCrashReport = Path.Combine(CrashReportsPath, crashName);
76            Directory.CreateDirectory(currentCrashReport);
77
78            //Store the steps which we have completed.
79            int currentStep = 0;
80
81            try
82            {
83                //First, write a user-readable summary
84                WriteDebugLog(currentCrashReport, e);
85                ++currentStep;
86
87                //Take a screenshot
88                WriteScreenshot(currentCrashReport);
89                ++currentStep;
90
91                //Write a memory dump to the folder
92                WriteMemoryDump(currentCrashReport, e);
93                ++currentStep;
94            }
95            catch
96            {
97                //If an exception was caught while creating the report, we should just
98                //abort as that may cause a cascade. However, we need to remove the
99                //report folder if the crash report is empty.
100                if (currentStep == 0)
101                    Directory.Delete(currentCrashReport);
102            }
103        }
104
105        /// <summary>
106        /// Enumerates the list of crash dumps waiting for upload.
107        /// </summary>
108        /// <returns>A string array containing the list of dumps waiting for upload.</returns>
109        public BlackBoxReport[] GetDumps()
110        {
111            DirectoryInfo dirInfo = new DirectoryInfo(CrashReportsPath);
112            List<BlackBoxReport> result = new List<BlackBoxReport>();
113            if (dirInfo.Exists)
114                foreach (DirectoryInfo subDir in dirInfo.GetDirectories())
115                    try
116                    {
117                        result.Add(new BlackBoxReport(Path.Combine(CrashReportsPath, subDir.Name)));
118                    }
119                    catch (InvalidDataException)
120                    {
121                        //Do nothing: invalid reports are automatically deleted.
122                    }
123
124            return result.ToArray();
125        }
126
127        /// <summary>
128        /// Constructor. Use the <see cref="Initialise"/> function to use this class.
129        /// </summary>
130        private BlackBox()
131        {
132            AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
133            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
134        }
135
136        /// <summary>
137        /// Called when an unhandled exception is raised in the application.
138        /// </summary>
139        private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
140        {
141            CreateReport(e.ExceptionObject as Exception);
142        }
143
144        /// <summary>
145        /// Dumps the contents of memory to a dumpfile.
146        /// </summary>
147        /// <param name="dumpFolder">Path to the folder to store the dump file.</param>
148        /// <param name="e">The exception which is being handled.</param>
149        private void WriteMemoryDump(string dumpFolder, Exception e)
150        {
151            //Open a file stream
152            using (FileStream stream = new FileStream(Path.Combine(dumpFolder, MemoryDumpFileName),
153                FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
154            {
155                //Store the exception information
156                NativeMethods.MiniDumpExceptionInfo exception =
157                    new NativeMethods.MiniDumpExceptionInfo();
158                exception.ClientPointers = false;
159                exception.ExceptionPointers = Marshal.GetExceptionPointers();
160                exception.ThreadId = (uint)AppDomain.GetCurrentThreadId();
161
162                NativeMethods.MiniDumpWriteDump(Process.GetCurrentProcess().Handle,
163                    (uint)Process.GetCurrentProcess().Id, stream.SafeFileHandle,
164                    NativeMethods.MiniDumpType.MiniDumpWithFullMemory,
165                    ref exception, IntPtr.Zero, IntPtr.Zero);
166            }
167        }
168
169        /// <summary>
170        /// Writes a debug log to the given directory.
171        /// </summary>
172        /// <param name="screenshotPath">The path to store the screenshot into.</param>
173        /// <param name="exception">The exception to log about.</param>
174        private void WriteDebugLog(string dumpFolder, Exception exception)
175        {
176            using (FileStream file = new FileStream(Path.Combine(dumpFolder, DebugLogFileName),
177                FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
178            using (StreamWriter stream = new StreamWriter(file))
179            {
180                //Application information
181                string separator = new string('-', 76);
182                string lineFormat = "{0,15}: {1}";
183                stream.WriteLine("Application Information");
184                stream.WriteLine(separator);
185                stream.WriteLine(string.Format(lineFormat, "Version",
186                    Assembly.GetEntryAssembly().GetName().Version));
187                StringBuilder commandLine = new StringBuilder();
188                foreach (string param in Environment.GetCommandLineArgs())
189                {
190                    commandLine.Append(param);
191                    commandLine.Append(' ');
192                }
193                stream.WriteLine(string.Format(lineFormat, "Command Line",
194                    commandLine.ToString().Trim()));
195
196                //Exception Information
197                stream.WriteLine();
198                stream.WriteLine("Exception Information (Outermost to innermost)");
199                stream.WriteLine(separator);
200
201                //Open a stream to the Stack Trace Log file. We want to separate the stack
202                //trace do we can check against the server to see if the crash is a new one
203                using (StreamWriter stackTraceLog = new StreamWriter(
204                    Path.Combine(dumpFolder, BlackBoxReport.StackTraceFileName)))
205                {
206                    Exception currentException = exception;
207                    for (uint i = 1; currentException != null; ++i)
208                    {
209                        stream.WriteLine(string.Format("Exception {0}:", i));
210                        stream.WriteLine(string.Format(lineFormat, "Message", currentException.Message));
211                        stream.WriteLine(string.Format(lineFormat, "Exception Type",
212                            currentException.GetType().FullName));
213                        stackTraceLog.WriteLine(string.Format("Exception {0}: {1}", i,
214                            currentException.GetType().FullName));
215
216                        //Parse the stack trace
217                        string[] stackTrace = currentException.StackTrace.Split(new char[] { '\n' });
218                        for (uint j = 0; j < stackTrace.Length; ++j)
219                        {
220                            stream.WriteLine(string.Format(lineFormat,
221                                string.Format("Stack Trace [{0}]", j), stackTrace[j].Trim()));
222                            stackTraceLog.WriteLine(string.Format("{0}", stackTrace[j].Trim()));
223                        }
224
225                        uint k = 0;
226                        foreach (System.Collections.DictionaryEntry value in currentException.Data)
227                            stream.WriteLine(string.Format(lineFormat, string.Format("Data[{0}]", ++k),
228                                string.Format("{0} {1}", value.Key.ToString(), value.Value.ToString())));
229
230                        //End the exception and get the inner exception.
231                        stream.WriteLine();
232                        currentException = exception.InnerException;
233                    }
234                }
235            }
236        }
237
238        /// <summary>
239        /// Writes a screenshot to the given directory
240        /// </summary>
241        /// <param name="dumpFolder">The path to save the screenshot to.</param>
242        private void WriteScreenshot(string dumpFolder)
243        {
244            //Get the size of the screen
245            Rectangle rect = new Rectangle(int.MaxValue, int.MaxValue, int.MinValue, int.MinValue);
246            foreach (Screen screen in Screen.AllScreens)
247                rect = Rectangle.Union(rect, screen.Bounds);
248
249            //Copy a screen DC to the screenshot bitmap
250            Bitmap screenShot = new Bitmap(rect.Width, rect.Height);
251            Graphics bitmap = Graphics.FromImage(screenShot);
252            bitmap.CopyFromScreen(0, 0, 0, 0, rect.Size, CopyPixelOperation.SourceCopy);
253
254            //Save the bitmap to disk
255            screenShot.Save(Path.Combine(dumpFolder, ScreenshotFileName), ImageFormat.Png);
256        }
257
258        /// <summary>
259        /// The global BlackBox instance.
260        /// </summary>
261        private static BlackBox Instance;
262
263        /// <summary>
264        /// The path to all Eraser crash reports.
265        /// </summary>
266        private static readonly string CrashReportsPath = Path.Combine(Environment.GetFolderPath(
267            Environment.SpecialFolder.LocalApplicationData), @"Eraser 6\Crash Reports");
268
269        /// <summary>
270        /// The report name format.
271        /// </summary>
272        internal static readonly string CrashReportName = "yyyyMMdd HHmmss.FFF";
273
274        /// <summary>
275        /// The file name of the memory dump.
276        /// </summary>
277        ///
278        internal static readonly string MemoryDumpFileName = "Memory.dmp";
279
280        /// <summary>
281        /// The file name of the debug log.
282        /// </summary>
283        internal static readonly string DebugLogFileName = "Debug.log";
284
285        /// <summary>
286        /// The file name of the screenshot.
287        /// </summary>
288        internal static readonly string ScreenshotFileName = "Screenshot.png";
289    }
290
291    /// <summary>
292    /// Represents one BlackBox crash report.
293    /// </summary>
294    public class BlackBoxReport
295    {
296        /// <summary>
297        /// Constructor.
298        /// </summary>
299        /// <param name="path">Path to the folder containing the memory dump, screenshot and
300        /// debug log.</param>
301        internal BlackBoxReport(string path)
302        {
303            Path = path;
304
305            string stackTracePath = System.IO.Path.Combine(Path, StackTraceFileName);
306            if (!System.IO.File.Exists(stackTracePath))
307            {
308                Delete();
309                throw new InvalidDataException("The BlackBox report is corrupt.");
310            }
311
312            string[] stackTrace = null;
313            using (StreamReader reader = new StreamReader(stackTracePath))
314                stackTrace = reader.ReadToEnd().Split(new char[] { '\n' });
315
316            //Parse the lines in the file.
317            StackTraceCache = new List<BlackBoxExceptionEntry>();
318            List<string> currentException = new List<string>();
319            string exceptionType = null;
320            foreach (string str in stackTrace)
321            {
322                if (str.StartsWith("Exception "))
323                {
324                    //Add the current exception to the list of exceptions.
325                    if (currentException.Count != 0)
326                    {
327                        StackTraceCache.Add(new BlackBoxExceptionEntry(exceptionType,
328                            new List<string>(currentException)));
329                        currentException.Clear();
330                    }
331
332                    //Set the exception type for the next exception.
333                    exceptionType = str.Substring(str.IndexOf(':') + 1).Trim();
334                }
335                else if (!string.IsNullOrEmpty(str.Trim()))
336                {
337                    currentException.Add(str.Trim());
338                }
339            }
340
341            if (currentException.Count != 0)
342                StackTraceCache.Add(new BlackBoxExceptionEntry(exceptionType, currentException));
343        }
344
345        /// <summary>
346        /// Deletes the report and its contents.
347        /// </summary>
348        public void Delete()
349        {
350            Directory.Delete(Path, true);
351        }
352
353        /// <summary>
354        /// The name of the report.
355        /// </summary>
356        public string Name
357        {
358            get
359            {
360                return System.IO.Path.GetFileName(Path);
361            }
362        }
363
364        /// <summary>
365        /// The timestamp of the report.
366        /// </summary>
367        public DateTime Timestamp
368        {
369            get
370            {
371                return DateTime.ParseExact(Name, BlackBox.CrashReportName,
372                    CultureInfo.InvariantCulture).ToLocalTime();
373            }
374        }
375
376        /// <summary>
377        /// The path to the folder containing the report.
378        /// </summary>
379        public string Path
380        {
381            get;
382            private set;
383        }
384
385        /// <summary>
386        /// The files which comprise the error report.
387        /// </summary>
388        public ReadOnlyCollection<FileInfo> Files
389        {
390            get
391            {
392                List<FileInfo> result = new List<FileInfo>();
393                DirectoryInfo directory = new DirectoryInfo(Path);
394                foreach (FileInfo file in directory.GetFiles())
395                    if (!InternalFiles.Contains(file.Name))
396                        result.Add(file);
397
398                return result.AsReadOnly();
399            }
400        }
401
402        /// <summary>
403        /// Gets a read-only stream which reads the Debug log.
404        /// </summary>
405        public Stream DebugLog
406        {
407            get
408            {
409                return new FileStream(System.IO.Path.Combine(Path, BlackBox.DebugLogFileName),
410                    FileMode.Open, FileAccess.Read);
411            }
412        }
413
414        /// <summary>
415        /// Gets the stack trace for this crash report.
416        /// </summary>
417        public ReadOnlyCollection<BlackBoxExceptionEntry> StackTrace
418        {
419            get
420            {
421                return StackTraceCache.AsReadOnly();
422            }
423        }
424
425        /// <summary>
426        /// Gets or sets whether the given report has been uploaded to the server.
427        /// </summary>
428        public bool Submitted
429        {
430            get
431            {
432                byte[] buffer = new byte[1];
433                using (FileStream stream = new FileStream(System.IO.Path.Combine(Path, StatusFileName),
434                    FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read))
435                {
436                    stream.Read(buffer, 0, buffer.Length);
437                }
438
439                return buffer[0] == 1;
440            }
441
442            set
443            {
444                byte[] buffer = { Convert.ToByte(value) };
445                using (FileStream stream = new FileStream(System.IO.Path.Combine(Path, StatusFileName),
446                    FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read))
447                {
448                    stream.Write(buffer, 0, buffer.Length);
449                }
450            }
451        }
452
453        public override string ToString()
454        {
455            return Name;
456        }
457
458        /// <summary>
459        /// The backing variable for the <see cref="StackTrace"/> field.
460        /// </summary>
461        private List<BlackBoxExceptionEntry> StackTraceCache;
462
463        /// <summary>
464        /// The file name for the status file.
465        /// </summary>
466        private static readonly string StatusFileName = "Status.txt";
467
468        /// <summary>
469        /// The file name of the stack trace.
470        /// </summary>
471        internal static readonly string StackTraceFileName = "Stack Trace.log";
472
473        /// <summary>
474        /// The list of files internal to the report.
475        /// </summary>
476        private static readonly List<string> InternalFiles = new List<string>(
477            new string[] {
478                 StackTraceFileName,
479                 StatusFileName
480            }
481        );
482    }
483
484    /// <summary>
485    /// Represents one exception which can be chained <see cref="InnerException"/>
486    /// to represent the exception handled by BlackBox
487    /// </summary>
488    public class BlackBoxExceptionEntry
489    {
490        /// <summary>
491        /// Constructor.
492        /// </summary>
493        /// <param name="exceptionType">The type of the exception.</param>
494        /// <param name="stackTrace">The stack trace for this exception.</param>
495        internal BlackBoxExceptionEntry(string exceptionType, List<string> stackTrace)
496        {
497            ExceptionType = exceptionType;
498            StackTraceCache = stackTrace;
499        }
500
501        /// <summary>
502        /// The type of the exception.
503        /// </summary>
504        public string ExceptionType
505        {
506            get;
507            private set;
508        }
509
510        /// <summary>
511        /// The stack trace for this exception.
512        /// </summary>
513        public ReadOnlyCollection<string> StackTrace
514        {
515            get
516            {
517                return StackTraceCache.AsReadOnly();
518            }
519        }
520
521        /// <summary>
522        /// The backing variable for the <see cref="StackTrace"/> property.
523        /// </summary>
524        private List<string> StackTraceCache;
525    }
526
527    /// <summary>
528    /// Uploads <see cref="BlackBoxReport"/>s to the Eraser server.
529    /// </summary>
530    public class BlackBoxReportUploader
531    {
532        /// <summary>
533        /// Constructor.
534        /// </summary>
535        /// <param name="report">The report to upload.</param>
536        public BlackBoxReportUploader(BlackBoxReport report)
537        {
538            Report = report;
539            if (!Directory.Exists(UploadTempDir))
540                Directory.CreateDirectory(UploadTempDir);
541
542            ReportBaseName = Path.Combine(UploadTempDir, Report.Name);
543        }
544
545        /// <summary>
546        /// Gets from the server based on the stack trace whether this report is
547        /// new.
548        /// </summary>
549        public bool IsNew
550        {
551            get
552            {
553                PostDataBuilder builder = new PostDataBuilder();
554                builder.AddPart(new PostDataField("action", "status"));
555                AddStackTraceToRequest(Report.StackTrace, builder);
556
557                WebRequest reportRequest = HttpWebRequest.Create(BlackBoxServer);
558                reportRequest.ContentType = builder.ContentType;
559                reportRequest.Method = "POST";
560                using (Stream formStream = builder.Stream)
561                {
562                    reportRequest.ContentLength = formStream.Length;
563                    using (Stream requestStream = reportRequest.GetRequestStream())
564                    {
565                        int lastRead = 0;
566                        byte[] buffer = new byte[32768];
567                        while ((lastRead = formStream.Read(buffer, 0, buffer.Length)) != 0)
568                            requestStream.Write(buffer, 0, lastRead);
569                    }
570                }
571
572                try
573                {
574                    HttpWebResponse response = reportRequest.GetResponse() as HttpWebResponse;
575                    using (Stream responseStream = response.GetResponseStream())
576                    {
577                        XmlReader reader = XmlReader.Create(responseStream);
578                        reader.ReadToFollowing("crashReport");
579                        string reportStatus = reader.GetAttribute("status");
580                        switch (reportStatus)
581                        {
582                            case "exists":
583                                Report.Submitted = true;
584                                return false;
585
586                            case "new":
587                                return true;
588
589                            default:
590                                throw new InvalidDataException(
591                                    "Unknown crash report server response.");
592                        }
593                    }
594                }
595                catch (WebException e)
596                {
597                    using (Stream responseStream = e.Response.GetResponseStream())
598                    {
599                        try
600                        {
601                            XmlReader reader = XmlReader.Create(responseStream);
602                            reader.ReadToFollowing("error");
603                            throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture,
604                                "The server encountered a problem while processing the request: {0}",
605                                reader.ReadString()));
606                        }
607                        catch (XmlException)
608                        {
609                        }
610                    }
611
612                    throw new InvalidDataException(((HttpWebResponse)e.Response).StatusDescription);
613                }
614            }
615        }
616
617        /// <summary>
618        /// Compresses the report for uploading.
619        /// </summary>
620        /// <param name="progress">The <see cref="ProgressManager"/> instance that the
621        /// Upload function is using.</param>
622        /// <param name="progressChanged">The progress changed event handler that should
623        /// be called for upload progress updates.</param>
624        private void Compress(SteppedProgressManager progress,
625            ProgressChangedEventHandler progressChanged)
626        {
627            using (FileStream archiveStream = new FileStream(ReportBaseName + ".tar",
628                    FileMode.Create, FileAccess.Write))
629            {
630                //Add the report into a tar file
631                TarArchive archive = TarArchive.CreateOutputTarArchive(archiveStream);
632                foreach (FileInfo file in Report.Files)
633                {
634                    TarEntry entry = TarEntry.CreateEntryFromFile(file.FullName);
635                    entry.Name = Path.GetFileName(entry.Name);
636                    archive.WriteEntry(entry, false);
637                }
638                archive.Close();
639            }
640
641            ProgressManager step = new ProgressManager();
642            progress.Steps.Add(new SteppedProgressManagerStep(step, 0.5f, "Compressing"));
643            using (FileStream bzipFile = new FileStream(ReportBaseName + ".tbz",
644                FileMode.Create))
645            using (FileStream tarStream = new FileStream(ReportBaseName + ".tar",
646                FileMode.Open, FileAccess.Read, FileShare.Read, 262144, FileOptions.DeleteOnClose))
647            using (BZip2OutputStream bzipStream = new BZip2OutputStream(bzipFile, 262144))
648            {
649                //Compress the tar file
650                int lastRead = 0;
651                byte[] buffer = new byte[524288];
652                while ((lastRead = tarStream.Read(buffer, 0, buffer.Length)) != 0)
653                {
654                    bzipStream.Write(buffer, 0, lastRead);
655                    step.Completed = tarStream.Position;
656                    step.Total = tarStream.Length;
657
658                    if (progressChanged != null)
659                        progressChanged(this, new ProgressChangedEventArgs(progress, null));
660                }
661            }
662        }
663
664        /// <summary>
665        /// Compresses the report, then uploads it to the server.
666        /// </summary>
667        /// <param name="progressChanged">The progress changed event handler that should
668        /// be called for upload progress updates.</param>
669        public void Submit(ProgressChangedEventHandler progressChanged)
670        {
671            SteppedProgressManager overallProgress = new SteppedProgressManager();
672            Compress(overallProgress, progressChanged);
673
674            using (FileStream bzipFile = new FileStream(ReportBaseName + ".tbz",
675                FileMode.Open, FileAccess.Read, FileShare.Read, 131072, FileOptions.DeleteOnClose))
676            using (Stream logFile = Report.DebugLog)
677            {
678                //Build the POST request
679                PostDataBuilder builder = new PostDataBuilder();
680                builder.AddPart(new PostDataField("action", "upload"));
681                builder.AddPart(new PostDataFileField("crashReport", "Report.tbz", bzipFile));
682                AddStackTraceToRequest(Report.StackTrace, builder);
683
684                //Upload the POST request
685                WebRequest reportRequest = HttpWebRequest.Create(BlackBoxServer);
686                reportRequest.ContentType = builder.ContentType;
687                reportRequest.Method = "POST";
688                reportRequest.Timeout = int.MaxValue;
689                using (Stream formStream = builder.Stream)
690                {
691                    ProgressManager progress = new ProgressManager();
692                    overallProgress.Steps.Add(new SteppedProgressManagerStep(
693                        progress, 0.5f, "Uploading"));
694                    reportRequest.ContentLength = formStream.Length;
695
696                    using (Stream requestStream = reportRequest.GetRequestStream())
697                    {
698                        int lastRead = 0;
699                        byte[] buffer = new byte[32768];
700                        while ((lastRead = formStream.Read(buffer, 0, buffer.Length)) != 0)
701                        {
702                            requestStream.Write(buffer, 0, lastRead);
703
704                            progress.Completed = formStream.Position;
705                            progress.Total = formStream.Length;
706                            progressChanged(this, new ProgressChangedEventArgs(overallProgress, null));
707                        }
708                    }
709                }
710
711                try
712                {
713                    reportRequest.GetResponse();
714                    Report.Submitted = true;
715                }
716                catch (WebException e)
717                {
718                    using (Stream responseStream = e.Response.GetResponseStream())
719                    {
720                        try
721                        {
722                            XmlReader reader = XmlReader.Create(responseStream);
723                            reader.ReadToFollowing("error");
724                            throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture,
725                                "The server encountered a problem while processing the request: {0}",
726                                reader.ReadString()));
727                        }
728                        catch (XmlException)
729                        {
730                        }
731                    }
732
733                    throw new InvalidDataException(((HttpWebResponse)e.Response).StatusDescription);
734                }
735            }
736        }
737
738        /// <summary>
739        /// Adds the stack trace to the given form request.
740        /// </summary>
741        /// <param name="stackTrace">The stack trace to add.</param>
742        /// <param name="builder">The Form request builder to add the stack trace to.</param>
743        private static void AddStackTraceToRequest(IList<BlackBoxExceptionEntry> stackTrace,
744            PostDataBuilder builder)
745        {
746            int exceptionIndex = 0;
747            foreach (BlackBoxExceptionEntry exceptionStack in stackTrace)
748            {
749                foreach (string stackFrame in exceptionStack.StackTrace)
750                    builder.AddPart(new PostDataField(
751                        string.Format("stackTrace[{0}][]", exceptionIndex), stackFrame));
752                builder.AddPart(new PostDataField(string.Format(
753                    "stackTrace[{0}][exception]", exceptionIndex), exceptionStack.ExceptionType));
754                ++exceptionIndex;
755            }
756        }
757
758        /// <summary>
759        /// The path to where the temporary files are stored before uploading.
760        /// </summary>
761        private static readonly string UploadTempDir =
762            Path.Combine(Path.GetTempPath(), "Eraser Crash Reports");
763
764        /// <summary>
765        /// The URI to the BlackBox server.
766        /// </summary>
767        private static readonly Uri BlackBoxServer =
768            new Uri("http://eraser.heidi.ie/scripts/blackbox/upload.php");
769
770        /// <summary>
771        /// The report being uploaded.
772        /// </summary>
773        private BlackBoxReport Report;
774
775        /// <summary>
776        /// The base name of the report.
777        /// </summary>
778        private readonly string ReportBaseName;
779    }
780}
Note: See TracBrowser for help on using the repository browser.