superkaramba
themefile.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "themefile.h"
00025 #include "lineparser.h"
00026 #include "themelocale.h"
00027
00028 #include <KZip>
00029 #include <KTempDir>
00030 #include <KApplication>
00031 #include <KMessageBox>
00032 #include <KStandardDirs>
00033 #include <KLocale>
00034 #include <kio/job.h>
00035 #include <KIO/NetAccess>
00036 #include <kross/core/manager.h>
00037 #include <kross/core/interpreter.h>
00038
00039 #include <QDomNode>
00040
00041 class ZipFile
00042 {
00043 public:
00044 ZipFile() :
00045 m_zip(0), m_file(0), m_tempDir(0)
00046 {}
00047 void setFile(const QString& filename)
00048 {
00049 m_filename = filename;
00050 if (filename.isEmpty())
00051 return;
00052
00053 const KArchiveEntry* entry;
00054
00055 entry = m_dir->entry(filename);
00056 if (entry == 0 || !entry->isFile()) {
00057 m_file = 0;
00058 return;
00059 }
00060 m_file = static_cast<const KArchiveFile*>(entry);
00061 }
00062 void setZip(const QString& zipfile)
00063 {
00064 closeZip();
00065
00066 m_zip = new KZip(zipfile);
00067
00068 if (!m_zip->open(QIODevice::ReadOnly)) {
00069 qDebug("Unable to open '%s' for reading.", zipfile.toAscii().constData());
00070 return;
00071 }
00072 m_dir = m_zip->directory();
00073 if (m_dir == 0) {
00074 qDebug("Error reading directory contents of file %s", zipfile.toAscii().constData());
00075 return;
00076 }
00077 }
00078
00079 virtual ~ZipFile()
00080 {
00081 closeZip();
00082 }
00083
00084 void closeZip()
00085 {
00086 if (m_zip) {
00087 m_zip->close();
00088 delete m_zip;
00089 delete m_tempDir;
00090 }
00091 }
00092
00093 QByteArray data()
00094 {
00095 if (m_file)
00096 return m_file->data();
00097 else {
00098 if (!m_filename.isEmpty())
00099 qDebug("Error reading file %s from zip", m_filename.toAscii().constData());
00100 return QByteArray();
00101 }
00102 }
00103
00104 bool exists()
00105 {
00106 return (m_file != 0);
00107 }
00108
00109 QString extractArchive()
00110 {
00111 QString tmpPath = KStandardDirs::locateLocal("tmp", "runningThemes/");
00112
00113 m_tempDir = new KTempDir(tmpPath);
00114 m_tempDir->setAutoRemove(true);
00115
00116 m_dir->copyTo(m_tempDir->name());
00117
00118 return m_tempDir->name();
00119 }
00120
00121 private:
00122 KZip* m_zip;
00123 const KArchiveFile* m_file;
00124 QString m_filename;
00125 const KArchiveDirectory* m_dir;
00126 KTempDir* m_tempDir;
00127 };
00128
00129 class ThemeFile::Private
00130 {
00131 public:
00132 QString path;
00133 bool zipTheme;
00134 QString file;
00135 QString id;
00136 QString mo;
00137 QString name;
00138 QString theme;
00139 QString script;
00140 QString icon;
00141 QString version;
00142 QString license;
00143 QTextStream* stream;
00144 QByteArray ba;
00145 QFile fl;
00146 QString description;
00147 QString author;
00148 QString authorEmail;
00149 QString homepage;
00150 ThemeLocale* locale;
00151 ZipFile* zip;
00152 KUrl UrlPath;
00153
00154 Private()
00155 : zipTheme(false), stream(0), locale(0), zip(0)
00156 {
00157 }
00158
00159 ~Private()
00160 {
00161 delete stream;
00162 delete locale;
00163 delete zip;
00164 }
00165
00166 };
00167
00168 ThemeFile::ThemeFile(const KUrl& url)
00169 : d(new Private)
00170 {
00171 if (url.isValid())
00172 set(url);
00173 }
00174
00175 ThemeFile::~ThemeFile()
00176 {
00177 delete d;
00178 }
00179
00180 bool ThemeFile::open()
00181 {
00182 bool result = false;
00183
00184 close();
00185
00186 if (d->zipTheme) {
00187 d->zip->setFile(d->theme);
00188 d->ba = d->zip->data();
00189 if (d->ba.size() > 0) {
00190 d->stream = new QTextStream(d->ba, QIODevice::ReadOnly);
00191 result = true;
00192 }
00193 } else {
00194 d->fl.setFileName(d->file);
00195
00196 if (d->fl.open(QIODevice::ReadOnly | QIODevice::Text)) {
00197 d->stream = new QTextStream(&d->fl);
00198 result = true;
00199 }
00200 }
00201 return result;
00202 }
00203
00204 bool ThemeFile::nextLine(LineParser& parser)
00205 {
00206 parser.set("");
00207
00208 if (d->stream) {
00209 QString result = d->stream->readLine();
00210
00211 if (result.isNull())
00212 return false;
00213 parser.set(result);
00214 return true;
00215 }
00216 return false;
00217 }
00218
00219 bool ThemeFile::close()
00220 {
00221 if (d->stream) {
00222 delete d->stream;
00223 d->stream = 0;
00224 d->fl.close();
00225 d->ba.resize(0);
00226 return true;
00227 }
00228 return false;
00229 }
00230
00231 bool ThemeFile::isValid() const
00232 {
00233 return (exists() && !d->name.isEmpty() && !d->theme.isEmpty());
00234 }
00235
00236 bool ThemeFile::exists() const
00237 {
00238 QFileInfo file(d->file);
00239 return file.exists();
00240 }
00241
00242 QPixmap ThemeFile::icon() const
00243 {
00244 QPixmap icon;
00245 icon.loadFromData(readThemeFile(d->icon));
00246 return icon;
00247 }
00248
00249 bool ThemeFile::set(const KUrl &url)
00250 {
00251 if (!url.isLocalFile() && !url.protocol().isEmpty()) {
00252 if (KMessageBox::warningContinueCancel(qApp->activeWindow(),
00253 i18n("You are about to install and run %1 SuperKaramba theme. Since "
00254 "themes can contain executable code you should only install themes "
00255 "from sources that you trust. Continue?",
00256 url.prettyUrl()),
00257 i18n("Executable Code Warning"),
00258 KGuiItem(i18n("Install")))
00259
00260 == KMessageBox::Cancel) {
00261 return false;
00262 }
00263
00264 QDir themeDir(KStandardDirs::locateLocal("appdata", "themes/", true));
00265 QFileInfo localFile = themeDir.filePath(url.fileName());
00266
00267 if (localFile.exists()) {
00268 if (KMessageBox::warningContinueCancel(qApp->activeWindow(),
00269 i18n("%1 already exists. Do you want to overwrite it?", localFile.filePath()),
00270 i18n("File Exists"),
00271 KGuiItem(i18n("Overwrite"))
00272 )
00273 == KMessageBox::Cancel) {
00274 return false;
00275 }
00276 }
00277
00278 KIO::Job *job = KIO::file_copy(url, localFile.filePath(), -1, KIO::Overwrite);
00279
00280 if (!KIO::NetAccess::synchronousRun(job, qApp->activeWindow())) {
00281 return false;
00282 }
00283
00284 d->file = localFile.filePath();
00285 } else {
00286 if (url.directory().isEmpty() || url.directory() == "/")
00287 d->file = canonicalFile(QDir::current().filePath(url.fileName()));
00288 else
00289 d->file = canonicalFile(url.path());
00290 if (!exists())
00291 return false;
00292 }
00293
00294 d->UrlPath = url;
00295
00296 QFileInfo fi(d->file);
00297
00298 d->name = fi.completeBaseName();
00299 d->theme = d->name + ".theme";
00300 d->script = d->name;
00301
00302 bool fileExtensionFound = false;
00303 QStringList availInterp = Kross::Manager::self().interpreters();
00304 foreach (QString interpreter, availInterp) {
00305 QString fileExtension = Kross::Manager::self().interpreterInfo(interpreter)->wildcard();
00306 fileExtension.remove(0, 1);
00307
00308 if (fileExists(fi.path() + '/' + d->script + fileExtension)) {
00309 d->script += fileExtension;
00310 fileExtensionFound = true;
00311 break;
00312 }
00313 }
00314 if (!fileExtensionFound) {
00315 d->script += ".py";
00316 }
00317
00318 d->id = d->name;
00319
00320 if (isZipFile(d->file)) {
00321 d->path = d->file;
00322 d->zipTheme = true;
00323 d->zip = new ZipFile();
00324 d->zip->setZip(d->file);
00325 } else {
00326 d->path = fi.absoluteDir().absolutePath() + '/';
00327 d->zipTheme = false;
00328 }
00329 parseXml();
00330
00331 QFileInfo fimo(d->script);
00332 if (d->script.isEmpty())
00333 fimo.setFile(d->theme);
00334 else
00335 fimo.setFile(d->script);
00336 d->mo = fimo.completeBaseName();
00337
00338 d->locale = new ThemeLocale(this);
00339 return isValid();
00340 }
00341
00342 KUrl ThemeFile::getUrlPath()
00343 {
00344 return d->UrlPath;
00345 }
00346
00347 void ThemeFile::parseXml()
00348 {
00349 if (!fileExists("maindata.xml"))
00350 return;
00351 QByteArray ba = readThemeFile("maindata.xml");
00352 QDomDocument doc("superkaramba_theme");
00353 doc.setContent(ba);
00354 QDomElement element = doc.documentElement();
00355
00356 QDomNode n = element.firstChild();
00357 while (!n.isNull()) {
00358 QDomElement e = n.toElement();
00359 if (!e.isNull()) {
00360 if (e.tagName() == "name") {
00361 d->name = e.text();
00362 } else if (e.tagName() == "themefile") {
00363 d->theme = e.text();
00364 } else if (e.tagName() == "python_module") {
00365 d->script = e.text();
00366 } else if (e.tagName() == "script_module") {
00367 d->script = e.text();
00368 } else if (e.tagName() == "description") {
00369 d->description = e.text();
00370 } else if (e.tagName() == "author") {
00371 d->author = e.text();
00372 } else if (e.tagName() == "author_email") {
00373 d->authorEmail = e.text();
00374 } else if (e.tagName() == "homepage") {
00375 d->homepage = e.text();
00376 } else if (e.tagName() == "icon") {
00377 d->icon = e.text();
00378 } else if (e.tagName() == "version") {
00379 d->version = e.text();
00380 } else if (e.tagName() == "license") {
00381 d->license = e.text();
00382 }
00383 }
00384 n = n.nextSibling();
00385 }
00386 }
00387
00388 bool ThemeFile::canUninstall() const
00389 {
00390 QFileInfo fi(file());
00391 if (fi.permission(QFile::WriteUser) ||
00392 fi.permission(QFile::WriteGroup) ||
00393 fi.permission(QFile::WriteOther))
00394 return true;
00395 return false;
00396 }
00397
00398 bool ThemeFile::isThemeFile(const QString& filename) const
00399 {
00400 QFileInfo fileInfo(filename);
00401
00402 return fileInfo.isRelative();
00403 }
00404
00405 bool ThemeFile::fileExists(const QString& filename) const
00406 {
00407 if (isThemeFile(filename)) {
00408 if (isZipTheme()) {
00409 d->zip->setFile(filename);
00410 return d->zip->exists();
00411 } else
00412 return QFileInfo(path() + '/' + filename).exists();
00413 } else
00414 return QFileInfo(filename).exists();
00415 }
00416
00417 QByteArray ThemeFile::readThemeFile(const QString& filename) const
00418 {
00419
00420
00421 QByteArray ba;
00422
00423 if (isZipTheme()) {
00424 d->zip->setFile(filename);
00425 ba = d->zip->data();
00426 } else {
00427 QFile file(path() + '/' + filename);
00428
00429 if (file.open(QIODevice::ReadOnly)) {
00430 ba = file.readAll();
00431 file.close();
00432 }
00433 }
00434
00435
00436 return ba;
00437 }
00438
00439 bool ThemeFile::isZipFile(const QString& filename)
00440 {
00441 QFile file(filename);
00442
00443 if (file.open(QIODevice::ReadOnly)) {
00444 unsigned char buf[5];
00445
00446 if (file.read((char*)buf, 4) == 4) {
00447 if (buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4)
00448 return true;
00449 }
00450 }
00451 return false;
00452 }
00453
00454 bool ThemeFile::scriptModuleExists() const
00455 {
00456 if (d->script.isEmpty()) {
00457 return false;
00458 }
00459
00460 if (fileExists(d->script)) {
00461 return true;
00462 }
00463
00464 return false;
00465 }
00466
00467 QString ThemeFile::canonicalFile(const QString& file)
00468 {
00469
00470 QFileInfo fi(file);
00471 return QDir(fi.dir().canonicalPath()).filePath(fi.fileName());
00472 }
00473
00474 QString ThemeFile::extractArchive() const
00475 {
00476 if (isZipTheme()) {
00477 return d->zip->extractArchive();
00478 }
00479
00480 return QString();
00481 }
00482
00483 bool ThemeFile::isZipTheme() const
00484 {
00485 return d->zipTheme;
00486 }
00487
00488 const QString& ThemeFile::name() const
00489 {
00490 return d->name;
00491 }
00492
00493 const QString& ThemeFile::version() const
00494 {
00495 return d->version;
00496 }
00497
00498 const QString& ThemeFile::license() const
00499 {
00500 return d->license;
00501 }
00502
00503 const QString& ThemeFile::id() const
00504 {
00505 return d->id;
00506 }
00507
00508 const QString& ThemeFile::mo() const
00509 {
00510 return d->mo;
00511 }
00512
00513 const QString& ThemeFile::file() const
00514 {
00515 return d->file;
00516 }
00517
00518 const QString& ThemeFile::scriptModule() const
00519 {
00520 return d->script;
00521 }
00522
00523 const QString& ThemeFile::path() const
00524 {
00525 return d->path;
00526 }
00527
00528 const QString& ThemeFile::description() const
00529 {
00530 return d->description;
00531 }
00532
00533 const QString& ThemeFile::author() const
00534 {
00535 return d->author;
00536 }
00537
00538 const QString& ThemeFile::authorEmail() const
00539 {
00540 return d->authorEmail;
00541 }
00542
00543 const QString& ThemeFile::homepage() const
00544 {
00545 return d->homepage;
00546 }
00547
00548 const ThemeLocale* ThemeFile::locale() const
00549 {
00550 return d->locale;
00551 }
00552