AdapterCallbacks.cs
author Thomas
Mon, 03 Sep 2018 14:04:38 +0200
branchOUT-283
changeset 2353 81975e284b07
parent 2166 adead2ee36ee
child 2408 f4f711fe1c98
permissions -rw-r--r--
Add MAPI methods to set properties via p/invoke
     1 ´╗┐using pEp.UI;
     2 using pEpCOMServerAdapterLib;
     3 using System.Runtime.InteropServices;
     4 using System.Windows.Forms;
     5 using System;
     6 using Outlook = Microsoft.Office.Interop.Outlook;
     7 
     8 namespace pEp
     9 {
    10     /// <summary>
    11     /// Callback implementation for the events the pEpEngine fires.
    12     /// </summary>
    13     /// <remarks>We need to have this in a class
    14     /// deriving from <see cref="StandardOleMarshalObject"/>, as this guarantees that the calls
    15     /// are scheduled on the primary thread. We're using a callback interface instead of events
    16     /// because it seems difficult to marshal complex datatypes (structs) for COM events, the
    17     /// default glue code generated by the VS wizard is not up to the task.
    18     /// </remarks>
    19     internal class AdapterCallbacks : StandardOleMarshalObject, IpEpEngineCallbacks
    20     {
    21         private static  HandshakeDialog      handshakeDialog         = null;
    22         private static  Timer                timer                   = null;
    23         private const   int                  FAST_POLLING_INTERVAL   = 1000;    // 1 second in ms
    24         private const   int                  MAX_POLLING_COUNT       = 600;     // 600 times each second => max. 10 minutes
    25 
    26         /// <summary>
    27         /// Event handler for when the pEp engine requests a handshake during key synchronization.
    28         /// </summary>
    29         /// <param name="own">The personal identity for the handshake.</param>
    30         /// <param name="partner">The communication partner identity for the handshake.</param>
    31         /// <param name="signal">The reason for the handshake notification.</param>
    32         /// <returns>The user selected result.</returns>
    33         public SyncHandshakeResult NotifyHandshake(ref pEpIdentity own,
    34                                                    ref pEpIdentity partner,
    35                                                    SyncHandshakeSignal signal)
    36         {
    37             bool? dialogResult = null;
    38             SyncHandshakeResult handshakeResult = SyncHandshakeResult.SyncHandshakeCancel;
    39             HandshakeDialog.HandshakeMode mode;
    40             PEPIdentity ownIdentity;
    41             PEPIdentity partnerIdentity;
    42 
    43             Log.Verbose("NotifyHandshake: Started with signal " + signal.ToString());
    44 
    45             /* Doublecheck that sync is enabled for this account.
    46              * Do not show any dialog for accounts with sync disabled.
    47              */
    48             bool notForSync = false;
    49             try
    50             {
    51                 notForSync = own.Flags.HasFlag(pEpIdentityFlags.pEpIdfNotForSync);
    52             }
    53             catch (Exception ex)
    54             {
    55                 notForSync = false;
    56                 Log.Error("NotifyHandshake: Error reading NotForSync flag. " + ex.ToString());
    57             }
    58 
    59             // If sync is disabled, return cancel
    60             if (notForSync)
    61             {
    62                 Log.Warning("NotifyHandshake: callback received for account with NotForSync flag. Ignoring request.");
    63                 return handshakeResult;
    64             }
    65 
    66             /* Close handshake dialog if it was still open and the signal type
    67              * is not "undefined", in which case the status quo should be preserved.
    68              */
    69             if ((handshakeDialog != null) &&
    70                 (signal != SyncHandshakeSignal.SyncNotifyUndefined))
    71             {
    72                 try
    73                 {
    74                     handshakeDialog.Close();
    75                 }
    76                 catch (Exception ex)
    77                 {
    78                     Log.Error("Could not close handshake dialog. " + ex.ToString());
    79                 }
    80 
    81                 // We set the result in case of an reentrant call, so the
    82                 // "parent" also gets the information.
    83                 handshakeDialog.DialogResult = null;
    84                 handshakeDialog = null;
    85             }
    86 
    87             // The action to take depends on the given signal
    88             switch (signal)
    89             {
    90                 // No action necessary
    91                 case SyncHandshakeSignal.SyncNotifyUndefined:
    92                     {
    93                         handshakeResult = SyncHandshakeResult.SyncHandshakeCancel;
    94                         break;
    95                     }
    96                 /* Currently, all of the following signals trigger showing the handshake dialog.
    97                  */
    98                 case SyncHandshakeSignal.SyncNotifyInitAddOurDevice:
    99                 case SyncHandshakeSignal.SyncNotifyInitAddOtherDevice:
   100                 case SyncHandshakeSignal.SyncNotifyInitFormGroup:
   101                 case SyncHandshakeSignal.SyncNotifyInitMoveOurDevice:
   102                     {
   103                         // Syncronize engine with settings just in case (should already be synced)
   104                         Globals.ThisAddIn.SyncWithSettings(true);
   105 
   106                         // Ensure identities are updated with fingerprint
   107                         ownIdentity = new PEPIdentity(ThisAddIn.PEPEngine.Myself(own));
   108                         partnerIdentity = new PEPIdentity(ThisAddIn.PEPEngine.UpdateIdentity(partner));
   109 
   110                         // Only the partner color is shown in the UI so get the rating
   111                         try
   112                         {
   113                             partnerIdentity.Rating = ThisAddIn.PEPEngine.IdentityRating(partnerIdentity.ToCOMType());
   114                         }
   115                         catch { }
   116 
   117                         // Specify handshake type
   118                         if (signal == SyncHandshakeSignal.SyncNotifyInitMoveOurDevice)
   119                         {
   120                             mode = HandshakeDialog.HandshakeMode.SyncTypeB;
   121                         }
   122                         else
   123                         {
   124                             mode = HandshakeDialog.HandshakeMode.SyncTypeA;
   125                         }
   126 
   127                         // Show handshake dialog
   128                         if (handshakeDialog != null)
   129                         {
   130                             try
   131                             {
   132                                 handshakeDialog.Close();
   133                             }
   134                             catch (Exception)
   135                             {
   136                                 throw;
   137                             }
   138                         }
   139 
   140                         handshakeDialog = new HandshakeDialog(ownIdentity,
   141                                                               partnerIdentity,
   142                                                               mode);
   143 
   144                         // Build dialog, but only show if building was successful
   145                         if (handshakeDialog.BuildDialog() &&
   146                             handshakeDialog?.Items?.Count > 0)
   147                         {
   148                             dialogResult = handshakeDialog.ShowDialog();
   149                         }
   150                         else
   151                         {
   152                             Log.Error("NotifyHandshake: Error creating handshake dialog. Dialog was not shown.");
   153                         }
   154                                                 
   155                         handshakeDialog = null;
   156                                                
   157                         // Send result back to the engine
   158                         switch (dialogResult)
   159                         {
   160                             case true:
   161                                 {
   162                                     handshakeResult = SyncHandshakeResult.SyncHandshakeAccepted;
   163                                     break;
   164                                 }
   165                             case false:
   166                                 {
   167                                     handshakeResult = SyncHandshakeResult.SyncHandshakeRejected;
   168                                     break;
   169                                 }
   170                             case null:
   171                             default: // this should never happen if we cover all enums
   172                                 {
   173                                     handshakeResult = SyncHandshakeResult.SyncHandshakeCancel;
   174                                     break;
   175                                 }
   176                         }
   177 
   178                         break;
   179                     }
   180                 // In case of timeout, the handshake gets cancelled (dialog is already closed at this point)
   181                 case SyncHandshakeSignal.SyncNotifyTimeout:
   182                     {
   183                         handshakeResult = SyncHandshakeResult.SyncHandshakeCancel;
   184                         break;
   185                     }
   186                 // Signal to close the dialog (actually, it is already closed at this point)
   187                 case SyncHandshakeSignal.SyncNotifyOvertaken:
   188                     {
   189                         handshakeResult = SyncHandshakeResult.SyncHandshakeCancel;
   190                         break;
   191                     }
   192                 // Currently, there is no action defined for the next three signals
   193                 case SyncHandshakeSignal.SyncNotifyAcceptedDeviceAdded:
   194                 case SyncHandshakeSignal.SyncNotifyAcceptedGroupCreated:
   195                 case SyncHandshakeSignal.SyncNotifyAcceptedDeviceMoved:
   196                 default:
   197                     {
   198                         handshakeResult = SyncHandshakeResult.SyncHandshakeCancel;
   199                         break;
   200                     }
   201             }
   202 
   203             Log.Verbose("NotifyHandshake: Completed, " + handshakeResult.ToString());
   204 
   205             return (handshakeResult);
   206         }
   207 
   208         /// <summary>
   209         /// Event handler for when the pEp engine requests a message to be sent by the application.
   210         /// </summary>
   211         /// <param name="msg">The message to send.</param>
   212         public void MessageToSend(ref TextMessage msg)
   213         {
   214             PEPMessage newMessage;
   215 
   216             Log.Verbose("MessageToSend: Started");
   217             try
   218             {
   219                 Log.SensitiveData("MessageToSend: Started sending message with subject " + msg.ShortMsg);
   220             }
   221             catch { }         
   222 
   223             var result = PEPMessage.Create(msg, out newMessage);
   224             if (result != Globals.ReturnStatus.Success)
   225             {
   226                 // no need to log here, as PEPMessage.Create already logs the failure.
   227                 Marshal.ThrowExceptionForHR(0xD /* ERROR_INVALID_DATA */ );
   228             }
   229 
   230             // Validate the From/To identities which always must match
   231             if ((newMessage.From == null) ||
   232                 (newMessage.To.Count != 1) ||
   233                 (newMessage.From.EqualsByAddress(newMessage.To[0]) == false))
   234             {
   235                 Log.Error("MessageToSend: From does not match To");
   236                 Marshal.ThrowExceptionForHR(0xD /* ERROR_INVALID_DATA */);
   237             }
   238 
   239             // Now send the message
   240             try
   241             {
   242                 // Note: It's important that validateSendingAccount is true for sync
   243                 Globals.ThisAddIn.CreateAndSendMessage(newMessage, true, true);
   244             }
   245             catch (Exception ex)
   246             {
   247                 Log.Error("MessageToSend: Error trying to send message, " + ex.ToString());
   248                 Marshal.ThrowExceptionForHR(0xD /* ERROR_INVALID_DATA */);
   249             }
   250 
   251             Log.Verbose("MessageToSend: Completed");
   252 
   253             return;
   254         }
   255 
   256         /// <summary>
   257         /// Event handler for when the engine requests fast polling during keysync.
   258         /// </summary>
   259         /// <param name="enableFastPolling">Whether to enable or disable fast polling.</param>
   260         public void NeedFastPolling(bool enableFastPolling)
   261         {
   262             int counter = 0;
   263             Outlook.NameSpace ns = null;
   264 
   265             if (enableFastPolling)
   266             {
   267                 // Enable also the automatic cleaning of key sync messages
   268                 try
   269                 {
   270                     Globals.ThisAddIn.ToggleInboxCleaning(true);
   271                 }
   272                 catch { }
   273 
   274                 Log.Verbose("NeedFastPolling: Enabling fast polling.");
   275 
   276                 try
   277                 {
   278                     timer = new Timer();
   279                     timer.Interval = FAST_POLLING_INTERVAL;
   280                     timer.Tick += (s, e) =>
   281                     {
   282                         if (counter++ < MAX_POLLING_COUNT)
   283                         {
   284                             try
   285                             {
   286                                 ns = Globals.ThisAddIn?.Application?.Session;
   287                                 ns?.SendAndReceive(false);
   288                             }
   289                             catch (Exception ex)
   290                             {
   291                                 Log.Error("NeedFastPolling: Error during polling. " + ex.Message);
   292                             }
   293                             finally
   294                             {
   295                                 ns = null;
   296                             }
   297                         }
   298                     };
   299                     timer.Start();
   300                 }
   301                 catch (Exception ex)
   302                 {
   303                     Log.Verbose("NeedFastPolling: Error occured. " + ex.ToString());
   304                     timer?.Stop();
   305                     timer?.Dispose();
   306                     timer = null;
   307                 }
   308             }
   309             else
   310             {
   311                 Log.Verbose("NeedFastPolling: Disabling fast polling.");
   312                 timer?.Stop();
   313                 timer?.Dispose();
   314                 timer = null;
   315             }
   316         }
   317     }
   318 }