~ubuntu-branches/debian/stretch/akonadi/stretch

« back to all changes in this revision

Viewing changes to .pc/upstream_dont_leak_old_external_payload_files.patch/server/src/storage/partstreamer.cpp

  • Committer: Package Import Robot
  • Author(s): Lisandro Damián Nicanor Pérez Meyer
  • Date: 2015-06-30 12:03:32 UTC
  • Revision ID: package-import@ubuntu.com-20150630120332-jzlmtqpwv0pa0tj0
Tags: 1.13.0-3
* Team upload.
* Apply upstream_dont_leak_old_external_payload_files.patch which fixes a bug
  that let old files be kept when they should be removed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2014  Daniel Vrátil <dvratil@redhat.com>
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2.1 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Lesser General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Lesser General Public
 
15
 * License along with this library; if not, write to the Free Software
 
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
17
 *
 
18
 */
 
19
 
 
20
#include "partstreamer.h"
 
21
#include "parthelper.h"
 
22
#include "parttypehelper.h"
 
23
#include "selectquerybuilder.h"
 
24
#include "dbconfig.h"
 
25
#include "connection.h"
 
26
#include "capabilities_p.h"
 
27
#include "imapstreamparser.h"
 
28
#include "response.h"
 
29
 
 
30
#include <libs/imapparser_p.h>
 
31
#include <libs/protocol_p.h>
 
32
#include <shared/akstandarddirs.h>
 
33
 
 
34
#include <unistd.h>
 
35
 
 
36
#include <QFile>
 
37
#include <QFileInfo>
 
38
 
 
39
using namespace Akonadi;
 
40
using namespace Akonadi::Server;
 
41
 
 
42
PartStreamer::PartStreamer(Connection *connection, ImapStreamParser *parser,
 
43
                           const PimItem &pimItem, QObject *parent)
 
44
    : QObject(parent)
 
45
    , mConnection(connection)
 
46
    , mStreamParser(parser)
 
47
    , mItem(pimItem)
 
48
{
 
49
    // Make sure the file_db_data path exists
 
50
    AkStandardDirs::saveDir( "data", QLatin1String( "file_db_data" ) );
 
51
}
 
52
 
 
53
PartStreamer::~PartStreamer()
 
54
{
 
55
}
 
56
 
 
57
QByteArray PartStreamer::error() const
 
58
{
 
59
    return mError;
 
60
}
 
61
 
 
62
bool PartStreamer::streamNonliteral(Part& part, qint64& partSize, QByteArray& value)
 
63
{
 
64
    value = mStreamParser->readString();
 
65
    if (part.partType().ns() == QLatin1String("PLD")) {
 
66
        partSize = value.size();
 
67
    }
 
68
    if (!mDataChanged) {
 
69
        mDataChanged = (value.size() != part.datasize());
 
70
    }
 
71
 
 
72
    // only relevant for non-literals or non-external literals
 
73
    // only fallback to data comparision if part already exists and sizes match
 
74
    if (!mDataChanged) {
 
75
        mDataChanged = (value != PartHelper::translateData(part));
 
76
    }
 
77
 
 
78
    if (mDataChanged) {
 
79
        if (part.isValid()) {
 
80
            PartHelper::update(&part, value, value.size());
 
81
        } else {
 
82
//           akDebug() << "insert from Store::handleLine: " << value.left(100);
 
83
            part.setData(value);
 
84
            part.setDatasize(value.size());
 
85
            if (!PartHelper::insert(&part)) {
 
86
                mError = "Unable to add item part";
 
87
                return false;
 
88
            }
 
89
        }
 
90
    }
 
91
 
 
92
    return true;
 
93
}
 
94
 
 
95
bool PartStreamer::streamLiteral(Part& part, qint64& partSize, QByteArray& value)
 
