themefile.cpp
00001 /**************************************************************************** 00002 * themefile.cpp - Theme file handling 00003 * 00004 * Copyright (C) 2003 Hans Karlsson <karlsson.h@home.se> 00005 * Copyright (C) 2003-2004 Adam Geitgey <adam@rootnode.org> 00006 * Copyright (c) 2004 Petri Damst� <damu@iki.fi> 00007 * 00008 * This file is part of SuperKaramba. 00009 * 00010 * SuperKaramba is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. 00014 * 00015 * SuperKaramba is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU General Public License for more details. 00019 * 00020 * You should have received a copy of the GNU General Public License 00021 * along with SuperKaramba; if not, write to the Free Software 00022 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00023 ****************************************************************************/ 00024 #include "themefile.h" 00025 #include "lineparser.h" 00026 #include "themelocale.h" 00027 #include <kdebug.h> 00028 #include <kurl.h> 00029 #include <kzip.h> 00030 #include <kapplication.h> 00031 #include <kmessagebox.h> 00032 #include <kstandarddirs.h> 00033 #include <klocale.h> 00034 #include <kio/netaccess.h> 00035 #include <tqtextstream.h> 00036 #include <tqfileinfo.h> 00037 #include <tqdom.h> 00038 #include <tqdir.h> 00039 00040 class ZipFile 00041 { 00042 public: 00043 ZipFile() : 00044 m_zip(0), m_file(0) 00045 { 00046 } 00047 void setFile(const TQString& 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 { 00058 m_file = 0; 00059 return; 00060 } 00061 m_file = static_cast<const KArchiveFile*>(entry); 00062 } 00063 void setZip(const TQString& zipfile) 00064 { 00065 closeZip(); 00066 00067 m_zip = new KZip(zipfile); 00068 00069 if(!m_zip->open(IO_ReadOnly)) 00070 { 00071 qDebug("Unable to open '%s' for reading.", zipfile.ascii()); 00072 return; 00073 } 00074 m_dir = m_zip->directory(); 00075 if(m_dir == 0) 00076 { 00077 qDebug("Error reading directory contents of file %s", zipfile.ascii()); 00078 return; 00079 } 00080 } 00081 00082 virtual ~ZipFile() 00083 { 00084 closeZip(); 00085 } 00086 00087 void closeZip() 00088 { 00089 if(m_zip) 00090 { 00091 m_zip->close(); 00092 delete m_zip; 00093 } 00094 } 00095 00096 TQByteArray data() 00097 { 00098 if(m_file) 00099 return m_file->data(); 00100 else 00101 { 00102 if(!m_filename.isEmpty()) 00103 qDebug("Error reading file %s from zip", m_filename.ascii()); 00104 return TQByteArray(); 00105 } 00106 } 00107 00108 bool exists() 00109 { 00110 return (m_file != 0); 00111 } 00112 00113 private: 00114 KZip* m_zip; 00115 const KArchiveFile* m_file; 00116 TQString m_filename; 00117 const KArchiveDirectory* m_dir; 00118 }; 00119 00120 ThemeFile::ThemeFile(const KURL& url) 00121 : m_stream(0), m_locale(0), m_zip(0) 00122 { 00123 if(url.isValid()) 00124 set(url); 00125 } 00126 00127 ThemeFile::~ThemeFile() 00128 { 00129 delete m_stream; 00130 delete m_locale; 00131 delete m_zip; 00132 } 00133 00134 bool ThemeFile::open() 00135 { 00136 bool result = false; 00137 00138 close(); 00139 00140 if(m_zipTheme) 00141 { 00142 m_zip->setFile(m_theme); 00143 m_ba = m_zip->data(); 00144 if(m_ba.size() > 0) 00145 { 00146 m_stream = new TQTextStream(m_ba, IO_ReadOnly); 00147 result = true; 00148 } 00149 } 00150 else 00151 { 00152 m_fl.setName(m_file); 00153 00154 if(m_fl.open(IO_ReadOnly|IO_Translate)) 00155 { 00156 m_stream = new TQTextStream(&m_fl); // use a text stream 00157 result = true; 00158 } 00159 } 00160 return result; 00161 } 00162 00163 bool ThemeFile::nextLine(LineParser& parser) 00164 { 00165 parser.set(""); 00166 00167 if(m_stream) 00168 { 00169 TQString result = m_stream->readLine(); 00170 00171 if(result.isNull()) 00172 return false; 00173 parser.set(result); 00174 return true; 00175 } 00176 return false; 00177 } 00178 00179 bool ThemeFile::close() 00180 { 00181 if(m_stream) 00182 { 00183 delete m_stream; 00184 m_stream = 0; 00185 m_fl.close(); 00186 m_ba.resize(0); 00187 return true; 00188 } 00189 return false; 00190 } 00191 00192 bool ThemeFile::isValid() const 00193 { 00194 return (exists() && !m_name.isEmpty() && !m_theme.isEmpty()); 00195 } 00196 00197 bool ThemeFile::exists() const 00198 { 00199 TQFileInfo file(m_file); 00200 return file.exists(); 00201 } 00202 00203 TQPixmap ThemeFile::icon() const 00204 { 00205 return TQPixmap(readThemeFile(m_icon)); 00206 } 00207 00208 bool ThemeFile::set(const KURL &url) 00209 { 00210 if(!url.isLocalFile() && !url.protocol().isEmpty()) 00211 { 00212 if(KMessageBox::warningContinueCancel(TQT_TQWIDGET(kapp->activeWindow()), 00213 i18n("You are about to install and run %1 SuperKaramba theme. Since " 00214 "themes can contain executable code you should only install themes " 00215 "from sources that you trust. Continue?"), i18n("Executable Code Warning"), i18n("Install") 00216 .arg(url.prettyURL())) 00217 == KMessageBox::Cancel) 00218 { 00219 return false; 00220 } 00221 00222 TQDir themeDir(locateLocal("appdata", "themes/", true)); 00223 TQFileInfo localFile = themeDir.filePath(url.fileName()); 00224 00225 if(localFile.exists()) 00226 { 00227 if(KMessageBox::warningContinueCancel(TQT_TQWIDGET(kapp->activeWindow()), 00228 i18n("%1 already exists. Do you want to overwrite it?") 00229 .arg(localFile.filePath()),i18n("File Exists"),i18n("Overwrite")) 00230 == KMessageBox::Cancel) 00231 { 00232 return false; 00233 } 00234 } 00235 if(!KIO::NetAccess::file_copy(url, localFile.filePath(), -1, true, 00236 false, kapp->mainWidget())) 00237 { 00238 return false; 00239 } 00240 m_file = localFile.filePath(); 00241 } 00242 else 00243 { 00244 if(url.directory().isEmpty() || url.directory() == "/") 00245 m_file = canonicalFile(TQDir::current().filePath(url.fileName())); 00246 else 00247 m_file = canonicalFile(url.path()); 00248 if(!exists()) 00249 return false; 00250 } 00251 00252 TQFileInfo fi(m_file); 00253 00254 m_name = fi.baseName( TRUE ); 00255 m_theme = m_name + ".theme"; 00256 m_python = m_name; 00257 m_id = m_name; 00258 00259 if(isZipFile(m_file)) 00260 { 00261 m_path = m_file; 00262 m_zipTheme = true; 00263 m_zip = new ZipFile(); 00264 m_zip->setZip(m_file); 00265 } 00266 else 00267 { 00268 m_path = fi.dirPath(true) + "/"; 00269 m_zipTheme = false; 00270 } 00271 parseXml(); 00272 00273 TQFileInfo fimo(m_python); 00274 if(m_python.isEmpty()) 00275 fimo.setFile(m_theme); 00276 else 00277 fimo.setFile(m_python); 00278 m_mo = fimo.baseName( TRUE ); 00279 00280 m_locale = new ThemeLocale(this); 00281 return isValid(); 00282 } 00283 00284 void ThemeFile::parseXml() 00285 { 00286 if(!fileExists("maindata.xml")) 00287 return; 00288 TQByteArray ba = readThemeFile("maindata.xml"); 00289 TQDomDocument doc("superkaramba_theme"); 00290 doc.setContent(ba); 00291 TQDomElement element = doc.documentElement(); 00292 00293 TQDomNode n = element.firstChild(); 00294 while(!n.isNull()) 00295 { 00296 TQDomElement e = n.toElement(); 00297 if(!e.isNull()) 00298 { 00299 if(e.tagName() == "name") 00300 m_name = e.text(); 00301 else if(e.tagName() == "themefile") 00302 m_theme = e.text(); 00303 else if(e.tagName() == "python_module") 00304 { 00305 m_python = e.text(); 00306 if(m_python.right(3).lower() == ".py") 00307 m_python.remove(m_python.length() - 3, 3); 00308 } 00309 else if(e.tagName() == "description") 00310 m_description = e.text(); 00311 else if(e.tagName() == "author") 00312 m_author = e.text(); 00313 else if(e.tagName() == "author_email") 00314 m_authorEmail = e.text(); 00315 else if(e.tagName() == "homepage") 00316 m_homepage = e.text(); 00317 else if(e.tagName() == "icon") 00318 m_icon = e.text(); 00319 else if(e.tagName() == "version") 00320 m_version = e.text(); 00321 else if(e.tagName() == "license") 00322 m_license = e.text(); 00323 } 00324 n = n.nextSibling(); 00325 } 00326 } 00327 00328 bool ThemeFile::canUninstall() const 00329 { 00330 TQFileInfo fi(file()); 00331 if(fi.permission(TQFileInfo::WriteUser) || 00332 fi.permission(TQFileInfo::WriteGroup) || 00333 fi.permission(TQFileInfo::WriteOther)) 00334 return true; 00335 return false; 00336 } 00337 00338 bool ThemeFile::isThemeFile(const TQString& filename) const 00339 { 00340 TQFileInfo fileInfo(filename); 00341 00342 return fileInfo.isRelative(); 00343 } 00344 00345 bool ThemeFile::fileExists(const TQString& filename) const 00346 { 00347 if(isThemeFile(filename)) 00348 { 00349 if(isZipTheme()) 00350 { 00351 m_zip->setFile(filename); 00352 return m_zip->exists(); 00353 } 00354 else 00355 return TQFileInfo(path() + "/" + filename).exists(); 00356 } 00357 else 00358 return TQFileInfo(filename).exists(); 00359 } 00360 00361 TQByteArray ThemeFile::readThemeFile(const TQString& filename) const 00362 { 00363 //TQTime time; 00364 //time.start(); 00365 TQByteArray ba; 00366 00367 if(isZipTheme()) 00368 { 00369 m_zip->setFile(filename); 00370 ba = m_zip->data(); 00371 } 00372 else 00373 { 00374 TQFile file(path() + "/" + filename); 00375 00376 if(file.open(IO_ReadOnly)) 00377 { 00378 ba = file.readAll(); 00379 file.close(); 00380 } 00381 } 00382 //kdDebug() << "Read theme file: " << filename << ", " << time.elapsed() 00383 // << "ms" << endl; 00384 return ba; 00385 } 00386 00387 bool ThemeFile::isZipFile(const TQString& filename) 00388 { 00389 TQFile file(filename); 00390 00391 if(file.open(IO_ReadOnly)) 00392 { 00393 unsigned char buf[5]; 00394 00395 if(file.readBlock((char*)buf, 4) == 4) 00396 { 00397 if(buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4) 00398 return true; 00399 } 00400 } 00401 return false; 00402 } 00403 00404 bool ThemeFile::pythonModuleExists() const 00405 { 00406 return (!m_python.isEmpty() && fileExists(m_python + ".py")); 00407 } 00408 00409 TQString ThemeFile::canonicalFile(const TQString& file) 00410 { 00411 // Get absolute path with NO symlinks 00412 TQFileInfo fi(file); 00413 return TQDir(fi.dir().canonicalPath()).filePath(fi.fileName()); 00414 }