kfileio.cpp
00001 // kfileio.cpp 00002 // Author: Stefan Taferner <taferner@kde.org> 00003 // License: GPL 00004 00005 #ifdef HAVE_CONFIG_H 00006 #include <config.h> 00007 #endif 00008 00009 #include <kmessagebox.h> 00010 #include <kdebug.h> 00011 00012 #include <assert.h> 00013 #include <tqdir.h> 00014 00015 #include <klocale.h> 00016 #include <kstdguiitem.h> 00017 00018 #include <tqwidget.h> 00019 #include <tqfile.h> 00020 #include <tqfileinfo.h> 00021 #include <sys/stat.h> 00022 #include <sys/types.h> 00023 00024 #include <kdepimmacros.h> 00025 00026 namespace KPIM { 00027 00028 //----------------------------------------------------------------------------- 00029 static void msgDialog(const TQString &msg) 00030 { 00031 KMessageBox::sorry(0, msg, i18n("File I/O Error")); 00032 } 00033 00034 00035 //----------------------------------------------------------------------------- 00036 KDE_EXPORT TQCString kFileToString(const TQString &aFileName, bool aEnsureNL, bool aVerbose) 00037 { 00038 TQCString result; 00039 TQFileInfo info(aFileName); 00040 unsigned int readLen; 00041 unsigned int len = info.size(); 00042 TQFile file(aFileName); 00043 00044 //assert(aFileName!=0); 00045 if( aFileName.isEmpty() ) 00046 return ""; 00047 00048 if (!info.exists()) 00049 { 00050 if (aVerbose) 00051 msgDialog(i18n("The specified file does not exist:\n%1").arg(aFileName)); 00052 return TQCString(); 00053 } 00054 if (info.isDir()) 00055 { 00056 if (aVerbose) 00057 msgDialog(i18n("This is a folder and not a file:\n%1").arg(aFileName)); 00058 return TQCString(); 00059 } 00060 if (!info.isReadable()) 00061 { 00062 if (aVerbose) 00063 msgDialog(i18n("You do not have read permissions " 00064 "to the file:\n%1").arg(aFileName)); 00065 return TQCString(); 00066 } 00067 if (len <= 0) return TQCString(); 00068 00069 if (!file.open(IO_Raw|IO_ReadOnly)) 00070 { 00071 if (aVerbose) switch(file.status()) 00072 { 00073 case IO_ReadError: 00074 msgDialog(i18n("Could not read file:\n%1").arg(aFileName)); 00075 break; 00076 case IO_OpenError: 00077 msgDialog(i18n("Could not open file:\n%1").arg(aFileName)); 00078 break; 00079 default: 00080 msgDialog(i18n("Error while reading file:\n%1").arg(aFileName)); 00081 } 00082 return TQCString(); 00083 } 00084 00085 result.resize(len + (int)aEnsureNL + 1); 00086 readLen = file.readBlock(result.data(), len); 00087 if (aEnsureNL && result[len-1]!='\n') 00088 { 00089 result[len++] = '\n'; 00090 readLen++; 00091 } 00092 result[len] = '\0'; 00093 00094 if (readLen < len) 00095 { 00096 TQString msg = i18n("Could only read %1 bytes of %2.") 00097 .arg(readLen).arg(len); 00098 msgDialog(msg); 00099 return TQCString(); 00100 } 00101 00102 return result; 00103 } 00104 00105 //----------------------------------------------------------------------------- 00106 #if 0 // unused 00107 TQByteArray kFileToBytes(const TQString &aFileName, bool aVerbose) 00108 { 00109 TQByteArray result; 00110 TQFileInfo info(aFileName); 00111 unsigned int readLen; 00112 unsigned int len = info.size(); 00113 TQFile file(aFileName); 00114 00115 //assert(aFileName!=0); 00116 if( aFileName.isEmpty() ) 00117 return result; 00118 00119 if (!info.exists()) 00120 { 00121 if (aVerbose) 00122 msgDialog(i18n("The specified file does not exist:\n%1") 00123 .arg(aFileName)); 00124 return result; 00125 } 00126 if (info.isDir()) 00127 { 00128 if (aVerbose) 00129 msgDialog(i18n("This is a folder and not a file:\n%1") 00130 .arg(aFileName)); 00131 return result; 00132 } 00133 if (!info.isReadable()) 00134 { 00135 if (aVerbose) 00136 msgDialog(i18n("You do not have read permissions " 00137 "to the file:\n%1").arg(aFileName)); 00138 return result; 00139 } 00140 if (len <= 0) return result; 00141 00142 if (!file.open(IO_Raw|IO_ReadOnly)) 00143 { 00144 if (aVerbose) switch(file.status()) 00145 { 00146 case IO_ReadError: 00147 msgDialog(i18n("Could not read file:\n%1").arg(aFileName)); 00148 break; 00149 case IO_OpenError: 00150 msgDialog(i18n("Could not open file:\n%1").arg(aFileName)); 00151 break; 00152 default: 00153 msgDialog(i18n("Error while reading file:\n%1").arg(aFileName)); 00154 } 00155 return result; 00156 } 00157 00158 result.resize(len); 00159 readLen = file.readBlock(result.data(), len); 00160 kdDebug(5300) << TQString( "len %1" ).arg(len) << endl; 00161 00162 if (readLen < len) 00163 { 00164 TQString msg; 00165 msg = i18n("Could only read %1 bytes of %2.") 00166 .arg(readLen).arg(len); 00167 msgDialog(msg); 00168 return result; 00169 } 00170 00171 return result; 00172 } 00173 #endif 00174 00175 //----------------------------------------------------------------------------- 00176 KDE_EXPORT bool kBytesToFile(const char* aBuffer, int len, 00177 const TQString &aFileName, 00178 bool aAskIfExists, bool aBackup, bool aVerbose) 00179 { 00180 // TODO: use KSaveFile 00181 TQFile file(aFileName); 00182 int writeLen, rc; 00183 00184 //assert(aFileName!=0); 00185 if(aFileName.isEmpty()) 00186 return FALSE; 00187 00188 if (file.exists()) 00189 { 00190 if (aAskIfExists) 00191 { 00192 TQString str; 00193 str = i18n("File %1 exists.\nDo you want to replace it?") 00194 .arg(aFileName); 00195 rc = KMessageBox::warningContinueCancel(0, 00196 str, i18n("Save to File"), i18n("&Replace")); 00197 if (rc != KMessageBox::Continue) return FALSE; 00198 } 00199 if (aBackup) 00200 { 00201 // make a backup copy 00202 // TODO: use KSaveFile::backupFile() 00203 TQString bakName = aFileName; 00204 bakName += '~'; 00205 TQFile::remove(bakName); 00206 if( !TQDir::current().rename(aFileName, bakName) ) 00207 { 00208 // failed to rename file 00209 if (!aVerbose) return FALSE; 00210 rc = KMessageBox::warningContinueCancel(0, 00211 i18n("Failed to make a backup copy of %1.\nContinue anyway?") 00212 .arg(aFileName), 00213 i18n("Save to File"), KStdGuiItem::save() ); 00214 if (rc != KMessageBox::Continue) return FALSE; 00215 } 00216 } 00217 } 00218 00219 if (!file.open(IO_Raw|IO_WriteOnly|IO_Truncate)) 00220 { 00221 if (aVerbose) switch(file.status()) 00222 { 00223 case IO_WriteError: 00224 msgDialog(i18n("Could not write to file:\n%1").arg(aFileName)); 00225 break; 00226 case IO_OpenError: 00227 msgDialog(i18n("Could not open file for writing:\n%1") 00228 .arg(aFileName)); 00229 break; 00230 default: 00231 msgDialog(i18n("Error while writing file:\n%1").arg(aFileName)); 00232 } 00233 return FALSE; 00234 } 00235 00236 writeLen = file.writeBlock(aBuffer, len); 00237 00238 if (writeLen < 0) 00239 { 00240 if (aVerbose) 00241 msgDialog(i18n("Could not write to file:\n%1").arg(aFileName)); 00242 return FALSE; 00243 } 00244 else if (writeLen < len) 00245 { 00246 TQString msg = i18n("Could only write %1 bytes of %2.") 00247 .arg(writeLen).arg(len); 00248 if (aVerbose) 00249 msgDialog(msg); 00250 return FALSE; 00251 } 00252 00253 return TRUE; 00254 } 00255 00256 KDE_EXPORT bool kCStringToFile(const TQCString& aBuffer, const TQString &aFileName, 00257 bool aAskIfExists, bool aBackup, bool aVerbose) 00258 { 00259 return kBytesToFile(aBuffer, aBuffer.length(), aFileName, aAskIfExists, 00260 aBackup, aVerbose); 00261 } 00262 00263 KDE_EXPORT bool kByteArrayToFile(const TQByteArray& aBuffer, const TQString &aFileName, 00264 bool aAskIfExists, bool aBackup, bool aVerbose) 00265 { 00266 return kBytesToFile(aBuffer, aBuffer.size(), aFileName, aAskIfExists, 00267 aBackup, aVerbose); 00268 } 00269 00270 00271 TQString checkAndCorrectPermissionsIfPossible( const TQString &toCheck, 00272 const bool recursive, const bool wantItReadable, 00273 const bool wantItWritable ) 00274 { 00275 // First we have to find out which type the toCheck is. This can be 00276 // a directory (follow if recursive) or a file (check permissions). 00277 // Symlinks are followed as expected. 00278 TQFileInfo fiToCheck(toCheck); 00279 fiToCheck.setCaching(false); 00280 TQCString toCheckEnc = TQFile::encodeName(toCheck); 00281 TQString error; 00282 struct stat statbuffer; 00283 00284 if ( !fiToCheck.exists() ) { 00285 error.append( i18n("%1 does not exist") 00286 .arg(toCheck) + "\n"); 00287 } 00288 00289 // check the access bit of a folder. 00290 if ( fiToCheck.isDir() ) { 00291 if ( stat( toCheckEnc,&statbuffer ) != 0 ) { 00292 kdDebug() << "wantItA: Can't read perms of " << toCheck << endl; 00293 } 00294 TQDir g( toCheck ); 00295 if ( !g.isReadable() ) { 00296 if ( chmod( toCheckEnc, statbuffer.st_mode + S_IXUSR ) != 0 ) { 00297 error.append( i18n("%1 is not accessible and that is " 00298 "unchangeable.").arg(toCheck) + "\n"); 00299 } else { 00300 kdDebug() << "Changed access bit for " << toCheck << endl; 00301 } 00302 } 00303 } 00304 00305 // For each file or folder we can check if the file is readable 00306 // and writable, as requested. 00307 if ( fiToCheck.isFile() || fiToCheck.isDir() ) { 00308 00309 if ( !fiToCheck.isReadable() && wantItReadable ) { 00310 // Get the current permissions. No need to do anything with an 00311 // error, it will het added to errors anyhow, later on. 00312 if ( stat(toCheckEnc,&statbuffer) != 0 ) { 00313 kdDebug() << "wantItR: Can't read perms of " << toCheck << endl; 00314 } 00315 00316 // Lets try changing it. 00317 if ( chmod( toCheckEnc, statbuffer.st_mode + S_IRUSR ) != 0 ) { 00318 error.append( i18n("%1 is not readable and that is unchangeable.") 00319 .arg(toCheck) + "\n"); 00320 } else { 00321 kdDebug() << "Changed the read bit for " << toCheck << endl; 00322 } 00323 } 00324 00325 if ( !fiToCheck.isWritable() && wantItWritable ) { 00326 // Gets the current persmissions. Needed because it can be changed 00327 // curing previous operation. 00328 if (stat(toCheckEnc,&statbuffer) != 0) { 00329 kdDebug() << "wantItW: Can't read perms of " << toCheck << endl; 00330 } 00331 00332 // Lets try changing it. 00333 if ( chmod (toCheckEnc, statbuffer.st_mode + S_IWUSR ) != 0 ) { 00334 error.append( i18n("%1 is not writable and that is unchangeable.") 00335 .arg(toCheck) + "\n"); 00336 } else { 00337 kdDebug() << "Changed the write bit for " << toCheck << endl; 00338 } 00339 } 00340 } 00341 00342 // If it is a folder and recursive is true, then we check the contents of 00343 // the folder. 00344 if ( fiToCheck.isDir() && recursive ){ 00345 TQDir g(toCheck); 00346 // First check if the folder is readable for us. If not, we get 00347 // some ugly crashes. 00348 if ( !g.isReadable() ){ 00349 error.append(i18n("Folder %1 is inaccessible.").arg(toCheck) + "\n"); 00350 } else { 00351 const TQFileInfoList *list = g.entryInfoList(); 00352 TQFileInfoListIterator it( *list ); 00353 TQFileInfo *fi; 00354 while ((fi = it.current()) != 0) { 00355 TQString newToCheck = toCheck + "/" + fi->fileName(); 00356 TQFileInfo fiNewToCheck(newToCheck); 00357 if ( fi->fileName() != "." && fi->fileName() != ".." ) { 00358 error.append ( checkAndCorrectPermissionsIfPossible( newToCheck, 00359 recursive, wantItReadable, wantItWritable) ); 00360 } 00361 ++it; 00362 } 00363 } 00364 } 00365 return error; 00366 } 00367 00368 bool checkAndCorrectPermissionsIfPossibleWithErrorHandling( TQWidget *parent, 00369 const TQString &toCheck, const bool recursive, const bool wantItReadable, 00370 const bool wantItWritable ) 00371 { 00372 TQString error = checkAndCorrectPermissionsIfPossible(toCheck, recursive, 00373 wantItReadable, wantItWritable); 00374 // There is no KMessageBox with Retry, Cancel and Details. 00375 // so, I can't provide a functionality to recheck. So it now 00376 // it is just a warning. 00377 if ( !error.isEmpty() ) { 00378 kdDebug() << "checkPermissions found:" << error << endl; 00379 KMessageBox::detailedSorry(parent, 00380 i18n("Some files or folders do not have " 00381 "the right permissions, please correct them " 00382 "manually."), 00383 error, i18n("Permissions Check"), false); 00384 return false; 00385 } else { 00386 return true; 00387 } 00388 } 00389 00390 }