96
{
 
97
    const qint64 dataSize = mStreamParser->remainingLiteralSize();
 
98
    if (part.partType().ns() == QLatin1String("PLD")) {
 
99
        partSize = dataSize;
 
100
        // Shortcut: if sizes differ, we don't need to compare data later no in order
 
101
        // to detect whether the part has changed
 
102
        if (!mDataChanged) {
 
103
            mDataChanged = (partSize != part.datasize());
 
104
        }
 
105
    }
 
106
 
 
107
    //actual case when streaming storage is used: external payload is enabled, data is big enough in a literal
 
108
    const bool storeInFile = dataSize > DbConfig::configuredDatabase()->sizeThreshold();
 
109
    if (storeInFile) {
 
110
        return streamLiteralToFile(dataSize, part, value);
 
111
    } else {
 
112
        mStreamParser->sendContinuationResponse(dataSize);
 
113
        //don't write in streaming way as the data goes to the database
 
114
        while (!mStreamParser->atLiteralEnd()) {
 
115
            value += mStreamParser->readLiteralPart();
 
116
        }
 
117
        if (part.isValid()) {
 
118
            PartHelper::update(&part, value, value.size());
 
119
        } else {
 
120
            part.setData(value);
 
121
            part.setDatasize(value.size());
 
122
            if (!part.insert()) {
 
123
              mError = "Failed to insert part to database";
 
124
              return false;
 
125
            }
 
126
        }
 
127
    }
 
128
 
 
129
    return true;
 
130
}
 
131
 
 
132
 
 
133
bool PartStreamer::streamLiteralToFile(qint64 dataSize, Part &part, QByteArray &value)
 
134
{
 
135
    const bool directStreaming = mConnection->capabilities().directStreaming();
 
136
 
 
137
    QByteArray origData;
 
138
    if (!mDataChanged && mCheckChanged) {
 
139
        origData = PartHelper::translateData(part);
 
140
    }
 
141
 
 
142
    if (directStreaming) {
 
143
        bool ok = streamLiteralToFileDirectly(dataSize, part);
 
144
        if (!ok) {
 
145
            return false;
 
146
        }
 
147
    } else {
 
148
        mStreamParser->sendContinuationResponse(dataSize);
 
149
 
 
150
        // use first part as value for the initial insert into / update to the database.
 
151
        // this will give us a proper filename to stream the rest of the parts contents into
 
152
        // NOTE: we have to set the correct size (== dataSize) directly
 
153
        value = mStreamParser->readLiteralPart();
 
154
        // akDebug() << Q_FUNC_INFO << "VALUE in STORE: " << value << value.size() << dataSize;
 
155
 
 
156
        if (part.isValid()) {
 
157
            PartHelper::update(&part, value, dataSize);
 
158
        } else {
 
159
            part.setData( value );
 
160
            part.setDatasize( dataSize );
 
161
            if ( !PartHelper::insert( &part ) ) {
 
162
                mError = "Unable to add item part";
 
163
                return false;
 
164
            }
 
165
        }
 
166
 
 
167
        //the actual streaming code for the remaining parts:
 
168
        // reads from the parser, writes immediately to the file
 
169
        QFile partFile(PartHelper::resolveAbsolutePath(part.data()));
 
170
        try {
 
171
            PartHelper::streamToFile(mStreamParser, partFile, QIODevice::WriteOnly | QIODevice::Append);
 
172
        } catch (const PartHelperException &e) {
 
173
            mError = e.what();
 
174
            return false;
 
175
        }
 
176
    }
 
177
 
 
178
    if (mCheckChanged && !mDataChanged) {
 
179
        // This is invoked only when part already exists, data sizes match and
 
180
        // caller wants to know whether parts really differ
 
181
        mDataChanged = (origData != PartHelper::translateData(part));
 
182
    }
 
183
 
 
184
    return true;
 
185
}
 
186
 
 
187
bool PartStreamer::streamLiteralToFileDirectly(qint64 dataSize, Part &part)
 
