File indexing completed on 2024-05-12 05:09:25
0001 /*************************************************************************** 0002 Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "filehandler.h" 0026 #include "tellico_strings.h" 0027 #include "netaccess.h" 0028 #include "../utils/cursorsaver.h" 0029 #include "../utils/guiproxy.h" 0030 #include "../utils/xmlhandler.h" 0031 #include "../tellico_debug.h" 0032 0033 #include <KLocalizedString> 0034 #include <KMessageBox> 0035 #include <KFileItem> 0036 #include <KIO/FileCopyJob> 0037 #include <KIO/DeleteJob> 0038 #include <KBackup> 0039 #include <KJobWidgets> 0040 0041 #include <QUrl> 0042 #include <QDomDocument> 0043 #include <QFile> 0044 #include <QTextStream> 0045 #include <QTemporaryFile> 0046 #include <QSaveFile> 0047 0048 namespace { 0049 static const int MAX_TEXT_CHUNK_WRITE_SIZE = 100 * 1024 * 1024; 0050 } 0051 0052 using Tellico::FileHandler; 0053 0054 FileHandler::FileRef::FileRef(const QUrl& url_, bool quiet_) : m_device(nullptr), m_isValid(false) { 0055 if(url_.isEmpty()) { 0056 return; 0057 } 0058 0059 if(!Tellico::NetAccess::download(url_, m_filename, GUI::Proxy::widget(), quiet_)) { 0060 QString s = Tellico::NetAccess::lastErrorString(); 0061 if(s.isEmpty()) { 0062 myLog() << "Can't download" << url_.toDisplayString(QUrl::PreferLocalFile); 0063 } else { 0064 myLog() << s; 0065 } 0066 if(!quiet_) { 0067 GUI::Proxy::sorry(s.isEmpty() ? i18n(errorLoad, url_.fileName()) : s); 0068 } 0069 return; 0070 } 0071 0072 m_device = new QFile(m_filename); 0073 m_isValid = true; 0074 } 0075 0076 FileHandler::FileRef::~FileRef() { 0077 if(!m_filename.isEmpty()) { 0078 Tellico::NetAccess::removeTempFile(m_filename); 0079 } 0080 if(m_device) { 0081 m_device->close(); 0082 } 0083 delete m_device; 0084 m_device = nullptr; 0085 m_isValid = false; 0086 } 0087 0088 bool FileHandler::FileRef::open(bool quiet_) { 0089 if(!isValid()) { 0090 return false; 0091 } 0092 if(!m_device || !m_device->open(QIODevice::ReadOnly)) { 0093 if(!quiet_) { 0094 QUrl u = QUrl::fromLocalFile(fileName()); 0095 GUI::Proxy::sorry(i18n(errorLoad, u.fileName())); 0096 } 0097 delete m_device; 0098 m_device = nullptr; 0099 m_isValid = false; 0100 return false; 0101 } 0102 return true; 0103 } 0104 0105 FileHandler::FileRef* FileHandler::fileRef(const QUrl& url_, bool quiet_) { 0106 return new FileRef(url_, quiet_); 0107 } 0108 0109 QString FileHandler::readTextFile(const QUrl& url_, bool quiet_/*=false*/, bool useUTF8_ /*false*/) { 0110 FileRef f(url_, quiet_); 0111 if(!f.isValid()) { 0112 return QString(); 0113 } 0114 0115 if(f.open(quiet_)) { 0116 QTextStream stream(f.file()); 0117 if(useUTF8_) { 0118 stream.setCodec("UTF-8"); 0119 } 0120 return stream.readAll(); 0121 } 0122 return QString(); 0123 } 0124 0125 QString FileHandler::readXMLFile(const QUrl& url_, bool quiet_/*=false*/) { 0126 FileRef f(url_, quiet_); 0127 if(!f.isValid()) { 0128 return QString(); 0129 } 0130 0131 if(f.open(quiet_)) { 0132 return XMLHandler::readXMLData(f.file()->readAll()); 0133 } 0134 return QString(); 0135 } 0136 0137 QDomDocument FileHandler::readXMLDocument(const QUrl& url_, bool processNamespace_, bool quiet_) { 0138 FileRef f(url_, quiet_); 0139 if(!f.isValid()) { 0140 return QDomDocument(); 0141 } 0142 0143 QDomDocument doc; 0144 QString errorMsg; 0145 int errorLine, errorColumn; 0146 if(!f.open(quiet_)) { 0147 return QDomDocument(); 0148 } 0149 if(!doc.setContent(f.file(), processNamespace_, &errorMsg, &errorLine, &errorColumn)) { 0150 if(!quiet_) { 0151 QString details = i18n("There is an XML parsing error in line %1, column %2.", errorLine, errorColumn); 0152 details += QLatin1String("\n"); 0153 details += i18n("The error message from Qt is:"); 0154 details += QLatin1String("\n\t") + errorMsg; 0155 GUI::CursorSaver cs(Qt::ArrowCursor); 0156 if(GUI::Proxy::widget()) { 0157 KMessageBox::detailedError(GUI::Proxy::widget(), i18n(errorLoad, url_.fileName()), details); 0158 } 0159 } 0160 return QDomDocument(); 0161 } 0162 return doc; 0163 } 0164 0165 QByteArray FileHandler::readDataFile(const QUrl& url_, bool quiet_) { 0166 FileRef f(url_, quiet_); 0167 if(!f.isValid()) { 0168 return QByteArray(); 0169 } 0170 0171 f.open(quiet_); 0172 return f.file()->readAll(); 0173 } 0174 0175 // TODO: really, this should be decoupled from the writeBackupFile() function 0176 // but every other function that calls it would need to be updated 0177 bool FileHandler::queryExists(const QUrl& url_) { 0178 if(url_.isEmpty() || !QFile::exists(url_.toLocalFile())) { 0179 return true; 0180 } 0181 0182 // no need to check if we're actually overwriting the current url 0183 // the TellicoImporter forces the write 0184 GUI::CursorSaver cs(Qt::ArrowCursor); 0185 QString str = i18n("A file named \"%1\" already exists. " 0186 "Are you sure you want to overwrite it?", url_.fileName()); 0187 int want_continue = KMessageBox::warningContinueCancel(GUI::Proxy::widget(), str, 0188 i18n("Overwrite File?"), 0189 KStandardGuiItem::overwrite()); 0190 0191 if(want_continue == KMessageBox::Cancel) { 0192 return false; 0193 } 0194 return writeBackupFile(url_); 0195 } 0196 0197 bool FileHandler::writeBackupFile(const QUrl& url_) { 0198 bool success = true; 0199 if(url_.isLocalFile()) { 0200 success = KBackup::simpleBackupFile(url_.toLocalFile()); 0201 } else { 0202 QUrl backup(url_); 0203 backup.setPath(backup.toLocalFile() + QLatin1Char('~')); 0204 KIO::DeleteJob* delJob = KIO::del(backup); 0205 KJobWidgets::setWindow(delJob, GUI::Proxy::widget()); 0206 delJob->exec(); // might fail if backup doesn't exist, that's ok 0207 KIO::FileCopyJob* job = KIO::file_copy(url_, backup, -1, KIO::Overwrite); 0208 KJobWidgets::setWindow(job, GUI::Proxy::widget()); 0209 success = job->exec(); 0210 } 0211 if(!success) { 0212 GUI::Proxy::sorry(i18n(errorWrite, url_.fileName() + QLatin1Char('~'))); 0213 } 0214 return success; 0215 } 0216 0217 bool FileHandler::writeTextURL(const QUrl& url_, const QString& text_, bool encodeUTF8_, bool force_, bool quiet_) { 0218 if((!force_ && !queryExists(url_)) || text_.isNull()) { 0219 return false; 0220 } 0221 0222 if(url_.isLocalFile()) { 0223 QSaveFile f(url_.toLocalFile()); 0224 f.open(QIODevice::WriteOnly); 0225 if(f.error() != QFile::NoError) { 0226 if(!quiet_) { 0227 GUI::Proxy::sorry(i18n(errorWrite, url_.fileName())); 0228 } 0229 return false; 0230 } 0231 return FileHandler::writeTextFile(f, text_, encodeUTF8_); 0232 } 0233 0234 // save to remote file 0235 QTemporaryFile tempfile; 0236 tempfile.open(); 0237 QSaveFile f(tempfile.fileName()); 0238 f.open(QIODevice::WriteOnly); 0239 if(f.error() != QFile::NoError) { 0240 tempfile.remove(); 0241 if(!quiet_) { 0242 GUI::Proxy::sorry(i18n(errorWrite, url_.fileName())); 0243 } 0244 return false; 0245 } 0246 0247 bool success = FileHandler::writeTextFile(f, text_, encodeUTF8_); 0248 if(success) { 0249 KIO::Job* job = KIO::file_copy(QUrl::fromLocalFile(tempfile.fileName()), url_, -1, KIO::Overwrite); 0250 KJobWidgets::setWindow(job, GUI::Proxy::widget()); 0251 success = job->exec(); 0252 if(!success && !quiet_) { 0253 GUI::Proxy::sorry(i18n(errorUpload, url_.fileName())); 0254 } 0255 } 0256 tempfile.remove(); 0257 0258 return success; 0259 } 0260 0261 bool FileHandler::writeTextFile(QSaveFile& file_, const QString& text_, bool encodeUTF8_) { 0262 QTextStream ts(&file_); 0263 if(encodeUTF8_) { 0264 ts.setCodec("UTF-8"); 0265 } 0266 // KDE Bug 380832. If string is longer than MAX_TEXT_CHUNK_WRITE_SIZE characters, split into chunks. 0267 for(int i = 0; i < text_.length(); i += MAX_TEXT_CHUNK_WRITE_SIZE) { 0268 ts << text_.midRef(i, MAX_TEXT_CHUNK_WRITE_SIZE); 0269 } 0270 file_.flush(); 0271 const bool success = file_.commit(); 0272 if(!success) { 0273 myLog() << "Failed to write text file:" << file_.error(); 0274 } 0275 return success; 0276 } 0277 0278 bool FileHandler::writeDataURL(const QUrl& url_, const QByteArray& data_, bool force_, bool quiet_) { 0279 if(!force_ && !queryExists(url_)) { 0280 return false; 0281 } 0282 0283 if(url_.isLocalFile()) { 0284 QSaveFile f(url_.toLocalFile()); 0285 f.open(QIODevice::WriteOnly); 0286 if(f.error() != QFile::NoError) { 0287 if(!quiet_) { 0288 GUI::Proxy::sorry(i18n(errorWrite, url_.fileName())); 0289 } 0290 return false; 0291 } 0292 return FileHandler::writeDataFile(f, data_); 0293 } 0294 0295 // save to remote file 0296 QTemporaryFile tempfile; 0297 tempfile.open(); 0298 QSaveFile f(tempfile.fileName()); 0299 f.open(QIODevice::WriteOnly); 0300 if(f.error() != QFile::NoError) { 0301 if(!quiet_) { 0302 GUI::Proxy::sorry(i18n(errorWrite, url_.fileName())); 0303 } 0304 return false; 0305 } 0306 0307 bool success = FileHandler::writeDataFile(f, data_); 0308 if(success) { 0309 KIO::Job* job = KIO::file_copy(QUrl::fromLocalFile(tempfile.fileName()), url_, -1, KIO::Overwrite); 0310 KJobWidgets::setWindow(job, GUI::Proxy::widget()); 0311 success = job->exec(); 0312 if(!success && !quiet_) { 0313 GUI::Proxy::sorry(i18n(errorUpload, url_.fileName())); 0314 } 0315 } 0316 tempfile.remove(); 0317 0318 return success; 0319 } 0320 0321 bool FileHandler::writeDataFile(QSaveFile& file_, const QByteArray& data_) { 0322 QDataStream s(&file_); 0323 s.writeRawData(data_.data(), data_.size()); 0324 file_.flush(); 0325 const bool success = file_.commit(); 0326 if(!success) { 0327 myDebug() << "Failed to write data file:" << file_.error(); 0328 } 0329 return success; 0330 }