diff --git a/kimap/acljobbase.cpp b/kimap/acljobbase.cpp index edb83add7..d2aec8827 100644 --- a/kimap/acljobbase.cpp +++ b/kimap/acljobbase.cpp @@ -1,100 +1,99 @@ /* Copyright (c) 2009 Andras Mantia This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "acljobbase.h" - -#include -#include - #include "acljobbase_p.h" #include "message_p.h" #include "session_p.h" +#include +#include + using namespace KIMAP; void AclJobBasePrivate::setIdentifier( const QByteArray &identifier ) { id = identifier; } QByteArray AclJobBasePrivate::identifier() const { return id; } bool AclJobBasePrivate::hasRightEnabled(Acl::Right right) { return rightList & right; } void AclJobBasePrivate::setRights(const QByteArray& rights) { switch ( rights[0] ) { case '+': modifier = AclJobBase::Add; break; case '-': modifier = AclJobBase::Remove; break; default: modifier = AclJobBase::Change; break; } rightList = Acl::rightsFromString(rights); } void AclJobBasePrivate::setRights(AclJobBase::AclModifier _modifier, Acl::Rights rights) { modifier = _modifier; rightList|= rights; } AclJobBase::AclJobBase( Session *session ) : Job( *new AclJobBasePrivate(session, i18n("AclJobBase")) ) { } AclJobBase::AclJobBase( JobPrivate &dd ) : Job(dd) { } AclJobBase::~AclJobBase() { } void AclJobBase::setMailBox( const QString &mailBox ) { Q_D(AclJobBase); d->mailBox = mailBox; } QString AclJobBase::mailBox() const { Q_D(const AclJobBase); return d->mailBox; } #include "acljobbase.moc" diff --git a/kimap/fetchjob.cpp b/kimap/fetchjob.cpp index 968f206d9..349f23239 100644 --- a/kimap/fetchjob.cpp +++ b/kimap/fetchjob.cpp @@ -1,515 +1,515 @@ /* Copyright (c) 2009 Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "fetchjob.h" #include #include #include "job_p.h" #include "message_p.h" #include "session_p.h" namespace KIMAP { class FetchJobPrivate : public JobPrivate { public: FetchJobPrivate( FetchJob *job, Session *session, const QString& name ) : JobPrivate( session, name ), q(job), uidBased(false) { } ~FetchJobPrivate() { } void parseBodyStructure( const QByteArray &structure, int &pos, KMime::Content *content ); void parsePart( const QByteArray &structure, int &pos, KMime::Content *content ); QByteArray parseString( const QByteArray &structure, int &pos ); QByteArray parseSentence( const QByteArray &structure, int &pos ); void skipLeadingSpaces( const QByteArray &structure, int &pos ); MessagePtr message(int id) { if ( !messages.contains(id) ) { messages[id] = MessagePtr(new KMime::Message); } return messages[id]; } ContentPtr part(int id, QByteArray partName) { if ( !parts[id].contains(partName) ) { parts[id][partName] = ContentPtr(new KMime::Content); } return parts[id][partName]; } void emitPendings() { if ( pendingUids.isEmpty() ) { return; } if ( !pendingParts.isEmpty() ) { emit q->partsReceived( selectedMailBox, pendingUids, pendingParts ); } else if ( !pendingSizes.isEmpty() || !pendingFlags.isEmpty() ) { emit q->headersReceived( selectedMailBox, pendingUids, pendingSizes, pendingFlags, pendingMessages ); } else { emit q->messagesReceived( selectedMailBox, pendingUids, pendingMessages ); } pendingUids.clear(); pendingMessages.clear(); pendingParts.clear(); pendingSizes.clear(); pendingFlags.clear(); } FetchJob * const q; ImapSet set; bool uidBased; FetchJob::FetchScope scope; QString selectedMailBox; QMap messages; QMap parts; QMap flags; QMap sizes; QMap uids; QTimer emitPendingsTimer; QMap pendingMessages; QMap pendingParts; QMap pendingFlags; QMap pendingSizes; QMap pendingUids; }; } using namespace KIMAP; FetchJob::FetchJob( Session *session ) : Job( *new FetchJobPrivate(this, session, i18n("Fetch")) ) { Q_D(FetchJob); d->scope.mode = FetchScope::Content; connect( &d->emitPendingsTimer, SIGNAL( timeout() ), this, SLOT( emitPendings() ) ); } FetchJob::~FetchJob() { } void FetchJob::setSequenceSet( const ImapSet &set ) { Q_D(FetchJob); d->set = set; } ImapSet FetchJob::sequenceSet() const { Q_D(const FetchJob); return d->set; } void FetchJob::setUidBased(bool uidBased) { Q_D(FetchJob); d->uidBased = uidBased; } bool FetchJob::isUidBased() const { Q_D(const FetchJob); return d->uidBased; } void FetchJob::setScope( const FetchScope &scope ) { Q_D(FetchJob); d->scope = scope; } FetchJob::FetchScope FetchJob::scope() const { Q_D(const FetchJob); return d->scope; } QMap FetchJob::messages() const { Q_D(const FetchJob); return d->messages; } QMap FetchJob::parts() const { Q_D(const FetchJob); return d->parts; } QMap FetchJob::flags() const { Q_D(const FetchJob); return d->flags; } QMap FetchJob::sizes() const { Q_D(const FetchJob); return d->sizes; } QMap FetchJob::uids() const { Q_D(const FetchJob); return d->uids; } void FetchJob::doStart() { Q_D(FetchJob); QByteArray parameters = d->set.toImapSequenceSet()+' '; switch ( d->scope.mode ) { case FetchScope::Headers: if ( d->scope.parts.isEmpty() ) { parameters+="(RFC822.SIZE INTERNALDATE BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT)] FLAGS UID)"; } else { parameters+='('; foreach ( const QByteArray &part, d->scope.parts ) { parameters+="BODY["+part+".MIME] "; } parameters+="UID)"; } break; case FetchScope::Flags: parameters+="(FLAGS UID)"; break; case FetchScope::Structure: parameters+="(BODYSTRUCTURE UID)"; break; case FetchScope::Content: if ( d->scope.parts.isEmpty() ) { parameters+="(BODY[] UID)"; } else { parameters+='('; foreach ( const QByteArray &part, d->scope.parts ) { parameters+="BODY["+part+"] "; } parameters+="UID)"; } break; } QByteArray command = "FETCH"; if ( d->uidBased ) { command = "UID "+command; } d->emitPendingsTimer.start( 100 ); d->selectedMailBox = d->sessionInternal()->selectedMailBox(); d->tag = d->sessionInternal()->sendCommand( command, parameters ); } void FetchJob::handleResponse( const Message &response ) { Q_D(FetchJob); // We can predict it'll be handled by handleErrorReplies() so stop // the timer now so that result() will really be the last emitted signal. if ( !response.content.isEmpty() && response.content.first().toString() == d->tag ) { d->emitPendingsTimer.stop(); d->emitPendings(); } if (handleErrorReplies(response) == NotHandled ) { if ( response.content.size() == 4 && response.content[2].toString()=="FETCH" && response.content[3].type()==Message::Part::List ) { qint64 id = response.content[1].toString().toLongLong(); QList content = response.content[3].toList(); for ( QList::ConstIterator it = content.constBegin(); it!=content.constEnd(); ++it ) { QByteArray str = *it; ++it; if ( str=="UID" ) { d->uids[id] = it->toLongLong(); } else if ( str=="RFC822.SIZE" ) { d->sizes[id] = it->toLongLong(); } else if ( str=="INTERNALDATE" ) { d->message(id)->date()->setDateTime( KDateTime::fromString( *it, KDateTime::RFCDate ) ); } else if ( str=="FLAGS" ) { if ( (*it).startsWith('(') && (*it).endsWith(')') ) { QByteArray str = *it; str.chop(1); str.remove(0, 1); d->flags[id] = str.split(' '); } else { d->flags[id] << *it; } } else if ( str=="BODYSTRUCTURE" ) { int pos = 0; d->parseBodyStructure(*it, pos, d->message(id).get()); d->message(id)->assemble(); - } else if ( str.startsWith("BODY[") ) { + } else if ( str.startsWith( "BODY[") ) { //krazy:exclude=strings if ( !str.endsWith(']') ) { // BODY[ ... ] might have been split, skip until we find the ] while ( !(*it).endsWith(']') ) ++it; ++it; } int index; if ( (index=str.indexOf("HEADER"))>0 || (index=str.indexOf("MIME"))>0 ) { // headers if ( str[index-1]=='.' ) { QByteArray partId = str.mid( 5, index-6 ); d->part( id, partId )->setHead(*it); d->part( id, partId )->parse(); } else { d->message(id)->setHead(*it); d->message(id)->parse(); } } else { // full payload if ( str=="BODY[]" ) { d->message(id)->setContent( KMime::CRLFtoLF(*it) ); d->message(id)->parse(); d->pendingUids[id] = d->uids[id]; d->pendingMessages[id] = d->message(id); } else { QByteArray partId = str.mid( 5, str.size()-6 ); d->part( id, partId )->setBody(*it); d->part( id, partId )->parse(); d->pendingUids[id] = d->uids[id]; d->pendingParts[id] = d->parts[id]; } } } } if ( d->scope.mode == FetchScope::Headers ) { d->pendingUids[id] = d->uids[id]; d->pendingSizes[id] = d->sizes[id]; d->pendingFlags[id] = d->flags[id]; d->pendingMessages[id] = d->message(id); } } } } void FetchJobPrivate::parseBodyStructure(const QByteArray &structure, int &pos, KMime::Content *content) { skipLeadingSpaces(structure, pos); if ( structure[pos]!='(' ) { return; } pos++; if ( structure[pos]!='(' ) { // simple part pos--; parsePart( structure, pos, content ); } else { // multi part content->contentType()->setMimeType("MULTIPART/MIXED"); while ( posaddContent( child ); parseBodyStructure( structure, pos, child ); child->assemble(); } QByteArray subType = parseString( structure, pos ); content->contentType()->setMimeType( "MULTIPART/"+subType ); parseSentence( structure, pos ); // Ditch the parameters... FIXME: Read it to get charset and name QByteArray disposition = parseSentence( structure, pos ); if ( disposition.contains("INLINE") ) { content->contentDisposition()->setDisposition( KMime::Headers::CDinline ); } else if ( disposition.contains("ATTACHMENT") ) { content->contentDisposition()->setDisposition( KMime::Headers::CDattachment ); } parseSentence( structure, pos ); // Ditch the body language } // Consume what's left while ( poscontentType()->setMimeType( mainType+'/'+subType ); parseSentence( structure, pos ); // Ditch the parameters... FIXME: Read it to get charset and name parseString( structure, pos ); // ... and the id content->contentDescription()->from7BitString( parseString( structure, pos ) ); parseString( structure, pos ); // Ditch the encoding too parseString( structure, pos ); // ... and the size if ( mainType=="TEXT" ) { parseString( structure, pos ); // ... and the line count } QByteArray disposition = parseSentence( structure, pos ); if ( disposition.contains("INLINE") ) { content->contentDisposition()->setDisposition( KMime::Headers::CDinline ); } else if ( disposition.contains("ATTACHMENT") ) { content->contentDisposition()->setDisposition( KMime::Headers::CDattachment ); } // Consume what's left while ( pos This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "getmetadatajob.h" #include #include #include "metadatajobbase_p.h" #include "message_p.h" #include "session_p.h" #include "rfccodecs.h" namespace KIMAP { class GetMetaDataJobPrivate : public MetaDataJobBasePrivate { public: GetMetaDataJobPrivate( Session *session, const QString& name ) : MetaDataJobBasePrivate(session, name), maxSize(-1), depth("0") { } ~GetMetaDataJobPrivate() { } qint64 maxSize; QByteArray depth; QList entries; QList attributes; QMap > > metadata; // ^ mailbox ^ entry ^attribute ^ value }; } using namespace KIMAP; GetMetaDataJob::GetMetaDataJob( Session *session ) : MetaDataJobBase( *new GetMetaDataJobPrivate(session, i18n("GetMetaData")) ) { } GetMetaDataJob::~GetMetaDataJob() { } void GetMetaDataJob::doStart() { Q_D(GetMetaDataJob); QByteArray parameters; parameters = '\"' + KIMAP::encodeImapFolderName( d->mailBox.toUtf8() ) + "\" "; QByteArray command = "GETMETADATA"; if (d->serverCapability == Annotatemore) { d->m_name = i18n("GetAnnotation"); command = "GETANNOTATION"; if (d->entries.size() > 1) - parameters += "("; - Q_FOREACH(QByteArray entry, d->entries) { + parameters += '('; + Q_FOREACH(const QByteArray &entry, d->entries) { parameters += '\"' + entry + "\" "; } if (d->entries.size() > 1) parameters[parameters.length() -1 ] = ')'; else parameters.truncate(parameters.length() -1); parameters += ' '; if (d->attributes.size() > 1) - parameters += "("; - Q_FOREACH(QByteArray attribute, d->attributes) { + parameters += '('; + Q_FOREACH(const QByteArray &attribute, d->attributes) { parameters += '\"' + attribute + "\" "; } if (d->attributes.size() > 1) parameters[parameters.length() -1 ] = ')'; else parameters.truncate(parameters.length() -1); } else { if (d->depth != "0") { parameters += "(DEPTH " + d->depth; } if (d->maxSize != -1) { parameters += "(MAXSIZE " + QByteArray::number(d->maxSize) + ')'; } if (d->depth != "0") { parameters += " )"; } if (d->entries.size() > 1) - parameters += "("; - Q_FOREACH(QByteArray entry, d->entries) { + parameters += '('; + Q_FOREACH(const QByteArray &entry, d->entries) { parameters += '\"' + entry + "\" "; } if (d->entries.size() > 1) parameters[parameters.length() -1 ] = ')'; } if (d->entries.isEmpty()) { parameters += ')'; } d->tag = d->sessionInternal()->sendCommand( command, parameters ); kDebug() << "SENT: " << command << " " << parameters; } void GetMetaDataJob::handleResponse( const Message &response ) { Q_D(GetMetaDataJob); kDebug() << "GOT: " << response.toString(); //TODO: handle NO error messages having [METADATA MAXSIZE NNN], [METADATA TOOMANY], [METADATA NOPRIVATE] (see rfc5464) // or [ANNOTATEMORE TOOBIG], [ANNOTATEMORE TOOMANY] respectively if (handleErrorReplies(response) == NotHandled ) { if ( response.content.size() >= 4 ) { if (d->serverCapability == Annotatemore && response.content[1].toString() == "ANNOTATION" ) { QString mailBox = QString::fromUtf8( KIMAP::decodeImapFolderName( response.content[2].toString() ) ); int i = 3; while (i < response.content.size() - 1) { QByteArray entry = response.content[i].toString(); QList attributes = response.content[i + 1].toList(); int j = 0; while ( j < attributes.size() - 1) { d->metadata[mailBox][entry][attributes[j]] = attributes[j + 1]; j += 2; } i += 2; } } else if (d->serverCapability == Metadata && response.content[1].toString() == "METADATA" ) { QString mailBox = QString::fromUtf8( KIMAP::decodeImapFolderName( response.content[2].toString() ) ); QList entries = response.content[3].toList(); int i = 0; while ( i < entries.size() - 1) { d->metadata[mailBox][entries[i]][""] = entries[i + 1]; i += 2; } } } } } void GetMetaDataJob::addEntry(const QByteArray &entry, const QByteArray &attribute) { Q_D(GetMetaDataJob); if (d->serverCapability == Annotatemore && attribute.isNull()) qWarning() << "In ANNOTATEMORE mode and attribute must be specified with addEnty!"; d->entries.append(entry); d->attributes.append(attribute); } void GetMetaDataJob::setMaximumSize(qint64 size) { Q_D(GetMetaDataJob); d->maxSize = size; } void GetMetaDataJob::setDepth(Depth depth) { Q_D(GetMetaDataJob); switch (depth) { case OneLevel: - d->depth = "1"; + d->depth = "1"; //krazy:exclude=doublequote_chars break; case AllLevels: d->depth = "infinity"; break; default: - d->depth = "0"; + d->depth = "0"; //krazy:exclude=doublequote_chars } } QByteArray GetMetaDataJob::metaData(const QString &mailBox, const QByteArray &entry, const QByteArray &attribute) const { Q_D(const GetMetaDataJob); QByteArray attr = attribute; if (d->serverCapability == Metadata) attr = ""; QByteArray result; if (d->metadata.contains(mailBox)) { if (d->metadata[mailBox].contains(entry)) { result = d->metadata[mailBox][entry].value(attr); } } return result; } QMap > GetMetaDataJob::allMetaData(const QString &mailBox) const { Q_D(const GetMetaDataJob); return d->metadata[mailBox]; } #include "getmetadatajob.moc" diff --git a/kimap/idlejob.cpp b/kimap/idlejob.cpp index 7f9b6745b..d4003bab4 100644 --- a/kimap/idlejob.cpp +++ b/kimap/idlejob.cpp @@ -1,119 +1,119 @@ /* Copyright (c) 2009 Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "idlejob.h" #include #include #include "job_p.h" #include "message_p.h" #include "session_p.h" namespace KIMAP { class IdleJobPrivate : public JobPrivate { public: IdleJobPrivate( IdleJob *job, Session *session, const QString& name ) : JobPrivate( session, name ), q(job), messageCount( -1 ), recentCount( -1 ), lastMessageCount( -1 ), lastRecentCount( -1 ) { } ~IdleJobPrivate() { } IdleJob * const q; int messageCount; int recentCount; int lastMessageCount; int lastRecentCount; }; } using namespace KIMAP; IdleJob::IdleJob( Session *session ) - : Job( *new IdleJobPrivate(this, session, i18n("Idle")) ) + : Job( *new IdleJobPrivate(this, session, i18nc("name of the idle job", "Idle")) ) { } IdleJob::~IdleJob() { } void KIMAP::IdleJob::stop() { Q_D(IdleJob); d->sessionInternal()->sendData( "DONE" ); } void IdleJob::doStart() { Q_D(IdleJob); d->tag = d->sessionInternal()->sendCommand( "IDLE" ); } void IdleJob::handleResponse( const Message &response ) { Q_D(IdleJob); if (handleErrorReplies(response) == NotHandled ) { if ( response.content.size() > 0 && response.content[0].toString()=="+" ) { // Got the continuation all is fine return; } else if ( response.content[2].toString()=="EXISTS" ) { d->messageCount = response.content[1].toString().toInt(); } else if ( response.content[2].toString()=="RECENT" ) { d->recentCount = response.content[1].toString().toInt(); } if ( d->messageCount>=0 && d->recentCount>=0 ) { emit mailBoxStats(this, d->sessionInternal()->selectedMailBox(), d->messageCount, d->recentCount); d->lastMessageCount = d->messageCount; d->lastRecentCount = d->recentCount; d->messageCount = -1; d->recentCount = -1; } } } QString KIMAP::IdleJob::lastMailBox() const { Q_D(const IdleJob); return d->sessionInternal()->selectedMailBox(); } int KIMAP::IdleJob::lastMessageCount() const { Q_D(const IdleJob); return d->lastMessageCount; } int KIMAP::IdleJob::lastRecentCount() const { Q_D(const IdleJob); return d->lastRecentCount; } #include "idlejob.moc" diff --git a/kimap/imapset.cpp b/kimap/imapset.cpp index 1399d3cab..502c94fbc 100644 --- a/kimap/imapset.cpp +++ b/kimap/imapset.cpp @@ -1,244 +1,244 @@ /* Copyright (c) 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "imapset.h" #include using namespace KIMAP; class ImapInterval::Private : public QSharedData { public: Private() : QSharedData(), begin( 0 ), end( 0 ) {} Private( const Private &other ) : QSharedData( other ) { begin = other.begin; end = other.end; } Id begin; Id end; }; class ImapSet::Private : public QSharedData { public: Private() : QSharedData() {} Private( const Private &other ) : QSharedData( other ) { } ImapInterval::List intervals; }; ImapInterval::ImapInterval() : d( new Private ) { } ImapInterval::ImapInterval(const ImapInterval & other) : d( other.d ) { } ImapInterval::ImapInterval(Id begin, Id end) : d( new Private ) { d->begin = begin; d->end = end; } ImapInterval::~ ImapInterval() { } ImapInterval& ImapInterval::operator =(const ImapInterval & other) { if ( this != & other ) d = other.d; return *this; } bool ImapInterval::operator ==(const ImapInterval & other) const { return ( d->begin == other.d->begin && d->end == other.d->end ); } ImapInterval::Id ImapInterval::size() const { if ( !d->begin && !d->end ) return 0; return d->end - d->begin + 1; } bool ImapInterval::hasDefinedBegin() const { return d->begin != 0; } ImapInterval::Id ImapInterval::begin() const { return d->begin; } bool ImapInterval::hasDefinedEnd() const { return d->end != 0; } ImapInterval::Id ImapInterval::end() const { if ( hasDefinedEnd() ) return d->end; return 0xFFFFFFFF; // should be INT_MAX, but where is that defined again? } void ImapInterval::setBegin(Id value) { Q_ASSERT( value >= 0 ); Q_ASSERT( value <= d->end || !hasDefinedEnd() ); d->begin = value; } void ImapInterval::setEnd(Id value) { Q_ASSERT( value >= 0 ); Q_ASSERT( value >= d->begin || !hasDefinedBegin() ); d->end = value; } QByteArray ImapInterval::toImapSequence() const { if ( size() == 0 ) return QByteArray(); if ( size() == 1 ) return QByteArray::number( d->begin ); QByteArray rv; rv += QByteArray::number( d->begin ) + ':'; if ( hasDefinedEnd() ) rv += QByteArray::number( d->end ); else rv += '*'; return rv; } ImapSet::ImapSet() : d( new Private ) { } ImapSet::ImapSet( Id begin, Id end ) : d( new Private ) { add( ImapInterval( begin, end ) ); } ImapSet::ImapSet( Id value ) : d( new Private ) { add( QList() << value ); } ImapSet::ImapSet(const ImapSet & other) : d( other.d ) { } ImapSet::~ImapSet() { } ImapSet & ImapSet::operator =(const ImapSet & other) { if ( this != &other ) d = other.d; return *this; } void ImapSet::add(const QList & values) { QList vals = values; qSort( vals ); for( int i = 0; i < vals.count(); ++i ) { const int begin = vals[i]; Q_ASSERT( begin >= 0 ); if ( i == vals.count() - 1 ) { d->intervals << ImapInterval( begin, begin ); break; } do { ++i; Q_ASSERT( vals[i] >= 0 ); if ( vals[i] != (vals[i - 1] + 1) ) { --i; break; } } while ( i < vals.count() - 1 ); d->intervals << ImapInterval( begin, vals[i] ); } } void ImapSet::add(const ImapInterval & interval) { d->intervals << interval; } QByteArray ImapSet::toImapSequenceSet() const { QList rv; - foreach ( const ImapInterval interval, d->intervals ) { + foreach ( const ImapInterval &interval, d->intervals ) { rv << interval.toImapSequence(); } QByteArray result = rv.first(); QList::ConstIterator it = rv.constBegin(); ++it; for ( ; it != rv.constEnd(); ++it ) { result += ',' + (*it); } return result; } ImapInterval::List ImapSet::intervals() const { return d->intervals; } bool ImapSet::isEmpty() const { return d->intervals.isEmpty(); } QDebug& operator<<( QDebug &d, const ImapInterval &interval ) { d << interval.toImapSequence(); return d; } diff --git a/kimap/loginjob.cpp b/kimap/loginjob.cpp index 95922e964..8772bac8f 100644 --- a/kimap/loginjob.cpp +++ b/kimap/loginjob.cpp @@ -1,437 +1,437 @@ /* Copyright (c) 2009 Kevin Ottens Copyright (c) 2009 Andras Mantia This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "loginjob.h" #include #include #include #include "job_p.h" #include "message_p.h" #include "session_p.h" #include "rfccodecs.h" #include "common.h" extern "C" { #include } static sasl_callback_t callbacks[] = { { SASL_CB_ECHOPROMPT, NULL, NULL }, { SASL_CB_NOECHOPROMPT, NULL, NULL }, { SASL_CB_GETREALM, NULL, NULL }, { SASL_CB_USER, NULL, NULL }, { SASL_CB_AUTHNAME, NULL, NULL }, { SASL_CB_PASS, NULL, NULL }, { SASL_CB_CANON_USER, NULL, NULL }, { SASL_CB_LIST_END, NULL, NULL } }; namespace KIMAP { class LoginJobPrivate : public JobPrivate { public: enum AuthState { StartTls = 0, Capability, Login, Authenticate }; LoginJobPrivate( LoginJob *job, Session *session, const QString& name ) : JobPrivate(session, name), q(job), encryptionMode(LoginJob::Unencrypted), authState(Login), plainLoginDisabled(false) { conn = 0; client_interact = 0; } ~LoginJobPrivate() { } bool sasl_interact(); bool startAuthentication(); bool answerChallenge(const QByteArray &data); void sslResponse(bool response); LoginJob *q; QString userName; QString password; LoginJob::EncryptionMode encryptionMode; QString authMode; AuthState authState; QStringList capabilities; bool plainLoginDisabled; sasl_conn_t *conn; sasl_interact_t *client_interact; }; } using namespace KIMAP; bool LoginJobPrivate::sasl_interact() { kDebug() <<"sasl_interact"; sasl_interact_t *interact = client_interact; //some mechanisms do not require username && pass, so it doesn't need a popup //window for getting this info for ( ; interact->id != SASL_CB_LIST_END; interact++ ) { if ( interact->id == SASL_CB_AUTHNAME || interact->id == SASL_CB_PASS ) { //TODO: dialog for use name?? break; } } interact = client_interact; while( interact->id != SASL_CB_LIST_END ) { kDebug() <<"SASL_INTERACT id:" << interact->id; switch( interact->id ) { case SASL_CB_USER: case SASL_CB_AUTHNAME: kDebug() <<"SASL_CB_[USER|AUTHNAME]: '" << userName <<"'"; interact->result = strdup( userName.toUtf8() ); interact->len = strlen( (const char *) interact->result ); break; case SASL_CB_PASS: kDebug() <<"SASL_CB_PASS: [hidden]"; interact->result = strdup( password.toUtf8() ); interact->len = strlen( (const char *) interact->result ); break; default: interact->result = 0; interact->len = 0; break; } interact++; } return true; } LoginJob::LoginJob( Session *session ) : Job( *new LoginJobPrivate(this, session, i18n("Login")) ) { Q_D(LoginJob); connect(d->sessionInternal(), SIGNAL(encryptionNegotiationResult(bool)), this, SLOT(sslResponse(bool))); } LoginJob::~LoginJob() { } QString LoginJob::userName() const { Q_D(const LoginJob); return d->userName; } void LoginJob::setUserName( const QString &userName ) { Q_D(LoginJob); d->userName = userName; } QString LoginJob::password() const { Q_D(const LoginJob); return d->password; } void LoginJob::setPassword( const QString &password ) { Q_D(LoginJob); d->password = password; } void LoginJob::doStart() { Q_D(LoginJob); if (d->encryptionMode == SslV2 || d->encryptionMode == SslV3 || d->encryptionMode == SslV3_1 || d->encryptionMode == AnySslVersion) { KTcpSocket::SslVersion version = KTcpSocket::SslV2; if (d->encryptionMode == SslV3) version = KTcpSocket::SslV3; if (d->encryptionMode == SslV3_1) version = KTcpSocket::SslV3_1; if (d->encryptionMode == AnySslVersion) version = KTcpSocket::AnySslVersion; d->sessionInternal()->startSsl(version); } else if (d->encryptionMode == Unencrypted ) { if (d->authMode.isEmpty()) { d->tag = d->sessionInternal()->sendCommand( "LOGIN", quoteIMAP( d->userName ).toUtf8() +' ' +quoteIMAP(d->password ).toUtf8() ); } else { if (!d->startAuthentication()) { emitResult(); } } } else if (d->encryptionMode == TlsV1) { d->authState = LoginJobPrivate::StartTls; d->tag = d->sessionInternal()->sendCommand( "STARTTLS" ); } } void LoginJob::handleResponse( const Message &response ) { Q_D(LoginJob); //set the actual command name for standard responses QString commandName = i18n("Login"); if (d->authState == LoginJobPrivate::Capability) { commandName = i18n("Capability"); } else if (d->authState == LoginJobPrivate::StartTls) { commandName = i18n("StartTls"); } if ( !response.content.isEmpty() && response.content.first().toString() == d->tag ) { if ( response.content.size() < 2 ) { setErrorText( i18n("%1 failed, malformed reply from the server.", commandName) ); emitResult(); } else if ( response.content[1].toString() != "OK" ) { //server replied with NO or BAD for SASL authentication if (d->authState == LoginJobPrivate::Authenticate) { sasl_dispose( &d->conn ); } setError( UserDefinedError ); setErrorText( i18n("%1 failed, server replied: %2", commandName, response.toString().constData()) ); emitResult(); } else if ( response.content[1].toString() == "OK") { if (d->authState == LoginJobPrivate::Authenticate) { sasl_dispose( &d->conn ); //SASL authentication done emitResult(); } else if (d->authState == LoginJobPrivate::Capability) { //cleartext login, if enabled if (d->authMode.isEmpty()) { if (d->plainLoginDisabled) { setError( UserDefinedError ); setErrorText( i18n("Login failed, plain login is disabled by the server.") ); emitResult(); } else { d->authState = LoginJobPrivate::Login; d->tag = d->sessionInternal()->sendCommand( "LOGIN", quoteIMAP( d->userName ).toUtf8() +' ' +quoteIMAP( d->password ).toUtf8() ); } } //find the selected SASL authentication method - Q_FOREACH(QString capability, d->capabilities) { - if (capability.startsWith("AUTH=")) { + Q_FOREACH(const QString &capability, d->capabilities) { + if (capability.startsWith(QLatin1String("AUTH="))) { QString authType = capability.mid(5); if (authType == d->authMode) { if (!d->startAuthentication()) { emitResult(); //problem, we're done } } } } } else if (d->authState == LoginJobPrivate::StartTls) { d->sessionInternal()->startSsl(KTcpSocket::TlsV1); } else { emitResult(); //got an OK, command done } } } else if ( response.content.size() >= 2 ) { if ( d->authState == LoginJobPrivate::Authenticate ) { if (!d->answerChallenge(response.content[1].toString())) { emitResult(); //error, we're done } } else if ( response.content[1].toString()=="CAPABILITY" ) { bool authModeSupported = d->authMode.isEmpty(); for (int i = 2; i < response.content.size(); ++i) { QString capability = response.content[i].toString(); d->capabilities << capability; if (capability == "LOGINDISABLED") { d->plainLoginDisabled = true; } QString authMode = capability.mid(5); if (authMode == d->authMode) { authModeSupported = true; } } kDebug() << "Capabilities after STARTTLS: " << d->capabilities; if (!authModeSupported) { setError( UserDefinedError ); setErrorText( i18n("Login failed, authentication mode %1 is not supported by the server.", d->authMode) ); d->authState = LoginJobPrivate::Login; //just to treat the upcoming OK correctly } } } } bool LoginJobPrivate::startAuthentication() { //SASL authentication if (!initSASL()) { q->setError( LoginJob::UserDefinedError ); q->setErrorText( i18n("Login failed, client cannot initialize the SASL library.") ); return false; } authState = LoginJobPrivate::Authenticate; const char *out = 0; uint outlen = 0; const char *mechusing = 0; int result = sasl_client_new( "imap", m_session->hostName().toLatin1(), 0, 0, callbacks, 0, &conn ); if ( result != SASL_OK ) { kDebug() <<"sasl_client_new failed with:" << result; q->setError( LoginJob::UserDefinedError ); q->setErrorText( QString::fromUtf8( sasl_errdetail( conn ) ) ); return false; } do { result = sasl_client_start(conn, authMode.toLatin1(), &client_interact, capabilities.contains("SASL-IR") ? &out : 0, &outlen, &mechusing); if ( result == SASL_INTERACT ) { if ( !sasl_interact() ) { sasl_dispose( &conn ); q->setError( LoginJob::UserDefinedError ); //TODO: check up the actual error return false; } } } while ( result == SASL_INTERACT ); if ( result != SASL_CONTINUE && result != SASL_OK ) { kDebug() <<"sasl_client_start failed with:" << result; q->setError( LoginJob::UserDefinedError ); q->setErrorText( QString::fromUtf8( sasl_errdetail( conn ) ) ); sasl_dispose( &conn ); return false; } QByteArray tmp = QByteArray::fromRawData( out, outlen ); QByteArray challenge = tmp.toBase64(); tag = sessionInternal()->sendCommand( "AUTHENTICATE", authMode.toLatin1() + ' ' + challenge ); return true; } bool LoginJobPrivate::answerChallenge(const QByteArray &data) { QByteArray challenge = data; int result = -1; const char *out = 0; uint outlen = 0; do { result = sasl_client_step(conn, challenge.isEmpty() ? 0 : challenge.data(), challenge.size(), &client_interact, &out, &outlen); if (result == SASL_INTERACT) { if ( !sasl_interact() ) { q->setError( LoginJob::UserDefinedError ); //TODO: check up the actual error sasl_dispose( &conn ); return false; } } } while ( result == SASL_INTERACT ); if ( result != SASL_CONTINUE && result != SASL_OK ) { kDebug() <<"sasl_client_step failed with:" << result; q->setError( LoginJob::UserDefinedError ); //TODO: check up the actual error q->setErrorText( QString::fromUtf8( sasl_errdetail( conn ) ) ); sasl_dispose( &conn ); return false; } QByteArray tmp = QByteArray::fromRawData( out, outlen ); challenge = tmp.toBase64(); sessionInternal()->sendData( challenge ); return true; } void LoginJobPrivate::sslResponse(bool response) { if (response) { authState = LoginJobPrivate::Capability; tag = sessionInternal()->sendCommand( "CAPABILITY" ); } else { q->setError( LoginJob::UserDefinedError ); q->setErrorText( i18n("Login failed, TLS negotiation failed." )); encryptionMode = LoginJob::Unencrypted; q->emitResult(); } } void LoginJob::setEncryptionMode(EncryptionMode mode) { Q_D(LoginJob); d->encryptionMode = mode; } LoginJob::EncryptionMode LoginJob::encryptionMode() { Q_D(LoginJob); return d->encryptionMode; } void LoginJob::setAuthenticationMode(AuthenticationMode mode) { Q_D(LoginJob); switch (mode) { case ClearText: d->authMode = ""; break; case Login: d->authMode = "LOGIN"; break; case Plain: d->authMode = "PLAIN"; break; case CramMD5: d->authMode = "CRAM-MD5"; break; case DigestMD5: d->authMode = "DIGEST-MD5"; break; case GSSAPI: d->authMode = "GSSAPI"; break; case Anonymous: d->authMode = "ANONYMOUS"; break; default: d->authMode = ""; } } void LoginJob::connectionLost() { Q_D(LoginJob); //don't emit the result if the connection was lost before getting the tls result, as it can mean //the TLS handshake failed and the socket was reconnected in normal mode if (d->authState != LoginJobPrivate::StartTls) { emitResult(); } } #include "loginjob.moc" diff --git a/kimap/metadatajobbase.cpp b/kimap/metadatajobbase.cpp index 0fa426cea..4eb900ffa 100644 --- a/kimap/metadatajobbase.cpp +++ b/kimap/metadatajobbase.cpp @@ -1,74 +1,72 @@ /* Copyright (c) 2009 Andras Mantia This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "metadatajobbase.h" - -#include -#include - #include "metadatajobbase_p.h" #include "message_p.h" #include "session_p.h" -using namespace KIMAP; +#include +#include +using namespace KIMAP; MetaDataJobBase::MetaDataJobBase( Session *session ) : Job( *new MetaDataJobBasePrivate(session, i18n("MetaDataJobBase")) ) { } MetaDataJobBase::MetaDataJobBase( JobPrivate &dd ) : Job(dd) { } MetaDataJobBase::~MetaDataJobBase() { } void MetaDataJobBase::setMailBox( const QString &mailBox ) { Q_D(MetaDataJobBase); d->mailBox = mailBox; } QString MetaDataJobBase::mailBox() const { Q_D(const MetaDataJobBase); return d->mailBox; } void MetaDataJobBase::setServerCapability(const ServerCapability& capability) { Q_D(MetaDataJobBase); d->serverCapability = capability; } MetaDataJobBase::ServerCapability MetaDataJobBase::serverCapability() const { Q_D(const MetaDataJobBase); return d->serverCapability; } #include "metadatajobbase.moc" diff --git a/kimap/quotajobbase.cpp b/kimap/quotajobbase.cpp index c7d894101..d017266af 100644 --- a/kimap/quotajobbase.cpp +++ b/kimap/quotajobbase.cpp @@ -1,93 +1,92 @@ /* Copyright (c) 2009 Andras Mantia This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "quotajobbase.h" - -#include -#include - #include "quotajobbase_p.h" #include "message_p.h" #include "session_p.h" +#include +#include + using namespace KIMAP; QMap > QuotaJobBasePrivate::readQuota( const Message::Part &content ) { QMap > quota; QList quotas = content.toList(); int i = 0; while ( i < quotas.size() - 2 ) { QByteArray resource = quotas[i].toUpper(); qint64 usage = quotas[i+1].toInt(); qint64 limit = quotas[i+2].toInt(); quota[resource] = qMakePair(usage, limit); i += 3; } return quota; } QuotaJobBase::QuotaJobBase( Session *session ) : Job( *new QuotaJobBasePrivate(session, i18n("QuotaJobBase")) ) { } QuotaJobBase::QuotaJobBase( JobPrivate &dd ) : Job(dd) { } QuotaJobBase::~QuotaJobBase() { } qint64 QuotaJobBase::usage(const QByteArray& resource) { Q_D(QuotaJobBase); QByteArray r = resource.toUpper(); if (d->quota.contains(r)) { return d->quota[r].first; } return -1; } qint64 QuotaJobBase::limit(const QByteArray& resource) { Q_D(QuotaJobBase); QByteArray r = resource.toUpper(); if (d->quota.contains(r)) { return d->quota[r].second; } return -1; } #include "quotajobbase.moc" diff --git a/kimap/searchjob.cpp b/kimap/searchjob.cpp index fa53c665f..29bf352ef 100644 --- a/kimap/searchjob.cpp +++ b/kimap/searchjob.cpp @@ -1,301 +1,301 @@ /* Copyright (c) 2009 Andras Mantia This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "searchjob.h" #include #include #include #include "job_p.h" #include "message_p.h" #include "session_p.h" //TODO: when custom error codes are introduced, handle the NO [TRYCREATE] response namespace KIMAP { class SearchJobPrivate : public JobPrivate { public: SearchJobPrivate( Session *session, const QString& name ) : JobPrivate(session, name), logic(SearchJob::And) { criteriaMap[SearchJob::All] = "ALL"; criteriaMap[SearchJob::Answered] = "ANSWERED"; criteriaMap[SearchJob::BCC] = "BCC"; criteriaMap[SearchJob::Before] = "BEFORE"; criteriaMap[SearchJob::Body] = "BODY"; criteriaMap[SearchJob::CC] = "CC"; criteriaMap[SearchJob::Deleted] = "DELETED"; criteriaMap[SearchJob::Draft] = "DRAFT"; criteriaMap[SearchJob::Flagged] = "FLAGGED"; criteriaMap[SearchJob::From] = "FROM"; criteriaMap[SearchJob::Header] = "HEADER"; criteriaMap[SearchJob::Keyword] = "KEYWORD"; criteriaMap[SearchJob::Larger] = "LARGER"; criteriaMap[SearchJob::New] = "NEW"; criteriaMap[SearchJob::Old] = "OLD"; criteriaMap[SearchJob::On] = "ON"; criteriaMap[SearchJob::Recent] = "RECENT"; criteriaMap[SearchJob::Seen] = "SEEN"; criteriaMap[SearchJob::SentBefore] = "SENTBEFORE"; criteriaMap[SearchJob::SentOn] = "SENTON"; criteriaMap[SearchJob::SentSince] = "SENTSINCE"; criteriaMap[SearchJob::Since] = "SINCE"; criteriaMap[SearchJob::Smaller] = "SMALLER"; criteriaMap[SearchJob::Subject] = "SUBJECT"; criteriaMap[SearchJob::Text] = "TEXT"; criteriaMap[SearchJob::To] = "TO"; criteriaMap[SearchJob::Uid] = "UID"; criteriaMap[SearchJob::Unanswered] = "UNANSWERED"; criteriaMap[SearchJob::Undeleted] = "UNDELETED"; criteriaMap[SearchJob::Undraft] = "UNDRAFT"; criteriaMap[SearchJob::Unflagged] = "UNFLAGGED"; criteriaMap[SearchJob::Unkeyword] = "UNKEYWORD"; criteriaMap[SearchJob::Unseen] = "UNSEEN"; //don't use QDate::shortMonthName(), it returns a localized month name months[1] = "Jan"; months[2] = "Feb"; months[3] = "Mar"; months[4] = "Apr"; months[5] = "May"; months[6] = "Jun"; months[7] = "Jul"; months[8] = "Aug"; months[9] = "Sep"; months[10] = "Oct"; months[11] = "Nov"; months[12] = "Dec"; nextContent = 0; uidBased = false; } ~SearchJobPrivate() { } QByteArray charset; QList criterias; QMap criteriaMap; QMap months; SearchJob::SearchLogic logic; QList contents; QList results; uint nextContent; bool uidBased; }; } using namespace KIMAP; SearchJob::SearchJob( Session *session ) - : Job( *new SearchJobPrivate(session, i18n("Search")) ) + : Job( *new SearchJobPrivate(session, i18nc("Name of the search job", "Search")) ) { } SearchJob::~SearchJob() { } void SearchJob::doStart() { Q_D(SearchJob); QByteArray searchKey; if (!d->charset.isEmpty()) { searchKey = "[CHARSET] " + d->charset; } if (d->logic == SearchJob::Not) { searchKey += "NOT"; } else if (d->logic == SearchJob::Or) { searchKey += "OR"; } - Q_FOREACH(QByteArray key, d->criterias) { - searchKey += " (" + key + ")"; + Q_FOREACH(const QByteArray &key, d->criterias) { + searchKey += " (" + key + ')'; } QByteArray command = "SEARCH"; if ( d->uidBased ) { command = "UID "+ command; } d->tag = d->sessionInternal()->sendCommand( command, searchKey ); } void SearchJob::handleResponse( const Message &response ) { Q_D(SearchJob); if (handleErrorReplies(response) == NotHandled ) { if ( response.content[0].toString() == "+" ) { d->sessionInternal()->sendData( d->contents[d->nextContent] ); d->nextContent++; } else if ( response.content[1].toString() == "SEARCH" ) { for(int i = 2; i < response.content.size(); i++) { d->results.append(response.content[i].toString().toInt()); } } } } void SearchJob::setCharset( const QByteArray &charset ) { Q_D(SearchJob); d->charset = charset; } QByteArray SearchJob::charset() const { Q_D(const SearchJob); return d->charset; } void SearchJob::setSearchLogic( SearchLogic logic ) { Q_D(SearchJob); d->logic = logic; } void SearchJob::addSearchCriteria( SearchCriteria criteria ) { Q_D(SearchJob); switch (criteria) { case All: case Answered: case Deleted: case Draft: case Flagged: case New: case Old: case Recent: case Seen: case Unanswered: case Undeleted: case Undraft: case Unflagged: case Unseen: d->criterias.append(d->criteriaMap[criteria]); break; default: //TODO Discuss if we keep error checking here, or accept anything, even if it is wrong kDebug() << "Criteria " << d->criteriaMap[criteria] << " needs an argument, but none was specified."; break; } } void SearchJob::addSearchCriteria( SearchCriteria criteria, int argument ) { Q_D(SearchJob); switch (criteria) { case Larger: case Smaller: - d->criterias.append(d->criteriaMap[criteria] + " " + QByteArray::number(argument)); + d->criterias.append(d->criteriaMap[criteria] + ' ' + QByteArray::number(argument)); break; default: //TODO Discuss if we keep error checking here, or accept anything, even if it is wrong kDebug() << "Criteria " << d->criteriaMap[criteria] << " doesn't accept an integer as an argument."; break; } } void SearchJob::addSearchCriteria( SearchCriteria criteria, const QByteArray &argument ) { Q_D(SearchJob); switch (criteria) { case BCC: case Body: case CC: case From: case Subject: case Text: case To: d->contents.append(argument); d->criterias.append(d->criteriaMap[criteria] + " {" + QByteArray::number(argument.size()) + '}'); break; case Keyword: case Unkeyword: d->criterias.append(d->criteriaMap[criteria] + ' ' + argument); break; case Header: { int pos = argument.indexOf(' '); QByteArray fieldName = argument.left(pos); QByteArray content = argument.mid(pos + 1); d->contents.append(content); d->criterias.append(d->criteriaMap[criteria] + ' ' + fieldName + " {" + QByteArray::number(content.size()) + '}'); break; } default: //TODO Discuss if we keep error checking here, or accept anything, even if it is wrong kDebug() << "Criteria " << d->criteriaMap[criteria] << " doesn't accept any argument."; break; } } void SearchJob::addSearchCriteria( SearchCriteria criteria, const QDate &argument ) { Q_D(SearchJob); switch (criteria) { case Before: case On: case SentBefore: case SentSince: case Since: { QByteArray date = QByteArray::number(argument.day()) + '-'; date += d->months[argument.month()] + '-'; date += QByteArray::number(argument.year()); d->criterias.append(d->criteriaMap[criteria] + " \"" + date + '\"'); break; } default: //TODO Discuss if we keep error checking here, or accept anything, even if it is wrong kDebug() << "Criteria " << d->criteriaMap[criteria] << " doesn't accept a date as argument."; break; } } void SearchJob::addSearchCriteria( const QByteArray &searchCriteria ) { Q_D(SearchJob); d->criterias.append(searchCriteria); } void SearchJob::setUidBased(bool uidBased) { Q_D(SearchJob); d->uidBased = uidBased; } bool SearchJob::isUidBased() const { Q_D(const SearchJob); return d->uidBased; } QList SearchJob::foundItems() { Q_D(const SearchJob); return d->results; } #include "searchjob.moc" diff --git a/kimap/selectjob.cpp b/kimap/selectjob.cpp index 9f56d18f8..de0c039fc 100644 --- a/kimap/selectjob.cpp +++ b/kimap/selectjob.cpp @@ -1,195 +1,195 @@ /* Copyright (c) 2009 Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "selectjob.h" #include #include "job_p.h" #include "message_p.h" #include "session_p.h" #include "rfccodecs.h" namespace KIMAP { class SelectJobPrivate : public JobPrivate { public: SelectJobPrivate( Session *session, const QString& name ) : JobPrivate(session, name), readOnly(false), messageCount(-1), recentCount(-1), firstUnseenIndex(-1), uidValidity(-1), nextUid(-1) { } ~SelectJobPrivate() { } QString mailBox; bool readOnly; QList flags; QList permanentFlags; int messageCount; int recentCount; int firstUnseenIndex; qint64 uidValidity; qint64 nextUid; }; } using namespace KIMAP; SelectJob::SelectJob( Session *session ) - : Job( *new SelectJobPrivate(session, i18n("Select")) ) + : Job( *new SelectJobPrivate(session, i18nc("name of the select job", "Select")) ) { } SelectJob::~SelectJob() { } void SelectJob::setMailBox( const QString &mailBox ) { Q_D(SelectJob); d->mailBox = mailBox; } QString SelectJob::mailBox() const { Q_D(const SelectJob); return d->mailBox; } void SelectJob::setOpenReadOnly( bool readOnly ) { Q_D(SelectJob); d->readOnly = readOnly; } bool SelectJob::isOpenReadOnly() const { Q_D(const SelectJob); return d->readOnly; } QList SelectJob::flags() const { Q_D(const SelectJob); return d->flags; } QList SelectJob::permanentFlags() const { Q_D(const SelectJob); return d->permanentFlags; } int SelectJob::messageCount() const { Q_D(const SelectJob); return d->messageCount; } int SelectJob::recentCount() const { Q_D(const SelectJob); return d->recentCount; } int SelectJob::firstUnseenIndex() const { Q_D(const SelectJob); return d->firstUnseenIndex; } qint64 SelectJob::uidValidity() const { Q_D(const SelectJob); return d->uidValidity; } qint64 SelectJob::nextUid() const { Q_D(const SelectJob); return d->nextUid; } void SelectJob::doStart() { Q_D(SelectJob); QByteArray command = "SELECT"; if ( d->readOnly ) { command = "EXAMINE"; } d->tag = d->sessionInternal()->sendCommand( command, '\"'+KIMAP::encodeImapFolderName( d->mailBox.toUtf8() )+'\"' ); } void SelectJob::handleResponse( const Message &response ) { Q_D(SelectJob); if ( handleErrorReplies(response) == NotHandled) { if ( response.content.size() >= 2 ) { QByteArray code = response.content[1].toString(); if ( code=="OK" ) { if ( response.responseCode.size() < 2 ) return; code = response.responseCode[0].toString(); if ( code=="PERMANENTFLAGS" ) { d->permanentFlags = response.responseCode[1].toList(); } else { bool isInt; if ( code=="UIDVALIDITY" ) { qint64 value = response.responseCode[1].toString().toLongLong(&isInt); if ( !isInt ) return; d->uidValidity = value; } else { qint64 value = response.responseCode[1].toString().toLongLong(&isInt); if ( !isInt ) return; if ( code=="UNSEEN" ) { d->firstUnseenIndex = value; } else if ( code=="UIDNEXT" ) { d->nextUid = value; } } } } else if ( code=="FLAGS" ) { d->flags = response.content[2].toList(); } else { bool isInt; int value = response.content[1].toString().toInt(&isInt); if ( !isInt || response.content.size()<3 ) return; code = response.content[2].toString(); if ( code=="EXISTS" ) { d->messageCount = value; } else if ( code=="RECENT" ) { d->recentCount = value; } } } else { qDebug("%s", response.toString().constData()); } } else { Q_ASSERT( error() || d->sessionInternal()->selectedMailBox() == d->mailBox ); } } #include "selectjob.moc" diff --git a/kimap/session.cpp b/kimap/session.cpp index 6fcc81208..938c536d3 100644 --- a/kimap/session.cpp +++ b/kimap/session.cpp @@ -1,264 +1,264 @@ /* Copyright (c) 2009 Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "session.h" #include "session_p.h" #include "sessionuiproxy.h" #include #include #include #include #include "job.h" #include "message_p.h" #include "sessionthread_p.h" #include "rfccodecs.h" Q_DECLARE_METATYPE(KTcpSocket::SslVersion) Q_DECLARE_METATYPE(QSslSocket::SslMode) static const int _kimap_sslVersionId = qRegisterMetaType(); using namespace KIMAP; Session::Session( const QString &hostName, quint16 port, QObject *parent) : QObject(parent), d(new SessionPrivate(this)) { d->state = Disconnected; d->jobRunning = false; d->thread = new SessionThread(hostName, port, this); connect(d->thread, SIGNAL(encryptionNegotiationResult(bool)), d, SIGNAL(encryptionNegotiationResult(bool))); connect(d->thread, SIGNAL(sslError(const KSslErrorUiData&)), this, SLOT(handleSslError(const KSslErrorUiData&))); d->thread->start(); } Session::~Session() { delete d->thread; } void Session::setUiProxy(SessionUiProxy *proxy) { d->uiProxy = proxy; } QString Session::hostName() const { return d->thread->hostName(); } quint16 Session::port() const { return d->thread->port(); } Session::State Session::state() const { return d->state; } void SessionPrivate::handleSslError(const KSslErrorUiData& errorData) { if (uiProxy && uiProxy->ignoreSslError(errorData)) { QMetaObject::invokeMethod( thread, "sslErrorHandlerResponse", Q_ARG(bool, true) ); } else { QMetaObject::invokeMethod( thread, "sslErrorHandlerResponse", Q_ARG(bool, false) ); } } SessionPrivate::SessionPrivate( Session *session ) : q(session), uiProxy(0), currentJob(0), tagCount(0) { } void SessionPrivate::addJob(Job *job) { queue.append(job); QObject::connect( job, SIGNAL(result(KJob*)), q, SLOT(jobDone(KJob*)) ); QObject::connect( job, SIGNAL(destroyed(QObject*)), q, SLOT(jobDestroyed(QObject*)) ); startNext(); } void SessionPrivate::startNext() { QTimer::singleShot( 0, q, SLOT(doStartNext()) ); } void SessionPrivate::doStartNext() { if ( queue.isEmpty() || jobRunning || state==Session::Disconnected ) { return; } jobRunning = true; currentJob = queue.dequeue(); currentJob->doStart(); } void SessionPrivate::jobDone( KJob *job ) { Q_ASSERT( job == currentJob ); jobRunning = false; currentJob = 0; startNext(); } void SessionPrivate::jobDestroyed( QObject *job ) { queue.removeAll( static_cast( job ) ); if ( currentJob == job ) currentJob = 0; } void SessionPrivate::responseReceived( const Message &response ) { QByteArray tag; QByteArray code; if ( response.content.size()>=1 ) { tag = response.content[0].toString(); } if ( response.content.size()>=2 ) { code = response.content[1].toString(); } switch ( state ) { case Session::Disconnected: if ( code=="OK" ) { state = Session::NotAuthenticated; startNext(); } else if ( code=="PREAUTH" ) { state = Session::Authenticated; startNext(); } else { thread->closeSocket(); QTimer::singleShot( 1000, thread, SLOT( reconnect() ) ); } return; case Session::NotAuthenticated: if ( code=="OK" && tag==authTag ) { state = Session::Authenticated; } break; case Session::Authenticated: if ( code=="OK" && tag==selectTag ) { state = Session::Selected; currentMailBox = upcomingMailBox; } break; case Session::Selected: if ( ( code=="OK" && tag==closeTag ) || ( code!="OK" && tag==selectTag) ) { state = Session::Authenticated; currentMailBox = QByteArray(); } else if ( code=="OK" && tag==selectTag ) { currentMailBox = upcomingMailBox; } break; } if (tag==authTag) authTag.clear(); if (tag==selectTag) selectTag.clear(); if (tag==closeTag) closeTag.clear(); // If a job is running forward it the response if ( currentJob!=0 ) { currentJob->handleResponse( response ); } else { qWarning() << "A message was received from the server with no job to handle it"; } } QByteArray SessionPrivate::sendCommand( const QByteArray &command, const QByteArray &args ) { - QByteArray tag = "A" + QByteArray::number(++tagCount).rightJustified(6, '0'); + QByteArray tag = 'A' + QByteArray::number(++tagCount).rightJustified(6, '0'); QByteArray payload = tag+' '+command; if ( !args.isEmpty() ) { payload+= ' '+args; } payload+="\r\n"; thread->sendData(payload); if ( command=="LOGIN" || command=="AUTHENTICATE" ) { authTag = tag; } else if ( command=="SELECT" || command=="EXAMINE" ) { selectTag = tag; upcomingMailBox = args; upcomingMailBox.remove( 0, 1 ); upcomingMailBox.chop( 1 ); upcomingMailBox = KIMAP::decodeImapFolderName( upcomingMailBox ); } else if ( command=="CLOSE" ) { closeTag = tag; } return tag; } void SessionPrivate::sendData( const QByteArray &data ) { thread->sendData(data+"\r\n"); } void SessionPrivate::socketConnected() { state = Session::NotAuthenticated; startNext(); } void SessionPrivate::socketDisconnected() { state = Session::Disconnected; thread->closeSocket(); if ( currentJob ) { currentJob->connectionLost(); } } void SessionPrivate::socketError() { //qWarning() << "Socket error occurred:" << socket->errorString(); socketDisconnected(); } void SessionPrivate::startSsl(const KTcpSocket::SslVersion &version) { QMetaObject::invokeMethod( thread, "startSsl", Qt::QueuedConnection, Q_ARG(KTcpSocket::SslVersion, version) ); } QString SessionPrivate::selectedMailBox() const { return QString::fromUtf8( currentMailBox ); } #include "session.moc" #include "session_p.moc" diff --git a/kimap/setmetadatajob.cpp b/kimap/setmetadatajob.cpp index 2496e6d6c..73902db98 100644 --- a/kimap/setmetadatajob.cpp +++ b/kimap/setmetadatajob.cpp @@ -1,154 +1,154 @@ /* Copyright (c) 2009 Andras Mantia This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "setmetadatajob.h" #include #include #include "metadatajobbase_p.h" #include "message_p.h" #include "session_p.h" #include "rfccodecs.h" namespace KIMAP { class SetMetaDataJobPrivate : public MetaDataJobBasePrivate { public: SetMetaDataJobPrivate( Session *session, const QString& name ) : MetaDataJobBasePrivate(session, name), metaDataErrors(0), maxAcceptedSize(-1) { } ~SetMetaDataJobPrivate() { } QMap entries; QMap::ConstIterator entriesIt; QByteArray entryName; SetMetaDataJob::MetaDataErrors metaDataErrors; qint64 maxAcceptedSize; }; } using namespace KIMAP; SetMetaDataJob::SetMetaDataJob( Session *session ) : MetaDataJobBase( *new SetMetaDataJobPrivate(session, i18n("SetMetaData")) ) { } SetMetaDataJob::~SetMetaDataJob() { } void SetMetaDataJob::doStart() { Q_D(SetMetaDataJob); QByteArray parameters; parameters = '\"' + KIMAP::encodeImapFolderName( d->mailBox.toUtf8() ) + "\" "; d->entriesIt = d->entries.constBegin(); QByteArray command = "SETMETADATA"; if (d->serverCapability == Annotatemore) { command = "SETANNOTATION"; parameters += '\"' + d->entryName + "\" ("; d->m_name = i18n("SetAnnotation"); if (!d->entries.isEmpty()) { for (; d->entriesIt != d->entries.constEnd(); ++d->entriesIt) { parameters += '\"' + d->entriesIt.key() + "\" \"" + d->entriesIt.value() + "\" "; } parameters[parameters.length() - 1] = ')'; } } else { - parameters += "("; + parameters += '('; if (!d->entries.isEmpty()) { parameters += '\"' + d->entriesIt.key() + '\"'; parameters += ' '; parameters +=" {" + QByteArray::number(d->entriesIt.value().size()) + '}'; } } if (d->entries.isEmpty()) { parameters += ')'; } d->tag = d->sessionInternal()->sendCommand( command, parameters ); // kDebug() << "SENT: " << command << " " << parameters; } void SetMetaDataJob::handleResponse( const Message &response ) { Q_D(SetMetaDataJob); //TODO: Test if a server can really return more then one untagged NO response. If not, no need to OR the error codes if ( !response.content.isEmpty() && response.content.first().toString() == d->tag ) { if ( response.content[1].toString() == "NO" ) { setError( UserDefinedError ); setErrorText( i18n("%1 failed, server replied: %2", d->m_name, response.toString().constData()) ); if (response.content[2].toString() == "[ANNOTATEMORE TOOMANY]" || response.content[2].toString() == "[METADATA TOOMANY]") { d->metaDataErrors |= TooMany; } else if (response.content[2].toString() == "[ANNOTATEMORE TOOBIG]" || response.content[2].toString().startsWith("[METADATA MAXSIZE")) { d->metaDataErrors |= TooBig; d->maxAcceptedSize = -1; - if (response.content[2].toString().startsWith("[METADATA MAXSIZE")) { + if (response.content[2].toString().startsWith("[METADATA MAXSIZE")) { //krazy:exclude=strings QByteArray max = response.content[2].toString(); - max.replace("[METADATA MAXSIZE", ""); - max.replace("]",""); + max.replace("[METADATA MAXSIZE",""); //krazy:exclude=doublequote_chars + max.replace("]", ""); //krazy:exclude=doublequote_chars d->maxAcceptedSize = max.toLongLong(); } } else if (response.content[2].toString() == "[METADATA NOPRIVATE]") { d->metaDataErrors |= NoPrivate; } } else if ( response.content.size() < 2 ) { setErrorText( i18n("%1 failed, malformed reply from the server.", d->m_name) ); } else if ( response.content[1].toString() != "OK" ) { setError( UserDefinedError ); setErrorText( i18n("%1 failed, server replied: %2", d->m_name, response.toString().constData()) ); } emitResult(); } else if ( d->serverCapability == Metadata && response.content[0].toString() == "+" ) { QByteArray content = d->entriesIt.value(); ++d->entriesIt; if (d->entriesIt == d->entries.constEnd()) { content += ')'; } else { content +=" {" + QByteArray::number(d->entriesIt.value().size()) + '}'; } // kDebug() << "SENT: " << content; d->sessionInternal()->sendData( content ); } } void SetMetaDataJob::addMetaData(const QByteArray &name, const QByteArray &value) { Q_D(SetMetaDataJob); d->entries[name] = value; } void SetMetaDataJob::setEntry(const QByteArray &entry) { Q_D(SetMetaDataJob); d->entryName = entry; } SetMetaDataJob::MetaDataErrors SetMetaDataJob::metaDataErrors() const { Q_D(const SetMetaDataJob); return d->metaDataErrors; } #include "setmetadatajob.moc" diff --git a/kimap/setmetadatajob.h b/kimap/setmetadatajob.h index 4302fcfb2..d6d64e4b8 100644 --- a/kimap/setmetadatajob.h +++ b/kimap/setmetadatajob.h @@ -1,90 +1,90 @@ /* Copyright (c) 2009 Andras Mantia This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIMAP_SETMETADATAJOB_H #define KIMAP_SETMETADATAJOB_H #include "kimap_export.h" #include "metadatajobbase.h" namespace KIMAP { class Session; struct Message; class SetMetaDataJobPrivate; class KIMAP_EXPORT SetMetaDataJob : public MetaDataJobBase { Q_OBJECT Q_DECLARE_PRIVATE(SetMetaDataJob) friend class SessionPrivate; public: explicit SetMetaDataJob( Session *session ); virtual ~SetMetaDataJob(); /** * Add a metadata to the mailbox. Depending on the supported standard by the server (setServerCapability), * the @param name can have a different meaning. * @param name the entry name if serverCapability() returns Metadata (RFC5464 mode), the attribute value name * if serverCapability() is Annotatemore (draft-daboo-imap-annotatemore-07 mode). * @param value the value of the entry/attribute */ void addMetaData(const QByteArray &name, const QByteArray &value); /** * Set the entry name for the metada, if the job is operating in Annotatemore mode. In Metadata mode, this setting is * ignored. * @param entry the metadata entry name */ void setEntry(const QByteArray &entry); enum MetaDataError { NoError = 0, TooMany = 1, TooBig = 2, NoPrivate = 4 }; // Q_DECLARE_WHATEVER_THAT_WAS missing Q_DECLARE_FLAGS(MetaDataErrors, MetaDataError) /** - * The metadata errors recived from the server. + * The metadata errors received from the server. * @return OR connected error codes, see MetaDataError */ MetaDataErrors metaDataErrors() const; /** * The maximum accepted metadata size. * @return the accepted metadata size, -1 means the limit is unknown. */ qint64 maxAcceptedSize(); protected: virtual void doStart(); virtual void handleResponse( const Message &response ); }; } Q_DECLARE_OPERATORS_FOR_FLAGS( KIMAP::SetMetaDataJob::MetaDataErrors ) #endif diff --git a/kimap/tests/testimapserver.cpp b/kimap/tests/testimapserver.cpp index ef74d7d2a..e1c06aa21 100644 --- a/kimap/tests/testimapserver.cpp +++ b/kimap/tests/testimapserver.cpp @@ -1,579 +1,582 @@ /** * This file is part of the KDE project * Copyright (C) 2009 Kevin Ottens * Copyright (C) 2009 Andras Mantia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "kimap/acl.h" #include "kimap/session.h" #include "kimap/appendjob.h" #include "kimap/capabilitiesjob.h" #include "kimap/fetchjob.h" #include "kimap/listjob.h" #include "kimap/loginjob.h" #include "kimap/logoutjob.h" #include "kimap/selectjob.h" #include "kimap/closejob.h" #include "kimap/expungejob.h" #include "kimap/createjob.h" #include "kimap/deletejob.h" #include "kimap/subscribejob.h" #include "kimap/unsubscribejob.h" #include "kimap/renamejob.h" #include "kimap/storejob.h" #include "kimap/sessionuiproxy.h" #include "kimap/setacljob.h" #include "kimap/getacljob.h" #include "kimap/deleteacljob.h" #include "kimap/myrightsjob.h" #include "kimap/listrightsjob.h" #include "kimap/setmetadatajob.h" #include "kimap/getmetadatajob.h" using namespace KIMAP; class UiProxy: public SessionUiProxy { public: bool ignoreSslError(const KSslErrorUiData& errorData) { if (KIO::SslUi::askIgnoreSslErrors(errorData, KIO::SslUi::StoreRules)) { return true; } else { return false; } } }; void dumpContentHelper(KMime::Content *part, const QString &partId = QString()) { if (partId.isEmpty()) { kDebug() << "** Message root **"; } else { kDebug() << "** Part" << partId << "**"; } kDebug() << part->head(); KMime::Content::List children = part->contents(); for (int i=0; isetIncludeUnsubscribed(includeUnsubscribed); list->exec(); Q_ASSERT_X(list->error()==0, "ListJob", list->errorString().toLocal8Bit()); int count = list->mailBoxes().size(); for (int i=0; imailBoxes()[i]; - if (descriptor.name.endsWith(nameFilter)) + if (descriptor.name.endsWith(nameFilter)) { kDebug() << descriptor.separator << descriptor.name; + } } } void testMetaData(Session *session) { kDebug() << "TESTING: METADATA commands"; CreateJob *create = new CreateJob(session); create->setMailBox("INBOX/TestFolder"); create->exec(); SetMetaDataJob *setmetadata = new SetMetaDataJob(session); setmetadata->setMailBox("INBOX/TestFolder"); setmetadata->setServerCapability(SetMetaDataJob::Annotatemore); setmetadata->setEntry("/comment"); setmetadata->addMetaData("value.priv", "My new comment"); setmetadata->exec(); setmetadata = new SetMetaDataJob(session); setmetadata->setMailBox("INBOX/TestFolder"); setmetadata->setServerCapability(SetMetaDataJob::Annotatemore); setmetadata->setEntry("/check"); setmetadata->addMetaData("value.priv", "true"); setmetadata->exec(); GetMetaDataJob *getmetadata = new GetMetaDataJob(session); getmetadata->setMailBox("INBOX/TestFolder"); getmetadata->setServerCapability(SetMetaDataJob::Annotatemore); getmetadata->addEntry("/*","value.priv"); getmetadata->exec(); Q_ASSERT_X(getmetadata->metaData("INBOX/TestFolder", "/check", "value.priv") == "true", "", "/check metadata should be true"); Q_ASSERT_X(getmetadata->metaData("INBOX/TestFolder", "/comment", "value.priv") == "My new comment", "", "/check metadata should be My new comment"); //cleanup DeleteJob *deletejob = new DeleteJob(session); deletejob->setMailBox("INBOX/TestFolder"); deletejob->exec(); } void testAcl(Session *session, const QString &user) { kDebug() << "TESTING: ACL commands"; CreateJob *create = new CreateJob(session); create->setMailBox("INBOX/TestFolder"); create->exec(); ListRightsJob *listRights = new ListRightsJob(session); listRights->setMailBox("INBOX/TestFolder"); listRights->setIdentifier(user.toLatin1()); listRights->exec(); kDebug() << "Default rights on INBOX/TestFolder: " << Acl::rightsToString(listRights->defaultRights()); QList possible = listRights->possibleRights(); QStringList strList; Q_FOREACH(Acl::Rights r, possible) { strList << Acl::rightsToString(r); } kDebug() << "Possible rights on INBOX/TestFolder: " << strList; MyRightsJob *myRights = new MyRightsJob(session); myRights->setMailBox("INBOX/TestFolder"); myRights->exec(); Acl::Rights mine = myRights->rights(); kDebug() << "My rights on INBOX/TestFolder: " << Acl::rightsToString(mine); kDebug() << "Reading INBOX/TestFolder is possible: " << myRights->hasRightEnabled(Acl::Read); Q_ASSERT_X(myRights->hasRightEnabled(Acl::Read), "Reading INBOX is NOT possible", ""); GetAclJob *getAcl= new GetAclJob(session); getAcl->setMailBox("INBOX/TestFolder"); getAcl->exec(); kDebug() << "Anyone rights on INBOX/TestFolder: " << getAcl->rights("anyone"); Acl::Rights users = getAcl->rights(user.toLatin1()); kDebug() << user << " rights on INBOX/TestFolder: " << Acl::rightsToString(users); Q_ASSERT_X(mine == users, "GETACL returns different rights for the same user", ""); kDebug() << "Removing Delete right "; mine = Acl::Delete; SetAclJob *setAcl= new SetAclJob(session); setAcl->setMailBox("INBOX/TestFolder"); setAcl->setIdentifier(user.toLatin1()); setAcl->setRights(AclJobBase::Remove, mine); setAcl->exec(); getAcl= new GetAclJob(session); getAcl->setMailBox("INBOX/TestFolder"); getAcl->exec(); users = getAcl->rights(user.toLatin1()); kDebug() << user << " rights on INBOX/TestFolder: " << Acl::rightsToString(users); kDebug() << "Adding back Delete right "; mine = Acl::Delete; setAcl= new SetAclJob(session); setAcl->setMailBox("INBOX/TestFolder"); setAcl->setIdentifier(user.toLatin1()); setAcl->setRights(AclJobBase::Add, mine); setAcl->exec(); getAcl= new GetAclJob(session); getAcl->setMailBox("INBOX/TestFolder"); getAcl->exec(); users = getAcl->rights(user.toLatin1()); kDebug() << user << " rights on INBOX/TestFolder: " << Acl::rightsToString(users); //cleanup DeleteJob *deletejob = new DeleteJob(session); deletejob->setMailBox("INBOX/TestFolder"); deletejob->exec(); } void testAppendAndStore(Session *session) { kDebug() << "TESTING: APPEND and STORE"; //setup CreateJob *create = new CreateJob(session); create->setMailBox("INBOX/TestFolder"); create->exec(); QByteArray testMailContent = "Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)\r\n" "From: Fred Foobar \r\n" "Subject: afternoon meeting\r\n" "To: mooch@owatagu.siam.edu\r\n" "Message-Id: \r\n" "MIME-Version: 1.0\r\n" "Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n" "\r\n" "Hello Joe, do you think we can meet at 3:30 tomorrow?\r\n"; kDebug() << "Append a message in INBOX/TestFolder..."; AppendJob *append = new AppendJob(session); append->setMailBox("INBOX/TestFolder"); append->setContent(testMailContent); append->exec(); Q_ASSERT_X(append->error()==0, "AppendJob", append->errorString().toLocal8Bit()); kDebug() << "Read the message back and compare..."; SelectJob *select = new SelectJob(session); select->setMailBox("INBOX/TestFolder"); select->exec(); FetchJob *fetch = new FetchJob(session); FetchJob::FetchScope scope; fetch->setSequenceSet(ImapSet(1)); scope.parts.clear(); scope.mode = FetchJob::FetchScope::Content; fetch->setScope(scope); fetch->exec(); MessagePtr message = fetch->messages()[1]; Q_ASSERT_X(fetch->error()==0, "FetchJob", fetch->errorString().toLocal8Bit()); testMailContent.replace( "\r\n", "\n" ); Q_ASSERT_X(testMailContent==message->head()+"\n"+message->body(), "Message differs from reference", message->head()+"\n"+message->body()); fetch = new FetchJob(session); fetch->setSequenceSet(ImapSet(1)); scope.parts.clear(); scope.mode = FetchJob::FetchScope::Flags; fetch->setScope(scope); fetch->exec(); MessageFlags expectedFlags = fetch->flags()[1]; kDebug() << "Read the message flags:" << expectedFlags; kDebug() << "Add the \\Deleted flag..."; expectedFlags << "\\Deleted"; qSort(expectedFlags); StoreJob *store = new StoreJob(session); store->setSequenceSet(ImapSet(1)); store->setMode(StoreJob::AppendFlags); store->setFlags(QList() << "\\Deleted"); store->exec(); Q_ASSERT_X(store->error()==0, "StoreJob", store->errorString().toLocal8Bit()); QList resultingFlags = store->resultingFlags()[1]; qSort(resultingFlags); - if (expectedFlags!=resultingFlags) kDebug() << resultingFlags; + if (expectedFlags!=resultingFlags) { + kDebug() << resultingFlags; + } Q_ASSERT(expectedFlags==resultingFlags); select = new SelectJob(session); select->setMailBox("INBOX"); select->exec(); //cleanup DeleteJob *deletejob = new DeleteJob(session); deletejob->setMailBox("INBOX/TestFolder"); deletejob->exec(); deletejob = new DeleteJob(session); deletejob->setMailBox("INBOX/RenamedTestFolder"); deletejob->exec(); } void testRename(Session *session) { kDebug() << "TESTING: RENAME"; //setup CreateJob *create = new CreateJob(session); create->setMailBox("INBOX/TestFolder"); create->exec(); kDebug() << "Listing mailboxes with name TestFolder:"; listFolders(session, true, "TestFolder"); //actual tests kDebug() << "Renaming to RenamedTestFolder"; RenameJob *rename = new RenameJob(session); rename->setSourceMailBox("INBOX/TestFolder"); rename->setDestinationMailBox("INBOX/RenamedTestFolder"); rename->exec(); kDebug() << "Listing mailboxes with name TestFolder:"; listFolders(session, true, "TestFolder"); kDebug() << "Listing mailboxes with name RenamedTestFolder:"; listFolders(session, true, "RenamedTestFolder"); //cleanup DeleteJob *deletejob = new DeleteJob(session); deletejob->setMailBox("INBOX/TestFolder"); deletejob->exec(); deletejob = new DeleteJob(session); deletejob->setMailBox("INBOX/RenamedTestFolder"); deletejob->exec(); } void testSubscribe(Session *session) { kDebug() << "TESTING: SUBSCRIBE/UNSUBSCRIBE"; //setup CreateJob *create = new CreateJob(session); create->setMailBox("INBOX/TestFolder"); create->exec(); kDebug() << "Listing subscribed mailboxes with name TestFolder:"; listFolders(session, false, "TestFolder"); //actual tests kDebug() << "Subscribing to INBOX/TestFolder"; SubscribeJob *subscribe = new SubscribeJob(session); subscribe->setMailBox("INBOX/TestFolder"); subscribe->exec(); kDebug() << "Listing subscribed mailboxes with name TestFolder:"; listFolders(session, false, "TestFolder"); kDebug() << "Unsubscribing from INBOX/TestFolder"; UnsubscribeJob *unsubscribe = new UnsubscribeJob(session); unsubscribe->setMailBox("INBOX/TestFolder"); unsubscribe->exec(); kDebug() << "Listing subscribed mailboxes with name TestFolder:"; listFolders(session, false, "TestFolder"); //cleanup DeleteJob *deletejob = new DeleteJob(session); deletejob->setMailBox("INBOX/TestFolder"); deletejob->exec(); } void testDelete(Session *session) { kDebug() << "TESTING: DELETE"; kDebug() << "Creating INBOX/TestFolder:"; CreateJob *create = new CreateJob(session); create->setMailBox("INBOX/TestFolder"); create->exec(); kDebug() << "Listing with name TestFolder before DELETE:"; listFolders(session, true, "TestFolder"); kDebug() << "Deleting INBOX/TestFolder"; DeleteJob *deletejob = new DeleteJob(session); deletejob->setMailBox("INBOX/TestFolder"); deletejob->exec(); kDebug() << "Listing with name TestFolder after DELETE:"; listFolders(session, true, "TestFolder"); } int main( int argc, char **argv ) { KAboutData about("TestImapServer", 0, ki18n("TestImapServer"), "version"); KComponentData cData(&about); if (argc < 4) { kError() << "Not enough parameters, expecting: "; } QString server = QString::fromLocal8Bit(argv[1]); int port = 143; if ( server.count( ':' ) == 1 ) { port = server.split( ':' ).last().toInt(); server = server.split( ':' ).first(); } QString user = QString::fromLocal8Bit(argv[2]); QString password = QString::fromLocal8Bit(argv[3]); kDebug() << "Querying:" << server << port << user << password; qDebug(); QApplication app(argc, argv); Session session(server, port); UiProxy *proxy = new UiProxy(); session.setUiProxy(proxy); kDebug() << "Logging in..."; LoginJob *login = new LoginJob(&session); login->setEncryptionMode(LoginJob::TlsV1); login->setAuthenticationMode(LoginJob::Plain); login->setUserName(user); login->setPassword(password); login->exec(); qDebug(); if (login->encryptionMode() == LoginJob::Unencrypted) { kDebug() << "Encrypted login not possible, try to log in without encryption"; login = new LoginJob(&session); login->setUserName(user); login->setPassword(password); login->exec(); Q_ASSERT_X(login->error()==0, "LoginJob", login->errorString().toLocal8Bit()); Q_ASSERT(session.state()==Session::Authenticated); qDebug(); } kDebug() << "Asking for capabilities:"; CapabilitiesJob *capabilities = new CapabilitiesJob(&session); capabilities->exec(); Q_ASSERT_X(capabilities->error()==0, "CapabilitiesJob", capabilities->errorString().toLocal8Bit()); Q_ASSERT(session.state()==Session::Authenticated); kDebug() << capabilities->capabilities(); qDebug(); kDebug() << "Listing mailboxes:"; listFolders(&session); Q_ASSERT(session.state()==Session::Authenticated); kDebug() << "Selecting INBOX:"; SelectJob *select = new SelectJob(&session); select->setMailBox("INBOX"); select->exec(); Q_ASSERT_X(select->error()==0, "SelectJob", select->errorString().toLocal8Bit()); Q_ASSERT(session.state()==Session::Selected); kDebug() << "Flags:" << select->flags(); kDebug() << "Permanent flags:" << select->permanentFlags(); kDebug() << "Total Number of Messages:" << select->messageCount(); kDebug() << "Number of recent Messages:" << select->recentCount(); kDebug() << "First Unseen Message Index:" << select->firstUnseenIndex(); kDebug() << "UID validity:" << select->uidValidity(); kDebug() << "Next UID:" << select->nextUid(); qDebug(); kDebug() << "Fetching first 3 messages headers:"; FetchJob *fetch = new FetchJob(&session); FetchJob::FetchScope scope; fetch->setSequenceSet(ImapSet(1, 3)); scope.parts.clear(); scope.mode = FetchJob::FetchScope::Headers; fetch->setScope(scope); fetch->exec(); Q_ASSERT_X(fetch->error()==0, "FetchJob", fetch->errorString().toLocal8Bit()); Q_ASSERT(session.state()==Session::Selected); QMap messages = fetch->messages(); foreach (qint64 id, messages.keys()) { kDebug() << "* Message" << id << "(" << fetch->sizes()[id] << "bytes )"; kDebug() << " From :" << messages[id]->from()->asUnicodeString(); kDebug() << " To :" << messages[id]->to()->asUnicodeString(); kDebug() << " Date :" << messages[id]->date()->asUnicodeString(); kDebug() << " Subject :" << messages[id]->subject()->asUnicodeString(); kDebug() << " Message-ID:" << messages[id]->messageID()->asUnicodeString(); } qDebug(); kDebug() << "Fetching first 3 messages flags:"; fetch = new FetchJob(&session); fetch->setSequenceSet(ImapSet(1, 3)); scope.parts.clear(); scope.mode = FetchJob::FetchScope::Flags; fetch->setScope(scope); fetch->exec(); Q_ASSERT_X(fetch->error()==0, "FetchJob", fetch->errorString().toLocal8Bit()); Q_ASSERT(session.state()==Session::Selected); QMap flags = fetch->flags(); foreach (qint64 id, flags.keys()) { kDebug() << "* Message" << id << "flags:" << flags[id]; } qDebug(); kDebug() << "Fetching first message structure:"; fetch = new FetchJob(&session); fetch->setSequenceSet(ImapSet(1)); scope.parts.clear(); scope.mode = FetchJob::FetchScope::Structure; fetch->setScope(scope); fetch->exec(); Q_ASSERT_X(fetch->error()==0, "FetchJob", fetch->errorString().toLocal8Bit()); Q_ASSERT(session.state()==Session::Selected); MessagePtr message = fetch->messages()[1]; dumpContentHelper(message.get()); qDebug(); kDebug() << "Fetching first message second part headers:"; fetch = new FetchJob(&session); fetch->setSequenceSet(ImapSet(1)); scope.parts.clear(); scope.parts << "2"; scope.mode = FetchJob::FetchScope::Headers; fetch->setScope(scope); fetch->exec(); Q_ASSERT_X(fetch->error()==0, "FetchJob", fetch->errorString().toLocal8Bit()); Q_ASSERT(session.state()==Session::Selected); QMap allParts = fetch->parts(); foreach (qint64 id, allParts.keys()) { kDebug() << "* Message" << id << "parts headers"; MessageParts parts = allParts[id]; foreach (const QByteArray &partId, parts.keys()) { kDebug() << " ** Part" << partId; kDebug() << " Name :" << parts[partId]->contentType()->name(); kDebug() << " Mimetype :" << parts[partId]->contentType()->mimeType(); kDebug() << " Description:" << parts[partId]->contentDescription()->asUnicodeString().simplified(); } } qDebug(); kDebug() << "Fetching first message second part content:"; fetch = new FetchJob(&session); fetch->setSequenceSet(ImapSet(1)); scope.parts.clear(); scope.parts << "2"; scope.mode = FetchJob::FetchScope::Content; fetch->setScope(scope); fetch->exec(); Q_ASSERT_X(fetch->error()==0, "FetchJob", fetch->errorString().toLocal8Bit()); Q_ASSERT(session.state()==Session::Selected); allParts = fetch->parts(); foreach (int id, allParts.keys()) { MessageParts parts = allParts[id]; foreach (const QByteArray &partId, parts.keys()) { kDebug() << "* Message" << id << "part" << partId << "content:"; kDebug() << parts[partId]->body(); } } qDebug(); testDelete(&session); testSubscribe(&session); testRename(&session); testAppendAndStore(&session); testAcl(&session, user); testMetaData(&session); kDebug() << "Expunge INBOX:"; ExpungeJob *expunge = new ExpungeJob(&session); expunge->exec(); kDebug() << "Closing INBOX:"; CloseJob *close = new CloseJob(&session); close->exec(); Q_ASSERT(session.state()==Session::Authenticated); qDebug(); kDebug() << "Logging out..."; LogoutJob *logout = new LogoutJob(&session); logout->exec(); Q_ASSERT_X(logout->error()==0, "LogoutJob", logout->errorString().toLocal8Bit()); Q_ASSERT(session.state()==Session::Disconnected); return 0; }