188
{
 
189
    QString filename;
 
190
    if (part.isValid()) {
 
191
        if (part.external()) {
 
192
            // Part was external and is still external
 
193
            filename = QString::fromLatin1(part.data());
 
194
        } else {
 
195
            // Part wasn't external, but is now
 
196
            filename = PartHelper::fileNameForPart(&part);
 
197
        }
 
198
        filename = PartHelper::updateFileNameRevision(filename);
 
199
    }
 
200
 
 
201
 
 
202
    QFileInfo finfo(filename);
 
203
    if (finfo.isAbsolute()) {
 
204
        filename = finfo.fileName();
 
205
    }
 
206
 
 
207
    part.setExternal(true);
 
208
    part.setDatasize(dataSize);
 
209
    part.setData(filename.toLatin1());
 
210
 
 
211
    if (part.isValid()) {
 
212
        if (!part.update()) {
 
213
            mError = "Failed to update part in database";
 
214
            return false;
 
215
        }
 
216
    } else {
 
217
        if (!part.insert()) {
 
218
            mError = "Failed to insert part into database";
 
219
            return false;
 
220
        }
 
221
 
 
222
        filename = PartHelper::updateFileNameRevision(PartHelper::fileNameForPart(&part));
 
223
        part.setData(filename.toLatin1());
 
224
        part.update();
 
225
    }
 
226
 
 
227
    Response response;
 
228
    response.setContinuation();
 
229
    response.setString("STREAM [FILE " + part.data() + "]");
 
230
    Q_EMIT responseAvailable(response);
 
231
 
 
232
    const QByteArray reply = mStreamParser->peekString();
 
233
    if (reply.startsWith("NO")) {
 
234
        akError() << "Client failed to store payload into file";
 
235
        mError = "Client failed to store payload into file";
 
236
        return false;
 
237
    }
 
238
 
 
239
    QFile file(PartHelper::resolveAbsolutePath(filename.toLatin1()), this);
 
240
    if (!file.exists()) {
 
241
        akError() << "External payload file does not exist";
 
242
        mError = "External payload file does not exist";
 
243
        return false;
 
244
    }
 
245
 
 
246
    if (file.size() != dataSize) {
 
247
        akError() << "Size mismatch! Client advertised" << dataSize << "bytes, but the file is" << file.size() << "bytes!";
 
248
        mError = "Payload size mismatch";
 
249
        return false;
 
250
    }
 
251
 
 
252
    return true;
 
253
}
 
254
 
 
255
 
 
256
bool PartStreamer::stream(const QByteArray &command, bool checkExists,
 
257
                          QByteArray &partName, qint64 &partSize, bool *changed)
 
258
{
 
259
    int partVersion = 0;
 
260
    partSize = 0;
 
261
    mError.clear();
 
262
    mDataChanged = false;
 
263
    mCheckChanged = (changed != 0);
 
264
    if (changed != 0) {
 
265
        *changed = false;
 
266
    }
 
267
 
 
268
    ImapParser::splitVersionedKey(command, partName, partVersion);
 
269
    const PartType partType = PartTypeHelper::fromFqName( partName );
 
270
 
 
271
    Part part;
 
272
 
 
273
    if (checkExists || mCheckChanged) {
 
274
        SelectQueryBuilder<Part> qb;
 
275
        qb.addValueCondition(Part::pimItemIdColumn(), Query::Equals, mItem.id());
 
276
        qb.addValueCondition(Part::partTypeIdColumn(), Query::Equals, partType.id());
 
277
        if (!qb.exec()) {
 
278
            mError = "Unable to check item part existence";
 
279
            return false;
 
280
        }
 
281
 
 
282
        Part::List result = qb.result();
 
283
        if (!result.isEmpty()) {
 
284
            part = result.first();
 
285
        }
 
286
    }
 
287
 
 
288
    // Shortcut: newly created parts are always "changed"
 
289
    if (!part.isValid()) {
 
290
        mDataChanged = true;
 
291
    }
 
292
 
 
293
    part.setPartType(partType);
 
294
    part.setVersion(partVersion);
 
295
    part.setPimItemId(mItem.id());
 
296
 
 
297
    QByteArray value;
 
298
    bool ok;
 
299
    if (mStreamParser->hasLiteral(false)) {
 
300
        ok = streamLiteral(part, partSize, value);
 
301
    } else {
 
302
        ok = streamNonliteral(part, partSize, value);
 
303
    }
 
304
 
 
305
    if (ok && mCheckChanged) {
 
306
        *changed = mDataChanged;
 
307
    }
 
308
 
 
309
    return ok;
 
310
}
 
311
 
 
312
 
 
313