Globals.cs
author Dean
Tue, 13 Sep 2016 12:58:15 +0200
changeset 1278 d37fa426128d
parent 1249 00cc05656f71
child 1337 6b93b1a3b751
permissions -rw-r--r--
Update pEp website links (from Experimental_UI branch)
     1 using Microsoft.Win32;
     2 using pEp.UI;
     3 using System;
     4 using System.Collections.Generic;
     5 using System.IO;
     6 using System.Reflection;
     7 using System.Runtime.InteropServices;
     8 using System.Text;
     9 using System.Threading;
    10 using System.Windows;
    11 using System.Windows.Forms;
    12 using Office = Microsoft.Office.Core;
    13 
    14 namespace pEp
    15 {
    16     /// <summary>
    17     /// Class to store all global/shared data and methods.
    18     /// This class is a shared partial class with the internally generated code.
    19     /// </summary>
    20     internal sealed partial class Globals
    21     {
    22         public const string CUSTOM_SENT_FOLDER_NAME    = "Sent";
    23         public const string PEP_COPYRIGHT              = "Copyright © 2014-2016 p≡p Security SA, Luxembourg";
    24         public const string PEP_DATA_FILE_NAME         = "pEp";                           // The file name of the pEp data store. This does NOT include extension (.pst)
    25         public const string PEP_DISPLAY_NAME           = "p≡p for Outlook";               // Display name to the user
    26         public const string PEP_DISPLAY_NAME_READER    = "p≡p Reader for Outlook";        // Display name to the user
    27 #if DEBUG
    28         public const string PEP_NAME_ADDIN_DESC        = "pEp";                           // Description of the add-in within Outlook itself (shared with reader)
    29 #else
    30         public const string PEP_NAME_ADDIN_DESC        = "pEp for Outlook";               // Description of the add-in within Outlook itself (shared with reader)
    31 #endif
    32         public const string PEP_NAME_INSTALL           = "pEp for Outlook";               // Name for Windows installer, must sync with installation code (shared with reader)
    33         public const string PEP_WEBSITE_LINK           = "https://prettyeasyprivacy.com";
    34         public const string PEP_WEBSITE_UPGRADE_LINK   = "https://pep.digitalcourage.de/shop/p8801p-for-outlook.html";
    35 
    36 #if READER_RELEASE_MODE
    37         public const ReleaseMode RELEASE_MODE = ReleaseMode.Reader;
    38 #else
    39         public const ReleaseMode RELEASE_MODE = ReleaseMode.Standard;
    40 #endif
    41 
    42         // Engine translations
    43         public const int PHRASE_ID_TRUSTWORDS_LANGUAGE = 1000;
    44 
    45         /// <summary>
    46         /// Enumeration to define the supported release modes.
    47         /// </summary>
    48         public enum ReleaseMode
    49         {
    50             Standard,
    51             Reader
    52         }
    53 
    54         /// <summary>
    55         /// Enumeration to define the return status of methods.
    56         /// </summary>
    57         public enum ReturnStatus
    58         {
    59             /// <summary>
    60             /// An unspecified failure occured.
    61             /// </summary>
    62             Failure,
    63 
    64             /// <summary>
    65             /// A failure occured because there is no internet or Exchange connection.
    66             /// </summary>
    67             FailureNoConnection,
    68 
    69             /// <summary>
    70             /// Successfully completed with no unexpected failures.
    71             /// </summary>
    72             Success
    73         }
    74 
    75         private static bool               eventsAreConnected = false;
    76         private static ResourceDictionary _ResourceDict      = null;
    77 
    78         /**************************************************************
    79          * 
    80          * Property Accessors
    81          * 
    82          *************************************************************/
    83 
    84         /// <summary>
    85         /// Gets the shared XAML resource dictionary.
    86         /// </summary>
    87         public static ResourceDictionary ResourceDict
    88         {
    89             get
    90             {
    91                 if (_ResourceDict == null)
    92                 {
    93                     // Load resource dictionary
    94                     _ResourceDict = new ResourceDictionary();
    95                     _ResourceDict.Source = new Uri("pack://application:,,,/pEp;component/Resources/Dictionary.xaml", UriKind.RelativeOrAbsolute);
    96 
    97                     return (_ResourceDict);
    98                 }
    99                 else
   100                 {
   101                     return (_ResourceDict);
   102                 }
   103             }
   104         }
   105 
   106         /**************************************************************
   107          * 
   108          * Methods
   109          * 
   110          *************************************************************/
   111 
   112         /// <summary>
   113         /// Builds a crash report and displays it to the user before sending. 
   114         /// </summary>
   115         /// <param name="exception">The exception to build the report with.</param>
   116         /// <param name="summaryMessage">The message summarizing where the error occured or what it is.</param>
   117         /// <param name="allowRestart">True to allow the add-in to attempt to restart itself.</param>
   118         /// <param name="isEngineCrash">True if the engine generated the error. This will not attempt to get
   119         /// an engine log as the engine itself is not working (avoids infinite loop).</param>
   120         public static void StopAndSendCrashReport(Exception exception = null,
   121                                                   string summaryMessage = null,
   122                                                   bool allowRestart = true,
   123                                                   bool isEngineCrash = false)
   124         {
   125             bool continueSend = true;
   126             string engineLog = "";
   127             string log = "";
   128             FormCrashReport form;
   129             FormControlCrashReport.State stateIn = null;
   130             DialogResult result;
   131             Office.COMAddIns comAddIns = null;
   132             Office.COMAddIn addIn = null;
   133             PEPMessage newMessage;
   134             PEPAttachment attachment = null;
   135 
   136             // Show the report before sending
   137             if (continueSend)
   138             {
   139                 stateIn = new FormControlCrashReport.State();
   140                 stateIn.AddressTo = Globals.ThisAddIn.Settings.CrashReportSendAddress;
   141                 stateIn.AddressFrom = null; // A message can be sent without a "from" address
   142 
   143                 // Get log
   144                 try
   145                 {
   146                     log = string.Join(Environment.NewLine, Log.Read());
   147                 }
   148                 catch
   149                 {
   150                     log = "";
   151                 }
   152 
   153                 // Get engine log
   154                 if (isEngineCrash == false)
   155                 {
   156                     try
   157                     {
   158                         engineLog = ThisAddIn.PEPEngine.get_crashdump_log();
   159                     }
   160                     catch (COMException ex)
   161                     {
   162                         engineLog = ex.ToString();
   163                     }
   164                     catch (Exception ex)
   165                     {
   166                         engineLog = ex.ToString();
   167                     }
   168                 }
   169 
   170                 // Double check for null log files
   171                 if (log == null) { log = ""; }
   172                 if (engineLog == null) { engineLog = ""; }
   173 
   174                 // Build report header
   175                 stateIn.ReportText = string.Join(Environment.NewLine, new string[]
   176                     {
   177                         "Summary---------------------------------------------------",
   178                         "",
   179                         "Date: " + System.DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss K"),
   180                         Globals.GetSystemInfo(),
   181                         "",
   182                         (string.IsNullOrWhiteSpace(summaryMessage) ? "no message" : summaryMessage),
   183                         "",
   184                         "Exception Details-----------------------------------------",
   185                         "",
   186                         (exception == null ? "no exception" : exception.ToString()),
   187                         "",
   188                         "p≡p for Outlook Log---------------------------------------",
   189                         "",
   190                         log,
   191                         "",
   192                         "p≡p Engine Log--------------------------------------------",
   193                         "",
   194                         engineLog,
   195                         "",
   196                         "END OF REPORT"
   197                     });
   198 
   199                 if (Globals.ThisAddIn.Settings.IsCrashReportVisible)
   200                 {
   201                     form = new FormCrashReport();
   202                     form.StartPosition = FormStartPosition.CenterParent;
   203                     form.FormControl.DisplayState = stateIn;
   204                     result = form.ShowDialog();
   205 
   206                     if (result != DialogResult.OK)
   207                     {
   208                         continueSend = false;
   209                     }
   210                 }
   211             }
   212 
   213             // Build and send message
   214             if (continueSend)
   215             {
   216                 newMessage = new PEPMessage();
   217                 newMessage.ShortMsg = "Crash Report";
   218                 newMessage.To.Add(new PEPIdentity(stateIn.AddressTo));
   219                 newMessage.LongMsg = stateIn.ReportText;
   220                 newMessage.Direction = pEpCOMServerAdapterLib._pEp_msg_direction.pEp_dir_outgoing;
   221 
   222                 // Add logs as attachments
   223                 if (string.IsNullOrEmpty(log) == false)
   224                 {
   225                     attachment = new PEPAttachment();
   226                     attachment.Data = Encoding.UTF8.GetBytes(log);
   227                     attachment.FileName = "pEp Log.txt";
   228                     newMessage.Attachments.Add(attachment);
   229                 }
   230 
   231                 if (string.IsNullOrEmpty(engineLog) == false)
   232                 {
   233                     attachment = new PEPAttachment();
   234                     attachment.Data = Encoding.UTF8.GetBytes(engineLog);
   235                     attachment.FileName = "pEp Engine Log.txt";
   236                     newMessage.Attachments.Add(attachment);
   237                 }
   238 
   239                 // Try to send the message but ignore any errors
   240                 try
   241                 {
   242                     Globals.ThisAddIn.SendWithoutProcessing(newMessage, true);
   243                 }
   244                 catch { }
   245             }
   246 
   247             // Stop logging
   248             Log.Close();
   249 
   250             // Locate the Outlook add-in
   251             comAddIns = Globals.ThisAddIn.Application.COMAddIns;
   252             foreach (Office.COMAddIn addin in comAddIns)
   253             {
   254                 if (string.Equals(addin.Description, Globals.PEP_NAME_ADDIN_DESC, StringComparison.OrdinalIgnoreCase))
   255                 {
   256                     addIn = addin;
   257                     break;
   258                 }
   259             }
   260 
   261             // Shutdown add-in
   262             if (addIn != null)
   263             {
   264                 addIn.Connect = false;
   265             }
   266 
   267             return;
   268         }
   269 
   270         /// <summary>
   271         /// Connects or disconnects all global error handling events.
   272         /// </summary>
   273         /// <param name="connect">True to connect events, false to disconnect.</param>
   274         public static void ConnectEvents(bool connect)
   275         {
   276             // Connect events only if not already connected
   277             if ((connect == true) &&
   278                 (eventsAreConnected == false))
   279             {
   280                 System.Windows.Forms.Application.ThreadException += Application_UnhandledException;
   281                 AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
   282 
   283                 eventsAreConnected = true;
   284             }
   285             // Always attempt to disconnect
   286             else if (connect == false)
   287             {
   288                 System.Windows.Forms.Application.ThreadException -= Application_UnhandledException;
   289                 AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
   290 
   291                 eventsAreConnected = false;
   292             }
   293 
   294             return;
   295         }
   296 
   297         /// <summary>
   298         /// Gets the current system information string.
   299         /// </summary>
   300         /// <returns>The system information as a string.</returns>
   301         public static string GetSystemInfo()
   302         {
   303             string result = "";
   304             List<KVPair<string, string>> systemInfo;
   305             List<string> list = new List<string>();
   306 
   307             try
   308             {
   309                 systemInfo = Globals.GetSystemInfoList();
   310 
   311                 for (int i = 0; i < systemInfo.Count; i++)
   312                 {
   313                     list.Add(systemInfo[i].Key + " " + systemInfo[i].Value);
   314                 }
   315 
   316                 result = string.Join(Environment.NewLine, list);
   317             }
   318             catch { }
   319 
   320             return (result);
   321         }
   322 
   323         /// <summary>
   324         /// Gets the current system information as a list of Key-Value pairs.
   325         /// </summary>
   326         /// <returns>The system information as a list.</returns>
   327         public static List<KVPair<string, string>> GetSystemInfoList()
   328         {
   329             string serial;
   330             string engineVersion;
   331             List<KVPair<string, string>> systemInfo = new List<KVPair<string, string>>();
   332             Office.LanguageSettings lang = null;
   333 
   334             try
   335             {
   336                 lang = Globals.ThisAddIn.Application.LanguageSettings;
   337 
   338                 // Get the engine version
   339                 try
   340                 {
   341                     engineVersion = ThisAddIn.PEPEngine.get_engine_version();
   342                 }
   343                 catch
   344                 {
   345                     engineVersion = null;
   346                 }
   347 
   348                 // Use only the first 10-digits which is the serial itself
   349                 serial = Properties.Settings.Default.Serial;
   350                 serial = (serial != null ? serial : "");
   351                 serial = (serial.Length > 10) ? serial.Substring(0, 10) : serial;
   352 
   353                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_Serial, (Globals.RELEASE_MODE == Globals.ReleaseMode.Reader ? Properties.Resources.SystemInfo_Reader : serial)));
   354                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_AssemblyVersion, Assembly.GetExecutingAssembly().GetName().Version.ToString()));
   355                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_OutlookVersion, Globals.ThisAddIn.Application.Version));
   356                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_OSVersion, Environment.OSVersion.VersionString));
   357                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_PEPEngineVersion, (engineVersion != null ? engineVersion : "-")));
   358                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_64BitOS, Environment.Is64BitOperatingSystem.ToString()));
   359                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_64BitProcess, Environment.Is64BitProcess.ToString()));
   360                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_InstallLanguage, ((Office.MsoLanguageID)lang.LanguageID[Office.MsoAppLanguageID.msoLanguageIDInstall]).ToString()));
   361                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_UILanguage, ((Office.MsoLanguageID)lang.LanguageID[Office.MsoAppLanguageID.msoLanguageIDUI]).ToString()));
   362             }
   363             catch (Exception ex)
   364             {
   365                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_Exception, ex.ToString()));
   366             }
   367             finally
   368             {
   369                 if (lang != null)
   370                 {
   371                     Marshal.ReleaseComObject(lang);
   372                     lang = null;
   373                 }
   374             }
   375 
   376             return (systemInfo);
   377         }
   378 
   379         /// <summary>
   380         /// Gets the path of the GPA .exe using the install directory from the registry.
   381         /// WARNING: this method can return a null path.
   382         /// </summary>
   383         /// <returns>The path of the GPA .exe, otherwise null.</returns>
   384         public static string GetGPAPath()
   385         {
   386             // We put a general catch block around here, just to be safe - disabling
   387             // the GPA button is the better alternative to crashing if something goes
   388             // wrong here. 
   389             try
   390             {
   391                 foreach (var gpgPath in TryToGuessGpgPathes())
   392                 {
   393                     var gpaPath = System.IO.Path.Combine(gpgPath, "gpa.exe");
   394                     if (File.Exists(gpaPath))
   395                         return gpaPath;
   396                 }
   397             }
   398             catch (Exception ex)
   399             {
   400                 // As I do not expext any exceptions to actually appear at this level,
   401                 // we log them (even in non-verbose level).
   402                 Log.Error("GetGpaPath: " + ex.Message);
   403             }
   404 
   405             return null;
   406         }
   407 
   408         /// <summary>
   409         /// Helper method which tries to find possible GnuPG installation pathes, with
   410         /// decreasing confidence level.
   411         /// </summary>
   412         /// <returns></returns>
   413         private static IEnumerable<string> TryToGuessGpgPathes()
   414         {
   415             // We just try all possible registry views to find GPA. As we just start it as an externa process,
   416             // we don´t care whether its 32 bit or 64 bit.
   417 
   418             // We try the 32 bit view first, as that's what we currently deploy.
   419             var gpgPath = TryToReadGpgPathFromRegistry(RegistryView.Registry32);
   420 
   421             if (!string.IsNullOrEmpty(gpgPath))
   422                 yield return gpgPath;
   423 
   424             gpgPath = TryToReadGpgPathFromRegistry(RegistryView.Registry64);
   425 
   426             if (!string.IsNullOrEmpty(gpgPath))
   427                 yield return gpgPath;
   428 
   429             // Use backup hardcoded directory locations as fallbacks. However, we log this issue
   430             // as it is not expected (broken installation?)
   431             Log.Warning("TryToGuessGpgPathes: Not found in registry, trying backup hard-coded directory pathes.");
   432 
   433             yield return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
   434                 "GNU", "GnuPG");
   435 
   436             yield return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
   437                 "GNU", "GnuPG");
   438         }
   439 
   440         private static string TryToReadGpgPathFromRegistry(RegistryView view)
   441         {
   442             try
   443             {
   444                 using (var rootKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view))
   445                 using (RegistryKey rk = rootKey.OpenSubKey("SOFTWARE\\GNU\\GnuPG"))
   446                 {
   447                     var gpgPath = (string)rk.GetValue("Install Directory");
   448                     return gpgPath;
   449                 }
   450             }
   451             catch (Exception ex)
   452             {
   453                 // Exceptions here are expected (the key does not exist in the view we currently 
   454                 // tried), thus we only long them in verbose mode.
   455                 Log.Verbose("TryToReadGpgPathFromRegistry: " + ex.Message);
   456                 return null;
   457             }
   458         }
   459 
   460         /**************************************************************
   461          * 
   462          * Event Handling
   463          * 
   464          *************************************************************/
   465 
   466         /// <summary>
   467         /// Event handler for when an unhandled error occurs within the current application.
   468         /// </summary>
   469         private static void Application_UnhandledException(object sender, ThreadExceptionEventArgs e)
   470         {
   471             Globals.StopAndSendCrashReport(e.Exception);
   472             return;
   473         }
   474 
   475         /// <summary>
   476         /// Event handler for when an unhandled error occurs within the current domain.
   477         /// </summary>
   478         private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
   479         {
   480             Globals.StopAndSendCrashReport();
   481             return;
   482         }
   483 
   484     }
   485 }