AdapterCallbacks.cs
author Thomas
Thu, 04 Jul 2019 16:50:28 +0200
branchtest_sync_folder
changeset 2677 0df7d44b2439
parent 2671 abecd33bc6c7
child 2673 a7f1c342d0ba
child 2678 01bf495e6075
child 2680 c13616f01c2b
permissions -rw-r--r--
Move sync messages to inbox/pEp folder to hide them
Thomas@2651
     1
´╗┐using pEp.UI;
Dean@1344
     2
using pEpCOMServerAdapterLib;
Thomas@2613
     3
using System;
Thomas@2639
     4
using System.Linq;
Dean@1344
     5
using System.Runtime.InteropServices;
Thomas@2617
     6
using System.Threading.Tasks;
Dean@1344
     7
Dean@1344
     8
namespace pEp
Dean@1344
     9
{
Dean@1344
    10
    /// <summary>
Dean@1344
    11
    /// Callback implementation for the events the pEpEngine fires.
Thomas@2617
    12
    /// </summary>   
Thomas@2617
    13
    internal class AdapterCallbacks : IpEpEngineCallbacks
Dean@1344
    14
    {
Thomas@2617
    15
        private static  HandshakeDialog             handshakeDialog         = null;
Thomas@1522
    16
Thomas@2617
    17
        #region Callbacks
Dean@1344
    18
        /// <summary>
Dean@1344
    19
        /// Event handler for when the pEp engine requests a handshake during key synchronization.
Dean@1344
    20
        /// </summary>
Dean@1344
    21
        /// <param name="own">The personal identity for the handshake.</param>
Dean@1344
    22
        /// <param name="partner">The communication partner identity for the handshake.</param>
Thomas@1522
    23
        /// <param name="signal">The reason for the handshake notification.</param>
Thomas@2620
    24
        public void NotifyHandshake(ref pEpIdentity own,
Thomas@2620
    25
                                    ref pEpIdentity partner,
Thomas@2620
    26
                                    SyncHandshakeSignal signal)
Dean@1344
    27
        {
Thomas@2623
    28
            Log.Verbose("NotifyHandshake: Received signal " + signal.ToString());
Thomas@2623
    29
Thomas@2623
    30
#if DEBUG
Thomas@2642
    31
            Notification.Show(signal.ToString(), "NotifyHandshake called with signal " + signal.ToString());
Thomas@2623
    32
#endif
Dean@1344
    33
Thomas@1522
    34
            /* Close handshake dialog if it was still open and the signal type
Thomas@1522
    35
             * is not "undefined", in which case the status quo should be preserved.
Thomas@1522
    36
             */
Thomas@2617
    37
            if ((AdapterCallbacks.handshakeDialog != null) &&
Thomas@1522
    38
                (signal != SyncHandshakeSignal.SyncNotifyUndefined))
Thomas@1522
    39
            {
Thomas@1522
    40
                try
Thomas@1522
    41
                {
Thomas@2617
    42
                    AdapterCallbacks.handshakeDialog.Close();
Thomas@1522
    43
                }
Thomas@1936
    44
                catch (Exception ex)
Thomas@1522
    45
                {
Thomas@1936
    46
                    Log.Error("Could not close handshake dialog. " + ex.ToString());
Thomas@1522
    47
                }
Dean@1344
    48
Thomas@1572
    49
                // We set the result in case of an reentrant call, so the
Thomas@1572
    50
                // "parent" also gets the information.
Thomas@2617
    51
                AdapterCallbacks.handshakeDialog.DialogResult = null;
Thomas@2617
    52
                AdapterCallbacks.handshakeDialog = null;
Thomas@1522
    53
            }
Thomas@1522
    54
Thomas@1522
    55
            // The action to take depends on the given signal
Thomas@1522
    56
            switch (signal)
Dean@1402
    57
            {
Thomas@1522
    58
                // No action necessary
Thomas@1522
    59
                case SyncHandshakeSignal.SyncNotifyUndefined:
Dean@1344
    60
                    {
Dean@1344
    61
                        break;
Dean@1344
    62
                    }
Thomas@1581
    63
                /* Currently, all of the following signals trigger showing the handshake dialog.
Thomas@1522
    64
                 */
Thomas@1522
    65
                case SyncHandshakeSignal.SyncNotifyInitAddOurDevice:
Thomas@1522
    66
                case SyncHandshakeSignal.SyncNotifyInitAddOtherDevice:
Thomas@1522
    67
                case SyncHandshakeSignal.SyncNotifyInitFormGroup:
Dean@1344
    68
                    {
Thomas@2671
    69
                        // Stop sync queue
Thomas@2671
    70
                        ThisAddIn.SyncQueue?.Stop();
Thomas@2671
    71
Thomas@1522
    72
                        // Syncronize engine with settings just in case (should already be synced)
Thomas@1557
    73
                        Globals.ThisAddIn.SyncWithSettings(true);
Thomas@1522
    74
Thomas@2620
    75
                        // If sync is disabled, cancel
Thomas@2617
    76
                        if (own.Flags.HasFlag(pEpIdentityFlags.pEpIdfNotForSync))
Thomas@1522
    77
                        {
Thomas@2617
    78
                            Log.Warning("NotifyHandshake: callback received for account with NotForSync flag. Ignoring request.");
Thomas@2620
    79
                            return;
Thomas@1522
    80
                        }
Thomas@1572
    81
Thomas@2617
    82
                        // Close handshake dialog if necessary
Thomas@2617
    83
                        if (AdapterCallbacks.handshakeDialog != null)
nikolaj@1844
    84
                        {
Thomas@2617
    85
                            AdapterCallbacks.handshakeDialog.Close();
Thomas@1889
    86
                        }
Thomas@2613
    87
Thomas@2617
    88
                        // Show handshake dialog in new STA thread
Thomas@2620
    89
                        this.ShowHandshakeDialog(own, partner);
Thomas@1522
    90
Dean@1344
    91
                        break;
Dean@1344
    92
                    }
Thomas@1522
    93
                // In case of timeout, the handshake gets cancelled (dialog is already closed at this point)
Thomas@1522
    94
                case SyncHandshakeSignal.SyncNotifyTimeout:
Thomas@1522
    95
                    {
Thomas@2659
    96
                        ThisAddIn.SyncQueue?.Stop();
Thomas@1522
    97
                        break;
Thomas@1522
    98
                    }
Thomas@2622
    99
                // Signal to show notification that a sync process was successful
Thomas@2622
   100
                case SyncHandshakeSignal.SyncNotifyAcceptedDeviceAdded:
Thomas@2626
   101
                    {
Thomas@2642
   102
                        Notification.Show(Properties.Resources.KeySync_DeviceGroup, Properties.Resources.KeySync_NotifyAcceptedDeviceAdded);
Thomas@2626
   103
                        break;
Thomas@2626
   104
                    }
Thomas@2622
   105
                case SyncHandshakeSignal.SyncNotifyAcceptedGroupCreated:
Thomas@2622
   106
                    {
Thomas@2642
   107
                        Notification.Show(Properties.Resources.KeySync_DeviceGroup, Properties.Resources.KeySync_NotifyAcceptedGroupCreated);
Thomas@2622
   108
                        break;
Thomas@2622
   109
                    }
Thomas@1644
   110
                // Signal to close the dialog (actually, it is already closed at this point)
Thomas@1644
   111
                case SyncHandshakeSignal.SyncNotifyOvertaken:
Thomas@1644
   112
                    {
Thomas@1644
   113
                        break;
Thomas@1644
   114
                    }
Thomas@2622
   115
                // Signal to show that the device is in state sole
Thomas@2622
   116
                case SyncHandshakeSignal.SyncNotifySole:
Thomas@2622
   117
                    {
Thomas@2642
   118
                        Globals.ThisAddIn.Settings.IsGrouped = false;
Thomas@2622
   119
                        break;
Thomas@2622
   120
                    }
Thomas@2622
   121
                // Signal to show that the device is already grouped
Thomas@2622
   122
                case SyncHandshakeSignal.SyncNotifyInGroup:
Thomas@2622
   123
                    {
Thomas@2642
   124
                        Globals.ThisAddIn.Settings.IsGrouped = true;
Thomas@2659
   125
                        ThisAddIn.SyncQueue?.Stop();
Thomas@2622
   126
                        break;
Thomas@2622
   127
                    }
Thomas@1522
   128
                default:
Dean@1344
   129
                    {
Dean@1344
   130
                        break;
Dean@1344
   131
                    }
Dean@1344
   132
            }
Dean@1344
   133
Thomas@2620
   134
            Log.Verbose("NotifyHandshake: Completed.");
Dean@1344
   135
        }
Dean@1344
   136
Dean@1344
   137
        /// <summary>
Dean@1344
   138
        /// Event handler for when the pEp engine requests a message to be sent by the application.
Dean@1344
   139
        /// </summary>
Dean@1344
   140
        /// <param name="msg">The message to send.</param>
Dean@1344
   141
        public void MessageToSend(ref TextMessage msg)
Dean@1344
   142
        {
Thomas@1979
   143
            Log.Verbose("MessageToSend: Started");
Thomas@1624
   144
            try
Thomas@1624
   145
            {
Thomas@1979
   146
                Log.SensitiveData("MessageToSend: Started sending message with subject " + msg.ShortMsg);
Thomas@1624
   147
            }
Thomas@2613
   148
            catch { }
Dean@1344
   149
Thomas@2523
   150
            if (PEPMessage.Create(msg, out PEPMessage newMessage) != Globals.ReturnStatus.Success)
Dean@1344
   151
            {
Dean@1344
   152
                // no need to log here, as PEPMessage.Create already logs the failure.
Dean@1344
   153
                Marshal.ThrowExceptionForHR(0xD /* ERROR_INVALID_DATA */ );
Dean@1344
   154
            }
Dean@1344
   155
Dean@1344
   156
            // Now send the message
Dean@1344
   157
            try
Dean@1344
   158
            {
Thomas@2646
   159
                // Set the auto consume header on the outer message
Thomas@2646
   160
                newMessage.AutoConsume = "yes";
Thomas@2646
   161
Dean@1455
   162
                // Note: It's important that validateSendingAccount is true for sync
Thomas@1833
   163
                Globals.ThisAddIn.CreateAndSendMessage(newMessage, true, true);
Thomas@2641
   164
Thomas@2641
   165
                // Enable automatic deletion of sync messages
Thomas@2641
   166
                Globals.ThisAddIn.ToggleInboxCleaning(true);
Dean@1344
   167
            }
Dean@1344
   168
            catch (Exception ex)
Dean@1344
   169
            {
Dean@1455
   170
                Log.Error("MessageToSend: Error trying to send message, " + ex.ToString());
Dean@1344
   171
                Marshal.ThrowExceptionForHR(0xD /* ERROR_INVALID_DATA */);
Dean@1344
   172
            }
Dean@1344
   173
Dean@1344
   174
            Log.Verbose("MessageToSend: Completed");
Dean@1344
   175
        }
Thomas@2617
   176
        #endregion
Thomas@2617
   177
Thomas@2617
   178
        #region Helper methods
Thomas@2617
   179
Thomas@2617
   180
        /// <summary>
Thomas@2617
   181
        /// Wrapper method for the asynchronous call to show the handshake dialog in a separate STA thread.
Thomas@2617
   182
        /// </summary>
Thomas@2617
   183
        /// <param name="own">The personal identity for the handshake.</param>
Thomas@2617
   184
        /// <param name="partner">The communication partner identity for the handshake.</param>
Thomas@2617
   185
        /// <param name="signal">The reason for the handshake notification.</param>
Thomas@2620
   186
        public void ShowHandshakeDialog(pEpIdentity own, pEpIdentity partner)
Thomas@2617
   187
        {
Thomas@2617
   188
            // Show the dialog
Thomas@2617
   189
            Task<bool?> task = this.ShowHandshakeDialogAsync(own, partner);
Thomas@2620
   190
            bool? result = task.Result;
Thomas@2617
   191
Thomas@2639
   192
            // Get the identities to synchronize
Thomas@2639
   193
            pEpIdentity[] syncIdentities = null;
Thomas@2639
   194
            try
Thomas@2639
   195
            {
Thomas@2639
   196
                syncIdentities = AdapterCallbacks.handshakeDialog.SyncIdentities.Where(a => (a.Synchronize == true)).Select(b => b.Identity)?.ToArray();
Thomas@2639
   197
            }
Thomas@2639
   198
            catch (Exception ex)
Thomas@2639
   199
            {
Thomas@2639
   200
                // If an error occurs, only sync the own identity
Thomas@2639
   201
                syncIdentities = new pEpIdentity[] { own };
Thomas@2639
   202
                Log.Error("ShowhandshakeDialog: Error getting sync identities. " + ex.ToString());
Thomas@2639
   203
            }
Thomas@2639
   204
Thomas@2620
   205
            // At this point, the handshake dialog is already closed
Thomas@2620
   206
            AdapterCallbacks.handshakeDialog = null;
Thomas@2620
   207
Thomas@2620
   208
            // Check for exceptions
Thomas@2617
   209
            if (task.Exception != null)
Thomas@2617
   210
            {
Thomas@2617
   211
                Log.Error("ShowHandshakeDialog: Error occured. " + task.Exception);
Thomas@2617
   212
            }
Thomas@2617
   213
            else
Thomas@2617
   214
            {
Thomas@2620
   215
                SyncHandshakeResult handshakeResult = SyncHandshakeResult.SyncHandshakeCancel;
Thomas@2620
   216
Thomas@2620
   217
                switch (result)
Thomas@2620
   218
                {
Thomas@2620
   219
                    case true:
Thomas@2620
   220
                        {
Thomas@2620
   221
                            handshakeResult = SyncHandshakeResult.SyncHandshakeAccepted;
Thomas@2620
   222
                            break;
Thomas@2620
   223
                        }
Thomas@2620
   224
                    case false:
Thomas@2620
   225
                        {
Thomas@2620
   226
                            handshakeResult = SyncHandshakeResult.SyncHandshakeRejected;
Thomas@2620
   227
                            break;
Thomas@2620
   228
                        }
Thomas@2620
   229
                    case null:
Thomas@2620
   230
                    default:
Thomas@2620
   231
                        {
Thomas@2620
   232
                            handshakeResult = SyncHandshakeResult.SyncHandshakeCancel;
Thomas@2620
   233
                            break;
Thomas@2620
   234
                        }
Thomas@2620
   235
                }
Thomas@2620
   236
Thomas@2648
   237
                try
Thomas@2648
   238
                {
Thomas@2648
   239
                    ThisAddIn.PEPEngine.DeliverHandshakeResult(handshakeResult, syncIdentities);
Thomas@2648
   240
                }
Thomas@2648
   241
                catch (Exception ex)
Thomas@2648
   242
                {
Thomas@2648
   243
                    Log.Error("ShowHandshakeDialog: Error returning handshake result. " + ex.ToString());
Thomas@2648
   244
                }
Thomas@2617
   245
            }
Thomas@2617
   246
        }
Thomas@2617
   247
Thomas@2617
   248
        /// <summary>
Thomas@2617
   249
        /// Shows the handshake dialog in a separate STA thread and returns the dialog result.
Thomas@2617
   250
        /// </summary>
Thomas@2617
   251
        /// <param name="own">The personal identity for the handshake.</param>
Thomas@2617
   252
        /// <param name="partner">The communication partner identity for the handshake.</param>
Thomas@2617
   253
        /// <param name="signal">The reason for the handshake notification.</param>
Thomas@2617
   254
        /// <returns>The user selected result.</returns>
Thomas@2620
   255
        public async Task<bool?> ShowHandshakeDialogAsync(pEpIdentity own, pEpIdentity partner)
Thomas@2617
   256
        {
Thomas@2617
   257
            // Create a new STA thread and show dialog on it
Thomas@2650
   258
            return await Extensions.TaskExtensions.StartSTATask<bool?>(() =>
Thomas@2617
   259
            {
Thomas@2617
   260
                // Create the handshake dialog
Thomas@2620
   261
                AdapterCallbacks.handshakeDialog = new HandshakeDialog(new PEPIdentity(own),
Thomas@2620
   262
                                                                       new PEPIdentity(partner),
Thomas@2617
   263
                                                                       HandshakeDialog.HandshakeMode.SyncTypeA);
Thomas@2617
   264
Thomas@2617
   265
Thomas@2617
   266
                // Build dialog, but only show if building was successful
Thomas@2617
   267
                if (AdapterCallbacks.handshakeDialog.BuildDialog() &&
Thomas@2617
   268
                    AdapterCallbacks.handshakeDialog?.Items?.Count > 0)
Thomas@2650
   269
                {
Thomas@2617
   270
                    return AdapterCallbacks.handshakeDialog.ShowDialog();
Thomas@2617
   271
                }
Thomas@2617
   272
                else
Thomas@2617
   273
                {
Thomas@2617
   274
                    Log.Error("NotifyHandshake: Error creating handshake dialog. Dialog was not shown.");
Thomas@2617
   275
                    return null;
Thomas@2617
   276
                }
Thomas@2617
   277
            });
Thomas@2617
   278
        }
Thomas@2617
   279
        #endregion
Dean@1344
   280
    }
Dean@1344
   281
}