~ubuntu-branches/ubuntu/vivid/akonadi/vivid

« back to all changes in this revision

Viewing changes to server/src/handler/akappend.cpp

  • Committer: Package Import Robot
  • Author(s): Scarlett Clark
  • Date: 2014-07-18 15:38:49 UTC
  • mfrom: (1.1.52)
  • Revision ID: package-import@ubuntu.com-20140718153849-mrt24e3jsxxikq7m
Tags: 1.12.91-0ubuntu1
* New upstream release needed for KDE 4.13.90 beta 2
* Remove patches applied upstream.
* Update symbols with batchpatch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
#include "storage/transaction.h"
35
35
#include "storage/parttypehelper.h"
36
36
#include "storage/dbconfig.h"
 
37
#include "storage/partstreamer.h"
37
38
#include "storage/parthelper.h"
38
39
#include "libs/protocol_p.h"
39
40
 
51
52
{
52
53
}
53
54
 
54
 
bool AkAppend::buildPimItem( PimItem &item, const QByteArray &mailbox, qint64 size, const QList<QByteArray> &flags, const QDateTime &dateTime, QList<QByteArray> &itemFlags )
55
 
{
 
55
QByteArray AkAppend::parseFlag( const QByteArray &flag ) const
 
56
{
 
57
    const int pos1 = flag.indexOf( '[' );
 
58
    const int pos2 = flag.lastIndexOf( ']' );
 
59
    return flag.mid( pos1 + 1, pos2 - pos1 - 1 );
 
60
}
 
61
 
 
62
bool AkAppend::buildPimItem( PimItem &item, Collection &col,
 
63
                             ChangedAttributes &itemFlags,
 
64
                             ChangedAttributes &itemTagsRID,
 
65
                             ChangedAttributes &itemTagsGID )
 
66
{
 
67
      // Arguments:  mailbox name
 
68
      //        OPTIONAL flag parenthesized list
 
69
      //        OPTIONAL date/time string
 
70
      //        (partname literal)+
 
71
      //
 
72
      // Syntax:
 
73
      // x-akappend = "X-AKAPPEND" SP mailbox SP size [SP flag-list] [SP date-time] SP (partname SP literal)+
 
74
    const QByteArray mailbox = m_streamParser->readString();
 
75
 
 
76
    const qint64 size = m_streamParser->readNumber();
 
77
    // parse optional flag parenthesized list
 
78
    // Syntax:
 
79
    // flag-list      = "(" [flag *(SP flag)] ")"
 
80
    // flag           = "\ANSWERED" / "\FLAGGED" / "\DELETED" / "\SEEN" /
 
81
    //                  "\DRAFT" / flag-keyword / flag-extension
 
82
    //                    ; Does not include "\Recent"
 
83
    // flag-extension = "\" atom
 
84
    // flag-keyword   = atom
 
85
    QList<QByteArray> flags;
 
86
    if ( m_streamParser->hasList() ) {
 
87
      flags = m_streamParser->readParenthesizedList();
 
88
    }
 
89
 
 
90
    // parse optional date/time string
 
91
    QDateTime dateTime;
 
92
    if ( m_streamParser->hasDateTime() ) {
 
93
      dateTime = m_streamParser->readDateTime().toUTC();
 
94
      // FIXME Should we return an error if m_dateTime is invalid?
 
95
    } else {
 
96
      // if date/time is not given then it will be set to the current date/time
 
97
      // converted to UTC.
 
98
      dateTime = QDateTime::currentDateTime().toUTC();
 
99
    }
 
100
 
56
101
    Response response;
57
102
 
58
 
    const Collection col = HandlerHelper::collectionFromIdOrName( mailbox );
 
103
    col = HandlerHelper::collectionFromIdOrName( mailbox );
59
104
    if ( !col.isValid() ) {
60
105
      throw HandlerException( QByteArray( "Unknown collection for '" ) + mailbox + QByteArray( "'." ) );
61
106
    }
 
107
    if ( col.isVirtual() ) {
 
108
      throw HandlerException( "Cannot append item into virtual collection" );
 
109
    }
62
110
 
63
111
    QByteArray mt;
64
112
    QString remote_id;
66
114
    QString gid;
67
115
    Q_FOREACH ( const QByteArray &flag, flags ) {
68
116
      if ( flag.startsWith( AKONADI_FLAG_MIMETYPE ) ) {
69
 
        int pos1 = flag.indexOf( '[' );
70
 
        int pos2 = flag.indexOf( ']', pos1 );
71
 
        mt = flag.mid( pos1 + 1, pos2 - pos1 - 1 );
 
117
        mt = parseFlag( flag );
72
118
      } else if ( flag.startsWith( AKONADI_FLAG_REMOTEID ) ) {
73
 
        int pos1 = flag.indexOf( '[' );
74
 
        int pos2 = flag.lastIndexOf( ']' );
75
 
        remote_id = QString::fromUtf8( flag.mid( pos1 + 1, pos2 - pos1 - 1 ) );
 
119
        remote_id = QString::fromUtf8( parseFlag( flag ) );
76
120
      } else if ( flag.startsWith( AKONADI_FLAG_REMOTEREVISION ) ) {
77
 
        int pos1 = flag.indexOf( '[' );
78
 
        int pos2 = flag.lastIndexOf( ']' );
79
 
        remote_revision = QString::fromUtf8( flag.mid( pos1 + 1, pos2 - pos1 - 1 ) );
 
121
        remote_revision = QString::fromUtf8( parseFlag( flag ) );
80
122
      } else if ( flag.startsWith( AKONADI_FLAG_GID ) ) {
81
 
        int pos1 = flag.indexOf( '[' );
82
 
        int pos2 = flag.lastIndexOf( ']' );
83
 
        gid = QString::fromUtf8( flag.mid( pos1 + 1, pos2 - pos1 - 1 ) );
 
123
        gid = QString::fromUtf8( parseFlag( flag ) );
 
124
      } else if ( flag.startsWith( "+" AKONADI_FLAG_TAG ) ) {
 
125
        itemTagsGID.incremental = true;
 
126
        itemTagsGID.added.append( parseFlag( flag ) );
 
127
      } else if ( flag.startsWith( "-" AKONADI_FLAG_TAG ) ) {
 
128
        itemTagsGID.incremental = true;
 
129
        itemTagsGID.removed.append( parseFlag( flag ) );
 
130
      } else if ( flag.startsWith( AKONADI_FLAG_TAG ) ) {
 
131
        itemTagsGID.incremental = false;
 
132
        itemTagsGID.added.append( parseFlag( flag ) );
 
133
      } else if ( flag.startsWith( "+" AKONADI_FLAG_RTAG ) ) {
 
134
        itemTagsRID.incremental = true;
 
135
        itemTagsRID.added.append( parseFlag( flag ) );
 
136
      } else if ( flag.startsWith( "-" AKONADI_FLAG_RTAG ) ) {
 
137
        itemTagsRID.incremental = true;
 
138
        itemTagsRID.removed.append( parseFlag( flag ) );
 
139
      } else if ( flag.startsWith( AKONADI_FLAG_RTAG ) ) {
 
140
        itemTagsRID.incremental = false;
 
141
        itemTagsRID.added.append( parseFlag( flag ) );
 
142
      } else if ( flag.startsWith( '+' ) ) {
 
143
        itemFlags.incremental = true;
 
144
        itemFlags.added.append( flag.mid( 1 ) );
 
145
      } else if ( flag.startsWith( '-' ) ) {
 
146
        itemFlags.incremental = true;
 
147
        itemFlags.removed.append( flag.mid( 1 ) );
84
148
      } else {
85
 
        itemFlags << flag;
 
149
        itemFlags.incremental = false;
 
150
        itemFlags.added.append( flag );
86
151
      }
87
152
    }
88
153
    // standard imap does not know this attribute, so that's mail
190
255
  return true;
191
256
}
192
257
 
193
 
bool AkAppend::parseStream()
 
258
bool AkAppend::insertItem( PimItem &item, const Collection &parentCol,
 
259
                           const QVector<QByteArray> &itemFlags,
 
260
                           const QVector<QByteArray> &itemTagsRID,
 
261
                           const QVector<QByteArray> &itemTagsGID )
194
262
{
195
 
    // Arguments:  mailbox name
196
 
    //        OPTIONAL flag parenthesized list
197
 
    //        OPTIONAL date/time string
198
 
    //        (partname literal)+
199
 
    //
200
 
    // Syntax:
201
 
    // x-akappend = "X-AKAPPEND" SP mailbox SP size [SP flag-list] [SP date-time] SP (partname SP literal)+
202
 
 
203
 
  const QByteArray mailbox = m_streamParser->readString();
204
 
 
205
 
  const qint64 size = m_streamParser->readNumber();
206
 
 
207
 
  // parse optional flag parenthesized list
208
 
  // Syntax:
209
 
  // flag-list      = "(" [flag *(SP flag)] ")"
210
 
  // flag           = "\ANSWERED" / "\FLAGGED" / "\DELETED" / "\SEEN" /
211
 
  //                  "\DRAFT" / flag-keyword / flag-extension
212
 
  //                    ; Does not include "\Recent"
213
 
  // flag-extension = "\" atom
214
 
  // flag-keyword   = atom
215
 
  QList<QByteArray> flags;
216
 
  if ( m_streamParser->hasList() ) {
217
 
    flags = m_streamParser->readParenthesizedList();
218
 
  }
219
 
 
220
 
  // parse optional date/time string
221
 
  QDateTime dateTime;
222
 
  if ( m_streamParser->hasDateTime() ) {
223
 
    dateTime = m_streamParser->readDateTime().toUTC();
224
 
    // FIXME Should we return an error if m_dateTime is invalid?
225
 
  } else {
226
 
    // if date/time is not given then it will be set to the current date/time
227
 
    // converted to UTC.
228
 
    dateTime = QDateTime::currentDateTime().toUTC();
229
 
  }
230
 
 
231
 
  // FIXME: The streaming/reading of all item parts can hold the transaction for
232
 
  // unnecessary long time -> should we wrap the PimItem into one transaction
233
 
  // and try to insert Parts independently? In case we fail to insert a part,
234
 
  // it's not a problem as it can be re-fetched at any time, except for attributes.
235
 
  DataStore *db = DataStore::self();
236
 
  Transaction transaction( db );
237
 
 
238
 
  QList<QByteArray> itemFlags;
239
 
  PimItem item;
240
 
  if ( !buildPimItem( item, mailbox, size, flags, dateTime, itemFlags ) ) {
241
 
    return false;
242
 
  }
243
263
  if ( !item.insert() ) {
244
264
    return failureResponse( "Failed to append item" );
245
265
  }
246
266
 
247
267
  // set message flags
248
268
  // This will hit an entry in cache inserted there in buildPimItem()
249
 
  const Collection col = HandlerHelper::collectionFromIdOrName( mailbox );
250
269
  const Flag::List flagList = HandlerHelper::resolveFlags( itemFlags );
251
270
  bool flagsChanged = false;
252
 
  if ( !db->appendItemsFlags( PimItem::List() << item, flagList, flagsChanged, false, col, true ) ) {
 
271
  if ( !DataStore::self()->appendItemsFlags( PimItem::List() << item, flagList, &flagsChanged, false, parentCol, true ) ) {
253
272
    return failureResponse( "Unable to append item flags." );
254
273
  }
255
274
 
 
275
  Tag::List tagList;
 
276
  if ( !itemTagsGID.isEmpty() ) {
 
277
    tagList << HandlerHelper::resolveTagsByGID( itemTagsGID );
 
278
  }
 
279
  if ( !itemTagsRID.isEmpty() ) {
 
280
    tagList << HandlerHelper::resolveTagsByRID( itemTagsRID, connection()->context() );
 
281
  }
 
282
  bool tagsChanged;
 
283
  if ( !DataStore::self()->appendItemsTags( PimItem::List() << item, tagList, &tagsChanged, false, parentCol, true ) ) {
 
284
    return failureResponse( "Unable to append item tags." );
 
285
  }
 
286
 
256
287
  // Handle individual parts
257
288
  qint64 partSizes = 0;
258
289
  if ( connection()->capabilities().akAppendStreaming() ) {
259
 
    QByteArray error, partName /* unused */;
 
290
    QByteArray partName /* unused */;
260
291
    qint64 partSize;
261
292
    m_streamParser->beginList();
 
293
    PartStreamer streamer(connection(), m_streamParser, item, this);
 
294
    connect( &streamer, SIGNAL(responseAvailable(Akonadi::Server::Response)),
 
295
             this, SIGNAL(responseAvailable(Akonadi::Server::Response)) );
262
296
    while ( !m_streamParser->atListEnd() ) {
263
297
      QByteArray command = m_streamParser->readString();
264
298
      if ( command.isEmpty() ) {
265
299
        throw HandlerException( "Syntax error" );
266
300
      }
267
 
      if ( !PartHelper::storeStreamedParts( command, m_streamParser, item, false, partName, partSize, error ) ) {
268
 
        return failureResponse( error );
 
301
 
 
302
      if ( !streamer.stream( command, false, partName, partSize ) ) {
 
303
        throw HandlerException( streamer.error() );
269
304
      }
 
305
 
270
306
      partSizes += partSize;
271
307
    }
272
308
 
283
319
  }
284
320
 
285
321
  // Preprocessing
286
 
  const bool doPreprocessing = PreprocessorManager::instance()->isActive();
287
 
  if ( doPreprocessing ) {
 
322
  if ( PreprocessorManager::instance()->isActive() ) {
288
323
    Part hiddenAttribute;
289
324
    hiddenAttribute.setPimItemId( item.id() );
290
325
    hiddenAttribute.setPartType( PartTypeHelper::fromFqName( QString::fromLatin1( AKONADI_ATTRIBUTE_HIDDEN ) ) );
291
326
    hiddenAttribute.setData( QByteArray() );
 
327
    hiddenAttribute.setDatasize( 0 );
292
328
    // TODO: Handle errors? Technically, this is not a critical issue as no data are lost
293
329
    PartHelper::insert( &hiddenAttribute );
294
330
  }
295
331
 
296
 
  // All SQL is done, let's commit!
297
 
  if ( !transaction.commit() ) {
298
 
    return failureResponse( "Failed to commit transaction" );
299
 
  }
300
 
 
301
 
  DataStore::self()->notificationCollector()->itemAdded( item, col );
302
 
 
303
 
  if ( doPreprocessing ) {
 
332
  return true;
 
333
}
 
334
 
 
335
bool AkAppend::notify( const PimItem &item, const Collection &collection )
 
336
{
 
337
  DataStore::self()->notificationCollector()->itemAdded( item, collection );
 
338
 
 
339
  if ( PreprocessorManager::instance()->isActive() ) {
304
340
    // enqueue the item for preprocessing
305
 
    PreprocessorManager::instance()->beginHandleItem( item, db );
 
341
    PreprocessorManager::instance()->beginHandleItem( item, DataStore::self() );
306
342
  }
 
343
  return true;
 
344
}
307
345
 
 
346
bool AkAppend::sendResponse( const QByteArray &responseStr, const PimItem &item )
 
347
{
308
348
  // Date time is always stored in UTC time zone by the server.
309
349
  const QString datetime = QLocale::c().toString( item.datetime(), QLatin1String( "dd-MMM-yyyy hh:mm:ss +0000" ) );
310
350
 
311
 
  // ...aaaaaand done.
312
351
  Response response;
313
352
  response.setTag( tag() );
314
353
  response.setUserDefined();
316
355
  Q_EMIT responseAvailable( response );
317
356
 
318
357
  response.setSuccess();
319
 
  response.setString( "Append completed" );
 
358
  response.setString( responseStr );
320
359
  Q_EMIT responseAvailable( response );
321
360
  return true;
322
361
}
 
362
 
 
363
 
 
364
bool AkAppend::parseStream()
 
365
{
 
366
  // FIXME: The streaming/reading of all item parts can hold the transaction for
 
367
  // unnecessary long time -> should we wrap the PimItem into one transaction
 
368
  // and try to insert Parts independently? In case we fail to insert a part,
 
369
  // it's not a problem as it can be re-fetched at any time, except for attributes.
 
370
  DataStore *db = DataStore::self();
 
371
  Transaction transaction( db );
 
372
 
 
373
  ChangedAttributes itemFlags, itemTagsRID, itemTagsGID;
 
374
  Collection parentCol;
 
375
  PimItem item;
 
376
  if ( !buildPimItem( item, parentCol, itemFlags, itemTagsRID, itemTagsGID ) ) {
 
377
    return false;
 
378
  }
 
379
 
 
380
  if ( itemFlags.incremental ) {
 
381
    throw HandlerException( "Incremental flags changes are not allowed in AK-APPEND" );
 
382
  }
 
383
  if ( itemTagsRID.incremental || itemTagsRID.incremental ) {
 
384
    throw HandlerException( "Incremental tags changes are not allowed in AK-APPEND" );
 
385
  }
 
386
 
 
387
  if ( !insertItem( item, parentCol, itemFlags.added, itemTagsRID.added, itemTagsGID.added ) ) {
 
388
    return false;
 
389
  }
 
390
 
 
391
  // All SQL is done, let's commit!
 
392
  if ( !transaction.commit() ) {
 
393
    return failureResponse( "Failed to commit transaction" );
 
394
  }
 
395
 
 
396
  notify( item, parentCol );
 
397
  return sendResponse( "Append completed", item );
 
398
}