• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

KIMAP Library

  • sources
  • kde-4.14
  • kdepimlibs
  • kimap
searchjob.cpp
1 /*
2  Copyright (c) 2009 Andras Mantia <amantia@kde.org>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "searchjob.h"
21 
22 #include <KDE/KLocalizedString>
23 #include <KDE/KDebug>
24 
25 #include <QtCore/QDate>
26 
27 #include "job_p.h"
28 #include "message_p.h"
29 #include "session_p.h"
30 #include "imapset.h"
31 
32 namespace KIMAP
33 {
34 
35 
36 class Term::Private {
37 public:
38  Private(): isFuzzy(false), isNegated(false), isNull(false) {};
39  QByteArray command;
40  bool isFuzzy;
41  bool isNegated;
42  bool isNull;
43 };
44 
45 Term::Term()
46 : d(new Term::Private)
47 {
48  d->isNull = true;
49 }
50 
51 Term::Term(Term::Relation relation, const QVector<Term> &subterms)
52 : d(new Term::Private)
53 {
54  if (subterms.size() >= 2) {
55  d->command += "(";
56  if (relation == KIMAP::Term::Or) {
57  d->command += "OR ";
58  d->command += subterms.at(0).serialize() + ' ';
59  if (subterms.size() >= 3) {
60  Term t(relation, subterms.mid(1));
61  d->command += t.serialize();
62  } else if (subterms.size() == 2) {
63  d->command += subterms.at(1).serialize();
64  }
65  } else {
66  Q_FOREACH (const Term &t, subterms) {
67  d->command += t.serialize() + ' ';
68  }
69  if (!subterms.isEmpty()) {
70  d->command.chop(1);
71  }
72  }
73  d->command += ")";
74  } else if (subterms.size() == 1) {
75  d->command += subterms.first().serialize();
76  } else {
77  d->isNull = true;
78  }
79 }
80 
81 Term::Term(Term::SearchKey key, const QString& value)
82 : d(new Term::Private)
83 {
84  switch(key) {
85  case All:
86  d->command += "ALL";
87  break;
88  case Bcc:
89  d->command += "BCC";
90  break;
91  case Cc:
92  d->command += "CC";
93  break;
94  case Body:
95  d->command += "BODY";
96  break;
97  case From:
98  d->command += "FROM";
99  break;
100  case Keyword:
101  d->command += "KEYWORD";
102  break;
103  case Subject:
104  d->command += "SUBJECT";
105  break;
106  case Text:
107  d->command += "TEXT";
108  break;
109  case To:
110  d->command += "TO";
111  break;
112  }
113  if (key != All) {
114  d->command += " \"" + QByteArray(value.toUtf8().constData()) + "\"";
115  }
116 }
117 
118 Term::Term( const QString &header, const QString &value )
119 : d(new Term::Private)
120 {
121  d->command += "HEADER";
122  d->command += ' ' + QByteArray(header.toUtf8().constData());
123  d->command += " \"" + QByteArray(value.toUtf8().constData()) + "\"";
124 }
125 
126 Term::Term(Term::BooleanSearchKey key)
127 : d(new Term::Private)
128 {
129  switch (key) {
130  case Answered:
131  d->command = "ANSWERED";
132  break;
133  case Deleted:
134  d->command = "DELETED";
135  break;
136  case Draft:
137  d->command = "DRAFT";
138  break;
139  case Flagged:
140  d->command = "FLAGGED";
141  break;
142  case New:
143  d->command = "NEW";
144  break;
145  case Old:
146  d->command = "OLD";
147  break;
148  case Recent:
149  d->command = "RECENT";
150  break;
151  case Seen:
152  d->command = "SEEN";
153  break;
154  }
155 }
156 
157 QMap<int, QByteArray> initializeMonths()
158 {
159  QMap<int, QByteArray> months;
160  //don't use QDate::shortMonthName(), it returns a localized month name
161  months[1] = "Jan";
162  months[2] = "Feb";
163  months[3] = "Mar";
164  months[4] = "Apr";
165  months[5] = "May";
166  months[6] = "Jun";
167  months[7] = "Jul";
168  months[8] = "Aug";
169  months[9] = "Sep";
170  months[10] = "Oct";
171  months[11] = "Nov";
172  months[12] = "Dec";
173  return months;
174 }
175 
176 static QMap<int, QByteArray> months = initializeMonths();
177 
178 Term::Term(Term::DateSearchKey key, const QDate &date)
179 : d(new Term::Private)
180 {
181  switch (key) {
182  case Before:
183  d->command = "BEFORE";
184  break;
185  case On:
186  d->command = "ON";
187  break;
188  case SentBefore:
189  d->command = "SENTBEFORE";
190  break;
191  case SentOn:
192  d->command = "SENTON";
193  break;
194  case SentSince:
195  d->command = "SENTSINCE";
196  break;
197  case Since:
198  d->command = "SINCE";
199  break;
200  }
201  d->command += " \"";
202  d->command += QByteArray::number( date.day() ) + '-';
203  d->command += months[date.month()] + '-';
204  d->command += QByteArray::number( date.year() );
205  d->command += '\"';
206 }
207 
208 Term::Term(Term::NumberSearchKey key, int value)
209 : d(new Term::Private)
210 {
211  switch (key) {
212  case Larger:
213  d->command = "LARGER";
214  break;
215  case Smaller:
216  d->command = "SMALLER";
217  break;
218  }
219  d->command += " " + QByteArray::number(value);
220 }
221 
222 Term::Term(Term::SequenceSearchKey key, const ImapSet &set)
223 : d(new Term::Private)
224 {
225  switch (key) {
226  case Uid:
227  d->command = "UID";
228  break;
229  case SequenceNumber:
230  break;
231  }
232  d->command += " " + set.toImapSequenceSet();
233 }
234 
235 Term::Term(const Term& other)
236 : d(new Term::Private)
237 {
238  *d = *other.d;
239 }
240 
241 Term& Term::operator=(const Term& other)
242 {
243  *d = *other.d;
244  return *this;
245 }
246 
247 bool Term::operator==(const Term& other) const
248 {
249  return d->command == other.d->command &&
250  d->isNegated == other.d->isNegated &&
251  d->isFuzzy == other.d->isFuzzy;
252 }
253 
254 QByteArray Term::serialize() const
255 {
256  QByteArray command;
257  if (d->isFuzzy) {
258  command = "FUZZY ";
259  }
260  if (d->isNegated) {
261  command = "NOT ";
262  }
263  return command + d->command;
264 }
265 
266 Term &Term::setFuzzy(bool fuzzy)
267 {
268  d->isFuzzy = fuzzy;
269  return *this;
270 }
271 
272 Term &Term::setNegated(bool negated)
273 {
274  d->isNegated = negated;
275  return *this;
276 }
277 
278 bool Term::isNull() const
279 {
280  return d->isNull;
281 }
282 
283 //TODO: when custom error codes are introduced, handle the NO [TRYCREATE] response
284 
285  class SearchJobPrivate : public JobPrivate
286  {
287  public:
288  SearchJobPrivate( Session *session, const QString& name ) : JobPrivate( session, name ), logic( SearchJob::And ) {
289  criteriaMap[SearchJob::All] = "ALL";
290  criteriaMap[SearchJob::Answered] = "ANSWERED";
291  criteriaMap[SearchJob::BCC] = "BCC";
292  criteriaMap[SearchJob::Before] = "BEFORE";
293  criteriaMap[SearchJob::Body] = "BODY";
294  criteriaMap[SearchJob::CC] = "CC";
295  criteriaMap[SearchJob::Deleted] = "DELETED";
296  criteriaMap[SearchJob::Draft] = "DRAFT";
297  criteriaMap[SearchJob::Flagged] = "FLAGGED";
298  criteriaMap[SearchJob::From] = "FROM";
299  criteriaMap[SearchJob::Header] = "HEADER";
300  criteriaMap[SearchJob::Keyword] = "KEYWORD";
301  criteriaMap[SearchJob::Larger] = "LARGER";
302  criteriaMap[SearchJob::New] = "NEW";
303  criteriaMap[SearchJob::Old] = "OLD";
304  criteriaMap[SearchJob::On] = "ON";
305  criteriaMap[SearchJob::Recent] = "RECENT";
306  criteriaMap[SearchJob::Seen] = "SEEN";
307  criteriaMap[SearchJob::SentBefore] = "SENTBEFORE";
308  criteriaMap[SearchJob::SentOn] = "SENTON";
309  criteriaMap[SearchJob::SentSince] = "SENTSINCE";
310  criteriaMap[SearchJob::Since] = "SINCE";
311  criteriaMap[SearchJob::Smaller] = "SMALLER";
312  criteriaMap[SearchJob::Subject] = "SUBJECT";
313  criteriaMap[SearchJob::Text] = "TEXT";
314  criteriaMap[SearchJob::To] = "TO";
315  criteriaMap[SearchJob::Uid] = "UID";
316  criteriaMap[SearchJob::Unanswered] = "UNANSWERED";
317  criteriaMap[SearchJob::Undeleted] = "UNDELETED";
318  criteriaMap[SearchJob::Undraft] = "UNDRAFT";
319  criteriaMap[SearchJob::Unflagged] = "UNFLAGGED";
320  criteriaMap[SearchJob::Unkeyword] = "UNKEYWORD";
321  criteriaMap[SearchJob::Unseen] = "UNSEEN";
322 
323  //don't use QDate::shortMonthName(), it returns a localized month name
324  months[1] = "Jan";
325  months[2] = "Feb";
326  months[3] = "Mar";
327  months[4] = "Apr";
328  months[5] = "May";
329  months[6] = "Jun";
330  months[7] = "Jul";
331  months[8] = "Aug";
332  months[9] = "Sep";
333  months[10] = "Oct";
334  months[11] = "Nov";
335  months[12] = "Dec";
336 
337  nextContent = 0;
338  uidBased = false;
339  }
340  ~SearchJobPrivate() { }
341 
342  QByteArray charset;
343  QList<QByteArray> criterias;
344  QMap<SearchJob::SearchCriteria, QByteArray > criteriaMap;
345  QMap<int, QByteArray> months;
346  SearchJob::SearchLogic logic;
347  QList<QByteArray> contents;
348  QList<qint64> results;
349  uint nextContent;
350  bool uidBased;
351  Term term;
352  };
353 }
354 
355 using namespace KIMAP;
356 
357 SearchJob::SearchJob( Session *session )
358  : Job( *new SearchJobPrivate( session, i18nc( "Name of the search job", "Search" ) ) )
359 {
360 }
361 
362 SearchJob::~SearchJob()
363 {
364 }
365 
366 void SearchJob::setTerm(const Term &term)
367 {
368  Q_D( SearchJob );
369  d->term = term;
370 }
371 
372 void SearchJob::doStart()
373 {
374  Q_D( SearchJob );
375 
376  QByteArray searchKey;
377 
378  if ( !d->charset.isEmpty() ) {
379  searchKey = "CHARSET " + d->charset;
380  }
381 
382  if (!d->term.isNull()) {
383  const QByteArray term = d->term.serialize();
384  if (term.startsWith('(')) {
385  searchKey += term.mid(1, term.size() - 2);
386  } else {
387  searchKey += term;
388  }
389  } else {
390 
391  if ( d->logic == SearchJob::Not ) {
392  searchKey += "NOT ";
393  } else if ( d->logic == SearchJob::Or && d->criterias.size() > 1 ) {
394  searchKey += "OR ";
395  }
396 
397  if ( d->logic == SearchJob::And ) {
398  for ( int i = 0; i < d->criterias.size(); i++ ) {
399  const QByteArray key = d->criterias.at( i );
400  if ( i > 0 ) {
401  searchKey += ' ';
402  }
403  searchKey += key;
404  }
405  } else {
406  for ( int i = 0; i < d->criterias.size(); i++ ) {
407  const QByteArray key = d->criterias.at( i );
408  if ( i > 0 ) {
409  searchKey += ' ';
410  }
411  searchKey += '(' + key + ')';
412  }
413  }
414  }
415 
416  QByteArray command = "SEARCH";
417  if ( d->uidBased ) {
418  command = "UID " + command;
419  }
420 
421  d->tags << d->sessionInternal()->sendCommand( command, searchKey );
422 }
423 
424 void SearchJob::handleResponse( const Message &response )
425 {
426  Q_D( SearchJob );
427 
428  if ( handleErrorReplies( response ) == NotHandled ) {
429  if ( response.content.size() >= 1 && response.content[0].toString() == "+" ) {
430  if (d->term.isNull()) {
431  d->sessionInternal()->sendData( d->contents[d->nextContent] );
432  } else {
433  kWarning() << "The term API only supports inline strings.";
434  }
435  d->nextContent++;
436  } else if ( response.content.size() >= 2 && response.content[1].toString() == "SEARCH" ) {
437  for ( int i = 2; i < response.content.size(); i++ ) {
438  d->results.append( response.content[i].toString().toInt() );
439  }
440  }
441  }
442 }
443 
444 void SearchJob::setCharset( const QByteArray &charset )
445 {
446  Q_D( SearchJob );
447  d->charset = charset;
448 }
449 
450 QByteArray SearchJob::charset() const
451 {
452  Q_D( const SearchJob );
453  return d->charset;
454 }
455 
456 void SearchJob::setSearchLogic( SearchLogic logic )
457 {
458  Q_D( SearchJob );
459  d->logic = logic;
460 }
461 
462 void SearchJob::addSearchCriteria( SearchCriteria criteria )
463 {
464  Q_D( SearchJob );
465 
466  switch ( criteria ) {
467  case All:
468  case Answered:
469  case Deleted:
470  case Draft:
471  case Flagged:
472  case New:
473  case Old:
474  case Recent:
475  case Seen:
476  case Unanswered:
477  case Undeleted:
478  case Undraft:
479  case Unflagged:
480  case Unseen:
481  d->criterias.append( d->criteriaMap[criteria] );
482  break;
483  default:
484  //TODO Discuss if we keep error checking here, or accept anything, even if it is wrong
485  kDebug() << "Criteria " << d->criteriaMap[criteria] << " needs an argument, but none was specified.";
486  break;
487  }
488 }
489 
490 void SearchJob::addSearchCriteria( SearchCriteria criteria, int argument )
491 {
492  Q_D( SearchJob );
493  switch ( criteria ) {
494  case Larger:
495  case Smaller:
496  d->criterias.append( d->criteriaMap[criteria] + ' ' + QByteArray::number( argument ) );
497  break;
498  default:
499  //TODO Discuss if we keep error checking here, or accept anything, even if it is wrong
500  kDebug() << "Criteria " << d->criteriaMap[criteria] << " doesn't accept an integer as an argument.";
501  break;
502  }
503 }
504 
505 void SearchJob::addSearchCriteria( SearchCriteria criteria, const QByteArray &argument )
506 {
507  Q_D( SearchJob );
508  switch ( criteria ) {
509  case BCC:
510  case Body:
511  case CC:
512  case From:
513  case Subject:
514  case Text:
515  case To:
516  d->contents.append( argument );
517  d->criterias.append( d->criteriaMap[criteria] + " {" + QByteArray::number( argument.size() ) + '}' );
518  break;
519  case Keyword:
520  case Unkeyword:
521  case Header:
522  case Uid:
523  d->criterias.append( d->criteriaMap[criteria] + ' ' + argument );
524  break;
525  default:
526  //TODO Discuss if we keep error checking here, or accept anything, even if it is wrong
527  kDebug() << "Criteria " << d->criteriaMap[criteria] << " doesn't accept any argument.";
528  break;
529  }
530 }
531 
532 void SearchJob::addSearchCriteria( SearchCriteria criteria, const QDate &argument )
533 {
534  Q_D( SearchJob );
535  switch ( criteria ) {
536  case Before:
537  case On:
538  case SentBefore:
539  case SentSince:
540  case Since: {
541  QByteArray date = QByteArray::number( argument.day() ) + '-';
542  date += d->months[argument.month()] + '-';
543  date += QByteArray::number( argument.year() );
544  d->criterias.append( d->criteriaMap[criteria] + " \"" + date + '\"' );
545  break;
546  }
547  default:
548  //TODO Discuss if we keep error checking here, or accept anything, even if it is wrong
549  kDebug() << "Criteria " << d->criteriaMap[criteria] << " doesn't accept a date as argument.";
550  break;
551  }
552 }
553 
554 void SearchJob::addSearchCriteria( const QByteArray &searchCriteria )
555 {
556  Q_D( SearchJob );
557  d->criterias.append( searchCriteria );
558 }
559 
560 void SearchJob::setUidBased(bool uidBased)
561 {
562  Q_D( SearchJob );
563  d->uidBased = uidBased;
564 }
565 
566 bool SearchJob::isUidBased() const
567 {
568  Q_D( const SearchJob );
569  return d->uidBased;
570 }
571 
572 QList<qint64> SearchJob::results() const
573 {
574  Q_D( const SearchJob );
575  return d->results;
576 }
577 
578 QList<int> SearchJob::foundItems()
579 {
580  Q_D( const SearchJob );
581 
582  QList<int> results;
583  qCopy( d->results.begin(), d->results.end(), results.begin() );
584 
585  return results;
586 }
QByteArray
QByteArray::at
char at(int i) const
QMap< int, QByteArray >
QByteArray::chop
void chop(int n)
QByteArray::startsWith
bool startsWith(const QByteArray &ba) const
QVector::first
T & first()
QDate::month
int month() const
KIMAP::Term
A query term.
Definition: searchjob.h:43
QDate::day
int day() const
QByteArray::number
QByteArray number(int n, int base)
QByteArray::constData
const char * constData() const
QDate
QVector::mid
QVector< T > mid(int pos, int length) const
QDate::year
int year() const
QString
QList< QByteArray >
QByteArray::mid
QByteArray mid(int pos, int len) const
QByteArray::append
QByteArray & append(char ch)
KIMAP::ImapSet
Represents a set of natural numbers (1-> ) in a as compact as possible form.
Definition: imapset.h:140
QVector::at
const T & at(int i) const
QVector
QVector::isEmpty
bool isEmpty() const
KIMAP::ImapSet::toImapSequenceSet
QByteArray toImapSequenceSet() const
Returns a IMAP-compatible QByteArray representation of this set.
Definition: imapset.cpp:272
QSharedPointer::isNull
bool isNull() const
QByteArray::size
int size() const
QVector::size
int size() const
QList::begin
iterator begin()
QString::toUtf8
QByteArray toUtf8() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:37:03 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIMAP Library

Skip menu "KIMAP Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal