File indexing completed on 2024-05-12 16:45:40
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 myDebug() << "can't download" << url_.toDisplayString(QUrl::RemoveQuery); 0061 QString s = Tellico::NetAccess::lastErrorString(); 0062 if(!s.isEmpty()) { 0063 myDebug() << s; 0064 } 0065 if(!quiet_) { 0066 GUI::Proxy::sorry(s.isEmpty() ? i18n(errorLoad, url_.fileName()) : s); 0067 } 0068 return; 0069 } 0070 0071 m_device = new QFile(m_filename); 0072 m_isValid = true; 0073 } 0074 0075 FileHandler::FileRef::~FileRef() { 0076 if(!m_filename.isEmpty()) { 0077 Tellico::NetAccess::removeTempFile(m_filename); 0078 } 0079 if(m_device) { 0080 m_device->close(); 0081 } 0082 delete m_device; 0083 m_device = nullptr; 0084 m_isValid = false; 0085 } 0086 0087 bool FileHandler::FileRef::open(bool quiet_) { 0088 if(!isValid()) { 0089 return false; 0090 } 0091 if(!m_device || !m_device->open(QIODevice::ReadOnly)) { 0092 if(!quiet_) { 0093 QUrl u = QUrl::fromLocalFile(fileName()); 0094 GUI::Proxy::sorry(i18n(errorLoad, u.fileName())); 0095 } 0096 delete m_device; 0097 m_device = nullptr; 0098 m_isValid = false; 0099 return false; 0100 } 0101 return true; 0102 } 0103 0104 FileHandler::FileRef* FileHandler::fileRef(const QUrl& url_, bool quiet_) { 0105 return new FileRef(url_, quiet_); 0106 } 0107 0108 QString FileHandler::readTextFile(const QUrl& url_, bool quiet_/*=false*/, bool useUTF8_ /*false*/) { 0109 FileRef f(url_, quiet_); 0110 if(!f.isValid()) { 0111 return QString(); 0112 } 0113 0114 if(f.open(quiet_)) { 0115 QTextStream stream(f.file()); 0116 if(useUTF8_) { 0117 stream.setCodec("UTF-8"); 0118 } 0119 return stream.readAll(); 0120 } 0121 return QString(); 0122 } 0123 0124 QString FileHandler::readXMLFile(const QUrl& url_, bool quiet_/*=false*/) { 0125 FileRef f(url_, quiet_); 0126 if(!f.isValid()) { 0127 return QString(); 0128 } 0129 0130 if(f.open(quiet_)) { 0131 return XMLHandler::readXMLData(f.file()->readAll()); 0132 } 0133 return QString(); 0134 } 0135 0136 QDomDocument FileHandler::readXMLDocument(const QUrl& url_, bool processNamespace_, bool quiet_) { 0137 FileRef f(url_, quiet_); 0138 if(!f.isValid()) { 0139 return QDomDocument(); 0140 } 0141 0142 QDomDocument doc; 0143 QString errorMsg; 0144 int errorLine, errorColumn; 0145 if(!f.open(quiet_)) { 0146 return QDomDocument(); 0147 } 0148 if(!doc.setContent(f.file(), processNamespace_, &errorMsg, &errorLine, &errorColumn)) { 0149 if(!quiet_) { 0150 QString details = i18n("There is an XML parsing error in line %1, column %2.", errorLine, errorColumn); 0151 details += QLatin1String("\n"); 0152 details += i18n("The error message from Qt is:"); 0153 details += QLatin1String("\n\t") + errorMsg; 0154 GUI::CursorSaver cs(Qt::ArrowCursor); 0155 if(GUI::Proxy::widget()) { 0156 KMessageBox::detailedSorry(GUI::Proxy::widget(), i18n(errorLoad, url_.fileName()), details); 0157 } 0158 } 0159 return QDomDocument(); 0160 } 0161 return doc; 0162 } 0163 0164 QByteArray FileHandler::readDataFile(const QUrl& url_, bool quiet_) { 0165 FileRef f(url_, quiet_); 0166 if(!f.isValid()) { 0167 return QByteArray(); 0168 } 0169 0170 f.open(quiet_); 0171 return f.file()->readAll(); 0172 } 0173 0174 // TODO: really, this should be decoupled from the writeBackupFile() function 0175 // but every other function that calls it would need to be updated 0176 bool FileHandler::queryExists(const QUrl& url_) { 0177 if(url_.isEmpty() || !QFile::exists(url_.toLocalFile())) { 0178 return true; 0179 } 0180 0181 // no need to check if we're actually overwriting the current url 0182 // the TellicoImporter forces the write 0183 GUI::CursorSaver cs(Qt::ArrowCursor); 0184 QString str = i18n("A file named \"%1\" already exists. " 0185 "Are you sure you want to overwrite it?", url_.fileName()); 0186 int want_continue = KMessageBox::warningContinueCancel(GUI::Proxy::widget(), str, 0187 i18n("Overwrite File?"), 0188 KStandardGuiItem::overwrite()); 0189 0190 if(want_continue == KMessageBox::Cancel) { 0191 return false; 0192 } 0193 return writeBackupFile(url_); 0194 } 0195 0196 bool FileHandler::writeBackupFile(const QUrl& url_) { 0197 bool success = true; 0198 if(url_.isLocalFile()) { 0199 success = KBackup::simpleBackupFile(url_.toLocalFile()); 0200 } else { 0201 QUrl backup(url_); 0202 backup.setPath(backup.toLocalFile() + QLatin1Char('~')); 0203 KIO::DeleteJob* delJob = KIO::del(backup); 0204 KJobWidgets::setWindow(delJob, GUI::Proxy::widget()); 0205 delJob->exec(); // might fail if backup doesn't exist, that's ok 0206 KIO::FileCopyJob* job = KIO::file_copy(url_, backup, -1, KIO::Overwrite); 0207 KJobWidgets::setWindow(job, GUI::Proxy::widget()); 0208 success = job->exec(); 0209 } 0210 if(!success) { 0211 GUI::Proxy::sorry(i18n(errorWrite, url_.fileName() + QLatin1Char('~'))); 0212 } 0213 return success; 0214 } 0215 0216 bool FileHandler::writeTextURL(const QUrl& url_, const QString& text_, bool encodeUTF8_, bool force_, bool quiet_) { 0217 if((!force_ && !queryExists(url_)) || text_.isNull()) { 0218 if(text_.isNull()) { 0219 myDebug() << "null string for" << url_; 0220 } 0221 return false; 0222 } 0223 0224 if(url_.isLocalFile()) { 0225 QSaveFile f(url_.toLocalFile()); 0226 f.open(QIODevice::WriteOnly); 0227 if(f.error() != QFile::NoError) { 0228 if(!quiet_) { 0229 GUI::Proxy::sorry(i18n(errorWrite, url_.fileName())); 0230 } 0231 return false; 0232 } 0233 return FileHandler::writeTextFile(f, text_, encodeUTF8_); 0234 } 0235 0236 // save to remote file 0237 QTemporaryFile tempfile; 0238 tempfile.open(); 0239 QSaveFile f(tempfile.fileName()); 0240 f.open(QIODevice::WriteOnly); 0241 if(f.error() != QFile::NoError) { 0242 tempfile.remove(); 0243 if(!quiet_) { 0244 GUI::Proxy::sorry(i18n(errorWrite, url_.fileName())); 0245 } 0246 return false; 0247 } 0248 0249 bool success = FileHandler::writeTextFile(f, text_, encodeUTF8_); 0250 if(success) { 0251 KIO::Job* job = KIO::file_copy(QUrl::fromLocalFile(tempfile.fileName()), url_, -1, KIO::Overwrite); 0252 KJobWidgets::setWindow(job, GUI::Proxy::widget()); 0253 success = job->exec(); 0254 if(!success && !quiet_) { 0255 GUI::Proxy::sorry(i18n(errorUpload, url_.fileName())); 0256 } 0257 } 0258 tempfile.remove(); 0259 0260 return success; 0261 } 0262 0263 bool FileHandler::writeTextFile(QSaveFile& file_, const QString& text_, bool encodeUTF8_) { 0264 QTextStream ts(&file_); 0265 if(encodeUTF8_) { 0266 ts.setCodec("UTF-8"); 0267 } 0268 // KDE Bug 380832. If string is longer than MAX_TEXT_CHUNK_WRITE_SIZE characters, split into chunks. 0269 for(int i = 0; i < text_.length(); i += MAX_TEXT_CHUNK_WRITE_SIZE) { 0270 ts << text_.midRef(i, MAX_TEXT_CHUNK_WRITE_SIZE); 0271 } 0272 file_.flush(); 0273 bool success = file_.commit(); 0274 #ifndef NDEBUG 0275 if(!success) { 0276 myDebug() << "error = " << file_.error(); 0277 } 0278 #endif 0279 return success; 0280 } 0281 0282 bool FileHandler::writeDataURL(const QUrl& url_, const QByteArray& data_, bool force_, bool quiet_) { 0283 if(!force_ && !queryExists(url_)) { 0284 return false; 0285 } 0286 0287 if(url_.isLocalFile()) { 0288 QSaveFile f(url_.toLocalFile()); 0289 f.open(QIODevice::WriteOnly); 0290 if(f.error() != QFile::NoError) { 0291 if(!quiet_) { 0292 GUI::Proxy::sorry(i18n(errorWrite, url_.fileName())); 0293 } 0294 return false; 0295 } 0296 return FileHandler::writeDataFile(f, data_); 0297 } 0298 0299 // save to remote file 0300 QTemporaryFile tempfile; 0301 tempfile.open(); 0302 QSaveFile f(tempfile.fileName()); 0303 f.open(QIODevice::WriteOnly); 0304 if(f.error() != QFile::NoError) { 0305 if(!quiet_) { 0306 GUI::Proxy::sorry(i18n(errorWrite, url_.fileName())); 0307 } 0308 return false; 0309 } 0310 0311 bool success = FileHandler::writeDataFile(f, data_); 0312 if(success) { 0313 KIO::Job* job = KIO::file_copy(QUrl::fromLocalFile(tempfile.fileName()), url_, -1, KIO::Overwrite); 0314 KJobWidgets::setWindow(job, GUI::Proxy::widget()); 0315 success = job->exec(); 0316 if(!success && !quiet_) { 0317 GUI::Proxy::sorry(i18n(errorUpload, url_.fileName())); 0318 } 0319 } 0320 tempfile.remove(); 0321 0322 return success; 0323 } 0324 0325 bool FileHandler::writeDataFile(QSaveFile& file_, const QByteArray& data_) { 0326 // myDebug() << "Writing to" << file_.fileName(); 0327 QDataStream s(&file_); 0328 s.writeRawData(data_.data(), data_.size()); 0329 file_.flush(); 0330 const bool success = file_.commit(); 0331 #ifndef NDEBUG 0332 if(!success) { 0333 myDebug() << "error = " << file_.error(); 0334 } 0335 #endif 0336 return success; 0337 }