1 /***************************************************************************
2 Copyright 2006 David Nolden <david.nolden.kdevelop@art-master.de>
3 ***************************************************************************/
5 /***************************************************************************
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
12 ***************************************************************************/
15 #include "messagehistorymanager.h"
21 #include <klockfile.h>
22 #include <kio/netaccess.h>
24 #include "lib/network/serialization.h"
25 #include "lib/network/messageserialization.h"
27 #include "kdevteamwork_messages.h"
28 #include "kdevteamwork_user.h"
29 #include "teamworkfoldermanager.h"
32 #include <boost/serialization/vector.hpp>
33 #include <boost/serialization/map.hpp>
34 #include <boost/archive/xml_iarchive.hpp>
35 #include <boost/archive/xml_oarchive.hpp>
36 #include <boost/archive/binary_iarchive.hpp>
37 #include <boost/archive/binary_oarchive.hpp>
38 #include <boost/serialization/level.hpp>
41 #include "kdevteamwork_client.h"
44 using namespace Teamwork
;
46 struct FileEntryHeader
{
49 FileEntryHeader( int _messages
=0 ) : version(1), messages(_messages
) {
52 void read( istream
& str
) {
53 str
>> version
>> messages
;
55 void write( ostream
& str
) {
56 str
<< version
<< endl
<< messages
<< endl
;
60 HistoryMessageDesc::HistoryMessageDesc() {
63 HistoryMessageDesc::HistoryMessageDesc( const HistoryMessagePointer
& msg
)
66 HistoryMessagePointer::Locked l
= msg
;
68 isIncoming
= l
->info().isIncoming();
70 UserPointer::Locked lu
= userFromSession( l
->info().session() );
72 user
= lu
->identity();
79 MessageHistoryManager::MessageHistoryManager( Teamwork::LoggerPointer logger
) : m_logger(logger
) {
80 m_pendingTimer
= new QTimer();
81 m_pendingTimer
->start( 1000 );
82 m_lockFile
= new KLockFile( lockFileName() );
83 connect( m_pendingTimer
, SIGNAL( timeout() ), this, SLOT( writePending() ) );
86 MessageHistoryManager::~MessageHistoryManager() {
89 void MessageHistoryManager::addMessage( KDevTeamworkTextMessage
* msg
) {
90 HistoryMessageDesc
m( msg
);
92 emit
newMessage( msg
);
95 QList
< HistoryMessagePointer
> MessageHistoryManager::getMessages( const KDevTeamworkClientPointer
& client
, const QDate
& from
, const QDate
& until
, UserSet users
) {
97 QStringList interestingFiles
;
98 QList
< HistoryMessageDesc
> ret
;
100 KUrl kdir
= directory();
102 if( !kdir
.isLocalFile() )
103 throw QString( "directory \"%1\" is not local" ).arg( kdir
.prettyUrl() );
105 QDir
dir( kdir
.toLocalFile() );
106 if( !dir
.isReadable() ) throw QString( "directory \"%1\" is not readable" ).arg( dir
.path() );
108 QStringList files
= dir
.entryList( QDir::Files
);
110 for( QStringList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
111 QDate date
= QDate::fromString( *it
, Qt::ISODate
);
112 if( !date
.isValid() ) continue;
113 if( from
.isValid() ) {
114 if( until
.isValid() ) {
115 ///from "from" until "until"
116 if( date
>= from
&& date
<= until
)
117 interestingFiles
<< *it
;
119 ///from "from" until now
121 interestingFiles
<< *it
;
124 if( until
.isValid() ) {
125 ///from begin until "until"
127 interestingFiles
<< *it
;
130 interestingFiles
<< *it
;
135 for( QStringList::iterator it
= interestingFiles
.begin(); it
!= interestingFiles
.end(); ++it
) {
137 fileUrl
.addPath( *it
);
138 std::ifstream
f( fileUrl
.toLocalFile().toLatin1(), ios_base::binary
);
140 err() << "could not open" << ~fileUrl
.toLocalFile();
144 err() << "error while reading" << ~fileUrl
.toLocalFile();
148 FileEntryHeader header
;
151 //f.read( (char*)&header, sizeof( FileEntryHeader ) );
155 err() << "could not read a header from file \"" << ~*it
<< "\"";
158 out( Logger::Debug
) << "successfully read a header";
162 ///read the message from the archive
163 boost::archive::xml_iarchive
arch( f
);
164 QList
<HistoryMessageDesc
> messages
;
165 for( int a
= 0; a
< header
.messages
; a
++ ) {
166 HistoryMessageDesc msg
;
167 arch
>> boost::serialization::make_nvp( "message", msg
);
168 if( users
.empty() || users
.contains( msg
.user
) )
172 catch( std::exception
& exc
) {
173 err() << "error while deserializing from \"" << ~*it
<< "\":" << exc
.what();
180 catch( QString str
) {
181 err() << "error in MessageHistoryManager::getMessages:" << ~str
;
184 return fillMessageUsers( ret
, client
);
189 WaitError( const QString
& stri
) : str( stri
) {
193 void MessageHistoryManager::writePending()
197 KUrl dir
= directory();
198 if( !dir
.isLocalFile() ) throw QString( "the directory %1 is not local" ).arg( dir
.prettyUrl() );
200 m_pendingTimer
->start( 1000 );
201 QMap
< QDate
, QList
<HistoryMessageDesc
> > map
;
202 std::map
< Teamwork::UniqueMessageId
, HistoryGroupLocation
> insertions
;
204 for( QList
<HistoryMessageDesc
>::iterator it
= m_pending
.begin(); it
!= m_pending
.end(); ++it
) {
205 HistoryMessagePointer::Locked l
= it
->message
;
207 QDate
key( l
->creationTime().date() );
210 err() << "could not lock a message for storing it into the history, the message will be lost";
214 for( QMap
< QDate
, QList
<HistoryMessageDesc
> >::iterator it
= map
.begin(); it
!= map
.end(); ++it
) {
215 QDate
date( it
.key() );
217 if( !date
.isValid() ) {
218 err() << "tried to store" << it
->count() << "messages with invalid date";
222 if( !m_lockFile
->isLocked() ) {
223 switch( m_lockFile
->lock( KLockFile::NoBlockFlag
| KLockFile::ForceFlag
) ) {
224 case KLockFile::LockOK
:
226 case KLockFile::LockFail
:
227 throw WaitError( "locking the lockfile " + lockFileName() + " failed" );
229 case KLockFile::LockError
:
230 throw "an error occurred while locking the lockfile " + lockFileName();
232 case KLockFile::LockStale
:
233 throw WaitError( "the lockfile " + lockFileName() + " is stale" );
238 QString localFileName
= date
.toString( Qt::ISODate
);
239 file
.addPath( localFileName
);
240 std::string fileName
= ~file
.toLocalFile();
242 if( fileName
.empty() ) {
243 err() << "empty file-path for date" << ~date
.toString( Qt::ISODate
);
247 std::ofstream
f( fileName
.c_str(), ios_base::binary
| ios_base::app
);
250 err() << "could not open file \"" << fileName
<< "\" for read/write";
253 f
.seekp( 0, std::ios_base::end
);
255 FileEntryHeader
header( it
->count() );
257 HistoryGroupLocation location
;
258 location
.fileName
= ~localFileName
;
259 location
.offset
= f
.tellp();
262 //f.write( (char*)&header, sizeof( FileEntryHeader ) );
265 err() << "could not write header into file \"" << fileName
<< "\"";
270 ///Store the messages into the archive
271 boost::archive::xml_oarchive
arch( f
);
272 for( QList
<HistoryMessageDesc
>::iterator it2
= it
->begin(); it2
!= it
->end(); ++it2
) {
273 arch
<< boost::serialization::make_nvp( "message", *it2
);
275 HistoryMessagePointer::Locked l
= it2
->message
;
277 insertions
[ l
->info().uniqueId() ] = location
;
279 err() << "could not lock a message while storing it into the history";
283 catch( std::exception
& exc
) {
284 err() << "error while serializing into \"" << fileName
<< "\":" << exc
.what();
288 ///The index maps unique-message-ids to the files they can be found in(including offset to the FileEntryHeader)
289 std::map
<Teamwork::UniqueMessageId
, HistoryGroupLocation
> index
;
291 if( !insertions
.empty() ) {
295 catch( QString str
) {
296 out( Logger::Warning
) << "error in readIndex:" << str
;
299 index
.insert( insertions
.begin(), insertions
.end() );
307 catch( WaitError err
) {
308 out( Logger::Warning
) << "in MessageHistoryManager::writePending:" << err
.str
;
312 err() << "error in MessageHistoryManager::writePending:" << str
;
316 if( m_lockFile
->isLocked() )
317 m_lockFile
->unlock();
320 QString
MessageHistoryManager::lockFileName() {
321 KUrl file
= directory();
322 file
.addPath( ".lock" );
323 return file
.toLocalFile();
326 void MessageHistoryManager::readIndex( std::map
<Teamwork::UniqueMessageId
, HistoryGroupLocation
>& index
) throw( QString
) {
327 KUrl ind
= directory();
328 ind
.addPath( "index" );
329 if( !ind
.isLocalFile() ) throw QString( "the index-file is not local" );
331 std::string indexFile
= ~ind
.toLocalFile();
334 std::ifstream
f( indexFile
.c_str(), ios_base::binary
);
335 if( !f
.good() ) throw QString( "could not open index-file for reading" );
338 boost::archive::binary_iarchive
arch( f
);
341 catch( std::exception
& exc
) {
342 err() << "error while deserializing the index \"" << indexFile
<< "\":" << exc
.what();
343 throw QString( "error in readIndex" );
348 void MessageHistoryManager::writeIndex( const std::map
<Teamwork::UniqueMessageId
, HistoryGroupLocation
>& index
) throw( QString
) {
350 KUrl ind
= directory();
351 ind
.addPath( "index" );
352 if( !ind
.isLocalFile() ) throw QString( "the index-file is not local" );
354 std::string indexFile
= ~ind
.toLocalFile();
356 std::ofstream
f( indexFile
.c_str(), ios_base::binary
);
357 if( !f
.good() ) throw QString( "could not open index-file for writing" );
360 ///Store the messages into the archive
361 boost::archive::binary_oarchive
arch( f
);
364 catch( std::exception
& exc
) {
365 err() << "error while serializing the index \"" << indexFile
<< "\":" << exc
.what();
366 throw QString( "error in writeIndex" );
371 KUrl
MessageHistoryManager::directory() throw(QString
)
373 KUrl ul
= TeamworkFolderManager::teamworkAbsolute( "messages" );
374 TeamworkFolderManager::createTeamworkFolder();
376 if( !KIO::NetAccess::exists( ul
, KIO::NetAccess::SourceSide
, 0 ) )
377 KIO::NetAccess::mkdir( ul
, 0 );
379 if( !KIO::NetAccess::exists( ul
, KIO::NetAccess::SourceSide
, 0 ) )
380 throw QString( "could not create messages-directory " ) + ul
.toLocalFile();
385 void MessageHistoryManager::readMessages( const QString
& file
, uint offset
, QList
<HistoryMessageDesc
>& messages
) throw(QString
)
387 KUrl path
= directory();
388 path
.addPath( file
);
390 if( !path
.isLocalFile() ) throw QString( "message is not local" );
391 if( !KIO::NetAccess::exists( path
, KIO::NetAccess::SourceSide
, 0 ) ) throw QString( "index does not exist" );
393 std::ifstream
f( path
.toLocalFile().toLatin1(), ios_base::binary
);
395 if( !f
.good() || f
.eof() ) throw QString( "could not open file and seek to offset %1 in file %2" ).arg( offset
).arg( path
.toLocalFile() );
397 FileEntryHeader header
;
401 if( !f
.good() || f
.eof() )
402 throw QString( "could not read a header from file \"%1\"" ).arg( path
.toLocalFile() );
405 boost::archive::xml_iarchive
arch( f
);
406 for( int a
= 0; a
< header
.messages
; a
++ ) {
407 HistoryMessageDesc msg
;
408 arch
>> boost::serialization::make_nvp( "message", msg
);
412 catch( std::exception
& exc
) {
413 err() << "error while deserializing from \"" << ~path
.toLocalFile() << "\":" << exc
.what();
418 HistoryMessagePointer
MessageHistoryManager::getMessageFromId( Teamwork::UniqueMessageId id
, const KDevTeamworkClientPointer
& client
) {
419 std::map
<Teamwork::UniqueMessageId
, HistoryGroupLocation
> index
;
422 std::map
<Teamwork::UniqueMessageId
, HistoryGroupLocation
>::iterator it
= index
.find( id
);
423 if( it
!= index
.end() ) {
424 QList
<HistoryMessageDesc
> messages
;
425 readMessages( ~it
->second
.fileName
, it
->second
.offset
, messages
);
427 for( QList
<HistoryMessageDesc
>::iterator it
= messages
.begin(); it
!= messages
.end(); ++it
) {
428 HistoryMessagePointer::Locked l
= it
->message
;
430 if( l
->info().uniqueId() == id
) {
431 return fillMessageUser( *it
, client
);
436 return HistoryMessagePointer();
438 return HistoryMessagePointer();
441 catch( QString str
) {
442 err() << "error in messageFromId:" << ~str
;
443 return HistoryMessagePointer();
447 HistoryMessagePointer
MessageHistoryManager::fillMessageUser( const HistoryMessageDesc
& desc
, const KDevTeamworkClientPointer
& client
) {
448 HistoryMessagePointer::Locked lmsg
= desc
.message
;
449 KDevTeamworkClientPointer::Locked l
= client
;
451 err() << "could not lock KDevTeamworkClient";
452 return HistoryMessagePointer();
455 lmsg
->info().setUser( l
->getUser( desc
.user
) );
458 err() << "could not lock a message in fillMessageUsers";
461 return HistoryMessagePointer();
464 QList
<HistoryMessagePointer
> MessageHistoryManager::fillMessageUsers( const QList
<HistoryMessageDesc
>& messages
, const KDevTeamworkClientPointer
& client
) {
465 QList
<HistoryMessagePointer
> ret
;
466 KDevTeamworkClientPointer::Locked l
= client
;
469 foreach( const HistoryMessageDesc
& desc
, messages
) {
470 HistoryMessagePointer::Locked lmsg
= desc
.message
;
472 lmsg
->info().setUser( l
->getUser( desc
.user
) );
475 err() << "could not lock a message in fillMessageUsers";
479 err() << "could not lock teamwork-client";
485 Teamwork::LoggerPrinter
MessageHistoryManager::out( Teamwork::Logger::Level level
) {
486 Teamwork::LoggerPrinter
ret( m_logger
, level
);
487 ret
<< "in MessageHistoryManager: ";
492 Teamwork::LoggerPrinter
MessageHistoryManager::err() {
493 Teamwork::LoggerPrinter
ret( m_logger
, Teamwork::Logger::Error
);
494 ret
<< "Error in MessageHistoryManager: ";
498 BOOST_CLASS_IMPLEMENTATION(HistoryMessageDesc
, boost::serialization::object_serializable
)
500 #include "messagehistorymanager.moc"
502 // kate: space-indent on; indent-width 2; tab-width 2; replace-tabs on