OSDN Git Service

Updated list of known hosts (backport from LameXP).
[x264-launcher/x264-launcher.git] / src / thread_updater.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "thread_updater.h"
23
24 #include "global.h"
25
26 #include <QStringList>
27 #include <QFile>
28 #include <QFileInfo>
29 #include <QProcess>
30 #include <QUrl>
31 #include <QEventLoop>
32 #include <QTimer>
33
34 ///////////////////////////////////////////////////////////////////////////////
35 // CONSTANTS
36 ///////////////////////////////////////////////////////////////////////////////
37
38 static const char *header_id = "!Update";
39 static const char *section_id = "Simple x264 Launcher";
40
41 static const char *mirror_url_postfix[] = 
42 {
43         "update.ver",
44         "update_beta.ver",
45         NULL
46 };
47
48 static const char *update_mirrors_prim[] =
49 {
50         "http://muldersoft.com/",
51         "http://mulder.bplaced.net/",
52         "http://mulder.cwsurf.de/",
53         "http://mulder.6te.net/",
54         "http://mulder.webuda.com/",
55         "http://mulder.byethost13.com/",
56         "http://muldersoft.kilu.de/",
57         "http://mulder.pe.hu/",
58         "http://muldersoft.zxq.net/",
59         "http://lamexp.sourceforge.net/",
60         "http://lordmulder.github.io/LameXP/",
61         "http://lord_mulder.bitbucket.org/",
62         "http://www.tricksoft.de/",
63         NULL
64 };
65
66 static const char *update_mirrors_back[] =
67 {
68         "http://mplayer.savedonthe.net/",
69         NULL
70 };
71
72 static const char *known_hosts[] =              //Taken form: http://www.alexa.com/topsites !!!
73 {
74         "http://www.163.com/",
75         "http://www.7-zip.org/",
76         "http://www.ac3filter.net/",
77         "http://www.amazon.com/",
78         "http://antergos.com/",
79         "http://www.aol.com/",
80         "http://www.apache.org/",
81         "http://www.apple.com/",
82         "http://www.adobe.com/",
83         "http://www.avidemux.org/",
84         "http://www.babylon.com/",
85         "http://www.baidu.com/",
86         "http://bandcamp.com/",
87         "http://www.bbc.co.uk/",
88         "http://www.berlios.de/",
89         "http://www.bing.com/",
90         "http://www.bucketheadpikes.com/",
91         "http://www.ccc.de/",
92         "http://www.cnet.com/",
93         "http://cnzz.com/",
94         "http://www.codeplex.com/",
95         "http://www.ebay.com/",
96         "http://www.equation.com/",
97         "http://fc2.com/",
98         "http://fedoraproject.org/wiki/Fedora_Project_Wiki",
99         "http://blog.fefe.de/",
100         "http://www.ffmpeg.org/",
101         "http://blog.flickr.net/en",
102         "http://free-codecs.com/",
103         "http://blog.gitorious.org/",
104         "http://git-scm.com/",
105         "http://www.gmx.net/",
106         "http://www.gnome.org/",
107         "http://www.gnu.org/",
108         "http://go.com/",
109         "http://code.google.com/",
110         "http://www.heise.de/",
111         "http://www.huffingtonpost.co.uk/",
112         "http://www.iana.org/",
113         "http://www.imdb.com/",
114         "http://www.imgburn.com/",
115         "http://imgur.com/",
116         "http://en.jd.com/",
117         "http://kannmanumdieuhrzeitschonnbierchentrinken.de/",
118         "http://mirrors.kernel.org/",
119         "http://lame.sourceforge.net/",
120         "http://www.libav.org/",
121         "http://blog.linkedin.com/",
122         "http://www.linuxmint.com/",
123         "http://www.livedoor.com/",
124         "http://www.livejournal.com/",
125         "http://go.mail.ru/",
126         "http://www.mediafire.com/",
127         "http://ftp.mozilla.org/",
128         "http://mplayerhq.hu/",
129         "http://www.msn.com/en-us/",
130         "http://oss.netfarm.it/",
131         "http://www.nytimes.com/",
132         "http://www.opera.com/",
133         "http://pastie.org/",
134         "http://www.portablefreeware.com/",
135         "http://www.qt.io/",
136         "http://qt-project.org/",
137         "http://www.quakelive.com/",
138         "http://www.seamonkey-project.org/",
139         "http://www.sina.com.cn/",
140         "http://www.sohu.com/",
141         "http://www.soso.com/",
142         "http://sourceforge.net/",
143         "http://www.spiegel.de/",
144         "http://tdm-gcc.tdragon.net/",
145         "http://www.tdrsmusic.com/",
146         "http://www.ubuntu.com/",
147         "http://status.twitter.com/",
148         "http://www.uol.com.br/",
149         "http://www.videohelp.com/",
150         "http://www.videolan.org/",
151         "http://virtualdub.org/",
152         "http://blog.virustotal.com/",
153         "http://www.weibo.com/login.php",
154         "http://www.wikipedia.org/",
155         "http://www.winamp.com/",
156         "http://en.support.wordpress.com/",
157         "http://xiph.org/",
158         "http://us.mail.yahoo.com/",
159         "http://www.yandex.ru/",
160         "http://www.youtube.com/yt/about/",
161         "http://www.zedo.com/",
162         "http://ffmpeg.zeranoe.com/",
163         NULL
164 };
165
166 static const int MIN_CONNSCORE = 8;
167 static const int VERSION_INFO_EXPIRES_MONTHS = 6;
168 static char *USER_AGENT_STR = "Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20111106 IceCat/7.0.1";
169
170 //Helper function
171 static int getMaxProgress(void)
172 {
173         int counter = MIN_CONNSCORE + 2;
174         for(int i = 0; update_mirrors_prim[i]; i++) counter++;
175         for(int i = 0; update_mirrors_back[i]; i++) counter++;
176         return counter;
177 }
178
179 ////////////////////////////////////////////////////////////
180 // Update Info Class
181 ////////////////////////////////////////////////////////////
182
183 UpdateInfo::UpdateInfo(void)
184 {
185         resetInfo();
186 }
187         
188 void UpdateInfo::resetInfo(void)
189 {
190         m_buildNo = 0;
191         m_buildDate.setDate(1900, 1, 1);
192         m_downloadSite.clear();
193         m_downloadAddress.clear();
194         m_downloadFilename.clear();
195         m_downloadFilecode.clear();
196 }
197
198 ////////////////////////////////////////////////////////////
199 // Constructor & Destructor
200 ////////////////////////////////////////////////////////////
201
202 UpdateCheckThread::UpdateCheckThread(const QString &binWGet, const QString &binGnuPG, const QString &binKeys, const bool betaUpdates, const bool testMode)
203 :
204         m_updateInfo(new UpdateInfo()),
205         m_binaryWGet(binWGet),
206         m_binaryGnuPG(binGnuPG),
207         m_binaryKeys(binKeys),
208         m_betaUpdates(betaUpdates),
209         m_testMode(testMode),
210         m_maxProgress(getMaxProgress())
211 {
212         m_success = false;
213         m_status = UpdateStatus_NotStartedYet;
214         m_progress = 0;
215
216         if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryKeys.isEmpty())
217         {
218                 THROW("Tools not initialized correctly!");
219         }
220 }
221
222 UpdateCheckThread::~UpdateCheckThread(void)
223 {
224         delete m_updateInfo;
225 }
226
227 ////////////////////////////////////////////////////////////
228 // Protected functions
229 ////////////////////////////////////////////////////////////
230
231 void UpdateCheckThread::run(void)
232 {
233         qDebug("Update checker thread started!");
234
235         try
236         {
237                 m_testMode ? testKnownHosts() : checkForUpdates();
238         }
239         catch(const std::exception &error)
240         {
241                 fflush(stdout); fflush(stderr);
242                 fprintf(stderr, "\nGURU MEDITATION !!!\n\nException error:\n%s\n", error.what());
243                 x264_fatal_exit(L"Unhandeled C++ exception error, application will exit!");
244         }
245         catch(...)
246         {
247                 fflush(stdout); fflush(stderr);
248                 fprintf(stderr, "\nGURU MEDITATION !!!\n\nUnknown exception error!\n");
249                 x264_fatal_exit(L"Unhandeled C++ exception error, application will exit!");
250         }
251
252         qDebug("Update checker thread completed.");
253 }
254
255 void UpdateCheckThread::checkForUpdates(void)
256 {
257         // ----- Initialization ----- //
258
259         m_success = false;
260         m_updateInfo->resetInfo();
261         setProgress(0);
262
263         // ----- Test Internet Connection ----- //
264
265         int connectionScore = 0;
266         int maxConnectTries = (3 * MIN_CONNSCORE) / 2;
267         
268         log("Checking internet connection...");
269         setStatus(UpdateStatus_CheckingConnection);
270
271         const int networkStatus = x264_network_status();
272         if(networkStatus == x264_network_non)
273         {
274                 log("", "Operating system reports that the computer is currently offline !!!");
275                 setProgress(m_maxProgress);
276                 setStatus(UpdateStatus_ErrorNoConnection);
277                 return;
278         }
279         
280         setProgress(1);
281
282         // ----- Test Known Hosts Connectivity ----- //
283
284         QStringList hostList;
285         for(int i = 0; known_hosts[i]; i++)
286         {
287                 hostList << QString::fromLatin1(known_hosts[i]);
288         }
289
290         x264_seed_rand();
291
292         while(!(hostList.isEmpty() || (connectionScore >= MIN_CONNSCORE) || (maxConnectTries < 1)))
293         {
294                 switch(tryContactHost(hostList.takeAt(x264_rand() % hostList.count())))
295                 {
296                         case 01: connectionScore += 1; break;
297                         case 02: connectionScore += 2; break;
298                         default: maxConnectTries -= 1; break;
299                 }
300                 setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1));
301                 x264_sleep(64);
302         }
303
304         if(connectionScore < MIN_CONNSCORE)
305         {
306                 log("", "Connectivity test has failed: Internet connection appears to be broken!");
307                 setProgress(m_maxProgress);
308                 setStatus(UpdateStatus_ErrorConnectionTestFailed);
309                 return;
310         }
311
312         // ----- Build Mirror List ----- //
313
314         log("", "----", "", "Checking for updates online...");
315         setStatus(UpdateStatus_FetchingUpdates);
316
317         QStringList mirrorList;
318         for(int index = 0; update_mirrors_prim[index]; index++)
319         {
320                 mirrorList << QString::fromLatin1(update_mirrors_prim[index]);
321         }
322
323         x264_seed_rand();
324         if(const int len = mirrorList.count())
325         {
326                 const int rounds = len * 1097;
327                 for(int i = 0; i < rounds; i++)
328                 {
329                         mirrorList.swap(i % len, x264_rand() % len);
330                 }
331         }
332
333         for(int index = 0; update_mirrors_back[index]; index++)
334         {
335                 mirrorList << QString::fromLatin1(update_mirrors_back[index]);
336         }
337         
338         // ----- Fetch Update Info From Server ----- //
339
340         while(!mirrorList.isEmpty())
341         {
342                 QString currentMirror = mirrorList.takeFirst();
343                 setProgress(m_progress + 1);
344                 if(!m_success)
345                 {
346                         if(tryUpdateMirror(m_updateInfo, currentMirror))
347                         {
348                                 m_success = true;
349                         }
350                 }
351                 else
352                 {
353                         x264_sleep(64);
354                 }
355         }
356         
357         setProgress(m_maxProgress);
358
359         if(m_success)
360         {
361                 if(m_updateInfo->m_buildNo > x264_version_build())
362                 {
363                         setStatus(UpdateStatus_CompletedUpdateAvailable);
364                 }
365                 else if(m_updateInfo->m_buildNo == x264_version_build())
366                 {
367                         setStatus(UpdateStatus_CompletedNoUpdates);
368                 }
369                 else
370                 {
371                         setStatus(UpdateStatus_CompletedNewVersionOlder);
372                 }
373         }
374         else
375         {
376                 setStatus(UpdateStatus_ErrorFetchUpdateInfo);
377         }
378 }
379
380 void UpdateCheckThread::testKnownHosts(void)
381 {
382         QStringList hostList;
383         for(int i = 0; known_hosts[i]; i++)
384         {
385                 hostList << QString::fromLatin1(known_hosts[i]);
386         }
387
388         qDebug("\n[Known Hosts]");
389         log("Testing all known hosts...", "", "---");
390
391         int hostCount = hostList.count();
392         while(!hostList.isEmpty())
393         {
394                 QString currentHost = hostList.takeFirst();
395                 qDebug("Testing: %s", currentHost.toLatin1().constData());
396                 log("", "Testing:", currentHost, "");
397                 QString outFile = QString("%1/%2.htm").arg(x264_temp_directory(), x264_rand_str());
398                 bool httpOk = false;
399                 if(!getFile(currentHost, outFile, 0, &httpOk))
400                 {
401                         if(httpOk)
402                         {
403                                 qWarning("\nConnectivity test was SLOW on the following site:\n%s\n", currentHost.toLatin1().constData());
404                         }
405                         else
406                         {
407                                 qWarning("\nConnectivity test FAILED on the following site:\n%s\n", currentHost.toLatin1().constData());
408                         }
409                 }
410                 log("", "---");
411                 QFile::remove(outFile);
412         }
413 }
414
415 ////////////////////////////////////////////////////////////
416 // PRIVATE FUNCTIONS
417 ////////////////////////////////////////////////////////////
418
419 void UpdateCheckThread::setStatus(const int status)
420 {
421         if(m_status != status)
422         {
423                 m_status = status;
424                 emit statusChanged(status);
425         }
426 }
427
428 void UpdateCheckThread::setProgress(const int progress)
429 {
430         if(m_progress != progress)
431         {
432                 m_progress = progress;
433                 emit progressChanged(progress);
434         }
435 }
436
437 void UpdateCheckThread::log(const QString &str1, const QString &str2, const QString &str3, const QString &str4)
438 {
439         if(!str1.isNull()) emit messageLogged(str1);
440         if(!str2.isNull()) emit messageLogged(str2);
441         if(!str3.isNull()) emit messageLogged(str3);
442         if(!str4.isNull()) emit messageLogged(str4);
443 }
444
445 int UpdateCheckThread::tryContactHost(const QString &url)
446 {
447                 int result = -1; bool httpOkay = false;
448                 const QString outFile = QString("%1/%2.htm").arg(x264_temp_directory(), x264_rand_str());
449                 log("", "Testing host:", url);
450
451                 if(getFile(url, outFile, 0, &httpOkay))
452                 {
453                         log("Connection to host was established successfully.");
454                         result = 2;
455                 }
456                 else
457                 {
458                         if(httpOkay)
459                         {
460                                 log("Connection to host timed out after HTTP OK was received.");
461                                 result = 1;
462                         }
463                         else
464                         {
465                                 log("Connection failed: The host could not be reached!");
466                                 result = 0;
467                         }
468                 }
469
470                 QFile::remove(outFile);
471                 return result;
472 }
473
474 bool UpdateCheckThread::tryUpdateMirror(UpdateInfo *updateInfo, const QString &url)
475 {
476         bool success = false;
477         log("", "Trying mirror:", url);
478
479         const QString randPart = x264_rand_str();
480         const QString outFileVers = QString("%1/%2.ver").arg(x264_temp_directory(), randPart);
481         const QString outFileSign = QString("%1/%2.sig").arg(x264_temp_directory(), randPart);
482
483         if(getUpdateInfo(url, outFileVers, outFileSign))
484         {
485                 log("", "Download okay, checking signature:");
486                 if(checkSignature(outFileVers, outFileSign))
487                 {
488                         log("", "Signature okay, parsing info:");
489                         success = parseVersionInfo(outFileVers, updateInfo);
490                 }
491                 else
492                 {
493                         log("", "Bad signature, take care!");
494                 }
495         }
496         else
497         {
498                 log("", "Download has failed!");
499         }
500
501         QFile::remove(outFileVers);
502         QFile::remove(outFileSign);
503         
504         return success;
505 }
506
507 bool UpdateCheckThread::getUpdateInfo(const QString &url, const QString &outFileVers, const QString &outFileSign)
508 {
509         log("", "Downloading update info:");
510         if(!getFile(QString("%1%2"    ).arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVers))
511         {
512                 return false;
513         }
514
515         log("", "Downloading signature:");
516         if(!getFile(QString("%1%2.sig").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign))
517         {
518                 return false;
519         }
520
521         return true;
522 }
523
524 bool UpdateCheckThread::getFile(const QString &url, const QString &outFile, unsigned int maxRedir, bool *httpOk)
525 {
526         QFileInfo output(outFile);
527         output.setCaching(false);
528         if(httpOk) *httpOk = false;
529
530         if(output.exists())
531         {
532                 QFile::remove(output.canonicalFilePath());
533                 if(output.exists())
534                 {
535                         return false;
536                 }
537         }
538
539         QProcess process;
540         x264_init_process(process, output.absolutePath());
541
542         QStringList args;
543         args << "-T" << "15" << "--no-cache" << "--no-dns-cache" << QString().sprintf("--max-redirect=%u", maxRedir);
544         args << QString("--referer=%1://%2/").arg(QUrl(url).scheme(), QUrl(url).host()) << "-U" << USER_AGENT_STR;
545         args << "-O" << output.fileName() << url;
546
547         QEventLoop loop;
548         connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
549         connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()));
550         connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
551
552         QTimer timer;
553         timer.setSingleShot(true);
554         timer.setInterval(25000);
555         connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
556
557         const QRegExp httpResponseOK("200 OK$");
558         
559         process.start(m_binaryWGet, args);
560         
561         if(!process.waitForStarted())
562         {
563                 return false;
564         }
565
566         timer.start();
567
568         while(process.state() != QProcess::NotRunning)
569         {
570                 loop.exec();
571                 const bool bTimeOut = (!timer.isActive());
572                 while(process.canReadLine())
573                 {
574                         QString line = QString::fromLatin1(process.readLine()).simplified();
575                         if(line.contains(httpResponseOK))
576                         {
577                                 line.append(" [OK]");
578                                 if(httpOk) *httpOk = true;
579                         }
580                         log(line);
581                 }
582                 if(bTimeOut)
583                 {
584                         qWarning("WGet process timed out <-- killing!");
585                         process.kill();
586                         process.waitForFinished();
587                         log("!!! TIMEOUT !!!");
588                         return false;
589                 }
590         }
591         
592         timer.stop();
593         timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
594
595         log(QString().sprintf("Exited with code %d", process.exitCode()));
596         return (process.exitCode() == 0) && output.exists() && output.isFile();
597 }
598
599 bool UpdateCheckThread::checkSignature(const QString &file, const QString &signature)
600 {
601         if(QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0)
602         {
603                 qWarning("CheckSignature: File and signature should be in same folder!");
604                 return false;
605         }
606         if(QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0)
607         {
608                 qWarning("CheckSignature: File and keyring should be in same folder!");
609                 return false;
610         }
611
612         QProcess process;
613         x264_init_process(process, QFileInfo(file).absolutePath());
614
615         QEventLoop loop;
616         connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
617         connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()));
618         connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
619         
620         process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(m_binaryKeys).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName());
621
622         if(!process.waitForStarted())
623         {
624                 return false;
625         }
626
627         while(process.state() == QProcess::Running)
628         {
629                 loop.exec();
630                 while(process.canReadLine())
631                 {
632                         log(QString::fromLatin1(process.readLine()).simplified());
633                 }
634         }
635         
636         log(QString().sprintf("Exited with code %d", process.exitCode()));
637         return (process.exitCode() == 0);
638 }
639
640 bool UpdateCheckThread::parseVersionInfo(const QString &file, UpdateInfo *updateInfo)
641 {
642         QRegExp value("^(\\w+)=(.+)$");
643         QRegExp section("^\\[(.+)\\]$");
644
645         QDate updateInfoDate;
646         updateInfo->resetInfo();
647
648         QFile data(file);
649         if(!data.open(QIODevice::ReadOnly))
650         {
651                 qWarning("Cannot open update info file for reading!");
652                 return false;
653         }
654         
655         bool inHeader = false;
656         bool inSection = false;
657         
658         while(!data.atEnd())
659         {
660                 QString line = QString::fromLatin1(data.readLine()).trimmed();
661                 if(section.indexIn(line) >= 0)
662                 {
663                         log(QString("Sec: [%1]").arg(section.cap(1)));
664                         inSection = (section.cap(1).compare(section_id, Qt::CaseInsensitive) == 0);
665                         inHeader = (section.cap(1).compare(header_id, Qt::CaseInsensitive) == 0);
666                         continue;
667                 }
668                 if(inSection && (value.indexIn(line) >= 0))
669                 {
670                         log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
671                         if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0)
672                         {
673                                 bool ok = false;
674                                 unsigned int temp = value.cap(2).toUInt(&ok);
675                                 if(ok) updateInfo->m_buildNo = temp;
676                         }
677                         else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0)
678                         {
679                                 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
680                                 if(temp.isValid()) updateInfo->m_buildDate = temp;
681                         }
682                         else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0)
683                         {
684                                 updateInfo->m_downloadSite = value.cap(2).trimmed();
685                         }
686                         else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0)
687                         {
688                                 updateInfo->m_downloadAddress = value.cap(2).trimmed();
689                         }
690                         else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0)
691                         {
692                                 updateInfo->m_downloadFilename = value.cap(2).trimmed();
693                         }
694                         else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0)
695                         {
696                                 updateInfo->m_downloadFilecode = value.cap(2).trimmed();
697                         }
698                 }
699                 if(inHeader && (value.indexIn(line) >= 0))
700                 {
701                         log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
702                         if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0)
703                         {
704                                 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
705                                 if(temp.isValid()) updateInfoDate = temp;
706                         }
707                 }
708         }
709         
710         if(!updateInfoDate.isValid())
711         {
712                 updateInfo->resetInfo();
713                 log("WARNING: Version info timestamp is missing!");
714                 return false;
715         }
716         else if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < x264_current_date_safe())
717         {
718                 updateInfo->resetInfo();
719                 log(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate)));
720                 return false;
721         }
722         else if(x264_current_date_safe() < updateInfoDate)
723         {
724                 log("Version info is from the future, take care!");
725                 qWarning("Version info is from the future, take care!");
726         }
727
728         bool complete = true;
729
730         if(!(updateInfo->m_buildNo > 0)) complete = false;
731         if(!(updateInfo->m_buildDate.year() >= 2010)) complete = false;
732         if(updateInfo->m_downloadSite.isEmpty()) complete = false;
733         if(updateInfo->m_downloadAddress.isEmpty()) complete = false;
734         if(updateInfo->m_downloadFilename.isEmpty()) complete = false;
735         if(updateInfo->m_downloadFilecode.isEmpty()) complete = false;
736         
737         if(!complete)
738         {
739                 log("WARNING: Version info is incomplete!");
740         }
741
742         return complete;
743 }
744
745 ////////////////////////////////////////////////////////////
746 // SLOTS
747 ////////////////////////////////////////////////////////////
748
749 /*NONE*/
750
751 ////////////////////////////////////////////////////////////
752 // EVENTS
753 ////////////////////////////////////////////////////////////
754
755 /*NONE*/