source: filezilla/trunk/fuentes/src/interface/updater.cpp @ 3185

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 3 years ago

Update new version: 3.15.02

File size: 21.7 KB
Line 
1#include <filezilla.h>
2
3#if FZ_MANUALUPDATECHECK
4
5#include "buildinfo.h"
6#include "updater.h"
7#include "Options.h"
8#include "file_utils.h"
9#include <wx/base64.h>
10#include <wx/tokenzr.h>
11#include <string>
12
13#include <libfilezilla/local_filesys.hpp>
14
15#include <string>
16
17// This is ugly but does the job
18#define SHA512_STANDALONE
19typedef unsigned int uint32;
20namespace {
21#include "../putty/int64.h"
22#include "../putty/sshsh512.c"
23}
24
25BEGIN_EVENT_TABLE(CUpdater, wxEvtHandler)
26EVT_FZ_NOTIFICATION(wxID_ANY, CUpdater::OnEngineEvent)
27EVT_TIMER(wxID_ANY, CUpdater::OnTimer)
28END_EVENT_TABLE()
29
30// BASE-64 encoded DER without the BEGIN/END CERTIFICATE
31static char s_update_cert[] = "\
32MIIFsTCCA5ugAwIBAgIESnXLbzALBgkqhkiG9w0BAQ0wSTELMAkGA1UEBhMCREUx\n\
33GjAYBgNVBAoTEUZpbGVaaWxsYSBQcm9qZWN0MR4wHAYDVQQDExVmaWxlemlsbGEt\n\
34cHJvamVjdC5vcmcwHhcNMDkwODAyMTcyMjU2WhcNMzEwNjI4MTcyMjU4WjBJMQsw\n\
35CQYDVQQGEwJERTEaMBgGA1UEChMRRmlsZVppbGxhIFByb2plY3QxHjAcBgNVBAMT\n\
36FWZpbGV6aWxsYS1wcm9qZWN0Lm9yZzCCAh8wCwYJKoZIhvcNAQEBA4ICDgAwggIJ\n\
37AoICAJqWXy7YzVP5pOk8VB9bd/ROC9SVbAxJiFHh0I0/JmyW+jSfzFCYWr1DKGVv\n\
38Oui+qiUsaSgjWTh/UusnVu4Q4Lb00k7INRF6MFcGFkGNmOZPk4Qt0uuWMtsxiFek\n\
399QMPWSYs+bxk+M0u0rNOdAblsIzeV16yhfUQDtrJxPWbRpuLgp9/4/oNbixet7YM\n\
40pvwlns2o1KXcsNcBcXraux5QmnD4oJVYbTY2qxdMVyreA7dxd40c55F6FvA+L36L\n\
41Nv54VwRFSqY12KBG4I9Up+c9OQ9HMN0zm0FhYtYeKWzdMIRk06EKAxO7MUIcip3q\n\
427v9eROPnKM8Zh4dzkWnCleirW8EKFEm+4+A8pDqirMooiQqkkMesaJDV361UCoVo\n\
43fRhqfK+Prx0BaJK/5ZHN4tmgU5Tmq+z2m7aIKwOImj6VF3somVvmh0G/othnU2MH\n\
44GB7qFrIUMZc5VhrAwmmSA2Z/w4+0ToiR+IrdGmDKz3cVany3EZAzWRJUARaId9FH\n\
45v/ymA1xcFAKmfxsjGNlNpXd7b8UElS8+ccKL9m207k++IIjc0jUPgrM70rU3cv5M\n\
46Kevp971eHLhpWa9vrjbz/urDzBg3Dm8XEN09qwmABfIEnhm6f7oz2bYXjz73ImYj\n\
47rZsogz+Jsx3NWhHFUD42iA4ZnxHIEgchD/TAihpbdrEhgmdvAgMBAAGjgacwgaQw\n\
48EgYDVR0TAQH/BAgwBgEB/wIBAjAmBgNVHREEHzAdgRthZG1pbkBmaWxlemlsbGEt\n\
49cHJvamVjdC5vcmcwDwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUd4w2verFjXAn\n\
50CrNLor39nFtemNswNgYDVR0fBC8wLTAroCmgJ4YlaHR0cHM6Ly9jcmwuZmlsZXpp\n\
51bGxhLXByb2plY3Qub3JnL2NybDALBgkqhkiG9w0BAQ0DggIBAF3fmV/Bs4amV78d\n\
52uhe5PkW7yTO6iCfKJVDB22kXPvL0rzZn4SkIZNoac8Xl5vOoRd6k+06i3aJ78w+W\n\
539Z0HK1jUdjW7taYo4bU58nAp3Li+JwjE/lUBNqSKSescPjdZW0KzIIZls91W30yt\n\
54tGq85oWAuyVprHPlr2uWLg1q4eUdF6ZAz4cZ0+9divoMuk1HiWxi1Y/1fqPRzUFf\n\
55UGK0K36iPPz2ktzT7qJYXRfC5QDoX7tCuoDcO5nccVjDypRKxy45O5Ucm/fywiQW\n\
56NQfz/yQAmarQSCfDjNcHD1rdJ0lx9VWP6xi+Z8PGSlR9eDuMaqPVAE1DLHwMMTTZ\n\
5793PbfP2nvgbElgEki28LUalyVuzvrKcu/rL1LnCJA4jStgE/xjDofpYwgtG4ZSnE\n\
58KgNy48eStvNZbGhwn2YvrxyKmw58WSQG9ArOCHoLcWnpedSZuTrPTLfgNUx7DNbo\n\
59qJU36tgxiO0XLRRSetl7jkSIO6U1okVH0/tvstrXEWp4XwdlmoZf92VVBrkg3San\n\
60fA5hBaI2gpQwtpyOJzwLzsd43n4b1YcPiyzhifJGcqRCBZA1uArNsH5iG6z/qHXp\n\
61KjuMxZu8aM8W2gp8Yg8QZfh5St/nut6hnXb5A8Qr+Ixp97t34t264TBRQD6MuZc3\n\
62PqQuF7sJR6POArUVYkRD/2LIWsB7\n\
63";
64
65void version_information::update_available()
66{
67        if( !nightly_.url_.empty() && COptions::Get()->GetOptionVal(OPTION_UPDATECHECK_CHECKBETA) == 2 ) {
68                available_ = nightly_;
69        }
70        else if( !beta_.version_.empty() && COptions::Get()->GetOptionVal(OPTION_UPDATECHECK_CHECKBETA) != 0 ) {
71                available_ = beta_;
72        }
73        else if( !stable_.version_.empty() ) {
74                available_ = stable_;
75        }
76        else {
77                available_ = build();
78        }
79}
80
81static CUpdater* instance = 0;
82
83CUpdater::CUpdater(CUpdateHandler& parent, CFileZillaEngineContext& engine_context)
84        : state_(UpdaterState::idle)
85        , engine_(new CFileZillaEngine(engine_context))
86{
87        AddHandler(parent);
88        engine_->Init(this);
89}
90
91void CUpdater::Init()
92{
93        if( state_ == UpdaterState::checking || state_ == UpdaterState::newversion_downloading ) {
94                return;
95        }
96
97        raw_version_information_ = COptions::Get()->GetOption( OPTION_UPDATECHECK_NEWVERSION );
98
99        UpdaterState s = ProcessFinishedData(FZ_AUTOUPDATECHECK);
100
101        SetState(s);
102
103        AutoRunIfNeeded();
104
105        update_timer_.SetOwner(this);
106        update_timer_.Start(1000 * 3600);
107
108        if( !instance ) {
109                instance = this;
110        }
111}
112
113CUpdater::~CUpdater()
114{
115        if( instance == this ) {
116                instance  =0;
117        }
118        delete engine_;
119}
120
121CUpdater* CUpdater::GetInstance()
122{
123        return instance;
124}
125
126void CUpdater::AutoRunIfNeeded()
127{
128#if FZ_AUTOUPDATECHECK
129        if( state_ == UpdaterState::failed || state_ == UpdaterState::idle ) {
130                if( !COptions::Get()->GetOptionVal(OPTION_DEFAULT_DISABLEUPDATECHECK) && COptions::Get()->GetOptionVal(OPTION_UPDATECHECK) != 0 && LongTimeSinceLastCheck() ) {
131                        Run();
132                }
133        }
134#endif
135}
136
137void CUpdater::RunIfNeeded()
138{
139        build const b = AvailableBuild();
140        if( state_ == UpdaterState::idle || state_ == UpdaterState::failed ||
141                LongTimeSinceLastCheck() || (state_ == UpdaterState::newversion && !b.url_.empty()) ||
142                (state_ == UpdaterState::newversion_ready && !VerifyChecksum( DownloadedFile(), b.size_, b.hash_ ) ) )
143        {
144                Run();
145        }
146}
147
148bool CUpdater::LongTimeSinceLastCheck() const
149{
150        wxString const lastCheckStr = COptions::Get()->GetOption(OPTION_UPDATECHECK_LASTDATE);
151        if (lastCheckStr.empty())
152                return true;
153
154        fz::datetime lastCheck(lastCheckStr.ToStdWstring(), fz::datetime::utc);
155        if (!lastCheck.empty())
156                return true;
157
158        auto const span = fz::datetime::now() - lastCheck;
159
160        if (span.get_seconds() < 0)
161                // Last check in future
162                return true;
163
164        int days = 1;
165        if (!CBuildInfo::IsUnstable())
166                days = COptions::Get()->GetOptionVal(OPTION_UPDATECHECK_INTERVAL);
167        return span.get_days() >= days;
168}
169
170wxString CUpdater::GetUrl()
171{
172        wxString host = CBuildInfo::GetHostname();
173        if (host.empty())
174                host = _T("unknown");
175
176        wxString version(PACKAGE_VERSION, wxConvLocal);
177        version.Replace(_T(" "), _T("%20"));
178
179        wxString url = wxString::Format(_T("https://update.filezilla-project.org/update.php?platform=%s&version=%s"), host, version);
180#if defined(__WXMSW__) || defined(__WXMAC__)
181        // Makes not much sense to submit OS version on Linux, *BSD and the likes, too many flavours.
182        wxString osVersion = wxString::Format(_T("&osversion=%d.%d"), wxPlatformInfo::Get().GetOSMajorVersion(), wxPlatformInfo::Get().GetOSMinorVersion());
183        url += osVersion;
184#endif
185
186#ifdef __WXMSW__
187        if (wxIsPlatform64Bit())
188                url += _T("&osarch=64");
189        else
190                url += _T("&osarch=32");
191#endif
192
193        wxString const cpuCaps = CBuildInfo::GetCPUCaps(',');
194        if (!cpuCaps.empty()) {
195                url += _T("&cpuid=") + cpuCaps;
196        }
197
198        return url;
199}
200
201bool CUpdater::Run()
202{
203        if( state_ != UpdaterState::idle && state_ != UpdaterState::failed &&
204                state_ != UpdaterState::newversion && state_ != UpdaterState::newversion_ready )
205        {
206                return false;
207        }
208
209        auto  const t = fz::datetime::now();
210        COptions::Get()->SetOption(OPTION_UPDATECHECK_LASTDATE, t.format(_T("%Y-%m-%d %H:%M:%S"), fz::datetime::utc));
211
212        local_file_.clear();
213        log_ = wxString::Format(_("Started update check on %s\n"), t.format(_T("%Y-%m-%d %H:%M:%S"), fz::datetime::local));
214
215        wxString build = CBuildInfo::GetBuildType();
216        if( build.empty() ) {
217                build = _("custom");
218        }
219        log_ += wxString::Format(_("Own build type: %s\n"), build);
220
221        SetState(UpdaterState::checking);
222
223        m_use_internal_rootcert = true;
224        int res = Download(GetUrl(), wxString());
225
226        if (res != FZ_REPLY_WOULDBLOCK) {
227                SetState(UpdaterState::failed);
228        }
229        raw_version_information_.clear();
230
231        return state_ == UpdaterState::checking;
232}
233
234int CUpdater::Download(wxString const& url, wxString const& local_file)
235{
236        wxASSERT(pending_commands_.empty());
237        pending_commands_.clear();
238        pending_commands_.emplace_back(new CDisconnectCommand);
239        if (!CreateConnectCommand(url) || !CreateTransferCommand(url, local_file)) {
240                return FZ_REPLY_ERROR;
241        }
242
243        return ContinueDownload();
244}
245
246int CUpdater::ContinueDownload()
247{
248        if (pending_commands_.empty()) {
249                return FZ_REPLY_OK;
250        }
251
252        int res = engine_->Execute(*pending_commands_.front());
253        if (res == FZ_REPLY_OK) {
254                pending_commands_.pop_front();
255                return ContinueDownload();
256        }
257
258        return res;
259}
260
261bool CUpdater::CreateConnectCommand(wxString const& url)
262{
263        CServer s;
264        CServerPath path;
265        wxString error;
266        if( !s.ParseUrl( url, 0, wxString(), wxString(), error, path ) || (s.GetProtocol() != HTTP && s.GetProtocol() != HTTPS) ) {
267                return false;
268        }
269
270        pending_commands_.emplace_back(new CConnectCommand(s));
271        return true;
272}
273
274bool CUpdater::CreateTransferCommand(wxString const& url, wxString const& local_file)
275{
276        CFileTransferCommand::t_transferSettings transferSettings;
277
278        CServer s;
279        CServerPath path;
280        wxString error;
281        if( !s.ParseUrl( url, 0, wxString(), wxString(), error, path ) || (s.GetProtocol() != HTTP && s.GetProtocol() != HTTPS) ) {
282                return false;
283        }
284        wxString file = path.GetLastSegment();
285        path = path.GetParent();
286
287        pending_commands_.emplace_back(new CFileTransferCommand(local_file, path, file, true, transferSettings));
288        return true;
289}
290
291void CUpdater::OnEngineEvent(wxFzEvent& event)
292{
293        if (!engine_ || engine_ != event.engine_)
294                return;
295
296        std::unique_ptr<CNotification> notification;
297        while( (notification = engine_->GetNextNotification()) ) {
298                ProcessNotification(std::move(notification));
299        }
300}
301
302void CUpdater::ProcessNotification(std::unique_ptr<CNotification> && notification)
303{
304        if (state_ != UpdaterState::checking && state_ != UpdaterState::newversion_downloading) {
305                return;
306        }
307
308        switch (notification->GetID())
309        {
310        case nId_asyncrequest:
311                {
312                        auto pData = unique_static_cast<CAsyncRequestNotification>(std::move(notification));
313                        if (pData->GetRequestID() == reqId_fileexists) {
314                                static_cast<CFileExistsNotification *>(pData.get())->overwriteAction = CFileExistsNotification::resume;
315                        }
316                        else if (pData->GetRequestID() == reqId_certificate) {
317                                auto & certNotification = static_cast<CCertificateNotification &>(*pData.get());
318                                if (m_use_internal_rootcert) {
319                                        auto certs = certNotification.GetCertificates();
320                                        if( certs.size() > 1 ) {
321                                                auto ca = certs.back();
322
323                                                unsigned int ca_data_length{};
324                                                unsigned char const* ca_data = ca.GetRawData(ca_data_length);
325
326                                                wxMemoryBuffer updater_root = wxBase64Decode(s_update_cert, wxNO_LEN, wxBase64DecodeMode_SkipWS);
327                                                if( ca_data_length == updater_root.GetDataLen() && !memcmp(ca_data, updater_root.GetData(), ca_data_length) ) {
328                                                        certNotification.m_trusted = true;
329                                                }
330                                        }
331                                }
332                                else {
333                                        certNotification.m_trusted = true;
334                                }
335                        }
336                        engine_->SetAsyncRequestReply(std::move(pData));
337                }
338                break;
339        case nId_data:
340                ProcessData(static_cast<CDataNotification&>(*notification.get()));
341                break;
342        case nId_operation:
343                ProcessOperation(static_cast<COperationNotification const&>(*notification.get()));
344                break;
345        case nId_logmsg:
346                {
347                        auto const& msg = static_cast<CLogmsgNotification const&>(*notification.get());
348                        log_ += msg.msg + _T("\n");
349                }
350                break;
351        default:
352                break;
353        }
354}
355
356UpdaterState CUpdater::ProcessFinishedData(bool can_download)
357{
358        UpdaterState s = UpdaterState::failed;
359
360        ParseData();
361
362        if (version_information_.available_.version_.empty()) {
363                s = UpdaterState::idle;
364        }
365        else if (!version_information_.available_.url_.empty()) {
366
367                wxString const temp = GetTempFile();
368                wxString const local_file = GetLocalFile(version_information_.available_, true);
369                if (!local_file.empty() && fz::local_filesys::get_file_type(fz::to_native(local_file)) != fz::local_filesys::unknown) {
370                        local_file_ = local_file;
371                        log_ += wxString::Format(_("Local file is %s\n"), local_file);
372                        s = UpdaterState::newversion_ready;
373                }
374                else {
375                        // We got a checksum over a secure channel already.
376                        m_use_internal_rootcert = false;
377
378                        if (temp.empty() || local_file.empty()) {
379                                s = UpdaterState::newversion;
380                        }
381                        else {
382                                s = UpdaterState::newversion_downloading;
383                                auto size = fz::local_filesys::get_size(fz::to_native(temp));
384                                if (size >= 0 && size >= version_information_.available_.size_) {
385                                        s = ProcessFinishedDownload();
386                                }
387                                else if( !can_download || Download( version_information_.available_.url_, GetTempFile() ) != FZ_REPLY_WOULDBLOCK ) {
388                                        s = UpdaterState::newversion;
389                                }
390                        }
391                }
392        }
393        else {
394                s = UpdaterState::newversion;
395        }
396
397        return s;
398}
399
400void CUpdater::ProcessOperation(COperationNotification const& operation)
401{
402        if( state_ != UpdaterState::checking && state_ != UpdaterState::newversion_downloading ) {
403                return;
404        }
405
406        if (pending_commands_.empty()) {
407                SetState(UpdaterState::failed);
408                return;
409        }
410
411
412        UpdaterState s = UpdaterState::failed;
413
414        int res = operation.nReplyCode;
415        if (res == FZ_REPLY_OK || (operation.commandId == Command::disconnect && res & FZ_REPLY_DISCONNECTED)) {
416                pending_commands_.pop_front();
417                res = ContinueDownload();
418                if (res == FZ_REPLY_WOULDBLOCK) {
419                        return;
420                }
421        }
422
423        if (res != FZ_REPLY_OK) {
424                if (state_ != UpdaterState::checking) {
425                        s = UpdaterState::newversion;
426                }
427        }
428        else if( state_ == UpdaterState::checking ) {
429                s = ProcessFinishedData(true);
430        }
431        else {
432                s = ProcessFinishedDownload();
433        }
434        SetState(s);
435}
436
437UpdaterState CUpdater::ProcessFinishedDownload()
438{
439        UpdaterState s = UpdaterState::newversion;
440
441        wxString const temp = GetTempFile();
442        if( temp.empty() ) {
443                s = UpdaterState::newversion;
444        }
445        else if( !VerifyChecksum( temp, version_information_.available_.size_, version_information_.available_.hash_ ) ) {
446                wxLogNull log;
447                wxRemoveFile(temp);
448                s = UpdaterState::newversion;
449        }
450        else {
451                s = UpdaterState::newversion_ready;
452
453                wxString local_file = GetLocalFile( version_information_.available_, false );
454
455                wxLogNull log;
456                if (local_file.empty() || !wxRenameFile( temp, local_file, false ) ) {
457                        s = UpdaterState::newversion;
458                        wxRemoveFile( temp );
459                        log_ += wxString::Format(_("Could not create local file %s\n"), local_file);
460                }
461                else {
462                        local_file_ = local_file;
463                        log_ += wxString::Format(_("Local file is %s\n"), local_file);
464                }
465        }
466        return s;
467}
468
469wxString CUpdater::GetLocalFile(build const& b, bool allow_existing)
470{
471        wxString const fn = GetFilename( b.url_ );
472        wxString const dl = GetDownloadDir().GetPath();
473
474        int i = 1;
475        wxString f = dl + fn;
476
477        while (fz::local_filesys::get_file_type(fz::to_native(f)) != fz::local_filesys::unknown && (!allow_existing || !VerifyChecksum(f, b.size_, b.hash_))) {
478                if (++i > 99) {
479                        return wxString();
480                }
481                wxString ext;
482                int pos;
483                if (!fn.Right(8).CmpNoCase(_T(".tar.bz2"))) {
484                        pos = fn.size() - 8;
485                }
486                else {
487                        pos = fn.Find('.', true);
488                }
489
490                if (pos == -1) {
491                        f = dl + fn + wxString::Format(_T(" (%d)"), i);
492                }
493                else {
494                        f = dl + fn.Left(pos) + wxString::Format(_T(" (%d)"), i) + fn.Mid(pos);
495                }
496        }
497
498        return f;
499}
500
501void CUpdater::ProcessData(CDataNotification& dataNotification)
502{
503        if( state_ != UpdaterState::checking ) {
504                return;
505        }
506
507        int len;
508        char* data = dataNotification.Detach(len);
509
510        if( COptions::Get()->GetOptionVal(OPTION_LOGGING_DEBUGLEVEL) == 4 ) {
511                log_ += wxString::Format(_T("ProcessData %d\n"), len);
512        }
513
514        if( raw_version_information_.size() + len > 131072 ) {
515                log_ += _("Received version information is too large");
516                engine_->Cancel();
517                SetState(UpdaterState::failed);
518        }
519        else {
520                for (int i = 0; i < len; ++i) {
521                        if (data[i] < 10 || (unsigned char)data[i] > 127) {
522                                log_ += _("Received invalid character in version information");
523                                SetState(UpdaterState::failed);
524                                engine_->Cancel();
525                                break;
526                        }
527                }
528        }
529
530        if( state_ == UpdaterState::checking ) {
531                raw_version_information_ += wxString(data, wxConvUTF8, len);
532        }
533        delete [] data;
534}
535
536void CUpdater::ParseData()
537{
538        int64_t const ownVersionNumber = CBuildInfo::ConvertToVersionNumber(CBuildInfo::GetVersion().c_str());
539        version_information_ = version_information();
540
541        wxString raw_version_information = raw_version_information_;
542
543        log_ += wxString::Format(_("Parsing %d bytes of version information.\n"), static_cast<int>(raw_version_information.size()));
544
545        while( !raw_version_information.empty() ) {
546                wxString line;
547                int pos = raw_version_information.Find('\n');
548                if (pos != -1) {
549                        line = raw_version_information.Left(pos);
550                        raw_version_information = raw_version_information.Mid(pos + 1);
551                }
552                else {
553                        line = raw_version_information;
554                        raw_version_information.clear();
555                }
556
557                wxStringTokenizer tokens(line, _T(" \t\n"),  wxTOKEN_STRTOK);
558                if( !tokens.CountTokens() ) {
559                        // After empty line, changelog follows
560                        version_information_.changelog = raw_version_information;
561                        version_information_.changelog.Trim(true);
562                        version_information_.changelog.Trim(false);
563
564                        if( COptions::Get()->GetOptionVal(OPTION_LOGGING_DEBUGLEVEL) == 4 ) {
565                                log_ += wxString::Format(_T("Changelog: %s\n"), version_information_.changelog);
566                        }
567                        break;
568                }
569
570                if( tokens.CountTokens() != 2 && tokens.CountTokens() != 6 ) {
571                        if( COptions::Get()->GetOptionVal(OPTION_LOGGING_DEBUGLEVEL) == 4 ) {
572                                log_ += wxString::Format(_T("Skipping line with %d tokens\n"), static_cast<int>(tokens.CountTokens()));
573                        }
574                        continue;
575                }
576
577                wxString type = tokens.GetNextToken();
578                wxString versionOrDate = tokens.GetNextToken();
579
580                if (type == _T("nightly")) {
581                        fz::datetime nightlyDate(versionOrDate.ToStdWstring(), fz::datetime::utc);
582                        if (!nightlyDate.empty()) {
583                                if (COptions::Get()->GetOptionVal(OPTION_LOGGING_DEBUGLEVEL) == 4) {
584                                        log_ += _T("Could not parse nightly date\n");
585                                }
586                                continue;
587                        }
588
589                        fz::datetime buildDate = CBuildInfo::GetBuildDate();
590                        if (!buildDate.empty() || !nightlyDate.empty() || nightlyDate <= buildDate) {
591                                if( COptions::Get()->GetOptionVal(OPTION_LOGGING_DEBUGLEVEL) == 4 ) {
592                                        log_ += _T("Nightly isn't newer\n");
593                                }
594                                continue;
595                        }
596                }
597                else {
598                        int64_t v = CBuildInfo::ConvertToVersionNumber(versionOrDate.c_str());
599                        if (v <= ownVersionNumber)
600                                continue;
601                }
602
603                build* b = 0;
604                if( type == _T("nightly") && UpdatableBuild() ) {
605                        b = &version_information_.nightly_;
606                }
607                else if( type == _T("release") ) {
608                        b = &version_information_.stable_;
609                }
610                else if( type == _T("beta") ) {
611                        b = &version_information_.beta_;
612                }
613
614                if( b ) {
615                        b->version_ = versionOrDate;
616
617                        if( UpdatableBuild() && tokens.CountTokens() == 4 ) {
618                                wxString const url = tokens.GetNextToken();
619                                wxString const sizestr = tokens.GetNextToken();
620                                wxString const hash_algo = tokens.GetNextToken();
621                                wxString const hash = tokens.GetNextToken();
622
623                                if( GetFilename(url).empty() ) {
624                                        if( COptions::Get()->GetOptionVal(OPTION_LOGGING_DEBUGLEVEL) == 4 ) {
625                                                log_ += wxString::Format(_T("Could not extract filename from URL: %s\n"), url);
626                                        }
627                                        continue;
628                                }
629
630                                if( hash_algo.CmpNoCase(_T("sha512")) ) {
631                                        continue;
632                                }
633
634                                unsigned long long l = 0;
635                                if( !sizestr.ToULongLong(&l) ) {
636                                        if( COptions::Get()->GetOptionVal(OPTION_LOGGING_DEBUGLEVEL) == 4 ) {
637                                                log_ += wxString::Format(_T("Could not parse size: %s"), sizestr);
638                                        }
639                                        continue;
640                                }
641
642                                b->url_ = url;
643                                b->size_ = l;
644                                b->hash_ = hash;
645
646                                // @translator: Two examples: Found new nightly 2014-04-03\n, Found new release 3.9.0.1\n
647                                log_ += wxString::Format(_("Found new %s %s\n"), type, b->version_);
648                        }
649                }
650        }
651
652        version_information_.update_available();
653
654        COptions::Get()->SetOption( OPTION_UPDATECHECK_NEWVERSION, raw_version_information_ );
655}
656
657void CUpdater::OnTimer(wxTimerEvent&)
658{
659        AutoRunIfNeeded();
660}
661
662bool CUpdater::VerifyChecksum(wxString const& file, int64_t size, wxString const& checksum)
663{
664        if (file.empty() || checksum.empty()) {
665                return false;
666        }
667
668        auto filesize = fz::local_filesys::get_size(fz::to_native(file));
669        if (filesize < 0 || filesize != size) {
670                return false;
671        }
672
673        SHA512_State state;
674        SHA512_Init(&state);
675
676        {
677                wxLogNull null;
678                wxFile f;
679                if (!f.Open(file)) {
680                        return false;
681                }
682                char buffer[65536];
683                ssize_t read;
684                while ((read = f.Read(buffer, sizeof(buffer))) > 0) {
685                        SHA512_Bytes(&state, buffer, read);
686                }
687                if (read < 0) {
688                        return false;
689                }
690        }
691
692        unsigned char raw_digest[64];
693        SHA512_Final(&state, raw_digest);
694
695        wxString digest;
696        for (unsigned int i = 0; i < sizeof(raw_digest); ++i) {
697                unsigned char l = raw_digest[i] >> 4;
698                unsigned char r = raw_digest[i] & 0x0F;
699
700                if (l > 9)
701                        digest += 'a' + l - 10;
702                else
703                        digest += '0' + l;
704
705                if (r > 9)
706                        digest += 'a' + r - 10;
707                else
708                        digest += '0' + r;
709        }
710
711        if (checksum.CmpNoCase(digest)) {
712                log_ += wxString::Format(_("Checksum mismatch on file %s\n"), file);
713                return false;
714        }
715
716        log_ += wxString::Format(_("Checksum match on file %s\n"), file);
717        return true;
718}
719
720wxString CUpdater::GetTempFile() const
721{
722        wxASSERT( !version_information_.available_.hash_.empty() );
723        wxString ret = wxFileName::GetTempDir();
724        if (!ret.empty()) {
725                if (ret.Last() != wxFileName::GetPathSeparator()) {
726                        ret += wxFileName::GetPathSeparator();
727                }
728
729                ret += _T("fzupdate_") + version_information_.available_.hash_.Left(16) + _T(".tmp");
730        }
731
732        return ret;
733}
734
735wxString CUpdater::GetFilename( wxString const& url) const
736{
737        wxString ret;
738        int pos = url.Find('/', true);
739        if (pos != -1) {
740                ret = url.Mid(pos + 1);
741        }
742        size_t p = ret.find_first_of(_T("?#"));
743        if( p != std::string::npos ) {
744                ret = ret.substr(0, p);
745        }
746#ifdef __WXMSW__
747        ret.Replace(_T(":"), _T("_"));
748#endif
749
750        return ret;
751}
752
753void CUpdater::SetState( UpdaterState s )
754{
755        if( s != state_ ) {
756                state_ = s;
757
758                if (s != UpdaterState::checking && s != UpdaterState::newversion_downloading) {
759                        pending_commands_.clear();
760                }
761                build b = version_information_.available_;
762                for (auto const& handler : handlers_ ) {
763                        if( handler ) {
764                                handler->UpdaterStateChanged( s, b );
765                        }
766                }
767        }
768}
769
770wxString CUpdater::DownloadedFile() const
771{
772        wxString ret;
773        if( state_ == UpdaterState::newversion_ready ) {
774                ret = local_file_;
775        }
776        return ret;
777}
778
779void CUpdater::AddHandler( CUpdateHandler& handler )
780{
781        for( auto const& h : handlers_ ) {
782                if (h == &handler) {
783                        return;
784                }
785        }
786        for( auto& h : handlers_ ) {
787                if( !h ) {
788                        h = &handler;
789                        return;
790                }
791        }
792        handlers_.push_back(&handler);
793}
794
795void CUpdater::RemoveHandler( CUpdateHandler& handler )
796{
797        for (auto& h : handlers_) {
798                if (h == &handler) {
799                        // Set to 0 instead of removing from list to avoid issues with reentrancy.
800                        h = 0;
801                        return;
802                }
803        }
804}
805
806int64_t CUpdater::BytesDownloaded() const
807{
808        int64_t ret{-1};
809        if (state_ == UpdaterState::newversion_ready) {
810                if (!local_file_.empty()) {
811                        ret = fz::local_filesys::get_size(fz::to_native(local_file_));
812                }
813        }
814        else if( state_ == UpdaterState::newversion_downloading ) {
815                wxString const temp = GetTempFile();
816                if( !temp.empty() ) {
817                        ret = fz::local_filesys::get_size(fz::to_native(temp));
818                }
819        }
820        return ret;
821}
822
823bool CUpdater::UpdatableBuild() const
824{
825        return CBuildInfo::GetBuildType() == _T("nightly") || CBuildInfo::GetBuildType() == _T("official");
826}
827
828#endif
Note: See TracBrowser for help on using the repository browser.