Factor out the shared parts of the agent action manager setup.
[kdepim.git] / knode / knfolder.cpp
blob0330e0b075e5c2819d77dfaf2b524b024c1768e3
1 /*
2 KNode, the KDE newsreader
3 Copyright (c) 1999-2006 the KNode authors.
4 See file AUTHORS for details
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 You should have received a copy of the GNU General Public License
11 along with this program; if not, write to the Free Software Foundation,
12 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
15 #include "knfolder.h"
17 #include "articlewidget.h"
18 #include "knarticlefactory.h"
19 #include "knarticlemanager.h"
20 #include "knarticlewindow.h"
21 #include "kncollectionviewitem.h"
22 #include "knfoldermanager.h"
23 #include "knglobals.h"
24 #include "knhdrviewitem.h"
25 #include "knmainwidget.h"
26 #include "utilities.h"
27 #include "utils/scoped_cursor_override.h"
29 #include <QFileInfo>
30 #include <kconfig.h>
31 #include <kstandarddirs.h>
32 #include <kdebug.h>
33 #include <klocale.h>
35 using namespace KNode;
36 using namespace KNode::Utilities;
39 KNFolder::KNFolder()
40 : KNArticleCollection( KNArticleCollection::Ptr() ),
41 i_d(-1), p_arentId(-1), i_ndexDirty(false), w_asOpen(true)
46 KNFolder::KNFolder( int id, const QString &name, KNFolder::Ptr parent )
47 : KNArticleCollection(parent), i_d(id), i_ndexDirty(false), w_asOpen(true)
49 QString fname=path()+QString("custom_%1").arg(i_d);
51 n_ame = name;
52 m_boxFile.setFileName(fname+".mbox");
53 i_ndexFile.setFileName(fname+".idx");
54 i_nfoPath=fname+".info";
56 p_arentId=parent?parent->id():-1;
58 if(i_ndexFile.exists())
59 c_ount=i_ndexFile.size()/sizeof(DynData);
60 else
61 c_ount=0;
65 KNFolder::KNFolder( int id, const QString &name, const QString &prefix, KNFolder::Ptr parent )
66 : KNArticleCollection(parent), i_d(id), i_ndexDirty(false), w_asOpen(true)
68 QString fname=path()+QString("%1_%2").arg(prefix).arg(i_d);
70 n_ame = name;
71 m_boxFile.setFileName(fname+".mbox");
72 i_ndexFile.setFileName(fname+".idx");
73 i_nfoPath=fname+".info";
75 p_arentId=parent?parent->id():-1;
77 if(i_ndexFile.exists())
78 c_ount=i_ndexFile.size()/sizeof(DynData);
79 else
80 c_ount=0;
84 KNFolder::~KNFolder()
86 closeFiles();
90 void KNFolder::updateListItem()
92 if(l_istItem) {
93 l_istItem->setLabelText( n_ame );
94 if (!isRootFolder())
95 l_istItem->setTotalCount( c_ount );
100 QString KNFolder::path()
102 QString dir( KStandardDirs::locateLocal( "data", "knode/folders/" ) );
103 /*if (dir.isNull())
104 KNHelper::displayInternalFileError();*/
105 return dir;
109 bool KNFolder::readInfo(const QString &infoPath)
111 if(infoPath.isEmpty())
112 return false;
114 i_nfoPath=infoPath;
116 KConfig info(i_nfoPath, KConfig::SimpleConfig);
117 KConfigGroup grp(&info, QString());
118 if (!isRootFolder() && !isStandardFolder()) {
119 n_ame=grp.readEntry("name");
120 i_d=grp.readEntry("id", -1);
121 p_arentId=grp.readEntry("parentId", -1);
123 w_asOpen=grp.readEntry("wasOpen", true);
125 if(i_d>-1) {
126 QFileInfo fi(infoPath);
127 QString fname = fi.absolutePath() + '/' + fi.baseName();
128 closeFiles();
129 clear();
131 m_boxFile.setFileName(fname+".mbox");
132 i_ndexFile.setFileName(fname+".idx");
133 c_ount=i_ndexFile.exists() ? (i_ndexFile.size()/sizeof(DynData)) : 0;
136 return (i_d!=-1);
140 bool KNFolder::readInfo()
142 return readInfo(i_nfoPath);
146 void KNFolder::writeConfig()
148 if(!i_nfoPath.isEmpty()) {
149 KConfig info(i_nfoPath, KConfig::SimpleConfig);
150 KConfigGroup grp(&info, QString());
151 if (!isRootFolder() && !isStandardFolder()) {
152 grp.writeEntry("name", n_ame);
153 grp.writeEntry("id", i_d);
154 grp.writeEntry("parentId", p_arentId);
156 if(l_istItem)
157 grp.writeEntry("wasOpen", l_istItem->isExpanded());
162 void KNFolder::setParent( KNCollection::Ptr p )
164 p_arent = p;
165 p_arentId = p ? ( boost::static_pointer_cast<KNFolder>( p ) )->id() : -1;
169 bool KNFolder::loadHdrs()
171 if(isLoaded()) {
172 kDebug(5003) <<"KNFolder::loadHdrs() : already loaded";
173 return true;
176 if(!i_ndexFile.open(QIODevice::ReadOnly)) {
177 kError(5003) <<"KNFolder::loadHdrs() : cannot open index-file!";
178 closeFiles();
179 return false;
182 if(!m_boxFile.open(QIODevice::ReadOnly)) {
183 kError(5003) <<"KNFolder::loadHdrs() : cannot open mbox-file!";
184 closeFiles();
185 return false;
188 QByteArray tmp;
189 KNLocalArticle::Ptr art;
190 DynData dynamic;
191 int pos1=0, pos2=0, cnt=0, byteCount;
193 ScopedCursorOverride cursor( Qt::WaitCursor );
194 knGlobals.setStatusMsg(i18n(" Loading folder..."));
195 knGlobals.top->secureProcessEvents();
197 while(!i_ndexFile.atEnd()) {
199 //read index-data
200 byteCount=i_ndexFile.read((char*)(&dynamic), sizeof(DynData));
201 if(byteCount!=sizeof(DynData)) {
202 if( i_ndexFile.error() == QFile::NoError ) {
203 kWarning(5003) <<"KNFolder::loadHeaders() : found broken entry in index-file: Ignored!";
204 continue;
206 else {
207 kError(5003) <<"KNFolder::loadHeaders() : corrupted index-file, IO-error!";
208 closeFiles();
209 clear();
210 return false;
214 art = KNLocalArticle::Ptr( new KNLocalArticle( thisFolderPtr() ) );
216 //set index-data
217 dynamic.getData(art);
219 //read overview
220 if ( !m_boxFile.seek( art->startOffset() ) ) {
221 kError(5003) <<"KNFolder::loadHdrs() : cannot set mbox file-pointer!";
222 closeFiles();
223 clear();
224 return false;
226 tmp = m_boxFile.readLine();
227 if ( tmp.endsWith( '\n' ) )
228 tmp.resize( tmp.length() - 1 );
229 if(tmp.isEmpty()) {
230 if( m_boxFile.error() == QFile::NoError ) {
231 kWarning(5003) <<"found broken entry in mbox-file: Ignored!";
232 continue;
234 else {
235 kError(5003) <<"KNFolder::loadHdrs() : corrupted mbox-file, IO-error!";
236 closeFiles();
237 clear();
238 return false;
242 //set overview
243 bool end=false;
244 pos1 = tmp.indexOf( ' ' ) + 1;
245 pos2 = tmp.indexOf( '\t', pos1 );
246 if (pos2 == -1) {
247 pos2=tmp.length();
248 end=true;
250 art->subject()->from7BitString(tmp.mid(pos1, pos2-pos1));
252 if (!end) {
253 pos1=pos2+1;
254 pos2 = tmp.indexOf( '\t', pos1 );
255 if (pos2 == -1) {
256 pos2=tmp.length();
257 end=true;
259 art->newsgroups()->from7BitString(tmp.mid(pos1, pos2-pos1));
262 if (!end) {
263 pos1=pos2+1;
264 pos2 = tmp.indexOf( '\t', pos1 );
265 if (pos2 == -1) {
266 pos2=tmp.length();
267 end=true;
269 art->to()->from7BitString(tmp.mid(pos1,pos2-pos1));
272 if (!end) {
273 pos1=pos2+1;
274 pos2=tmp.length();
275 art->lines()->from7BitString(tmp.mid(pos1,pos2-pos1));
278 append( art );
279 cnt++;
282 closeFiles();
283 setLastID();
284 c_ount=cnt;
285 updateListItem();
287 knGlobals.setStatusMsg( QString() );
289 return true;
293 bool KNFolder::unloadHdrs(bool force)
295 if ( lockedArticles() > 0 ) {
296 return false;
299 if (!force && isNotUnloadable())
300 return false;
302 KNLocalArticle::Ptr a;
303 for(int idx=0; idx<length(); idx++) {
304 a=at(idx);
305 if (a->hasContent() && !knGlobals.articleManager()->unloadArticle(a, force))
306 return false;
308 syncIndex();
309 clear();
311 return true;
314 bool KNFolder::loadArticle( KNLocalArticle::Ptr a )
316 if(a->hasContent())
317 return true;
319 closeFiles();
320 if(!m_boxFile.open(QIODevice::ReadOnly)) {
321 kError(5003) <<"KNFolder::loadArticle(KNLocalArticle *a) : cannot open mbox file:"
322 << m_boxFile.fileName();
323 return false;
326 //set file-pointer
327 if ( !m_boxFile.seek( a->startOffset() ) ) {
328 kError(5003) <<"KNFolder::loadArticle(KNLocalArticle *a) : cannot set mbox file-pointer!";
329 closeFiles();
330 return false;
333 //read content
334 m_boxFile.readLine(); //skip X-KNode-Overview
336 unsigned int size = a->endOffset() - m_boxFile.pos() - 1;
337 QByteArray buff;
338 buff.resize( size + 10 );
339 int readBytes=m_boxFile.read(buff.data(), size);
340 closeFiles();
341 if ( readBytes < (int)(size) && m_boxFile.error() != QFile::NoError ) { // cannot read file
342 kError(5003) <<"KNFolder::loadArticle(KNLocalArticle *a) : corrupted mbox file, IO-error!";
343 return false;
346 //set content
347 buff.resize( readBytes );
348 a->setContent(buff);
349 a->parse();
351 return true;
355 bool KNFolder::saveArticles( KNLocalArticle::List &l )
357 if(!isLoaded()) // loading should not be done here - keep the StorageManager in sync !!
358 return false;
360 if(!m_boxFile.open(QIODevice::WriteOnly | QIODevice::Append)) {
361 kError(5003) <<"KNFolder::saveArticles() : cannot open mbox-file!";
362 closeFiles();
363 return false;
366 int addCnt=0;
367 bool ret=true;
368 bool clear=false;
369 QTextStream ts(&m_boxFile);
370 ts.setCodec( "ISO 8859-1" );
372 for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
374 clear=false;
375 if ( (*it)->id() == -1 || (*it)->collection().get() != this ) {
376 if ( (*it)->id() != -1 ) {
377 KNFolder::Ptr oldFolder = boost::static_pointer_cast<KNFolder>( (*it)->collection() );
378 if ( !(*it)->hasContent() )
379 if( !( clear = oldFolder->loadArticle( (*it) ) ) ) {
380 ret = false;
381 continue;
384 KNLocalArticle::List l;
385 l.append( (*it) );
386 oldFolder->removeArticles( l, false );
388 append( (*it) );
389 (*it)->setCollection( thisFolderPtr() );
390 addCnt++;
393 if ( byId( (*it)->id() ) == (*it) ) {
395 //MBox
396 ts << "From aaa@aaa Mon Jan 01 00:00:00 1997\n";
397 ts.flush();
398 (*it)->setStartOffset( m_boxFile.pos() ); //save offset
400 //write overview information
401 ts << "X-KNode-Overview: ";
402 ts << (*it)->subject()->as7BitString(false) << '\t';
404 KMime::Headers::Base* h;
405 if( ( h = (*it)->newsgroups( false ) ) !=0 )
406 ts << h->as7BitString(false);
407 ts << '\t';
409 if( (h = (*it)->to( false ) ) != 0 )
410 ts << h->as7BitString(false);
411 ts << '\t';
413 ts << (*it)->lines()->as7BitString(false) << '\n';
415 //write article
416 (*it)->toStream( ts );
417 ts << "\n";
418 ts.flush();
420 (*it)->setEndOffset( m_boxFile.pos() ); //save offset
422 //update
423 ArticleWidget::articleChanged( (*it) );
424 i_ndexDirty=true;
427 else {
428 kError(5003) <<"KNFolder::saveArticle() : article not in folder!";
429 ret=false;
432 if ( clear )
433 (*it)->KNLocalArticle::Content::clear();
436 closeFiles();
437 syncIndex();
439 if(addCnt>0) {
440 c_ount=length();
441 updateListItem();
442 knGlobals.articleManager()->updateViewForCollection( thisFolderPtr() );
445 return ret;
449 void KNFolder::removeArticles( KNLocalArticle::List &l, bool del )
451 if( !isLoaded() || l.isEmpty() )
452 return;
454 int delCnt = 0;
455 for ( int idx = 0; idx < l.count(); ++idx ) {
456 KNLocalArticle::Ptr a = l[ idx ];
457 if ( a->isLocked() ) {
458 continue;
461 // check if this article belongs to this folder
462 a = byId( a->id() );
463 if ( !a ) {
464 continue;
467 //update
468 KNGlobals::self()->articleFactory()->deleteComposerForArticle(a);
469 ArticleWindow::closeAllWindowsForArticle( a );
470 ArticleWidget::articleRemoved( a );
471 delete a->listItem();
473 //delete article
474 remove( a );
475 delCnt++;
476 if(!del)
477 a->setId(-1);
480 if(delCnt>0) {
481 compact();
482 c_ount-=delCnt;
483 updateListItem();
484 i_ndexDirty=true;
489 void KNFolder::deleteAll()
491 if ( lockedArticles() > 0 ) {
492 return;
495 if (!unloadHdrs(true))
496 return;
498 clear();
499 c_ount=0;
500 syncIndex(true);
501 updateListItem();
505 void KNFolder::deleteFiles()
507 m_boxFile.remove();
508 i_ndexFile.remove();
509 QFile::remove(i_nfoPath);
513 void KNFolder::syncIndex(bool force)
515 if(!i_ndexDirty && !force)
516 return;
518 if(!i_ndexFile.open(QIODevice::WriteOnly)) {
519 kError(5003) <<"KNFolder::syncIndex(bool force) : cannot open index-file!";
520 closeFiles();
521 return;
524 KNLocalArticle::Ptr a;
525 DynData d;
526 for(int idx=0; idx<length(); idx++) {
527 a=at(idx);
528 d.setData(a);
529 i_ndexFile.write((char*)(&d), sizeof(DynData));
531 closeFiles();
533 i_ndexDirty=false;
537 void KNFolder::closeFiles()
539 if(m_boxFile.isOpen())
540 m_boxFile.close();
541 if(i_ndexFile.isOpen())
542 i_ndexFile.close();
546 //==============================================================================
549 void KNFolder::DynData::setData( KNLocalArticle::Ptr a )
551 id=a->id();
552 so=a->startOffset();
553 eo=a->endOffset();
554 sId=a->serverId();
555 ti=a->date()->dateTime().toTime_t();
557 flags[0]=a->doMail();
558 flags[1]=a->mailed();
559 flags[2]=a->doPost();
560 flags[3]=a->posted();
561 flags[4]=a->canceled();
562 flags[5]=a->editDisabled();
566 void KNFolder::DynData::getData( KNLocalArticle::Ptr a )
568 a->setId(id);
569 KDateTime dt;
570 dt.setTime_t( ti );
571 a->date()->setDateTime( dt );
572 a->setStartOffset(so);
573 a->setEndOffset(eo);
574 a->setServerId(sId);
575 a->setDoMail(flags[0]);
576 a->setMailed(flags[1]);
577 a->setDoPost(flags[2]);
578 a->setPosted(flags[3]);
579 a->setCanceled(flags[4]);
580 a->setEditDisabled(flags[5]);
584 KNFolder::Ptr KNFolder::thisFolderPtr()
586 return KNGlobals::self()->folderManager()->folder( id() );