2014-11-19 17:18:01 +00:00
/***************************************************************************
* Copyright ( C ) 2009 Matthias Fuchs < mat69 @ gmx . net > *
* Copyright ( C ) 2012 Aish Raj Dahal < dahalaishraj @ gmail . com > *
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the *
* Free Software Foundation , Inc . , *
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "metalinker.h"
# include <QtCore/QFile>
# include <QtCore/QTextStream>
2015-11-25 10:40:41 +00:00
# include <QtXml/qdom.h>
2014-11-19 17:18:01 +00:00
# include <kdeversion.h>
# include <KDebug>
# include <KLocale>
# include <KSystemTimeZone>
const QString KGetMetalink : : Metalink : : KGET_DESCRIPTION = QString ( QString ( " KGet/ " ) + " 2. " + QString : : number ( KDE_VERSION_MINOR ) + ' . ' + QString : : number ( KDE_VERSION_RELEASE ) ) ;
const uint KGetMetalink : : Metalink : : MAX_URL_PRIORITY = 999999 ;
const uint KGetMetalink : : Metalink_v3 : : MAX_PREFERENCE = 100 ; //as defined in Metalink specification 3.0 2nd edition
namespace KGetMetalink
{
QString addaptHashType ( const QString & type , bool loaded ) ;
}
/**
* Adapts type to the way the hash is internally stored
* @ param type the hash - type
* @ param load true if the hash has been loaded , false if it should be saved
* @ note metalink wants sha1 in the form " sha-1 " , though
* the metalinker uses it internally in the form " sha1 " , this function
* transforms it to the correct form , it is only needed internally
*/
QString KGetMetalink : : addaptHashType ( const QString & type , bool loaded )
{
QString t = type ;
if ( loaded )
{
t . replace ( " sha- " , " sha " ) ;
}
else
{
t . replace ( " sha " , " sha- " ) ;
}
return t ;
}
void KGetMetalink : : DateConstruct : : setData ( const QDateTime & dateT , const QTime & timeZoneOff , bool negOff )
{
dateTime = dateT ;
timeZoneOffset = timeZoneOff ;
negativeOffset = negOff ;
}
void KGetMetalink : : DateConstruct : : setData ( const QString & dateConstruct )
{
if ( dateConstruct . isEmpty ( ) )
{
return ;
}
const QString exp = " yyyy-MM-ddThh:mm:ss " ;
const int length = exp . length ( ) ;
dateTime = QDateTime : : fromString ( dateConstruct . left ( length ) , exp ) ;
if ( dateTime . isValid ( ) )
{
int index = dateConstruct . indexOf ( ' + ' , length - 1 ) ;
if ( index > - 1 )
{
timeZoneOffset = QTime : : fromString ( dateConstruct . mid ( index + 1 ) , " hh:mm " ) ;
}
else
{
index = dateConstruct . indexOf ( ' - ' , length - 1 ) ;
if ( index > - 1 )
{
negativeOffset = true ;
timeZoneOffset = QTime : : fromString ( dateConstruct . mid ( index + 1 ) , " hh:mm " ) ;
}
}
}
}
bool KGetMetalink : : DateConstruct : : isNull ( ) const
{
return dateTime . isNull ( ) ;
}
bool KGetMetalink : : DateConstruct : : isValid ( ) const
{
return dateTime . isValid ( ) ;
}
QString KGetMetalink : : DateConstruct : : toString ( ) const
{
QString string ;
if ( dateTime . isValid ( ) )
{
string + = dateTime . toString ( Qt : : ISODate ) ;
}
if ( timeZoneOffset . isValid ( ) )
{
string + = ( negativeOffset ? ' - ' : ' + ' ) ;
string + = timeZoneOffset . toString ( " hh:mm " ) ;
}
else if ( ! string . isEmpty ( ) )
{
string + = ' Z ' ;
}
return string ;
}
void KGetMetalink : : DateConstruct : : clear ( )
{
dateTime = QDateTime ( ) ;
timeZoneOffset = QTime ( ) ;
}
void KGetMetalink : : UrlText : : clear ( )
{
name . clear ( ) ;
url . clear ( ) ;
}
void KGetMetalink : : CommonData : : load ( const QDomElement & e )
{
identity = e . firstChildElement ( " identity " ) . text ( ) ;
version = e . firstChildElement ( " version " ) . text ( ) ;
description = e . firstChildElement ( " description " ) . text ( ) ;
logo = KUrl ( e . firstChildElement ( " logo " ) . text ( ) ) ;
copyright = e . firstChildElement ( " copyright " ) . text ( ) ;
const QDomElement publisherElem = e . firstChildElement ( " publisher " ) ;
publisher . name = publisherElem . attribute ( " name " ) ;
publisher . url = KUrl ( publisherElem . attribute ( " url " ) ) ;
for ( QDomElement elemRes = e . firstChildElement ( " language " ) ; ! elemRes . isNull ( ) ; elemRes = elemRes . nextSiblingElement ( " language " ) ) {
languages < < elemRes . text ( ) ;
}
for ( QDomElement elemRes = e . firstChildElement ( " os " ) ; ! elemRes . isNull ( ) ; elemRes = elemRes . nextSiblingElement ( " os " ) ) {
oses < < elemRes . text ( ) ;
}
}
void KGetMetalink : : CommonData : : save ( QDomElement & e ) const
{
QDomDocument doc = e . ownerDocument ( ) ;
if ( ! copyright . isEmpty ( ) )
{
QDomElement elem = doc . createElement ( " copyright " ) ;
QDomText text = doc . createTextNode ( copyright ) ;
elem . appendChild ( text ) ;
e . appendChild ( elem ) ;
}
if ( ! description . isEmpty ( ) )
{
QDomElement elem = doc . createElement ( " description " ) ;
QDomText text = doc . createTextNode ( description ) ;
elem . appendChild ( text ) ;
e . appendChild ( elem ) ;
}
if ( ! identity . isEmpty ( ) )
{
QDomElement elem = doc . createElement ( " identity " ) ;
QDomText text = doc . createTextNode ( identity ) ;
elem . appendChild ( text ) ;
e . appendChild ( elem ) ;
}
if ( ! logo . isEmpty ( ) )
{
QDomElement elem = doc . createElement ( " logo " ) ;
QDomText text = doc . createTextNode ( logo . url ( ) ) ;
elem . appendChild ( text ) ;
e . appendChild ( elem ) ;
}
if ( ! publisher . isEmpty ( ) )
{
QDomElement elem = doc . createElement ( " publisher " ) ;
elem . setAttribute ( " url " , publisher . url . url ( ) ) ;
elem . setAttribute ( " name " , publisher . name ) ;
e . appendChild ( elem ) ;
}
if ( ! version . isEmpty ( ) )
{
QDomElement elem = doc . createElement ( " version " ) ;
QDomText text = doc . createTextNode ( version ) ;
elem . appendChild ( text ) ;
e . appendChild ( elem ) ;
}
foreach ( const QString & language , languages ) {
QDomElement elem = doc . createElement ( " language " ) ;
QDomText text = doc . createTextNode ( language ) ;
elem . appendChild ( text ) ;
e . appendChild ( elem ) ;
}
foreach ( const QString & os , oses ) {
QDomElement elem = doc . createElement ( " os " ) ;
QDomText text = doc . createTextNode ( os ) ;
elem . appendChild ( text ) ;
e . appendChild ( elem ) ;
}
}
void KGetMetalink : : CommonData : : clear ( )
{
identity . clear ( ) ;
version . clear ( ) ;
description . clear ( ) ;
oses . clear ( ) ;
logo . clear ( ) ;
languages . clear ( ) ;
publisher . clear ( ) ;
copyright . clear ( ) ;
}
bool KGetMetalink : : Metaurl : : operator < ( const KGetMetalink : : Metaurl & other ) const
{
return ( this - > priority > other . priority ) | | ( this - > priority = = 0 ) ;
}
void KGetMetalink : : Metaurl : : load ( const QDomElement & e )
{
type = e . attribute ( " mediatype " ) . toLower ( ) ;
priority = e . attribute ( " priority " ) . toUInt ( ) ;
if ( priority > Metalink : : MAX_URL_PRIORITY ) {
priority = Metalink : : MAX_URL_PRIORITY ;
}
name = e . attribute ( " name " ) ;
url = KUrl ( e . text ( ) ) ;
}
void KGetMetalink : : Metaurl : : save ( QDomElement & e ) const
{
QDomDocument doc = e . ownerDocument ( ) ;
QDomElement metaurl = doc . createElement ( " metaurl " ) ;
if ( priority )
{
metaurl . setAttribute ( " priority " , priority ) ;
}
if ( ! name . isEmpty ( ) )
{
metaurl . setAttribute ( " name " , name ) ;
}
metaurl . setAttribute ( " mediatype " , type ) ;
QDomText text = doc . createTextNode ( url . url ( ) ) ;
metaurl . appendChild ( text ) ;
e . appendChild ( metaurl ) ;
}
bool KGetMetalink : : Metaurl : : isValid ( )
{
return url . isValid ( ) & & url . hasHost ( ) & & ! url . protocol ( ) . isEmpty ( ) & & ! type . isEmpty ( ) ;
}
void KGetMetalink : : Metaurl : : clear ( )
{
type . clear ( ) ;
priority = 0 ;
name . clear ( ) ;
url . clear ( ) ;
}
bool KGetMetalink : : Url : : operator < ( const KGetMetalink : : Url & other ) const
{
bool smaller = ( this - > priority > other . priority ) | | ( ( this - > priority = = 0 ) & & ( other . priority ! = 0 ) ) ;
if ( ! smaller & & ( this - > priority = = other . priority ) ) {
QString countryCode = KGlobal : : locale ( ) - > country ( ) ;
if ( ! countryCode . isEmpty ( ) ) {
smaller = ( other . location . toLower ( ) = = countryCode . toLower ( ) ) ;
}
}
return smaller ;
}
void KGetMetalink : : Url : : load ( const QDomElement & e )
{
location = e . attribute ( " location " ) . toLower ( ) ;
priority = e . attribute ( " priority " ) . toUInt ( ) ;
if ( priority > Metalink : : MAX_URL_PRIORITY ) {
priority = Metalink : : MAX_URL_PRIORITY ;
}
url = KUrl ( e . text ( ) ) ;
}
void KGetMetalink : : Url : : save ( QDomElement & e ) const
{
QDomDocument doc = e . ownerDocument ( ) ;
QDomElement elem = doc . createElement ( " url " ) ;
if ( priority )
{
elem . setAttribute ( " priority " , priority ) ;
}
if ( ! location . isEmpty ( ) )
{
elem . setAttribute ( " location " , location ) ;
}
QDomText text = doc . createTextNode ( url . url ( ) ) ;
elem . appendChild ( text ) ;
e . appendChild ( elem ) ;
}
bool KGetMetalink : : Url : : isValid ( )
{
return url . isValid ( ) & & url . hasHost ( ) & & ! url . protocol ( ) . isEmpty ( ) ;
}
void KGetMetalink : : Url : : clear ( )
{
priority = 0 ;
location . clear ( ) ;
url . clear ( ) ;
}
void KGetMetalink : : Resources : : load ( const QDomElement & e )
{
for ( QDomElement elem = e . firstChildElement ( " url " ) ; ! elem . isNull ( ) ; elem = elem . nextSiblingElement ( " url " ) )
{
Url url ;
url . load ( elem ) ;
if ( url . isValid ( ) )
{
urls . append ( url ) ;
}
}
for ( QDomElement elem = e . firstChildElement ( " metaurl " ) ; ! elem . isNull ( ) ; elem = elem . nextSiblingElement ( " metaurl " ) )
{
Metaurl metaurl ;
metaurl . load ( elem ) ;
if ( metaurl . isValid ( ) )
{
metaurls . append ( metaurl ) ;
}
}
}
void KGetMetalink : : Resources : : save ( QDomElement & e ) const
{
foreach ( const Metaurl & metaurl , metaurls )
{
metaurl . save ( e ) ;
}
foreach ( const Url & url , urls )
{
url . save ( e ) ;
}
}
void KGetMetalink : : Resources : : clear ( )
{
urls . clear ( ) ;
metaurls . clear ( ) ;
}
void KGetMetalink : : Pieces : : load ( const QDomElement & e )
{
type = addaptHashType ( e . attribute ( " type " ) , true ) ;
length = e . attribute ( " length " ) . toULongLong ( ) ;
QDomNodeList hashesList = e . elementsByTagName ( " hash " ) ;
for ( int i = 0 ; i < hashesList . count ( ) ; + + i )
{
QDomElement element = hashesList . at ( i ) . toElement ( ) ;
hashes . append ( element . text ( ) ) ;
}
}
void KGetMetalink : : Pieces : : save ( QDomElement & e ) const
{
QDomDocument doc = e . ownerDocument ( ) ;
QDomElement pieces = doc . createElement ( " pieces " ) ;
pieces . setAttribute ( " type " , addaptHashType ( type , false ) ) ;
pieces . setAttribute ( " length " , length ) ;
for ( int i = 0 ; i < hashes . size ( ) ; + + i )
{
QDomElement hash = doc . createElement ( " hash " ) ;
QDomText text = doc . createTextNode ( hashes . at ( i ) ) ;
hash . appendChild ( text ) ;
pieces . appendChild ( hash ) ;
}
e . appendChild ( pieces ) ;
}
void KGetMetalink : : Pieces : : clear ( )
{
type . clear ( ) ;
length = 0 ;
hashes . clear ( ) ;
}
void KGetMetalink : : Verification : : load ( const QDomElement & e )
{
for ( QDomElement elem = e . firstChildElement ( " hash " ) ; ! elem . isNull ( ) ; elem = elem . nextSiblingElement ( " hash " ) ) {
QString type = elem . attribute ( " type " ) ;
const QString hash = elem . text ( ) ;
if ( ! type . isEmpty ( ) & & ! hash . isEmpty ( ) ) {
type = addaptHashType ( type , true ) ;
hashes [ type ] = hash ;
}
}
for ( QDomElement elem = e . firstChildElement ( " pieces " ) ; ! elem . isNull ( ) ; elem = elem . nextSiblingElement ( " pieces " ) ) {
Pieces piecesItem ;
piecesItem . load ( elem ) ;
pieces . append ( piecesItem ) ;
}
for ( QDomElement elem = e . firstChildElement ( " signature " ) ; ! elem . isNull ( ) ; elem = elem . nextSiblingElement ( " signature " ) ) {
QString type = elem . attribute ( " mediatype " ) ;
if ( type = = " application/pgp-signature " ) { //FIXME with 4.5 make it handle signatures by default with mime-type
type = " pgp " ;
}
const QString siganture = elem . text ( ) ;
if ( ! type . isEmpty ( ) & & ! siganture . isEmpty ( ) ) {
signatures [ type ] = siganture ;
}
}
}
void KGetMetalink : : Verification : : save ( QDomElement & e ) const
{
QDomDocument doc = e . ownerDocument ( ) ;
QHash < QString , QString > : : const_iterator it ;
QHash < QString , QString > : : const_iterator itEnd = hashes . constEnd ( ) ;
for ( it = hashes . constBegin ( ) ; it ! = itEnd ; + + it ) {
QDomElement hash = doc . createElement ( " hash " ) ;
hash . setAttribute ( " type " , addaptHashType ( it . key ( ) , false ) ) ;
QDomText text = doc . createTextNode ( it . value ( ) ) ;
hash . appendChild ( text ) ;
e . appendChild ( hash ) ;
}
foreach ( const Pieces & item , pieces ) {
item . save ( e ) ;
}
itEnd = signatures . constEnd ( ) ;
for ( it = signatures . constBegin ( ) ; it ! = itEnd ; + + it ) {
QString type = it . key ( ) ;
if ( type = = " pgp " ) { //FIXME with 4.5 make it handle signatures by default with mime-type
type = " application/pgp-signature " ;
}
QDomElement hash = doc . createElement ( " signature " ) ;
hash . setAttribute ( " mediatype " , type ) ;
QDomText text = doc . createTextNode ( it . value ( ) ) ;
hash . appendChild ( text ) ;
e . appendChild ( hash ) ;
}
}
void KGetMetalink : : Verification : : clear ( )
{
hashes . clear ( ) ;
pieces . clear ( ) ;
}
bool KGetMetalink : : File : : isValid ( ) const
{
return isValidNameAttribute ( ) & & resources . isValid ( ) ;
}
void KGetMetalink : : File : : load ( const QDomElement & e )
{
data . load ( e ) ;
name = QUrl : : fromPercentEncoding ( e . attribute ( " name " ) . toAscii ( ) ) ;
size = e . firstChildElement ( " size " ) . text ( ) . toULongLong ( ) ;
verification . load ( e ) ;
resources . load ( e ) ;
}
void KGetMetalink : : File : : save ( QDomElement & e ) const
{
if ( isValid ( ) )
{
QDomDocument doc = e . ownerDocument ( ) ;
QDomElement file = doc . createElement ( " file " ) ;
file . setAttribute ( " name " , name ) ;
if ( size )
{
QDomElement elem = doc . createElement ( " size " ) ;
QDomText text = doc . createTextNode ( QString : : number ( size ) ) ;
elem . appendChild ( text ) ;
file . appendChild ( elem ) ;
}
data . save ( file ) ;
resources . save ( file ) ;
verification . save ( file ) ;
e . appendChild ( file ) ;
}
}
void KGetMetalink : : File : : clear ( )
{
name . clear ( ) ;
verification . clear ( ) ;
size = 0 ;
data . clear ( ) ;
resources . clear ( ) ;
}
bool KGetMetalink : : File : : isValidNameAttribute ( ) const
{
if ( name . isEmpty ( ) ) {
kError ( 5001 ) < < " Name attribute of Metalink::File is empty. " ;
return false ;
}
if ( name . endsWith ( ' / ' ) ) {
kError ( 5001 ) < < " Name attribute of Metalink::File does not contain a file name: " < < name ;
return false ;
}
const QStringList components = name . split ( ' / ' ) ;
if ( name . startsWith ( ' / ' ) | | components . contains ( " .. " ) | | components . contains ( " . " ) ) {
kError ( 5001 ) < < " Name attribute of Metalink::File contains directory traversal directives: " < < name ;
return false ;
}
return true ;
}
bool KGetMetalink : : Files : : isValid ( ) const
{
if ( files . isEmpty ( ) ) {
return false ;
}
QStringList fileNames ;
foreach ( const File & file , files ) {
fileNames < < file . name ;
if ( ! file . isValid ( ) ) {
return false ;
}
}
//The value of name must be unique for each file
while ( ! fileNames . isEmpty ( ) ) {
const QString fileName = fileNames . takeFirst ( ) ;
if ( fileNames . contains ( fileName ) ) {
kError ( 5001 ) < < " Metalink::File name " < < fileName < < " exists multiple times. " ;
return false ;
}
}
return true ;
}
void KGetMetalink : : Files : : load ( const QDomElement & e )
{
for ( QDomElement elem = e . firstChildElement ( " file " ) ; ! elem . isNull ( ) ; elem = elem . nextSiblingElement ( " file " ) )
{
File file ;
file . load ( elem ) ;
files . append ( file ) ;
}
}
void KGetMetalink : : Files : : save ( QDomElement & e ) const
{
if ( e . isNull ( ) )
{
return ;
}
foreach ( const File & file , files )
{
file . save ( e ) ;
}
}
void KGetMetalink : : Files : : clear ( )
{
files . clear ( ) ;
}
bool KGetMetalink : : Metalink : : isValid ( ) const
{
return files . isValid ( ) ;
}
// #ifdef HAVE_NEPOMUK
// QHash<QUrl, Nepomuk::Variant> KGetMetalink::Files::properties() const
// {
// return data.properties();
// }
// #endif //HAVE_NEPOMUK
void KGetMetalink : : Metalink : : load ( const QDomElement & e )
{
QDomDocument doc = e . ownerDocument ( ) ;
const QDomElement metalink = doc . firstChildElement ( " metalink " ) ;
xmlns = metalink . attribute ( " xmlns " ) ;
generator = metalink . firstChildElement ( " generator " ) . text ( ) ;
updated . setData ( metalink . firstChildElement ( " updated " ) . text ( ) ) ;
published . setData ( metalink . firstChildElement ( " published " ) . text ( ) ) ;
updated . setData ( metalink . firstChildElement ( " updated " ) . text ( ) ) ;
const QDomElement originElem = metalink . firstChildElement ( " origin " ) ;
origin = KUrl ( metalink . firstChildElement ( " origin " ) . text ( ) ) ;
if ( originElem . hasAttribute ( " dynamic " ) ) {
bool worked = false ;
dynamic = originElem . attribute ( " dynamic " ) . toInt ( & worked ) ;
if ( ! worked ) {
dynamic = ( originElem . attribute ( " dynamic " ) = = " true " ) ;
}
}
files . load ( e ) ;
}
QDomDocument KGetMetalink : : Metalink : : save ( ) const
{
QDomDocument doc ;
QDomProcessingInstruction header = doc . createProcessingInstruction ( " xml " , " version= \" 1.0 \" encoding= \" UTF-8 \" " ) ;
doc . appendChild ( header ) ;
QDomElement metalink = doc . createElement ( " metalink " ) ;
metalink . setAttribute ( " xmlns " , " urn:ietf:params:xml:ns:metalink " ) ; //the xmlns value is ignored, instead the data format described in the specification is always used
QDomElement elem = doc . createElement ( " generator " ) ;
QDomText text = doc . createTextNode ( Metalink : : KGET_DESCRIPTION ) ; //the set generator is ignored, instead when saving KGET is always used
elem . appendChild ( text ) ;
metalink . appendChild ( elem ) ;
if ( ! origin . isEmpty ( ) ) {
QDomElement elem = doc . createElement ( " origin " ) ;
QDomText text = doc . createTextNode ( origin . url ( ) ) ;
elem . appendChild ( text ) ;
if ( dynamic ) {
elem . setAttribute ( " dynamic " , " true " ) ;
}
metalink . appendChild ( elem ) ;
}
if ( published . isValid ( ) ) {
QDomElement elem = doc . createElement ( " published " ) ;
QDomText text = doc . createTextNode ( published . toString ( ) ) ;
elem . appendChild ( text ) ;
metalink . appendChild ( elem ) ;
}
if ( updated . isValid ( ) ) {
QDomElement elem = doc . createElement ( " updated " ) ;
QDomText text = doc . createTextNode ( updated . toString ( ) ) ;
elem . appendChild ( text ) ;
metalink . appendChild ( elem ) ;
}
files . save ( metalink ) ;
doc . appendChild ( metalink ) ;
return doc ;
}
void KGetMetalink : : Metalink : : clear ( )
{
dynamic = false ;
xmlns . clear ( ) ;
published . clear ( ) ;
origin . clear ( ) ;
generator . clear ( ) ;
updated . clear ( ) ;
files . clear ( ) ;
}
KGetMetalink : : Metalink_v3 : : Metalink_v3 ( )
{
}
KGetMetalink : : Metalink KGetMetalink : : Metalink_v3 : : metalink ( )
{
return m_metalink ;
}
void KGetMetalink : : Metalink_v3 : : setMetalink ( const KGetMetalink : : Metalink & metalink )
{
m_metalink = metalink ;
}
void KGetMetalink : : Metalink_v3 : : load ( const QDomElement & e )
{
QDomDocument doc = e . ownerDocument ( ) ;
const QDomElement metalinkDom = doc . firstChildElement ( " metalink " ) ;
m_metalink . dynamic = ( metalinkDom . attribute ( " type " ) = = " dynamic " ) ;
m_metalink . origin = KUrl ( metalinkDom . attribute ( " origin " ) ) ;
m_metalink . generator = metalinkDom . attribute ( " generator " ) ;
m_metalink . published = parseDateConstruct ( metalinkDom . attribute ( " pubdate " ) ) ;
m_metalink . updated = parseDateConstruct ( metalinkDom . attribute ( " refreshdate " ) ) ;
parseFiles ( metalinkDom ) ;
}
void KGetMetalink : : Metalink_v3 : : parseFiles ( const QDomElement & e )
{
//here we assume that the CommonData set in metalink is for every file in the metalink
CommonData data ;
data = parseCommonData ( e ) ;
const QDomElement filesElem = e . firstChildElement ( " files " ) ;
CommonData filesData = parseCommonData ( filesElem ) ;
inheritCommonData ( data , & filesData ) ;
for ( QDomElement elem = filesElem . firstChildElement ( " file " ) ; ! elem . isNull ( ) ; elem = elem . nextSiblingElement ( " file " ) ) {
File file ;
file . name = QUrl : : fromPercentEncoding ( elem . attribute ( " name " ) . toAscii ( ) ) ;
file . size = elem . firstChildElement ( " size " ) . text ( ) . toULongLong ( ) ;
file . data = parseCommonData ( elem ) ;
inheritCommonData ( filesData , & file . data ) ;
file . resources = parseResources ( elem ) ;
//load the verification information
QDomElement veriE = elem . firstChildElement ( " verification " ) ;
for ( QDomElement elemVer = veriE . firstChildElement ( " hash " ) ; ! elemVer . isNull ( ) ; elemVer = elemVer . nextSiblingElement ( " hash " ) ) {
QString type = elemVer . attribute ( " type " ) ;
QString hash = elemVer . text ( ) ;
if ( ! type . isEmpty ( ) & & ! hash . isEmpty ( ) ) {
type = addaptHashType ( type , true ) ;
file . verification . hashes [ type ] = hash ;
}
}
for ( QDomElement elemVer = veriE . firstChildElement ( " pieces " ) ; ! elemVer . isNull ( ) ; elemVer = elemVer . nextSiblingElement ( " pieces " ) ) {
Pieces piecesItem ;
piecesItem . load ( elemVer ) ;
file . verification . pieces . append ( piecesItem ) ;
}
for ( QDomElement elemVer = veriE . firstChildElement ( " signature " ) ; ! elemVer . isNull ( ) ; elemVer = elemVer . nextSiblingElement ( " signature " ) ) {
const QString type = elemVer . attribute ( " type " ) ;
const QString signature = elemVer . text ( ) ;
if ( ! type . isEmpty ( ) & & ! signature . isEmpty ( ) ) {
file . verification . signatures [ type ] = signature ;
}
}
m_metalink . files . files . append ( file ) ;
}
}
KGetMetalink : : CommonData KGetMetalink : : Metalink_v3 : : parseCommonData ( const QDomElement & e )
{
CommonData data ;
data . load ( e ) ;
const QDomElement publisherElem = e . firstChildElement ( " publisher " ) ;
data . publisher . name = publisherElem . firstChildElement ( " name " ) . text ( ) ;
data . publisher . url = KUrl ( publisherElem . firstChildElement ( " url " ) . text ( ) ) ;
return data ;
}
void KGetMetalink : : Metalink_v3 : : inheritCommonData ( const KGetMetalink : : CommonData & ancestor , KGetMetalink : : CommonData * inheritor )
{
if ( ! inheritor ) {
return ;
}
//ensure that inheritance works
if ( inheritor - > identity . isEmpty ( ) ) {
inheritor - > identity = ancestor . identity ;
}
if ( inheritor - > version . isEmpty ( ) ) {
inheritor - > version = ancestor . version ;
}
if ( inheritor - > description . isEmpty ( ) ) {
inheritor - > description = ancestor . description ;
}
if ( inheritor - > oses . isEmpty ( ) ) {
inheritor - > oses = ancestor . oses ;
}
if ( inheritor - > logo . isEmpty ( ) ) {
inheritor - > logo = ancestor . logo ;
}
if ( inheritor - > languages . isEmpty ( ) ) {
inheritor - > languages = ancestor . languages ;
}
if ( inheritor - > copyright . isEmpty ( ) ) {
inheritor - > copyright = ancestor . copyright ;
}
if ( inheritor - > publisher . isEmpty ( ) ) {
inheritor - > publisher = ancestor . publisher ;
}
}
KGetMetalink : : Resources KGetMetalink : : Metalink_v3 : : parseResources ( const QDomElement & e )
{
Resources resources ;
QDomElement res = e . firstChildElement ( " resources " ) ;
for ( QDomElement elemRes = res . firstChildElement ( " url " ) ; ! elemRes . isNull ( ) ; elemRes = elemRes . nextSiblingElement ( " url " ) ) {
const QString location = elemRes . attribute ( " location " ) . toLower ( ) ;
uint preference = elemRes . attribute ( " preference " ) . toUInt ( ) ;
//the maximum preference we use is MAX_PREFERENCE
if ( preference > MAX_PREFERENCE ) {
preference = MAX_PREFERENCE ;
}
const int priority = MAX_PREFERENCE - preference + 1 ; //convert old preference to new priority
const KUrl link = KUrl ( elemRes . text ( ) ) ;
QString type ;
if ( link . fileName ( ) . endsWith ( QLatin1String ( " .torrent " ) ) ) {
type = " torrent " ;
}
if ( type . isEmpty ( ) ) {
Url url ;
if ( preference ) {
url . priority = priority ;
}
url . location = location ;
url . url = link ;
if ( url . isValid ( ) ) {
resources . urls . append ( url ) ;
}
} else {
//it might be a metaurl
Metaurl metaurl ;
if ( preference ) {
metaurl . priority = priority ;
}
metaurl . url = link ;
metaurl . type = type ;
if ( metaurl . isValid ( ) ) {
resources . metaurls . append ( metaurl ) ;
}
}
}
return resources ;
}
KGetMetalink : : DateConstruct KGetMetalink : : Metalink_v3 : : parseDateConstruct ( const QString & data )
{
DateConstruct dateConstruct ;
if ( data . isEmpty ( ) ) {
return dateConstruct ;
}
kDebug ( 5001 ) < < " Parsing " < < data ;
QString temp = data ;
QDateTime dateTime ;
QTime timeZoneOffset ;
//Date according to RFC 822, the year with four characters preferred
//e.g.: "Mon, 15 May 2006 00:00:01 GMT", "Fri, 01 Apr 2009 00:00:01 +1030"
//find the date
const QString weekdayExp = " ddd, " ;
const bool weekdayIncluded = ( temp . indexOf ( ' , ' ) = = 3 ) ;
int startPosition = ( weekdayIncluded ? weekdayExp . length ( ) : 0 ) ;
const QString dayMonthExp = " dd MMM " ;
const QString yearExp = " yy " ;
QString exp = dayMonthExp + yearExp + yearExp ;
int length = exp . length ( ) ;
QLocale locale = QLocale : : c ( ) ;
QDate date = locale . toDate ( temp . mid ( startPosition , length ) , exp ) ;
if ( ! date . isValid ( ) ) {
exp = dayMonthExp + yearExp ;
length = exp . length ( ) ;
date = locale . toDate ( temp . mid ( startPosition , length ) , exp ) ;
if ( ! date . isValid ( ) ) {
return dateConstruct ;
}
}
//find the time
dateTime . setDate ( date ) ;
temp = temp . mid ( startPosition ) ;
temp = temp . mid ( length + 1 ) ; //also remove the space
const QString hourExp = " hh " ;
const QString minuteExp = " mm " ;
const QString secondExp = " ss " ;
exp = hourExp + ' : ' + minuteExp + ' : ' + secondExp ;
length = exp . length ( ) ;
QTime time = QTime : : fromString ( temp . left ( length ) , exp ) ;
if ( ! time . isValid ( ) ) {
exp = hourExp + ' : ' + minuteExp ;
length = exp . length ( ) ;
time = QTime : : fromString ( temp . left ( length ) , exp ) ;
if ( ! time . isValid ( ) ) {
return dateConstruct ;
}
}
dateTime . setTime ( time ) ;
//find the offset
temp = temp . mid ( length + 1 ) ; //also remove the space
bool negativeOffset = false ;
if ( temp . length ( ) = = 3 ) { //e.g. GMT
KTimeZone timeZone = KSystemTimeZones : : readZone ( temp ) ;
if ( timeZone . isValid ( ) ) {
int offset = timeZone . currentOffset ( ) ;
negativeOffset = ( offset < 0 ) ;
timeZoneOffset = QTime ( 0 , 0 , 0 ) ;
timeZoneOffset = timeZoneOffset . addSecs ( qAbs ( offset ) ) ;
}
} else if ( temp . length ( ) = = 5 ) { //e.g. +1030
negativeOffset = ( temp [ 0 ] = = ' - ' ) ;
timeZoneOffset = QTime : : fromString ( temp . mid ( 1 , 4 ) , " hhmm " ) ;
}
dateConstruct . setData ( dateTime , timeZoneOffset , negativeOffset ) ;
return dateConstruct ;
}
QDomDocument KGetMetalink : : Metalink_v3 : : save ( ) const
{
QDomDocument doc ;
QDomProcessingInstruction header = doc . createProcessingInstruction ( " xml " , " version= \" 1.0 \" encoding= \" UTF-8 \" " ) ;
doc . appendChild ( header ) ;
QDomElement metalink = doc . createElement ( " metalink " ) ;
metalink . setAttribute ( " xmlns " , " http://www.metalinker.org/ " ) ;
metalink . setAttribute ( " version " , " 3.0 " ) ;
metalink . setAttribute ( " type " , ( m_metalink . dynamic ? " dynamic " : " static " ) ) ;
metalink . setAttribute ( " generator " , Metalink : : KGET_DESCRIPTION ) ; //the set generator is ignored, instead when saving KGET is always used
if ( m_metalink . published . isValid ( ) ) {
metalink . setAttribute ( " pubdate " , dateConstructToString ( m_metalink . published ) ) ;
}
if ( m_metalink . updated . isValid ( ) ) {
metalink . setAttribute ( " refreshdate " , dateConstructToString ( m_metalink . updated ) ) ;
}
if ( ! m_metalink . origin . isEmpty ( ) ) {
metalink . setAttribute ( " origin " , m_metalink . origin . url ( ) ) ;
}
saveFiles ( metalink ) ;
doc . appendChild ( metalink ) ;
return doc ;
}
void KGetMetalink : : Metalink_v3 : : saveFiles ( QDomElement & e ) const
{
QDomDocument doc = e . ownerDocument ( ) ;
QDomElement filesElem = doc . createElement ( " files " ) ;
foreach ( const File & file , m_metalink . files . files ) {
QDomElement elem = doc . createElement ( " file " ) ;
elem . setAttribute ( " name " , file . name ) ;
QDomElement size = doc . createElement ( " size " ) ;
QDomText text = doc . createTextNode ( QString : : number ( file . size ) ) ;
size . appendChild ( text ) ;
elem . appendChild ( size ) ;
saveCommonData ( file . data , elem ) ;
saveResources ( file . resources , elem ) ;
saveVerification ( file . verification , elem ) ;
filesElem . appendChild ( elem ) ;
}
e . appendChild ( filesElem ) ;
}
void KGetMetalink : : Metalink_v3 : : saveResources ( const Resources & resources , QDomElement & e ) const
{
QDomDocument doc = e . ownerDocument ( ) ;
QDomElement res = doc . createElement ( " resources " ) ;
foreach ( const Url & url , resources . urls ) {
QDomElement elem = doc . createElement ( " url " ) ;
const uint priority = url . priority ;
if ( priority ) {
int preference = MAX_PREFERENCE - priority + 1 ;
if ( preference < = 0 ) {
preference = 1 ; //HACK if priority is larger MAX_PREFERENCE makes it 1
}
elem . setAttribute ( " preference " , preference ) ;
}
if ( ! url . location . isEmpty ( ) ) {
elem . setAttribute ( " location " , url . location ) ;
}
QDomText text = doc . createTextNode ( url . url . url ( ) ) ;
elem . appendChild ( text ) ;
res . appendChild ( elem ) ;
}
foreach ( const Metaurl & metaurl , resources . metaurls ) {
if ( metaurl . type = = " torrent " ) {
QDomElement elem = doc . createElement ( " url " ) ;
elem . setAttribute ( " type " , " bittorrent " ) ;
const uint priority = metaurl . priority ;
if ( priority ) {
int preference = MAX_PREFERENCE - priority + 1 ;
if ( preference < = 0 ) {
preference = 1 ; //HACK if priority is larger MAX_PREFERENCE makes it 1
}
elem . setAttribute ( " preference " , preference ) ;
}
QDomText text = doc . createTextNode ( metaurl . url . url ( ) ) ;
elem . appendChild ( text ) ;
res . appendChild ( elem ) ;
}
}
e . appendChild ( res ) ;
}
void KGetMetalink : : Metalink_v3 : : saveVerification ( const KGetMetalink : : Verification & verification , QDomElement & e ) const
{
QDomDocument doc = e . ownerDocument ( ) ;
QDomElement veri = doc . createElement ( " verification " ) ;
QHash < QString , QString > : : const_iterator it ;
QHash < QString , QString > : : const_iterator itEnd = verification . hashes . constEnd ( ) ;
for ( it = verification . hashes . constBegin ( ) ; it ! = itEnd ; + + it ) {
QDomElement elem = doc . createElement ( " hash " ) ;
elem . setAttribute ( " type " , it . key ( ) ) ;
QDomText text = doc . createTextNode ( it . value ( ) ) ;
elem . appendChild ( text ) ;
veri . appendChild ( elem ) ;
}
foreach ( const Pieces & pieces , verification . pieces ) {
QDomElement elem = doc . createElement ( " pieces " ) ;
elem . setAttribute ( " type " , pieces . type ) ;
elem . setAttribute ( " length " , QString : : number ( pieces . length ) ) ;
for ( int i = 0 ; i < pieces . hashes . count ( ) ; + + i ) {
QDomElement hash = doc . createElement ( " hash " ) ;
hash . setAttribute ( " piece " , i ) ;
QDomText text = doc . createTextNode ( pieces . hashes . at ( i ) ) ;
hash . appendChild ( text ) ;
elem . appendChild ( hash ) ;
}
veri . appendChild ( elem ) ;
}
itEnd = verification . signatures . constEnd ( ) ;
for ( it = verification . signatures . constBegin ( ) ; it ! = itEnd ; + + it ) {
QDomElement elem = doc . createElement ( " signature " ) ;
elem . setAttribute ( " type " , it . key ( ) ) ;
QDomText text = doc . createTextNode ( it . value ( ) ) ;
elem . appendChild ( text ) ;
veri . appendChild ( elem ) ;
}
e . appendChild ( veri ) ;
}
void KGetMetalink : : Metalink_v3 : : saveCommonData ( const KGetMetalink : : CommonData & data , QDomElement & e ) const
{
QDomDocument doc = e . ownerDocument ( ) ;
CommonData commonData = data ;
if ( ! commonData . publisher . isEmpty ( ) ) {
QDomElement elem = doc . createElement ( " publisher " ) ;
QDomElement elemName = doc . createElement ( " name " ) ;
QDomElement elemUrl = doc . createElement ( " url " ) ;
QDomText text = doc . createTextNode ( commonData . publisher . name ) ;
elemName . appendChild ( text ) ;
elem . appendChild ( elemName ) ;
text = doc . createTextNode ( commonData . publisher . url . url ( ) ) ;
elemUrl . appendChild ( text ) ;
elem . appendChild ( elemUrl ) ;
e . appendChild ( elem ) ;
commonData . publisher . clear ( ) ;
}
if ( commonData . oses . count ( ) > 1 ) { //only one OS can be set in 3.0
commonData . oses . clear ( ) ;
}
commonData . save ( e ) ;
}
QString KGetMetalink : : Metalink_v3 : : dateConstructToString ( const KGetMetalink : : DateConstruct & date ) const
{
QString dateString ;
if ( ! date . isValid ( ) ) {
return dateString ;
}
QLocale locale = QLocale : : c ( ) ;
//"Fri, 01 Apr 2009 00:00:01 +1030"
dateString + = locale . toString ( date . dateTime , " ddd, dd MMM yyyy hh:mm:ss " ) ;
if ( date . timeZoneOffset . isValid ( ) ) {
dateString + = ( date . negativeOffset ? ' - ' : ' + ' ) ;
dateString + = date . timeZoneOffset . toString ( " hhmm " ) ;
} else {
dateString + = " +0000 " ;
}
return dateString ;
}
bool KGetMetalink : : HandleMetalink : : load ( const KUrl & destination , KGetMetalink : : Metalink * metalink )
{
QFile file ( destination . pathOrUrl ( ) ) ;
if ( ! file . open ( QIODevice : : ReadOnly ) )
{
return false ;
}
QDomDocument doc ;
if ( ! doc . setContent ( & file ) )
{
file . close ( ) ;
return false ;
}
file . close ( ) ;
QDomElement root = doc . documentElement ( ) ;
if ( root . attribute ( " xmlns " ) = = " urn:ietf:params:xml:ns:metalink " )
{
metalink - > load ( root ) ;
return true ;
}
else if ( ( root . attribute ( " xmlns " ) = = " http://www.metalinker.org/ " ) | | ( root . attribute ( " version " ) = = " 3.0 " ) )
{
Metalink_v3 metalink_v3 ;
metalink_v3 . load ( root ) ;
* metalink = metalink_v3 . metalink ( ) ;
return true ;
}
return false ;
}
bool KGetMetalink : : HandleMetalink : : load ( const QByteArray & data , KGetMetalink : : Metalink * metalink )
{
if ( data . isNull ( ) )
{
return false ;
}
QDomDocument doc ;
if ( ! doc . setContent ( data ) )
{
return false ;
}
metalink - > clear ( ) ;
QDomElement root = doc . documentElement ( ) ;
if ( root . attribute ( " xmlns " ) = = " urn:ietf:params:xml:ns:metalink " )
{
metalink - > load ( root ) ;
return true ;
}
else if ( ( root . attribute ( " xmlns " ) = = " http://www.metalinker.org/ " ) | | ( root . attribute ( " version " ) = = " 3.0 " ) )
{
Metalink_v3 metalink_v3 ;
metalink_v3 . load ( root ) ;
* metalink = metalink_v3 . metalink ( ) ;
return true ;
}
return false ;
}
bool KGetMetalink : : HandleMetalink : : save ( const KUrl & destination , KGetMetalink : : Metalink * metalink )
{
QFile file ( destination . pathOrUrl ( ) ) ;
if ( ! file . open ( QIODevice : : WriteOnly ) ) {
return false ;
}
QDomDocument doc ;
QString fileName = destination . fileName ( ) ;
if ( fileName . endsWith ( QLatin1String ( " meta4 " ) ) ) {
doc = metalink - > save ( ) ;
} else if ( fileName . endsWith ( QLatin1String ( " metalink " ) ) ) {
Metalink_v3 metalink_v3 ;
metalink_v3 . setMetalink ( * metalink ) ;
doc = metalink_v3 . save ( ) ;
} else {
file . close ( ) ;
return false ;
}
QTextStream stream ( & file ) ;
doc . save ( stream , 2 ) ;
file . close ( ) ;
return true ;
}
KGetMetalink : : MetalinkHttpParser : : ~ MetalinkHttpParser ( )
{
}
QString * KGetMetalink : : MetalinkHttpParser : : getEtag ( )
{
return & m_EtagValue ;
}
void KGetMetalink : : MetalinkHttpParser : : checkMetalinkHttp ( )
{
if ( ! m_Url . isValid ( ) ) {
kDebug ( ) < < " Url not valid " ;
return ;
}
KIO : : TransferJob * job ;
job = KIO : : get ( m_Url , KIO : : NoReload , KIO : : HideProgressInfo ) ;
job - > addMetaData ( " PropagateHttpHeader " , " true " ) ;
job - > setRedirectionHandlingEnabled ( false ) ;
connect ( job , SIGNAL ( result ( KJob * ) ) , this , SLOT ( slotHeaderResult ( KJob * ) ) ) ; // Finished
connect ( job , SIGNAL ( redirection ( KIO : : Job * , KUrl ) ) , this , SLOT ( slotRedirection ( KIO : : Job * , KUrl ) ) ) ; // Redirection
connect ( job , SIGNAL ( mimetype ( KIO : : Job * , QString ) ) , this , SLOT ( detectMime ( KIO : : Job * , QString ) ) ) ; // Mime detection.
kDebug ( ) < < " Verifying Metalink/HTTP Status " ;
m_loop . exec ( ) ;
}
void KGetMetalink : : MetalinkHttpParser : : detectMime ( KIO : : Job * job , const QString & type )
{
kDebug ( ) < < " Mime Type: " < < type ;
job - > kill ( ) ;
m_loop . exit ( ) ;
}
void KGetMetalink : : MetalinkHttpParser : : slotHeaderResult ( KJob * kjob )
{
KIO : : Job * job = qobject_cast < KIO : : Job * > ( kjob ) ;
const QString httpHeaders = job ? job - > queryMetaData ( " HTTP-Headers " ) : QString ( ) ;
parseHeaders ( httpHeaders ) ;
setMetalinkHSatus ( ) ;
// Handle the redirection... (Comment out if not desired)
if ( m_redirectionUrl . isValid ( ) ) {
m_Url = m_redirectionUrl ;
m_redirectionUrl = KUrl ( ) ;
checkMetalinkHttp ( ) ;
}
if ( m_loop . isRunning ( ) )
m_loop . exit ( ) ;
}
void KGetMetalink : : MetalinkHttpParser : : slotRedirection ( KIO : : Job * job , const KUrl & url )
{
Q_UNUSED ( job )
m_redirectionUrl = url ;
}
bool KGetMetalink : : MetalinkHttpParser : : isMetalinkHttp ( )
{
if ( m_MetalinkHSatus ) {
kDebug ( ) < < " Metalink Http detected " ;
}
else {
kDebug ( ) < < " No Metalink HTTP response found " ;
}
return m_MetalinkHSatus ;
}
void KGetMetalink : : MetalinkHttpParser : : parseHeaders ( const QString & httpHeader )
{
QString trimedHeader = httpHeader . mid ( httpHeader . indexOf ( ' \n ' ) + 1 ) . trimmed ( ) ;
foreach ( QString line , trimedHeader . split ( ' \n ' ) ) {
int colon = line . indexOf ( ' : ' ) ;
QString headerName = line . left ( colon ) . trimmed ( ) ;
QString headerValue = line . mid ( colon + 1 ) . trimmed ( ) ;
m_headerInfo . insertMulti ( headerName , headerValue ) ;
}
m_EtagValue = m_headerInfo . value ( " ETag " ) ;
}
void KGetMetalink : : MetalinkHttpParser : : setMetalinkHSatus ( )
{
bool linkStatus , digestStatus ;
linkStatus = digestStatus = false ;
if ( m_headerInfo . contains ( " link " ) ) {
QList < QString > linkValues = m_headerInfo . values ( " link " ) ;
foreach ( QString linkVal , linkValues ) {
if ( linkVal . contains ( " rel=duplicate " ) ) {
linkStatus = true ;
break ;
}
}
}
if ( m_headerInfo . contains ( " digest " ) ) {
QList < QString > digestValues = m_headerInfo . values ( " digest " ) ;
foreach ( QString digestVal , digestValues ) {
if ( digestVal . contains ( " sha-256 " , Qt : : CaseInsensitive ) ) {
digestStatus = true ;
break ;
}
}
}
if ( ( linkStatus ) & & ( digestStatus ) ) {
m_MetalinkHSatus = true ;
}
}
KUrl KGetMetalink : : MetalinkHttpParser : : getUrl ( )
{
return m_Url ;
}
QMultiMap < QString , QString > * KGetMetalink : : MetalinkHttpParser : : getHeaderInfo ( )
{
return & m_headerInfo ;
}
KGetMetalink : : HttpLinkHeader : : HttpLinkHeader ( const QString & headerLine )
: pref ( false )
{
parseHeaderLine ( headerLine ) ;
}
bool KGetMetalink : : HttpLinkHeader : : operator < ( const HttpLinkHeader & other ) const
{
return depth < other . depth ;
}
void KGetMetalink : : HttpLinkHeader : : parseHeaderLine ( const QString & line )
{
url = line . mid ( line . indexOf ( " < " ) + 1 , line . indexOf ( " > " ) - 1 ) . trimmed ( ) ;
const QList < QString > attribList = line . split ( " ; " ) ;
foreach ( const QString str , attribList ) {
const QString attribId = str . mid ( 0 , str . indexOf ( " = " ) ) . trimmed ( ) ;
const QString attribValue = str . mid ( str . indexOf ( " = " ) + 1 ) . trimmed ( ) ;
if ( attribId = = " rel " ) {
reltype = attribValue ;
}
else if ( attribId = = " depth " ) {
depth = attribValue . toInt ( ) ;
}
else if ( attribId = = " geo " ) {
geo = attribValue ;
}
else if ( attribId = = " pref " ) {
pref = true ;
}
else if ( attribId = = " pri " ) {
priority = attribValue . toUInt ( ) ;
}
else if ( attribId = = " type " ) {
type = attribValue ;
}
else if ( attribId = = " name " ) {
name = attribValue ;
}
}
}
2015-02-27 11:02:43 +00:00
# include "moc_metalinker.cpp"