Statistics
| Branch: | Revision:

root / trunk / NetSparkle / NetSparkle.cs @ 049333d2

History | View | Annotate | Download (26.1 kB)

1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Text;
5
using System.ComponentModel;
6
using System.Threading;
7
using System.Net;
8
using System.Windows;
9
using System.Windows.Forms;
10
using System.Drawing;
11
using System.Runtime.InteropServices;
12
using System.Management;
13
using System.Diagnostics;
14
using System.Security.Cryptography.X509Certificates;
15
using System.Net.Security;
16
using Point = System.Drawing.Point;
17

    
18
namespace AppLimit.NetSparkle
19
{
20
    public delegate void LoopStartedOperation(Object sender);
21
    public delegate void LoopFinishedOperation(Object sender, Boolean UpdateRequired);
22

    
23
    /// <summary>
24
    /// Everytime when netsparkle detects an update the 
25
    /// consumer can decide what should happen as next with the help 
26
    /// of the UpdateDatected event
27
    /// </summary>
28
    public enum nNextUpdateAction
29
    {
30
        showStandardUserInterface = 1,
31
        performUpdateUnattended = 2,
32
        prohibitUpdate = 3
33
    }
34

    
35
    /// <summary>
36
    /// Contains all information for the update detected event
37
    /// </summary>
38
    public class UpdateDetectedEventArgs : EventArgs
39
    {
40
        public nNextUpdateAction NextAction;
41
        public NetSparkleConfiguration ApplicationConfig;
42
        public NetSparkleAppCastItem LatestVersion;        
43
    }
44

    
45
    /// <summary>
46
    /// This delegate will be used when an update was detected to allow library 
47
    /// consumer to add own user interface capabilities.    
48
    /// </summary>
49
    /// <param name="sender"></param>
50
    /// <param name="e"></param>
51
    public delegate void UpdateDetected(Object sender, UpdateDetectedEventArgs e);
52

    
53
    public class Sparkle : IDisposable
54
    {
55
        private BackgroundWorker _worker = new BackgroundWorker();
56

    
57
        private String _AppCastUrl;
58
        private String _AppReferenceAssembly;
59

    
60
        private Boolean _DoInitialCheck;
61
        private Boolean _ForceInitialCheck;
62

    
63
        private EventWaitHandle _exitHandle;
64
        private EventWaitHandle _loopingHandle;
65
        
66
        private NetSparkleMainWindows _DiagnosticWindow;
67

    
68
        private TimeSpan _CheckFrequency;
69

    
70
        /// <summary>
71
        /// Enables system profiling against a profile server
72
        /// </summary>
73
        public Boolean EnableSystemProfiling = false;
74

    
75
        /// <summary>
76
        /// Hides the release notes view when an update was found. This 
77
        /// mode is switched on automatically when no sparkle:releaseNotesLink
78
        /// tag was found in the app cast         
79
        /// </summary>
80
        public Boolean HideReleaseNotes = false;
81

    
82
        /// <summary>
83
        /// Contains the profile url for System profiling
84
        /// </summary>
85
        public Uri SystemProfileUrl;
86

    
87
        /// <summary>
88
        /// This event will be raised when a check loop will be started
89
        /// </summary>
90
        public event LoopStartedOperation checkLoopStarted;
91

    
92
        /// <summary>
93
        /// This event will be raised when a check loop is finished
94
        /// </summary>
95
        public event LoopFinishedOperation checkLoopFinished;
96

    
97
        /// <summary>
98
        /// This event can be used to override the standard user interface
99
        /// process when an update is detected
100
        /// </summary>
101
        public event UpdateDetected updateDetected;
102

    
103
        /// <summary>
104
        /// This property holds an optional application icon
105
        /// which will be displayed in the software update dialog. The icon has
106
        /// to be 48x48 pixels.
107
        /// </summary>
108
        public Image ApplicationIcon { get; set; }
109

    
110
        /// <summary>
111
        /// This property returns an optional application icon 
112
        /// which will displayed in the windows as self
113
        /// </summary>
114
        public Icon ApplicationWindowIcon { get; set; }
115

    
116
        /// <summary>
117
        /// This property enables a diagnostic window for debug reasons
118
        /// </summary>
119
        public Boolean ShowDiagnosticWindow { get; set; }
120

    
121
        /// <summary>
122
        /// This property enables the silent mode, this means 
123
        /// the application will be updated without user interaction
124
        /// </summary>
125
        public Boolean EnableSilentMode { get; set; }
126

    
127
        /// <summary>
128
        /// This property returns true when the upadete loop is running
129
        /// and files when the loop is not running
130
        /// </summary>
131
        public Boolean IsUpdateLoopRunning
132
        {
133
            get
134
            {
135
                return _loopingHandle.WaitOne(0);
136
            }
137
        }
138

    
139
        /// <summary>
140
        /// This property defines if we trust every ssl connection also when 
141
        /// this connection has not a valid cert
142
        /// </summary>
143
        public Boolean TrustEverySSLConnection { get; set; }      
144

    
145
        /// <summary>
146
        /// ctor which needs the appcast url
147
        /// </summary>
148
        /// <param name="appcastUrl"></param>
149
        public Sparkle(String appcastUrl)
150
            : this(appcastUrl, null)
151
        { }
152

    
153
        /// <summary>
154
        /// ctor which needs the appcast url and a referenceassembly
155
        /// </summary>
156
        public Sparkle(String appcastUrl, String referenceAssembly)
157
            : this(appcastUrl, referenceAssembly, false)
158
        { }
159

    
160
        /// <summary>
161
        /// ctor which needs the appcast url and a referenceassembly
162
        /// </summary>        
163
        public Sparkle(String appcastUrl, String referenceAssembly, Boolean ShowDiagnostic)
164
        {
165
            // preconfige ssl trust
166
            TrustEverySSLConnection = false;
167

    
168
            // configure ssl cert link
169
            ServicePointManager.ServerCertificateValidationCallback += RemoteCertificateValidation;
170

    
171
            // enable visual style to ensure that we have XP style or higher
172
            // also in WPF applications
173
            System.Windows.Forms.Application.EnableVisualStyles();
174

    
175
            // reset vars
176
            ApplicationIcon = null;
177
            _AppReferenceAssembly = null;            
178

    
179
            // set var
180
            ShowDiagnosticWindow = ShowDiagnostic;
181

    
182
            // create the diagnotic window
183
            _DiagnosticWindow = new NetSparkleMainWindows();
184

    
185
            // set the reference assembly
186
            if (referenceAssembly != null)
187
            {
188
                _AppReferenceAssembly = referenceAssembly;
189
                _DiagnosticWindow.Report("Checking the following file: " + _AppReferenceAssembly);
190
            }
191

    
192
            // show if needed
193
            ShowDiagnosticWindowIfNeeded();            
194

    
195
            // adjust the delegates
196
            _worker.WorkerReportsProgress = true;
197
            _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
198
            _worker.ProgressChanged += new ProgressChangedEventHandler(_worker_ProgressChanged);
199

    
200
            // build the wait handle
201
            _exitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
202
            _loopingHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
203

    
204
            // set the url
205
            _AppCastUrl = appcastUrl;
206
            _DiagnosticWindow.Report("Using the following url: " + _AppCastUrl);            
207
        }
208

    
209
        /// <summary>
210
        /// The method starts a NetSparkle background loop
211
        /// If NetSparkle is configured to check for updates on startup, proceeds to perform 
212
        /// the check. You should only call this function when your app is initialized and 
213
        /// shows its main window.        
214
        /// </summary>        
215
        /// <param name="doInitialCheck"></param>
216
        public void StartLoop(Boolean doInitialCheck)
217
        {
218
            StartLoop(doInitialCheck, false);
219
        }
220

    
221
        /// <summary>
222
        /// The method starts a NetSparkle background loop
223
        /// If NetSparkle is configured to check for updates on startup, proceeds to perform 
224
        /// the check. You should only call this function when your app is initialized and 
225
        /// shows its main window.
226
        /// </summary>
227
        /// <param name="doInitialCheck"></param>
228
        /// <param name="checkFrequency"></param>
229
        public void StartLoop(Boolean doInitialCheck, TimeSpan checkFrequency)
230
        {
231
            StartLoop(doInitialCheck, false, checkFrequency);
232
        }
233

    
234
        /// <summary>
235
        /// The method starts a NetSparkle background loop
236
        /// If NetSparkle is configured to check for updates on startup, proceeds to perform 
237
        /// the check. You should only call this function when your app is initialized and 
238
        /// shows its main window.
239
        /// </summary>
240
        /// <param name="doInitialCheck"></param>
241
        /// <param name="forceInitialCheck"></param>
242
        public void StartLoop(Boolean doInitialCheck, Boolean forceInitialCheck)
243
        {
244
            StartLoop(doInitialCheck, forceInitialCheck, TimeSpan.FromHours(24));
245
        }
246

    
247
        /// <summary>
248
        /// The method starts a NetSparkle background loop
249
        /// If NetSparkle is configured to check for updates on startup, proceeds to perform 
250
        /// the check. You should only call this function when your app is initialized and 
251
        /// shows its main window.
252
        /// </summary>
253
        /// <param name="doInitialCheck"></param>
254
        /// <param name="forceInitialCheck"></param>
255
        /// <param name="checkFrequency"></param>
256
        public void StartLoop(Boolean doInitialCheck, Boolean forceInitialCheck, TimeSpan checkFrequency)
257
        {
258
            // first set the event handle
259
            _loopingHandle.Set();
260

    
261
            // Start the helper thread as a background worker to 
262
            // get well ui interaction                        
263

    
264
            // show if needed
265
            ShowDiagnosticWindowIfNeeded();
266

    
267
            // store infos
268
            _DoInitialCheck = doInitialCheck;
269
            _ForceInitialCheck = forceInitialCheck;
270
            _CheckFrequency = checkFrequency;
271

    
272
            // create and configure the worker
273
            _DiagnosticWindow.Report("Starting background worker");
274

    
275
            // start the work
276
            _worker.RunWorkerAsync();
277
        }
278

    
279
        /// <summary>
280
        /// This method will stop the sparkle background loop and is called
281
        /// through the disposable interface automatically
282
        /// </summary>
283
        public void StopLoop()
284
        {
285
            // ensure the work will finished
286
            _exitHandle.Set();                       
287
        }
288

    
289
        /// <summary>
290
        /// Is called in the using context and will stop all background activities
291
        /// </summary>
292
        public void Dispose()
293
        {
294
            StopLoop();
295
        }
296

    
297
        /// <summary>
298
        /// This method updates the profile information which can be sended to the server if enabled    
299
        /// </summary>
300
        /// <param name="config"></param>
301
        public void UpdateSystemProfileInformation(NetSparkleConfiguration config)
302
        {
303
            // check if profile data is enabled
304
            if (!EnableSystemProfiling)
305
                return;
306

    
307
            // check if we need an update
308
            if (DateTime.Now - config.LastProfileUpdate < new TimeSpan(7, 0, 0, 0))
309
                return;
310

    
311
            // touch the profile update time
312
            config.TouchProfileTime();
313

    
314
            // start the profile thread
315
            Thread t = new Thread(ProfileDataThreadStart);
316
            t.Start(config);
317
        }
318

    
319
        /// <summary>
320
        /// Profile data thread
321
        /// </summary>
322
        /// <param name="obj"></param>
323
        private void ProfileDataThreadStart(object obj)
324
        {
325
            try
326
            {
327
                if (SystemProfileUrl != null)
328
                {
329
                    // get the config
330
                    NetSparkleConfiguration config = obj as NetSparkleConfiguration;
331

    
332
                    // collect data
333
                    NetSparkleDeviceInventory inv = new NetSparkleDeviceInventory(config);
334
                    inv.CollectInventory();
335

    
336
                    // build url
337
                    String requestUrl = inv.BuildRequestUrl(SystemProfileUrl.ToString() + "?");
338

    
339
                    HttpWebRequest.DefaultWebProxy = HttpWebRequest.GetSystemWebProxy();
340

    
341
                    // perform the webrequest
342
                    HttpWebRequest request = HttpWebRequest.Create(requestUrl) as HttpWebRequest;
343
                    using (WebResponse response = request.GetResponse())
344
                    {
345
                        // close the response 
346
                        response.Close();
347
                    }
348
                }
349
            }
350
            catch (Exception ex)
351
            {
352
                // No exception during data send 
353
                ReportDiagnosticMessage(ex.Message);
354
            }
355
        }
356

    
357
        /// <summary>
358
        /// This method checks if an update is required. During this process the appcast
359
        /// will be downloaded and checked against the reference assembly. Ensure that
360
        /// the calling process has access to the internet and read access to the 
361
        /// reference assembly. This method is also called from the background loops.
362
        /// </summary>
363
        /// <param name="config"></param>
364
        /// <returns></returns>
365
        public Boolean IsUpdateRequired(NetSparkleConfiguration config, out NetSparkleAppCastItem latestVersion)
366
        {
367
            // report
368
            ReportDiagnosticMessage("Downloading and checking appcast");
369

    
370
            // init the appcast
371
            NetSparkleAppCast cast = new NetSparkleAppCast(_AppCastUrl, config);
372

    
373
            // check if any updates are available
374
            try
375
            {
376
                latestVersion = cast.GetLatestVersion();
377
            }
378
            catch (Exception e)
379
            {
380
                // show the exeception message 
381
                ReportDiagnosticMessage("Error during app cast download: " + e.Message);
382

    
383
                // just null the version info
384
                latestVersion = null;
385
            }
386

    
387
            if (latestVersion == null)
388
            {
389
                ReportDiagnosticMessage("No version information in app cast found");
390
                return false;
391
            }
392
            else
393
            {
394
                ReportDiagnosticMessage("Lastest version on the server is " + latestVersion.Version);
395
            }
396

    
397
            // set the last check time
398
            ReportDiagnosticMessage("Touch the last check timestamp");
399
            config.TouchCheckTime();
400

    
401
            // check if the available update has to be skipped
402
            if (latestVersion.Version.Equals(config.SkipThisVersion))
403
            {
404
                ReportDiagnosticMessage("Latest update has to be skipped (user decided to skip version " + config.SkipThisVersion + ")");
405
                return false;
406
            }
407

    
408
            // check if the version will be the same then the installed version
409
            Version v1 = new Version(config.InstalledVersion);
410
            Version v2 = new Version(latestVersion.Version);
411

    
412
            if (v2 <= v1)
413
            {
414
                ReportDiagnosticMessage("Installed version is valid, no update needed (" + config.InstalledVersion + ")");
415
                return false;
416
            }
417

    
418
            // ok we need an update
419
            return true;
420
        }
421

    
422
        /// <summary>
423
        /// This method reads the local sparkle configuration for the given
424
        /// reference assembly
425
        /// </summary>
426
        /// <param name="AppReferenceAssembly"></param>
427
        /// <returns></returns>
428
        public NetSparkleConfiguration GetApplicationConfig()
429
        {
430
            NetSparkleConfiguration config;
431
            config = new NetSparkleConfiguration(_AppReferenceAssembly);
432
            return config;
433
        }
434

    
435
        /// <summary>
436
        /// This method shows the update ui and allows to perform the 
437
        /// update process
438
        /// </summary>
439
        /// <param name="currentItem"></param>
440
        public void ShowUpdateNeededUI(NetSparkleAppCastItem currentItem)
441
        {
442

    
443
            // create the form
444
            NetSparkleForm frm = new NetSparkleForm(currentItem, ApplicationIcon, ApplicationWindowIcon);
445

    
446
            // configure the form
447
            frm.TopMost = true;
448

    
449
            if (HideReleaseNotes)
450
                frm.RemoveReleaseNotesControls();
451

    
452
            
453
            // show it
454
            DialogResult dlgResult = frm.ShowDialog();
455
            
456

    
457
            if (dlgResult == DialogResult.No)
458
            {
459
                // skip this version
460
                NetSparkleConfiguration config = new NetSparkleConfiguration(_AppReferenceAssembly);
461
                config.SetVersionToSkip(currentItem.Version);
462
            }
463
            else if (dlgResult == DialogResult.Yes)
464
            {
465
                // download the binaries
466
                InitDownloadAndInstallProcess(currentItem);
467
            }
468
            
469
        }
470

    
471
        /// <summary>
472
        /// This method reports a message in the diagnostic window
473
        /// </summary>
474
        /// <param name="message"></param>
475
        public void ReportDiagnosticMessage(String message)
476
        {
477
            if (_DiagnosticWindow.InvokeRequired)
478
            {
479
                _DiagnosticWindow.Invoke(new Action<String>(ReportDiagnosticMessage), message);                
480
            }
481
            else
482
            {
483
                _DiagnosticWindow.Report(message);
484
            }
485
        }
486

    
487
        /// <summary>
488
        /// This method will be executed as worker thread
489
        /// </summary>
490
        /// <param name="sender"></param>
491
        /// <param name="e"></param>
492
        void _worker_DoWork(object sender, DoWorkEventArgs e)
493
        {
494
            // store the did run once feature
495
            Boolean goIntoLoop = true;
496
            Boolean checkTSP = true;
497
            Boolean doInitialCheck = _DoInitialCheck;
498
            Boolean isInitialCheck = true;
499

    
500
            // start our lifecycles
501
            do
502
            {
503
                // set state
504
                Boolean bUpdateRequired = false;
505

    
506
                // notify
507
                if (checkLoopStarted != null)
508
                    checkLoopStarted(this);
509

    
510
                // report status
511
                if (doInitialCheck == false)
512
                {
513
                    ReportDiagnosticMessage("Initial check prohibited, going to wait");
514
                    doInitialCheck = true;
515
                    goto WaitSection;
516
                }
517

    
518
                // report status
519
                ReportDiagnosticMessage("Starting update loop...");
520

    
521
                // read the config
522
                ReportDiagnosticMessage("Reading config...");
523
                NetSparkleConfiguration config = GetApplicationConfig();
524

    
525
                // calc CheckTasp
526
                Boolean checkTSPInternal = checkTSP;
527

    
528
                if (isInitialCheck && checkTSPInternal)
529
                    checkTSPInternal = !_ForceInitialCheck;
530

    
531
                // check if it's ok the recheck to software state
532
                if (checkTSPInternal)
533
                {
534
                    TimeSpan csp = DateTime.Now - config.LastCheckTime;
535
                    if (csp < _CheckFrequency)
536
                    {
537
                        ReportDiagnosticMessage(String.Format("Update check performed within the last {0} minutes!", _CheckFrequency.TotalMinutes));
538
                        goto WaitSection;
539
                    }
540
                }
541
                else
542
                    checkTSP = true;
543

    
544
                // when sparkle will be deactivated wait an other cycle
545
                if (config.CheckForUpdate == false)
546
                {
547
                    ReportDiagnosticMessage("Check for updates disabled");
548
                    goto WaitSection;
549
                }
550

    
551
                // update the runonce feature
552
                goIntoLoop = !config.DidRunOnce;
553

    
554
                // update profile information is needed
555
                UpdateSystemProfileInformation(config);
556

    
557
                // check if update is required
558
                NetSparkleAppCastItem latestVersion = null;
559
                bUpdateRequired = IsUpdateRequired(config, out latestVersion);
560
                if (!bUpdateRequired)
561
                    goto WaitSection;
562

    
563
                // show the update window
564
                ReportDiagnosticMessage("Update needed from version " + config.InstalledVersion + " to version " + latestVersion.Version);
565

    
566
                // send notification if needed
567
                UpdateDetectedEventArgs ev = new UpdateDetectedEventArgs() { NextAction = nNextUpdateAction.showStandardUserInterface, ApplicationConfig = config, LatestVersion = latestVersion };
568
                if (updateDetected != null)
569
                    updateDetected(this, ev);
570
                
571
                // check results
572
                switch(ev.NextAction)
573
                {
574
                    case nNextUpdateAction.performUpdateUnattended:
575
                        {
576
                            ReportDiagnosticMessage("Unattended update whished from consumer");
577
                            EnableSilentMode = true;
578
                            _worker.ReportProgress(1, latestVersion);
579
                            break;
580
                        }
581
                    case nNextUpdateAction.prohibitUpdate:
582
                        {
583
                            ReportDiagnosticMessage("Update prohibited from consumer");
584
                            break;
585
                        }
586
                    case nNextUpdateAction.showStandardUserInterface:
587
                    default:
588
                        {
589
                            ReportDiagnosticMessage("Standard UI update whished from consumer");
590
                            _worker.ReportProgress(1, latestVersion);
591
                            break;
592
                        }
593
                }                
594

    
595
            WaitSection:
596
                // reset initialcheck
597
                isInitialCheck = false;
598

    
599
                // notify
600
                if (checkLoopFinished != null)
601
                    checkLoopFinished(this, bUpdateRequired);
602

    
603
                // report wait statement
604
                ReportDiagnosticMessage(String.Format("Sleeping for an other {0} minutes, exit event or force update check event", _CheckFrequency.TotalMinutes));
605

    
606
                // wait for
607
                if (!goIntoLoop)
608
                    break;
609
                else
610
                {
611
                    // build the event array
612
                    WaitHandle[] handles = new WaitHandle[1];
613
                    handles[0] = _exitHandle;
614

    
615
                    // wait for any
616
                    int i = WaitHandle.WaitAny(handles, _CheckFrequency);
617
                    if (WaitHandle.WaitTimeout == i)
618
                    {
619
                        ReportDiagnosticMessage(String.Format("{0} minutes are over", _CheckFrequency.TotalMinutes));
620
                        continue;
621
                    }
622

    
623
                    // check the exit hadnle
624
                    if (i == 0)
625
                    {
626
                        ReportDiagnosticMessage("Got exit signal");
627
                        break;
628
                    }
629

    
630
                    // check an other check needed
631
                    if (i == 1)
632
                    {
633
                        ReportDiagnosticMessage("Got force update check signal");
634
                        checkTSP = false;
635
                        continue;
636
                    }
637
                }
638
            } while (goIntoLoop);
639

    
640
            // reset the islooping handle
641
            _loopingHandle.Reset();
642
        }
643

    
644
        /// <summary>
645
        /// This method will be notified
646
        /// </summary>
647
        /// <param name="sender"></param>
648
        /// <param name="e"></param>
649
        private void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
650
        {
651
            switch (e.ProgressPercentage)
652
            {
653
                case 1:
654
                    {
655
                        // get the current item
656
                        NetSparkleAppCastItem currentItem = e.UserState as NetSparkleAppCastItem;
657

    
658
                        // show the update ui
659
                        if (EnableSilentMode == true)
660
                            InitDownloadAndInstallProcess(currentItem);
661
                        else
662
                            ShowUpdateNeededUI(currentItem);
663

    
664
                        break;
665
                    }
666
                case 0:
667
                    {
668
                        ReportDiagnosticMessage(e.UserState.ToString());
669
                        break;
670
                    }
671
            }
672
        }
673

    
674
        private void InitDownloadAndInstallProcess(NetSparkleAppCastItem item)
675
        {
676
            NetSparkleDownloadProgress dlProgress = new NetSparkleDownloadProgress(this, item, _AppReferenceAssembly, ApplicationIcon, ApplicationWindowIcon, EnableSilentMode);
677
            dlProgress.ShowDialog();
678
        }
679

    
680
        private void ShowDiagnosticWindowIfNeeded()
681
        {
682
            if (_DiagnosticWindow.InvokeRequired)
683
            {
684
                _DiagnosticWindow.Invoke(new Action(ShowDiagnosticWindowIfNeeded));
685
            }
686
            else
687
            {
688
                // check the diagnotic value
689
                NetSparkleConfiguration config = new NetSparkleConfiguration(_AppReferenceAssembly);
690
                if (config.ShowDiagnosticWindow || ShowDiagnosticWindow)
691
                {
692
                    Point newLocation = new Point();
693

    
694
                    newLocation.X = Screen.PrimaryScreen.Bounds.Width - _DiagnosticWindow.Width;
695
                    newLocation.Y = 0;
696

    
697
                    _DiagnosticWindow.Location = newLocation;
698
                    _DiagnosticWindow.Show();
699
                }
700
            }
701
        }
702

    
703
        private bool RemoteCertificateValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
704
        {
705
            if (TrustEverySSLConnection)
706
            {
707
                // verify if we talk about our app cast dll 
708
                HttpWebRequest req = sender as HttpWebRequest;
709
                if (req == null)
710
                    return (certificate is X509Certificate2) ? ((X509Certificate2)certificate).Verify() : false;
711

    
712
                // if so just return our trust 
713
                if (req.RequestUri.Equals(new Uri(_AppCastUrl)))
714
                    return true;
715
                else
716
                    return (certificate is X509Certificate2) ? ((X509Certificate2)certificate).Verify() : false;
717
            }
718
            else
719
            {
720
                // check our cert                 
721
                return (certificate is X509Certificate2) ? ((X509Certificate2)certificate).Verify() : false;
722
            }
723
        }
724
    }
725
}