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