00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include <config-kleopatra.h>
00034
00035 #include "classify.h"
00036
00037 #include <QString>
00038 #include <QStringList>
00039 #include <QFile>
00040 #include <QFileInfo>
00041 #include <QtAlgorithms>
00042 #include <QByteArrayMatcher>
00043
00044 #include <boost/range.hpp>
00045
00046 #ifdef __GNUC__
00047 # include <ext/algorithm>
00048 #endif
00049
00050 #include <functional>
00051
00052 using namespace boost;
00053 using namespace Kleo::Class;
00054
00055 namespace {
00056
00057 const unsigned int ExamineContentHint = 0x8000;
00058
00059 static const struct _classification {
00060 char extension[4];
00061 unsigned int classification;
00062 } classifications[] = {
00063
00064 { "asc", OpenPGP| Ascii | OpaqueSignature|DetachedSignature|CipherText|AnyCertStoreType | ExamineContentHint },
00065 { "crt", CMS | Binary | Certificate },
00066 { "der", CMS | Binary | Certificate },
00067 { "gpg", OpenPGP| Binary | OpaqueSignature|CipherText|AnyCertStoreType },
00068 { "p10", CMS | Ascii | CertificateRequest },
00069 { "p12", CMS | Binary | ExportedPSM },
00070 { "p7c", CMS | Binary | Certificate },
00071 { "p7m", CMS | Binary | CipherText },
00072 { "p7s", CMS | Binary | AnySignature },
00073 { "pem", CMS | Ascii | AnyType | ExamineContentHint },
00074 { "sig", OpenPGP|AnyFormat| DetachedSignature },
00075 };
00076
00077 static const unsigned int defaultClassification = NoClass;
00078
00079 template <template <typename U> class Op>
00080 struct ByExtension {
00081 typedef bool result_type;
00082
00083 template <typename T>
00084 bool operator()( const T & lhs, const T & rhs ) const {
00085 return Op<int>()( qstricmp( lhs.extension, rhs.extension ), 0 );
00086 }
00087 template <typename T>
00088 bool operator()( const T & lhs, const char * rhs ) const {
00089 return Op<int>()( qstricmp( lhs.extension, rhs ), 0 );
00090 }
00091 template <typename T>
00092 bool operator()( const char * lhs, const T & rhs ) const {
00093 return Op<int>()( qstricmp( lhs, rhs.extension ), 0 );
00094 }
00095 bool operator()( const char * lhs, const char * rhs ) const {
00096 return Op<int>()( qstricmp( lhs, rhs ), 0 );
00097 }
00098 };
00099
00100 static const struct _content_classification {
00101 char content[28];
00102 unsigned int classification;
00103 } content_classifications[] = {
00104 { "CERTIFICATE", Certificate },
00105 { "MESSAGE", OpaqueSignature|CipherText },
00106 { "PKCS12", ExportedPSM },
00107 { "PRIVATE KEY BLOCK", ExportedPSM },
00108 { "PUBLIC KEY BLOCK", Certificate },
00109 { "SIGNATURE", DetachedSignature },
00110 { "SIGNED MESSAGE", ClearsignedMessage },
00111 };
00112
00113 template <template <typename U> class Op>
00114 struct ByContent {
00115 typedef bool result_type;
00116
00117 const unsigned int N;
00118 explicit ByContent( unsigned int n ) : N( n ) {}
00119
00120 template <typename T>
00121 bool operator()( const T & lhs, const T & rhs ) const {
00122 return Op<int>()( qstrncmp( lhs.content, rhs.content, N ), 0 );
00123 }
00124 template <typename T>
00125 bool operator()( const T & lhs, const char * rhs ) const {
00126 return Op<int>()( qstrncmp( lhs.content, rhs, N ), 0 );
00127 }
00128 template <typename T>
00129 bool operator()( const char * lhs, const T & rhs ) const {
00130 return Op<int>()( qstrncmp( lhs, rhs.content, N ), 0 );
00131 }
00132 bool operator()( const char * lhs, const char * rhs ) const {
00133 return Op<int>()( qstrncmp( lhs, rhs, N ), 0 );
00134 }
00135 };
00136
00137 }
00138
00139
00140 unsigned int Kleo::classify( const QString & filename ) {
00141 #ifdef __GNUC__
00142 assert( __gnu_cxx::is_sorted( begin( classifications ), end( classifications ), ByExtension<std::less>() ) );
00143 #endif
00144
00145 const QFileInfo fi( filename );
00146
00147 const _classification * const it = qBinaryFind( begin( classifications ), end( classifications ),
00148 fi.suffix().toLatin1().constData(),
00149 ByExtension<std::less>() );
00150 if ( it == end( classifications ) )
00151 return defaultClassification;
00152 if ( !( it->classification & ExamineContentHint ) )
00153 return it->classification;
00154
00155 QFile file( filename );
00156 if ( !file.open( QIODevice::ReadOnly|QIODevice::Text ) )
00157 return it->classification;
00158
00159 const unsigned int contentClassification = classifyContent( file.read( 4096 ) );
00160 if ( contentClassification != defaultClassification )
00161 return contentClassification;
00162 else
00163 return it->classification;
00164 }
00165
00166 unsigned int Kleo::classifyContent( const QByteArray & data ) {
00167 #ifdef __GNUC__
00168 assert( __gnu_cxx::is_sorted( begin( content_classifications ), end( content_classifications ), ByContent<std::less>(100) ) );
00169 #endif
00170
00171 static const char beginString[] = "-----BEGIN ";
00172 static const QByteArrayMatcher beginMatcher( beginString );
00173 int pos = beginMatcher.indexIn( data );
00174 if ( pos < 0 )
00175 return defaultClassification;
00176 pos += sizeof beginString - 1;
00177
00178 const bool pgp = qstrncmp( data.data() + pos, "PGP ", 4 ) == 0;
00179 if ( pgp )
00180 pos += 4;
00181
00182 const int epos = data.indexOf( "-----\n", pos );
00183 if ( epos < 0 )
00184 return defaultClassification;
00185
00186 const _content_classification * const cit
00187 = qBinaryFind( begin( content_classifications ), end( content_classifications ),
00188 data.data() + pos, ByContent<std::less>( epos - pos ) );
00189
00190 if ( cit == end( content_classifications ) )
00191 return defaultClassification;
00192 else
00193 return cit->classification | ( pgp ? OpenPGP : CMS );
00194 }
00195
00196 QString Kleo::printableClassification( unsigned int classification ) {
00197 QStringList parts;
00198 if ( classification & CMS )
00199 parts.push_back( "CMS" );
00200 if ( classification & OpenPGP )
00201 parts.push_back( "OpenPGP" );
00202 if ( classification & Binary )
00203 parts.push_back( "Binary" );
00204 if ( classification & Ascii )
00205 parts.push_back( "Ascii" );
00206 if ( classification & DetachedSignature )
00207 parts.push_back( "DetachedSignature" );
00208 if ( classification & OpaqueSignature )
00209 parts.push_back( "OpaqueSignature" );
00210 if ( classification & ClearsignedMessage )
00211 parts.push_back( "ClearsignedMessage" );
00212 if ( classification & CipherText )
00213 parts.push_back( "CipherText" );
00214 if ( classification & Certificate )
00215 parts.push_back( "Certificate" );
00216 if ( classification & ExportedPSM )
00217 parts.push_back( "ExportedPSM" );
00218 if ( classification & CertificateRequest )
00219 parts.push_back( "CertificateRequest" );
00220 return parts.join( ", " );
00221 }
00222
00223 static QString chopped( QString s, unsigned int n ) {
00224 s.chop( n );
00225 return s;
00226 }
00227
00232 QString Kleo::findSignedData( const QString & signatureFileName ) {
00233 if ( !mayBeDetachedSignature( signatureFileName ) )
00234 return QString();
00235 const QString baseName = chopped( signatureFileName, 4 );
00236 return QFile::exists( baseName ) ? baseName : QString() ;
00237 }
00238
00245 QStringList Kleo::findSignatures( const QString & signedDataFileName ) {
00246 QStringList result;
00247 for ( unsigned int i = 0, end = size( classifications ) ; i < end ; ++i )
00248 if ( classifications[i].classification & DetachedSignature ) {
00249 const QString candiate = signedDataFileName + '.' + classifications[i].extension;
00250 if ( QFile::exists( candiate ) )
00251 result.push_back( candiate );
00252 }
00253 return result;
00254 }
00255
00260 QString Kleo::outputFileName( const QString & inputFileName ) {
00261 const QFileInfo fi( inputFileName );
00262
00263 if ( qBinaryFind( begin( classifications ), end( classifications ),
00264 fi.suffix().toLatin1().constData(),
00265 ByExtension<std::less>() ) == end( classifications ) )
00266 return inputFileName + ".out";
00267 else
00268 return chopped( inputFileName, 4 );
00269 }
00270
00275 const char * Kleo::outputFileExtension( unsigned int classification ) {
00276 for ( unsigned int i = 0 ; i < sizeof classifications / sizeof *classifications ; ++i )
00277 if ( ( classifications[i].classification & classification ) == classification )
00278 return classifications[i].extension;
00279 return 0;
00280 }