Statistics
| Branch: | Revision:

root / trunk / NetSparkle / NetSparkle.cs @ 349a2d0f

History | View | Annotate | Download (26.1 kB)

1
using System;
2
using System.ComponentModel;
3
using System.Reflection;
4
using System.Threading;
5
using System.Net;
6
using System.Windows.Forms;
7
using System.Drawing;
8
using System.Security.Cryptography.X509Certificates;
9
using System.Net.Security;
10
using log4net;
11
using Point = System.Drawing.Point;
12

    
13
namespace AppLimit.NetSparkle
14
{
15
    public delegate void LoopStartedOperation(Object sender);
16
    public delegate void LoopFinishedOperation(Object sender, Boolean UpdateRequired);
17

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

    
30
    /// <summary>
31
    /// Contains all information for the update detected event
32
    /// </summary>
33
    public class UpdateDetectedEventArgs : EventArgs
34
    {
35
        public nNextUpdateAction NextAction;
36
        public NetSparkleConfiguration ApplicationConfig;
37
        public NetSparkleAppCastItem LatestVersion;        
38
    }
39

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

    
48
    public class Sparkle : IDisposable
49
    {
50
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51

    
52
        private BackgroundWorker _worker = new BackgroundWorker();
53

    
54
        private String _AppCastUrl;
55
        private String _AppReferenceAssembly;
56

    
57
        private Boolean _DoInitialCheck;
58
        private Boolean _ForceInitialCheck;
59

    
60
        private EventWaitHandle _exitHandle;
61
        private EventWaitHandle _loopingHandle;
62
        
63
        private NetSparkleMainWindows _DiagnosticWindow;
64

    
65
        private TimeSpan _CheckFrequency;
66

    
67
        /// <summary>
68
        /// Enables system profiling against a profile server
69
        /// </summary>
70
        public Boolean EnableSystemProfiling = false;
71

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

    
79
        /// <summary>
80
        /// Contains the profile url for System profiling
81
        /// </summary>
82
        public Uri SystemProfileUrl;
83

    
84
        /// <summary>
85
        /// This event will be raised when a check loop will be started
86
        /// </summary>
87
        public event LoopStartedOperation checkLoopStarted;
88

    
89
        /// <summary>
90
        /// This event will be raised when a check loop is finished
91
        /// </summary>
92
        public event LoopFinishedOperation checkLoopFinished;
93

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

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

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

    
113
        /// <summary>
114
        /// This property enables a diagnostic window for debug reasons
115
        /// </summary>
116
        public Boolean ShowDiagnosticWindow { get; set; }
117

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

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

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

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

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

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

    
165
            // configure ssl cert link
166
            ServicePointManager.ServerCertificateValidationCallback += RemoteCertificateValidation;
167

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

    
172
            // reset vars
173
            ApplicationIcon = null;
174
            _AppReferenceAssembly = null;            
175

    
176
            // set var
177
            ShowDiagnosticWindow = ShowDiagnostic;
178

    
179
            // create the diagnotic window
180
            _DiagnosticWindow = new NetSparkleMainWindows();
181

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

    
189
            // show if needed
190
            ShowDiagnosticWindowIfNeeded();            
191

    
192
            // adjust the delegates
193
            _worker.WorkerReportsProgress = true;
194
            _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
195
            _worker.ProgressChanged += new ProgressChangedEventHandler(_worker_ProgressChanged);
196

    
197
            // build the wait handle
198
            _exitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
199
            _loopingHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
200

    
201
            // set the url
202
            _AppCastUrl = appcastUrl;
203
            _DiagnosticWindow.Report("Using the following url: " + _AppCastUrl);            
204
        }
205

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

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

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

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

    
258
            // Start the helper thread as a background worker to 
259
            // get well ui interaction                        
260

    
261
            // show if needed
262
            ShowDiagnosticWindowIfNeeded();
263

    
264
            // store infos
265
            _DoInitialCheck = doInitialCheck;
266
            _ForceInitialCheck = forceInitialCheck;
267
            _CheckFrequency = checkFrequency;
268

    
269
            // create and configure the worker
270
            _DiagnosticWindow.Report("Starting background worker");
271

    
272
            // start the work
273
            _worker.RunWorkerAsync();
274
        }
275

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

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

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

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

    
308
            // touch the profile update time
309
            config.TouchProfileTime();
310

    
311
            // start the profile thread
312
            Thread t = new Thread(ProfileDataThreadStart);
313
            t.Start(config);
314
        }
315

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

    
329
                    // collect data
330
                    NetSparkleDeviceInventory inv = new NetSparkleDeviceInventory(config);
331
                    inv.CollectInventory();
332

    
333
                    // build url
334
                    String requestUrl = inv.BuildRequestUrl(SystemProfileUrl.ToString() + "?");
335

    
336
                    HttpWebRequest.DefaultWebProxy = HttpWebRequest.GetSystemWebProxy();
337

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

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

    
367
            // init the appcast
368
            NetSparkleAppCast cast = new NetSparkleAppCast(_AppCastUrl, config);
369

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

    
380
                // just null the version info
381
                latestVersion = null;
382
            }
383

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

    
394
            // set the last check time
395
            ReportDiagnosticMessage("Touch the last check timestamp");
396
            config.TouchCheckTime();
397

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

    
405
            // check if the version will be the same then the installed version
406
            Version v1 = new Version(config.InstalledVersion);
407
            Version v2 = new Version(latestVersion.Version);
408

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

    
415
            // ok we need an update
416
            return true;
417
        }
418

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

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

    
440
            // create the form
441
            NetSparkleForm frm = new NetSparkleForm(currentItem, ApplicationIcon, ApplicationWindowIcon);
442

    
443
            // configure the form
444
            frm.TopMost = true;
445

    
446
            if (HideReleaseNotes)
447
                frm.RemoveReleaseNotesControls();
448

    
449
            
450
            // show it
451
            DialogResult dlgResult = frm.ShowDialog();
452
            
453

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

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

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

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

    
504
                // notify
505
                if (checkLoopStarted != null)
506
                    checkLoopStarted(this);
507

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

    
516
                // report status
517
                ReportDiagnosticMessage("Starting update loop...");
518

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

    
523
                // calc CheckTasp
524
                Boolean checkTSPInternal = checkTSP;
525

    
526
                if (isInitialCheck && checkTSPInternal)
527
                    checkTSPInternal = !_ForceInitialCheck;
528

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

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

    
549
                // update the runonce feature
550
                goIntoLoop = !config.DidRunOnce;
551

    
552
                // update profile information is needed
553
                UpdateSystemProfileInformation(config);
554

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

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

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

    
593
            WaitSection:
594
                // reset initialcheck
595
                isInitialCheck = false;
596

    
597
                // notify
598
                if (checkLoopFinished != null)
599
                    checkLoopFinished(this, bUpdateRequired);
600

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

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

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

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

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

    
638
            // reset the islooping handle
639
            _loopingHandle.Reset();
640
        }
641

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

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

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

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

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

    
692
                    newLocation.X = Screen.PrimaryScreen.Bounds.Width - _DiagnosticWindow.Width;
693
                    newLocation.Y = 0;
694

    
695
                    _DiagnosticWindow.Location = newLocation;
696
                    _DiagnosticWindow.Show();
697
                }
698
            }
699
        }
700

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

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