54
bool AkAppend::buildPimItem( PimItem &item, const QByteArray &mailbox, qint64 size, const QList<QByteArray> &flags, const QDateTime &dateTime, QList<QByteArray> &itemFlags )
55
QByteArray AkAppend::parseFlag( const QByteArray &flag ) const
57
const int pos1 = flag.indexOf( '[' );
58
const int pos2 = flag.lastIndexOf( ']' );
59
return flag.mid( pos1 + 1, pos2 - pos1 - 1 );
62
bool AkAppend::buildPimItem( PimItem &item, Collection &col,
63
ChangedAttributes &itemFlags,
64
ChangedAttributes &itemTagsRID,
65
ChangedAttributes &itemTagsGID )
67
// Arguments: mailbox name
68
// OPTIONAL flag parenthesized list
69
// OPTIONAL date/time string
70
// (partname literal)+
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();
76
const qint64 size = m_streamParser->readNumber();
77
// parse optional flag parenthesized list
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();
90
// parse optional date/time string
92
if ( m_streamParser->hasDateTime() ) {
93
dateTime = m_streamParser->readDateTime().toUTC();
94
// FIXME Should we return an error if m_dateTime is invalid?
96
// if date/time is not given then it will be set to the current date/time
98
dateTime = QDateTime::currentDateTime().toUTC();
56
101
Response response;
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( "'." ) );
107
if ( col.isVirtual() ) {
108
throw HandlerException( "Cannot append item into virtual collection" );
64
112
QString remote_id;
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 ) );
149
itemFlags.incremental = false;
150
itemFlags.added.append( flag );
88
153
// standard imap does not know this attribute, so that's mail
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 )
195
// Arguments: mailbox name
196
// OPTIONAL flag parenthesized list
197
// OPTIONAL date/time string
198
// (partname literal)+
201
// x-akappend = "X-AKAPPEND" SP mailbox SP size [SP flag-list] [SP date-time] SP (partname SP literal)+
203
const QByteArray mailbox = m_streamParser->readString();
205
const qint64 size = m_streamParser->readNumber();
207
// parse optional flag parenthesized list
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();
220
// parse optional date/time string
222
if ( m_streamParser->hasDateTime() ) {
223
dateTime = m_streamParser->readDateTime().toUTC();
224
// FIXME Should we return an error if m_dateTime is invalid?
226
// if date/time is not given then it will be set to the current date/time
228
dateTime = QDateTime::currentDateTime().toUTC();
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 );
238
QList<QByteArray> itemFlags;
240
if ( !buildPimItem( item, mailbox, size, flags, dateTime, itemFlags ) ) {
243
263
if ( !item.insert() ) {
244
264
return failureResponse( "Failed to append item" );
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." );
276
if ( !itemTagsGID.isEmpty() ) {
277
tagList << HandlerHelper::resolveTagsByGID( itemTagsGID );
279
if ( !itemTagsRID.isEmpty() ) {
280
tagList << HandlerHelper::resolveTagsByRID( itemTagsRID, connection()->context() );
283
if ( !DataStore::self()->appendItemsTags( PimItem::List() << item, tagList, &tagsChanged, false, parentCol, true ) ) {
284
return failureResponse( "Unable to append item tags." );
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 */;
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" );
267
if ( !PartHelper::storeStreamedParts( command, m_streamParser, item, false, partName, partSize, error ) ) {
268
return failureResponse( error );
302
if ( !streamer.stream( command, false, partName, partSize ) ) {
303
throw HandlerException( streamer.error() );
270
306
partSizes += partSize;
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 );
296
// All SQL is done, let's commit!
297
if ( !transaction.commit() ) {
298
return failureResponse( "Failed to commit transaction" );
301
DataStore::self()->notificationCollector()->itemAdded( item, col );
303
if ( doPreprocessing ) {
335
bool AkAppend::notify( const PimItem &item, const Collection &collection )
337
DataStore::self()->notificationCollector()->itemAdded( item, collection );
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() );
346
bool AkAppend::sendResponse( const QByteArray &responseStr, const PimItem &item )
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" ) );
312
351
Response response;
313
352
response.setTag( tag() );
314
353
response.setUserDefined();
316
355
Q_EMIT responseAvailable( response );
318
357
response.setSuccess();
319
response.setString( "Append completed" );
358
response.setString( responseStr );
320
359
Q_EMIT responseAvailable( response );
364
bool AkAppend::parseStream()
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 );
373
ChangedAttributes itemFlags, itemTagsRID, itemTagsGID;
374
Collection parentCol;
376
if ( !buildPimItem( item, parentCol, itemFlags, itemTagsRID, itemTagsGID ) ) {
380
if ( itemFlags.incremental ) {
381
throw HandlerException( "Incremental flags changes are not allowed in AK-APPEND" );
383
if ( itemTagsRID.incremental || itemTagsRID.incremental ) {
384
throw HandlerException( "Incremental tags changes are not allowed in AK-APPEND" );
387
if ( !insertItem( item, parentCol, itemFlags.added, itemTagsRID.added, itemTagsGID.added ) ) {
391
// All SQL is done, let's commit!
392
if ( !transaction.commit() ) {
393
return failureResponse( "Failed to commit transaction" );
396
notify( item, parentCol );
397
return sendResponse( "Append completed", item );