00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include <QByteArray>
00016 #include <QFileInfo>
00017 #include <QTextStream>
00018
00019 #include <kconfig.h>
00020 #include <kstandarddirs.h>
00021 #include <kdebug.h>
00022 #include <klocale.h>
00023
00024 #include "articlewidget.h"
00025 #include "knarticlemanager.h"
00026 #include "kncollectionviewitem.h"
00027 #include "knhdrviewitem.h"
00028 #include "utilities.h"
00029 #include "knglobals.h"
00030 #include "knarticlefactory.h"
00031 #include "knfolder.h"
00032 #include "knarticlewindow.h"
00033 #include "knmainwidget.h"
00034
00035 using namespace KNode;
00036
00037
00038 KNFolder::KNFolder()
00039 : KNArticleCollection(0), i_d(-1), p_arentId(-1), i_ndexDirty(false), w_asOpen(true)
00040 {
00041 }
00042
00043
00044 KNFolder::KNFolder(int id, const QString &name, KNFolder *parent)
00045 : KNArticleCollection(parent), i_d(id), i_ndexDirty(false), w_asOpen(true)
00046 {
00047 QString fname=path()+QString("custom_%1").arg(i_d);
00048
00049 n_ame = name;
00050 m_boxFile.setFileName(fname+".mbox");
00051 i_ndexFile.setFileName(fname+".idx");
00052 i_nfoPath=fname+".info";
00053
00054 p_arentId=parent?parent->id():-1;
00055
00056 if(i_ndexFile.exists())
00057 c_ount=i_ndexFile.size()/sizeof(DynData);
00058 else
00059 c_ount=0;
00060 }
00061
00062
00063 KNFolder::KNFolder(int id, const QString &name, const QString &prefix, KNFolder *parent)
00064 : KNArticleCollection(parent), i_d(id), i_ndexDirty(false), w_asOpen(true)
00065 {
00066 QString fname=path()+QString("%1_%2").arg(prefix).arg(i_d);
00067
00068 n_ame = name;
00069 m_boxFile.setFileName(fname+".mbox");
00070 i_ndexFile.setFileName(fname+".idx");
00071 i_nfoPath=fname+".info";
00072
00073 p_arentId=parent?parent->id():-1;
00074
00075 if(i_ndexFile.exists())
00076 c_ount=i_ndexFile.size()/sizeof(DynData);
00077 else
00078 c_ount=0;
00079 }
00080
00081
00082 KNFolder::~KNFolder()
00083 {
00084 closeFiles();
00085 }
00086
00087
00088 void KNFolder::updateListItem()
00089 {
00090 if(l_istItem) {
00091 l_istItem->setText(0, n_ame);
00092 if (!isRootFolder())
00093 l_istItem->setTotalCount( c_ount );
00094 }
00095 }
00096
00097
00098 QString KNFolder::path()
00099 {
00100 QString dir(KStandardDirs::locateLocal("data","knode/")+"folders/");
00101
00102
00103 return dir;
00104 }
00105
00106
00107 bool KNFolder::readInfo(const QString &infoPath)
00108 {
00109 if(infoPath.isEmpty())
00110 return false;
00111
00112 i_nfoPath=infoPath;
00113
00114 KConfig info(i_nfoPath, KConfig::SimpleConfig);
00115 KConfigGroup grp(&info, QString());
00116 if (!isRootFolder() && !isStandardFolder()) {
00117 n_ame=grp.readEntry("name");
00118 i_d=grp.readEntry("id", -1);
00119 p_arentId=grp.readEntry("parentId", -1);
00120 }
00121 w_asOpen=grp.readEntry("wasOpen", true);
00122
00123 if(i_d>-1) {
00124 QFileInfo fi(infoPath);
00125 QString fname = fi.absolutePath() + '/' + fi.baseName();
00126 closeFiles();
00127 clear();
00128
00129 m_boxFile.setFileName(fname+".mbox");
00130 i_ndexFile.setFileName(fname+".idx");
00131 c_ount=i_ndexFile.exists() ? (i_ndexFile.size()/sizeof(DynData)) : 0;
00132 }
00133
00134 return (i_d!=-1);
00135 }
00136
00137
00138 bool KNFolder::readInfo()
00139 {
00140 return readInfo(i_nfoPath);
00141 }
00142
00143
00144 void KNFolder::saveInfo()
00145 {
00146 if(!i_nfoPath.isEmpty()) {
00147 KConfig info(i_nfoPath, KConfig::SimpleConfig);
00148 KConfigGroup grp(&info, QString());
00149 if (!isRootFolder() && !isStandardFolder()) {
00150 grp.writeEntry("name", n_ame);
00151 grp.writeEntry("id", i_d);
00152 grp.writeEntry("parentId", p_arentId);
00153 }
00154 if(l_istItem)
00155 grp.writeEntry("wasOpen", l_istItem->isOpen());
00156 }
00157 }
00158
00159
00160 void KNFolder::setParent(KNCollection *p)
00161 {
00162 p_arent = p;
00163 p_arentId = p ? (static_cast<KNFolder*>(p))->id() : -1;
00164 }
00165
00166
00167 bool KNFolder::loadHdrs()
00168 {
00169 if(isLoaded()) {
00170 kDebug(5003) <<"KNFolder::loadHdrs() : already loaded";
00171 return true;
00172 }
00173
00174 if(!i_ndexFile.open(QIODevice::ReadOnly)) {
00175 kError(5003) <<"KNFolder::loadHdrs() : cannot open index-file!";
00176 closeFiles();
00177 return false;
00178 }
00179
00180 if(!m_boxFile.open(QIODevice::ReadOnly)) {
00181 kError(5003) <<"KNFolder::loadHdrs() : cannot open mbox-file!";
00182 closeFiles();
00183 return false;
00184 }
00185
00186 if(!resize(c_ount)) {
00187 closeFiles();
00188 return false;
00189 }
00190
00191 QByteArray tmp;
00192 KNLocalArticle *art;
00193 DynData dynamic;
00194 int pos1=0, pos2=0, cnt=0, byteCount;
00195
00196 knGlobals.top->setCursorBusy(true);
00197 knGlobals.setStatusMsg(i18n(" Loading folder..."));
00198 knGlobals.top->secureProcessEvents();
00199
00200 while(!i_ndexFile.atEnd()) {
00201
00202
00203 byteCount=i_ndexFile.read((char*)(&dynamic), sizeof(DynData));
00204 if(byteCount!=sizeof(DynData))
00205 if( i_ndexFile.error() == QFile::NoError ) {
00206 kWarning(5003) <<"KNFolder::loadHeaders() : found broken entry in index-file: Ignored!";
00207 continue;
00208 }
00209 else {
00210 kError(5003) <<"KNFolder::loadHeaders() : corrupted index-file, IO-error!";
00211 closeFiles();
00212 clear();
00213 knGlobals.top->setCursorBusy( false );
00214 return false;
00215 }
00216
00217 art=new KNLocalArticle(this);
00218
00219
00220 dynamic.getData(art);
00221
00222
00223 if ( !m_boxFile.seek( art->startOffset() ) ) {
00224 kError(5003) <<"KNFolder::loadHdrs() : cannot set mbox file-pointer!";
00225 closeFiles();
00226 clear();
00227 knGlobals.top->setCursorBusy( false );
00228 return false;
00229 }
00230 tmp = m_boxFile.readLine();
00231 if ( tmp.endsWith( '\n' ) )
00232 tmp.resize( tmp.length() - 1 );
00233 if(tmp.isEmpty()) {
00234 if( m_boxFile.error() == QFile::NoError ) {
00235 kWarning(5003) <<"found broken entry in mbox-file: Ignored!";
00236 delete art;
00237 continue;
00238 }
00239 else {
00240 kError(5003) <<"KNFolder::loadHdrs() : corrupted mbox-file, IO-error!";
00241 closeFiles();
00242 clear();
00243 knGlobals.top->setCursorBusy( false );
00244 return false;
00245 }
00246 }
00247
00248
00249 bool end=false;
00250 pos1 = tmp.indexOf( ' ' ) + 1;
00251 pos2 = tmp.indexOf( '\t', pos1 );
00252 if (pos2 == -1) {
00253 pos2=tmp.length();
00254 end=true;
00255 }
00256 art->subject()->from7BitString(tmp.mid(pos1, pos2-pos1));
00257
00258 if (!end) {
00259 pos1=pos2+1;
00260 pos2 = tmp.indexOf( '\t', pos1 );
00261 if (pos2 == -1) {
00262 pos2=tmp.length();
00263 end=true;
00264 }
00265 art->newsgroups()->from7BitString(tmp.mid(pos1, pos2-pos1));
00266 }
00267
00268 if (!end) {
00269 pos1=pos2+1;
00270 pos2 = tmp.indexOf( '\t', pos1 );
00271 if (pos2 == -1) {
00272 pos2=tmp.length();
00273 end=true;
00274 }
00275 art->to()->from7BitString(tmp.mid(pos1,pos2-pos1));
00276 }
00277
00278 if (!end) {
00279 pos1=pos2+1;
00280 pos2=tmp.length();
00281 art->lines()->from7BitString(tmp.mid(pos1,pos2-pos1));
00282 }
00283
00284 if(!append(art)) {
00285 kError(5003) <<"KNFolder::loadHdrs() : cannot append article!";
00286 delete art;
00287 clear();
00288 closeFiles();
00289
00290 knGlobals.setStatusMsg( QString() );
00291 knGlobals.top->setCursorBusy(false);
00292 return false;
00293 }
00294
00295 cnt++;
00296 }
00297
00298 closeFiles();
00299 setLastID();
00300 c_ount=cnt;
00301 updateListItem();
00302
00303 knGlobals.setStatusMsg( QString() );
00304 knGlobals.top->setCursorBusy(false);
00305
00306 return true;
00307 }
00308
00309
00310 bool KNFolder::unloadHdrs(bool force)
00311 {
00312 if(l_ockedArticles>0)
00313 return false;
00314
00315 if (!force && isNotUnloadable())
00316 return false;
00317
00318 KNLocalArticle *a;
00319 for(int idx=0; idx<length(); idx++) {
00320 a=at(idx);
00321 if (a->hasContent() && !knGlobals.articleManager()->unloadArticle(a, force))
00322 return false;
00323 }
00324 syncIndex();
00325 clear();
00326
00327 return true;
00328 }
00329
00330 bool KNFolder::loadArticle(KNLocalArticle *a)
00331 {
00332 if(a->hasContent())
00333 return true;
00334
00335 closeFiles();
00336 if(!m_boxFile.open(QIODevice::ReadOnly)) {
00337 kError(5003) <<"KNFolder::loadArticle(KNLocalArticle *a) : cannot open mbox file:"
00338 << m_boxFile.fileName();
00339 return false;
00340 }
00341
00342
00343 if ( !m_boxFile.seek( a->startOffset() ) ) {
00344 kError(5003) <<"KNFolder::loadArticle(KNLocalArticle *a) : cannot set mbox file-pointer!";
00345 closeFiles();
00346 return false;
00347 }
00348
00349
00350 m_boxFile.readLine();
00351
00352 unsigned int size = a->endOffset() - m_boxFile.pos() - 1;
00353 QByteArray buff;
00354 buff.resize( size + 10 );
00355 int readBytes=m_boxFile.read(buff.data(), size);
00356 closeFiles();
00357 if ( readBytes < (int)(size) && m_boxFile.error() != QFile::NoError ) {
00358 kError(5003) <<"KNFolder::loadArticle(KNLocalArticle *a) : corrupted mbox file, IO-error!";
00359 return false;
00360 }
00361
00362
00363 buff.resize( readBytes );
00364 a->setContent(buff);
00365 a->parse();
00366
00367 return true;
00368 }
00369
00370
00371 bool KNFolder::saveArticles( KNLocalArticle::List &l )
00372 {
00373 if(!isLoaded())
00374 return false;
00375
00376 if(!m_boxFile.open(QIODevice::WriteOnly | QIODevice::Append)) {
00377 kError(5003) <<"KNFolder::saveArticles() : cannot open mbox-file!";
00378 closeFiles();
00379 return false;
00380 }
00381
00382 int addCnt=0;
00383 bool ret=true;
00384 bool clear=false;
00385 QTextStream ts(&m_boxFile);
00386 ts.setCodec( "ISO 8859-1" );
00387
00388 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
00389
00390 clear=false;
00391 if ( (*it)->id() == -1 || (*it)->collection() != this ) {
00392 if ( (*it)->id() != -1 ) {
00393 KNFolder *oldFolder = static_cast<KNFolder*>( (*it)->collection() );
00394 if ( !(*it)->hasContent() )
00395 if( !( clear = oldFolder->loadArticle( (*it) ) ) ) {
00396 ret = false;
00397 continue;
00398 }
00399
00400 KNLocalArticle::List l;
00401 l.append( (*it) );
00402 oldFolder->removeArticles( l, false );
00403 }
00404 if ( !append( (*it) ) ) {
00405 kError(5003) <<"KNFolder::saveArticle(KNLocalArticle::List *l) : cannot append article!";
00406 ret = false;
00407 continue;
00408 (*it)->setCollection(0);
00409 }
00410 else {
00411 (*it)->setCollection(this);
00412 addCnt++;
00413 }
00414 }
00415
00416 if ( byId( (*it)->id() ) == (*it) ) {
00417
00418
00419 ts << "From aaa@aaa Mon Jan 01 00:00:00 1997\n";
00420 ts.flush();
00421 (*it)->setStartOffset( m_boxFile.pos() );
00422
00423
00424 ts << "X-KNode-Overview: ";
00425 ts << (*it)->subject()->as7BitString(false) << '\t';
00426
00427 KMime::Headers::Base* h;
00428 if( ( h = (*it)->newsgroups( false ) ) !=0 )
00429 ts << h->as7BitString(false);
00430 ts << '\t';
00431
00432 if( (h = (*it)->to( false ) ) != 0 )
00433 ts << h->as7BitString(false);
00434 ts << '\t';
00435
00436 ts << (*it)->lines()->as7BitString(false) << '\n';
00437
00438
00439 (*it)->toStream( ts );
00440 ts << "\n";
00441 ts.flush();
00442
00443 (*it)->setEndOffset( m_boxFile.pos() );
00444
00445
00446 ArticleWidget::articleChanged( (*it) );
00447 i_ndexDirty=true;
00448
00449 }
00450 else {
00451 kError(5003) <<"KNFolder::saveArticle() : article not in folder!";
00452 ret=false;
00453 }
00454
00455 if ( clear )
00456 (*it)->Content::clear();
00457 }
00458
00459 closeFiles();
00460 syncIndex();
00461
00462 if(addCnt>0) {
00463 c_ount=length();
00464 updateListItem();
00465 knGlobals.articleManager()->updateViewForCollection(this);
00466 }
00467
00468 return ret;
00469 }
00470
00471
00472 void KNFolder::removeArticles( KNLocalArticle::List &l, bool del )
00473 {
00474 if( !isLoaded() || l.isEmpty() )
00475 return;
00476
00477 int idx = 0, delCnt = 0, *positions;
00478 positions = new int[l.count()];
00479 KNLocalArticle *a = 0;
00480
00481 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it, ++idx ) {
00482 if ( (*it)->isLocked() )
00483 positions[idx] = -1;
00484 else
00485 positions[idx] = a_rticles.indexForId( (*it)->id() );
00486 }
00487
00488 for ( idx = 0; idx < (int)(l.count()); ++idx ) {
00489 if(positions[idx]==-1)
00490 continue;
00491
00492 a=at(positions[idx]);
00493
00494
00495 knGlobals.artFactory->deleteComposerForArticle(a);
00496 ArticleWindow::closeAllWindowsForArticle( a );
00497 ArticleWidget::articleRemoved( a );
00498 delete a->listItem();
00499
00500
00501 a_rticles.remove(positions[idx], del, false);
00502 delCnt++;
00503 if(!del)
00504 a->setId(-1);
00505 }
00506
00507 if(delCnt>0) {
00508 compact();
00509 c_ount-=delCnt;
00510 updateListItem();
00511 i_ndexDirty=true;
00512 }
00513 delete[] positions;
00514 }
00515
00516
00517 void KNFolder::deleteAll()
00518 {
00519 if(l_ockedArticles>0)
00520 return;
00521
00522 if (!unloadHdrs(true))
00523 return;
00524
00525 clear();
00526 c_ount=0;
00527 syncIndex(true);
00528 updateListItem();
00529 }
00530
00531
00532 void KNFolder::deleteFiles()
00533 {
00534 m_boxFile.remove();
00535 i_ndexFile.remove();
00536 QFile::remove(i_nfoPath);
00537 }
00538
00539
00540 void KNFolder::syncIndex(bool force)
00541 {
00542 if(!i_ndexDirty && !force)
00543 return;
00544
00545 if(!i_ndexFile.open(QIODevice::WriteOnly)) {
00546 kError(5003) <<"KNFolder::syncIndex(bool force) : cannot open index-file!";
00547 closeFiles();
00548 return;
00549 }
00550
00551 KNLocalArticle *a;
00552 DynData d;
00553 for(int idx=0; idx<length(); idx++) {
00554 a=at(idx);
00555 d.setData(a);
00556 i_ndexFile.write((char*)(&d), sizeof(DynData));
00557 }
00558 closeFiles();
00559
00560 i_ndexDirty=false;
00561 }
00562
00563
00564 void KNFolder::closeFiles()
00565 {
00566 if(m_boxFile.isOpen())
00567 m_boxFile.close();
00568 if(i_ndexFile.isOpen())
00569 i_ndexFile.close();
00570 }
00571
00572
00573
00574
00575
00576 void KNFolder::DynData::setData(KNLocalArticle *a)
00577 {
00578 id=a->id();
00579 so=a->startOffset();
00580 eo=a->endOffset();
00581 sId=a->serverId();
00582 ti=a->date()->dateTime().toTime_t();
00583
00584 flags[0]=a->doMail();
00585 flags[1]=a->mailed();
00586 flags[2]=a->doPost();
00587 flags[3]=a->posted();
00588 flags[4]=a->canceled();
00589 flags[5]=a->editDisabled();
00590 }
00591
00592
00593 void KNFolder::DynData::getData(KNLocalArticle *a)
00594 {
00595 a->setId(id);
00596 KDateTime dt;
00597 dt.setTime_t( ti );
00598 a->date()->setDateTime( dt );
00599 a->setStartOffset(so);
00600 a->setEndOffset(eo);
00601 a->setServerId(sId);
00602 a->setDoMail(flags[0]);
00603 a->setMailed(flags[1]);
00604 a->setDoPost(flags[2]);
00605 a->setPosted(flags[3]);
00606 a->setCanceled(flags[4]);
00607 a->setEditDisabled(flags[5]);
00608 }
00609