summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2017-09-13 12:42:31 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2017-09-13 12:42:31 +0200
commitc12a9a09da59b9e418316dba02e6215cb55e47ee (patch)
tree05498d9a42e399bcca787f40c1fc473fb09e680e
parent55fe06979ceebe67553135b43aa47e70d931304b (diff)
parentebdb89b8bb482bbb5ecd544c3d38bef35fc7d820 (diff)
downloadsink-c12a9a09da59b9e418316dba02e6215cb55e47ee.tar.gz
sink-c12a9a09da59b9e418316dba02e6215cb55e47ee.zip
Merge commit 'ebdb89b8bb482bbb5ecd544c3d38bef35fc7d820'v0.4.0
-rw-r--r--CMakeLists.txt9
-rw-r--r--cmake/modules/FindLibgit2.cmake2
-rw-r--r--common/CMakeLists.txt4
-rw-r--r--common/asyncutils.h2
-rw-r--r--common/definitions.cpp18
-rw-r--r--common/definitions.h8
-rw-r--r--common/domain/applicationdomaintype.cpp17
-rw-r--r--common/domain/applicationdomaintype.h15
-rw-r--r--common/domain/applicationdomaintype_p.h2
-rw-r--r--common/log.cpp50
-rw-r--r--common/mail/threadindexer.cpp30
-rw-r--r--common/mailpreprocessor.cpp22
-rw-r--r--common/modelresult.cpp10
-rw-r--r--common/notification.cpp2
-rw-r--r--common/notifier.cpp4
-rw-r--r--common/resource.cpp8
-rw-r--r--common/resourceaccess.cpp2
-rw-r--r--common/resourcefacade.cpp53
-rw-r--r--common/resourcefacade.h1
-rw-r--r--common/storage.h6
-rw-r--r--common/storage/entitystore.cpp12
-rw-r--r--common/storage_common.cpp12
-rw-r--r--common/storage_lmdb.cpp31
-rw-r--r--common/store.cpp44
-rw-r--r--common/store.h3
-rw-r--r--common/synchronizer.cpp19
-rw-r--r--dist/sink.spec6
-rw-r--r--examples/davresource/CMakeLists.txt3
-rw-r--r--examples/davresource/davresource.cpp28
-rw-r--r--examples/dummyresource/CMakeLists.txt3
-rw-r--r--examples/imapresource/CMakeLists.txt3
-rw-r--r--examples/imapresource/imapresource.cpp11
-rw-r--r--examples/imapresource/imapserverproxy.cpp35
-rw-r--r--examples/imapresource/imapserverproxy.h4
-rw-r--r--examples/maildirresource/CMakeLists.txt3
-rw-r--r--examples/mailtransportresource/CMakeLists.txt18
-rw-r--r--sinksh/CMakeLists.txt1
-rw-r--r--sinksh/syntax_modules/sink_info.cpp51
-rw-r--r--sinksh/syntax_modules/sink_inspect.cpp79
-rw-r--r--sinksh/syntax_modules/sink_show.cpp27
-rw-r--r--synchronizer/CMakeLists.txt9
-rw-r--r--tests/CMakeLists.txt11
-rw-r--r--tests/SinkTest.cmake19
-rw-r--r--tests/accountstest.cpp36
-rw-r--r--tests/clientapitest.cpp2
-rw-r--r--tests/hawd/CMakeLists.txt6
-rw-r--r--tests/mailtest.cpp9
-rw-r--r--tests/mailthreadtest.cpp64
-rw-r--r--tests/mailthreadtest.h1
-rw-r--r--tests/notificationtest.cpp32
-rw-r--r--tests/resourceconfigtest.cpp2
-rw-r--r--tests/storagetest.cpp29
-rw-r--r--tests/threaddata/thread1228
-rw-r--r--tests/threaddata/thread2129
-rw-r--r--tests/threaddata/thread3184
-rw-r--r--tests/threaddata/thread4187
-rw-r--r--tests/threaddata/thread5119
-rw-r--r--tests/threaddata/thread6175
-rw-r--r--tests/threaddata/thread7297
-rw-r--r--tests/threaddata/thread8253
-rw-r--r--tests/threaddata/thread9283
61 files changed, 2547 insertions, 186 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6690c4c..210774f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.0)
3cmake_policy(SET CMP0048 NEW) 3cmake_policy(SET CMP0048 NEW)
4cmake_policy(SET CMP0028 NEW) 4cmake_policy(SET CMP0028 NEW)
5 5
6project(sink VERSION 0.3.0) 6project(sink VERSION 0.4.0)
7 7
8option(BUILD_MAILDIR "BUILD_MAILDIR" ON) 8option(BUILD_MAILDIR "BUILD_MAILDIR" ON)
9option(BUILD_DAV "BUILD_DAV" ON) 9option(BUILD_DAV "BUILD_DAV" ON)
@@ -25,7 +25,12 @@ include(CMakePackageConfigHelpers)
25include(ECMSetupVersion) 25include(ECMSetupVersion)
26include(KDEInstallDirs) 26include(KDEInstallDirs)
27 27
28find_package(Qt5 COMPONENTS REQUIRED Core Network Gui) 28ecm_setup_version(PROJECT
29 SOVERSION sink_VERSION_MAJOR
30 VERSION_HEADER sink_version.h
31 )
32
33find_package(Qt5 COMPONENTS REQUIRED Core Concurrent Network Gui Test)
29find_package(KF5 COMPONENTS REQUIRED Mime Contacts) 34find_package(KF5 COMPONENTS REQUIRED Mime Contacts)
30find_package(FlatBuffers REQUIRED 1.4.0) 35find_package(FlatBuffers REQUIRED 1.4.0)
31find_package(KAsync REQUIRED 0.1.2) 36find_package(KAsync REQUIRED 0.1.2)
diff --git a/cmake/modules/FindLibgit2.cmake b/cmake/modules/FindLibgit2.cmake
index fbfb32f..3c0f78a 100644
--- a/cmake/modules/FindLibgit2.cmake
+++ b/cmake/modules/FindLibgit2.cmake
@@ -26,8 +26,6 @@ FIND_LIBRARY(LIBGIT2_LIBRARIES NAMES git2
26 ${PC_LIBGIT2_LIBRARY_DIRS} 26 ${PC_LIBGIT2_LIBRARY_DIRS}
27 ) 27 )
28 28
29message("foo: ${LIBGIT2_INCLUDE_DIR} : ${PC_LIBGIT2_INCLUDEDIR} : ${PC_LIBGIT2_INCLUDE_DIRS}")
30
31# get version from header, should work on windows, too 29# get version from header, should work on windows, too
32if(LIBGIT2_INCLUDE_DIR) 30if(LIBGIT2_INCLUDE_DIR)
33 file(STRINGS "${LIBGIT2_INCLUDE_DIR}/git2/version.h" LIBGIT2_H REGEX "^#define LIBGIT2_VERSION \"[^\"]*\"$") 31 file(STRINGS "${LIBGIT2_INCLUDE_DIR}/git2/version.h" LIBGIT2_H REGEX "^#define LIBGIT2_VERSION \"[^\"]*\"$")
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 001a412..8421fc2 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -116,13 +116,13 @@ SET_TARGET_PROPERTIES(${PROJECT_NAME}
116 EXPORT_NAME ${PROJECT_NAME} 116 EXPORT_NAME ${PROJECT_NAME}
117) 117)
118 118
119qt5_use_modules(${PROJECT_NAME} LINK_PUBLIC Network)
120qt5_use_modules(${PROJECT_NAME} LINK_PRIVATE Gui)
121target_link_libraries(${PROJECT_NAME} 119target_link_libraries(${PROJECT_NAME}
122PUBLIC 120PUBLIC
123 KAsync 121 KAsync
122 Qt5::Network
124PRIVATE 123PRIVATE
125 ${storage_LIBS} 124 ${storage_LIBS}
125 Qt5::Gui
126 KF5::Mime 126 KF5::Mime
127 KF5::Contacts 127 KF5::Contacts
128) 128)
diff --git a/common/asyncutils.h b/common/asyncutils.h
index 67b5928..c80af30 100644
--- a/common/asyncutils.h
+++ b/common/asyncutils.h
@@ -31,12 +31,12 @@ KAsync::Job<T> run(const std::function<T()> &f, bool runAsync = true)
31 return KAsync::start<T>([f](KAsync::Future<T> &future) { 31 return KAsync::start<T>([f](KAsync::Future<T> &future) {
32 auto result = QtConcurrent::run(f); 32 auto result = QtConcurrent::run(f);
33 auto watcher = new QFutureWatcher<T>; 33 auto watcher = new QFutureWatcher<T>;
34 watcher->setFuture(result);
35 QObject::connect(watcher, &QFutureWatcher<T>::finished, watcher, [&future, watcher]() { 34 QObject::connect(watcher, &QFutureWatcher<T>::finished, watcher, [&future, watcher]() {
36 future.setValue(watcher->future().result()); 35 future.setValue(watcher->future().result());
37 delete watcher; 36 delete watcher;
38 future.setFinished(); 37 future.setFinished();
39 }); 38 });
39 watcher->setFuture(result);
40 }); 40 });
41 } else { 41 } else {
42 return KAsync::start<T>([f]() { 42 return KAsync::start<T>([f]() {
diff --git a/common/definitions.cpp b/common/definitions.cpp
index 17977bc..ee18d52 100644
--- a/common/definitions.cpp
+++ b/common/definitions.cpp
@@ -39,11 +39,17 @@ QString Sink::storageLocation()
39 return dataLocation() + "/storage"; 39 return dataLocation() + "/storage";
40} 40}
41 41
42static QString sinkLocation(QStandardPaths::StandardLocation location)
43{
44 return QStandardPaths::writableLocation(location) + "/sink";
45}
46
42QString Sink::dataLocation() 47QString Sink::dataLocation()
43{ 48{
44 static QString location; 49 static QString location = sinkLocation(QStandardPaths::GenericDataLocation);
50 //Warning: This is not threadsafe, but clearLocationCache is only ever used in testcode. The initialization above is required to make at least the initialization threadsafe (relies on C++11 threadsafe initialization).
45 if (rereadDataLocation) { 51 if (rereadDataLocation) {
46 location = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/sink"; 52 location = sinkLocation(QStandardPaths::GenericDataLocation);
47 rereadDataLocation = false; 53 rereadDataLocation = false;
48 } 54 }
49 return location; 55 return location;
@@ -51,9 +57,10 @@ QString Sink::dataLocation()
51 57
52QString Sink::configLocation() 58QString Sink::configLocation()
53{ 59{
54 static QString location; 60 static QString location = sinkLocation(QStandardPaths::GenericConfigLocation);
61 //Warning: This is not threadsafe, but clearLocationCache is only ever used in testcode. The initialization above is required to make at least the initialization threadsafe (relies on C++11 threadsafe initialization).
55 if (rereadConfigLocation) { 62 if (rereadConfigLocation) {
56 location = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/sink"; 63 location = sinkLocation(QStandardPaths::GenericConfigLocation);
57 rereadConfigLocation = false; 64 rereadConfigLocation = false;
58 } 65 }
59 return location; 66 return location;
@@ -61,8 +68,9 @@ QString Sink::configLocation()
61 68
62QString Sink::temporaryFileLocation() 69QString Sink::temporaryFileLocation()
63{ 70{
64 static QString location; 71 static QString location = dataLocation() + "/temporaryFiles";
65 static bool dirCreated = false; 72 static bool dirCreated = false;
73 //Warning: This is not threadsafe, but clearLocationCache is only ever used in testcode. The initialization above is required to make at least the initialization threadsafe (relies on C++11 threadsafe initialization).
66 if (rereadTemporaryFileLocation) { 74 if (rereadTemporaryFileLocation) {
67 location = dataLocation() + "/temporaryFiles"; 75 location = dataLocation() + "/temporaryFiles";
68 dirCreated = QDir{}.mkpath(location); 76 dirCreated = QDir{}.mkpath(location);
diff --git a/common/definitions.h b/common/definitions.h
index ce9e794..7ef215b 100644
--- a/common/definitions.h
+++ b/common/definitions.h
@@ -25,10 +25,16 @@
25#include <QByteArray> 25#include <QByteArray>
26 26
27namespace Sink { 27namespace Sink {
28void SINK_EXPORT clearLocationCache();
29QString SINK_EXPORT storageLocation(); 28QString SINK_EXPORT storageLocation();
30QString SINK_EXPORT dataLocation(); 29QString SINK_EXPORT dataLocation();
31QString SINK_EXPORT configLocation(); 30QString SINK_EXPORT configLocation();
32QString SINK_EXPORT temporaryFileLocation(); 31QString SINK_EXPORT temporaryFileLocation();
33QString SINK_EXPORT resourceStorageLocation(const QByteArray &resourceInstanceIdentifier); 32QString SINK_EXPORT resourceStorageLocation(const QByteArray &resourceInstanceIdentifier);
33
34/**
35 * Clear the location cache and lookup locations again.
36 *
37 * Warning: Calling this results in non-threadsafe initialization, only use it in test-code.
38 */
39void SINK_EXPORT clearLocationCache();
34} 40}
diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp
index 3718f77..ee70c35 100644
--- a/common/domain/applicationdomaintype.cpp
+++ b/common/domain/applicationdomaintype.cpp
@@ -34,7 +34,13 @@ QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDom
34QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDomain::ApplicationDomainType &type) 34QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDomain::ApplicationDomainType &type)
35{ 35{
36 d << "ApplicationDomainType(\n"; 36 d << "ApplicationDomainType(\n";
37 auto properties = type.mAdaptor->availableProperties(); 37 auto properties = [&] {
38 if (!type.changedProperties().isEmpty()) {
39 return type.changedProperties();
40 } else {
41 return type.mAdaptor->availableProperties();
42 }
43 }();
38 std::sort(properties.begin(), properties.end()); 44 std::sort(properties.begin(), properties.end());
39 d << " " << "Id: " << "\t" << type.identifier() << "\n"; 45 d << " " << "Id: " << "\t" << type.identifier() << "\n";
40 d << " " << "Resource: " << "\t" << type.resourceInstanceIdentifier() << "\n"; 46 d << " " << "Resource: " << "\t" << type.resourceInstanceIdentifier() << "\n";
@@ -216,8 +222,13 @@ QVariant ApplicationDomainType::getProperty(const QByteArray &key) const
216void ApplicationDomainType::setProperty(const QByteArray &key, const QVariant &value) 222void ApplicationDomainType::setProperty(const QByteArray &key, const QVariant &value)
217{ 223{
218 Q_ASSERT(mAdaptor); 224 Q_ASSERT(mAdaptor);
219 mChangeSet->insert(key); 225 auto existing = mAdaptor->getProperty(key);
220 mAdaptor->setProperty(key, value); 226 if (existing.isValid() && existing == value) {
227 SinkTrace() << "Tried to set property that is still the same: " << key << value;
228 } else {
229 mChangeSet->insert(key);
230 mAdaptor->setProperty(key, value);
231 }
221} 232}
222 233
223void ApplicationDomainType::setResource(const QByteArray &identifier) 234void ApplicationDomainType::setResource(const QByteArray &identifier)
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h
index 602d54c..f7fd07e 100644
--- a/common/domain/applicationdomaintype.h
+++ b/common/domain/applicationdomaintype.h
@@ -101,6 +101,7 @@ enum SINK_EXPORT ErrorCode {
101 LoginError, 101 LoginError,
102 ConfigurationError, 102 ConfigurationError,
103 TransmissionError, 103 TransmissionError,
104 ConnectionLostError,
104}; 105};
105 106
106enum SINK_EXPORT SuccessCode { 107enum SINK_EXPORT SuccessCode {
@@ -118,12 +119,14 @@ enum SINK_EXPORT SyncStatus {
118 * The status of an account or resource. 119 * The status of an account or resource.
119 * 120 *
120 * It is set as follows: 121 * It is set as follows:
121 * * By default the status is offline. 122 * * By default the status is no status.
123 * * If a connection to the server failed the status is Offline.
122 * * If a connection to the server could be established the status is Connected. 124 * * If a connection to the server could be established the status is Connected.
123 * * If an error occurred that keeps the resource from operating (so non transient), the resource enters the error state. 125 * * If an error occurred that keeps the resource from operating (so non transient), the resource enters the error state.
124 * * If a long running operation is started the resource goes to the busy state (and return to the previous state after that). 126 * * If a long running operation is started the resource goes to the busy state (and return to the previous state after that).
125 */ 127 */
126enum SINK_EXPORT Status { 128enum SINK_EXPORT Status {
129 NoStatus,
127 OfflineStatus, 130 OfflineStatus,
128 ConnectedStatus, 131 ConnectedStatus,
129 BusyStatus, 132 BusyStatus,
@@ -270,7 +273,17 @@ public:
270 bool hasProperty(const QByteArray &key) const; 273 bool hasProperty(const QByteArray &key) const;
271 274
272 QVariant getProperty(const QByteArray &key) const; 275 QVariant getProperty(const QByteArray &key) const;
276
277 /**
278 * Set a property and record a changed property
279 *
280 * If the propery is available and did not change the call will be ignored.
281 */
273 void setProperty(const QByteArray &key, const QVariant &value); 282 void setProperty(const QByteArray &key, const QVariant &value);
283
284 /**
285 * Convenience method to set a reference property.
286 */
274 void setProperty(const QByteArray &key, const ApplicationDomainType &value); 287 void setProperty(const QByteArray &key, const ApplicationDomainType &value);
275 288
276 QByteArray getBlobProperty(const QByteArray &key) const; 289 QByteArray getBlobProperty(const QByteArray &key) const;
diff --git a/common/domain/applicationdomaintype_p.h b/common/domain/applicationdomaintype_p.h
index a5a6b1d..a60df38 100644
--- a/common/domain/applicationdomaintype_p.h
+++ b/common/domain/applicationdomaintype_p.h
@@ -45,5 +45,7 @@ struct TypeHelper {
45 } else { 45 } else {
46 Q_ASSERT(false); 46 Q_ASSERT(false);
47 } 47 }
48 //Silence compiler warning
49 return Func<Sink::ApplicationDomain::Mail>{}(std::forward<Args...>(args...));
48 } 50 }
49}; 51};
diff --git a/common/log.cpp b/common/log.cpp
index 5dfb872..bfc9d5e 100644
--- a/common/log.cpp
+++ b/common/log.cpp
@@ -10,7 +10,6 @@
10#include <QMutexLocker> 10#include <QMutexLocker>
11#include <iostream> 11#include <iostream>
12#include <unistd.h> 12#include <unistd.h>
13#include <memory>
14#include <atomic> 13#include <atomic>
15#include <definitions.h> 14#include <definitions.h>
16#include <QThreadStorage> 15#include <QThreadStorage>
@@ -27,10 +26,13 @@ static QSettings &config()
27 return *sSettings.localData(); 26 return *sSettings.localData();
28} 27}
29 28
30static QByteArray sPrimaryComponent; 29Q_GLOBAL_STATIC(QByteArray, sPrimaryComponent);
30
31void Sink::Log::setPrimaryComponent(const QString &component) 31void Sink::Log::setPrimaryComponent(const QString &component)
32{ 32{
33 sPrimaryComponent = component.toUtf8(); 33 if (!sPrimaryComponent.isDestroyed()) {
34 *sPrimaryComponent = component.toUtf8();
35 }
34} 36}
35 37
36class DebugStream : public QIODevice 38class DebugStream : public QIODevice
@@ -267,16 +269,21 @@ public:
267 QSet<QString> mDebugAreas; 269 QSet<QString> mDebugAreas;
268}; 270};
269 271
270static auto sDebugAreaCollector = std::unique_ptr<DebugAreaCollector>(new DebugAreaCollector); 272Q_GLOBAL_STATIC(DebugAreaCollector, sDebugAreaCollector);
271 273
272QSet<QString> Sink::Log::debugAreas() 274QSet<QString> Sink::Log::debugAreas()
273{ 275{
274 return sDebugAreaCollector->debugAreas(); 276 if (!sDebugAreaCollector.isDestroyed()) {
277 return sDebugAreaCollector->debugAreas();
278 }
279 return {};
275} 280}
276 281
277static void collectDebugArea(const QString &debugArea) 282static void collectDebugArea(const QString &debugArea)
278{ 283{
279 sDebugAreaCollector->add(debugArea); 284 if (!sDebugAreaCollector.isDestroyed()) {
285 sDebugAreaCollector->add(debugArea);
286 }
280} 287}
281 288
282static bool containsItemStartingWith(const QByteArray &pattern, const QByteArrayList &list) 289static bool containsItemStartingWith(const QByteArray &pattern, const QByteArrayList &list)
@@ -318,13 +325,17 @@ static QByteArray getFileName(const char *file)
318 325
319static QString assembleDebugArea(const char *debugArea, const char *debugComponent, const char *file) 326static QString assembleDebugArea(const char *debugArea, const char *debugComponent, const char *file)
320{ 327{
321 if (sPrimaryComponent.isEmpty()) { 328 if (!sPrimaryComponent.isDestroyed() && sPrimaryComponent->isEmpty()) {
322 sPrimaryComponent = getProgramName(); 329 *sPrimaryComponent = getProgramName();
330 }
331 if (!sPrimaryComponent.isDestroyed()) {
332 //Using stringbuilder for fewer allocations
333 return QLatin1String{*sPrimaryComponent} % QLatin1String{"."} %
334 (debugComponent ? (QLatin1String{debugComponent} + QLatin1String{"."}) : QLatin1String{""}) %
335 (debugArea ? QLatin1String{debugArea} : QLatin1String{getFileName(file)});
336 } else {
337 return {};
323 } 338 }
324 //Using stringbuilder for fewer allocations
325 return QLatin1String{sPrimaryComponent} % QLatin1String{"."} %
326 (debugComponent ? (QLatin1String{debugComponent} + QLatin1String{"."}) : QLatin1String{""}) %
327 (debugArea ? QLatin1String{debugArea} : QLatin1String{getFileName(file)});
328} 339}
329 340
330static bool isFiltered(DebugLevel debugLevel, const QByteArray &fullDebugArea) 341static bool isFiltered(DebugLevel debugLevel, const QByteArray &fullDebugArea)
@@ -346,14 +357,19 @@ bool Sink::Log::isFiltered(DebugLevel debugLevel, const char *debugArea, const c
346 return isFiltered(debugLevel, assembleDebugArea(debugArea, debugComponent, file).toLatin1()); 357 return isFiltered(debugLevel, assembleDebugArea(debugArea, debugComponent, file).toLatin1());
347} 358}
348 359
360Q_GLOBAL_STATIC(NullStream, sNullStream);
361Q_GLOBAL_STATIC(DebugStream, sDebugStream);
362
349QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, const char *function, const char *debugArea, const char *debugComponent) 363QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, const char *function, const char *debugArea, const char *debugComponent)
350{ 364{
351 const auto fullDebugArea = assembleDebugArea(debugArea, debugComponent, file); 365 const auto fullDebugArea = assembleDebugArea(debugArea, debugComponent, file);
352 collectDebugArea(fullDebugArea); 366 collectDebugArea(fullDebugArea);
353 367
354 static NullStream nullstream;
355 if (isFiltered(debugLevel, fullDebugArea.toLatin1())) { 368 if (isFiltered(debugLevel, fullDebugArea.toLatin1())) {
356 return QDebug(&nullstream); 369 if (!sNullStream.isDestroyed()) {
370 return QDebug(sNullStream);
371 }
372 return QDebug{QtDebugMsg};
357 } 373 }
358 374
359 QString prefix; 375 QString prefix;
@@ -418,8 +434,10 @@ QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file,
418 } 434 }
419 output += ":"; 435 output += ":";
420 436
421 static DebugStream stream; 437 if (sDebugStream.isDestroyed()) {
422 QDebug debug(&stream); 438 return QDebug{QtDebugMsg};
439 }
440 QDebug debug(sDebugStream);
423 441
424 debug.noquote().nospace() << output; 442 debug.noquote().nospace() << output;
425 443
diff --git a/common/mail/threadindexer.cpp b/common/mail/threadindexer.cpp
index 4171d85..1401fc8 100644
--- a/common/mail/threadindexer.cpp
+++ b/common/mail/threadindexer.cpp
@@ -34,9 +34,37 @@ void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const App
34 34
35 QVector<QByteArray> thread; 35 QVector<QByteArray> thread;
36 36
37 //a child already registered our thread. 37 //check if a child already registered our thread.
38 thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId); 38 thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId);
39 39
40 if (!thread.isEmpty()) {
41 //A child already registered our thread so we merge the childs thread
42 //* check if we have a parent thread, if not just continue as usual
43 //* get all messages that have the same threadid as the child
44 //* switch all to the parents thread
45 auto parentThread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId);
46 if (!parentThread.isEmpty()) {
47 auto childThreadId = thread.first();
48 auto parentThreadId = parentThread.first();
49 SinkTrace() << "Merging child thread: " << childThreadId << " into parent thread: " << parentThreadId;
50
51 //Ensure this mail ends up in the correct thread
52 index().unindex<Mail::MessageId, Mail::ThreadId>(messageId, childThreadId, transaction);
53 //We have to copy the id here, otherwise it doesn't survive the subsequent writes
54 thread = QVector<QByteArray>() << QByteArray{parentThreadId.data(), parentThreadId.size()};
55
56 //Merge all child messages into the correct thread
57 auto childThreadMessageIds = index().secondaryLookup<Mail::ThreadId, Mail::MessageId>(childThreadId);
58 for (const auto &msgId : childThreadMessageIds) {
59 SinkTrace() << "Merging child message: " << msgId;
60 index().unindex<Mail::MessageId, Mail::ThreadId>(msgId, childThreadId, transaction);
61 index().unindex<Mail::ThreadId, Mail::MessageId>(childThreadId, msgId, transaction);
62 index().index<Mail::MessageId, Mail::ThreadId>(msgId, parentThreadId, transaction);
63 index().index<Mail::ThreadId, Mail::MessageId>(parentThreadId, msgId, transaction);
64 }
65 }
66 }
67
40 //If parent is already available, add to thread of parent 68 //If parent is already available, add to thread of parent
41 if (thread.isEmpty() && parentMessageId.isValid()) { 69 if (thread.isEmpty() && parentMessageId.isValid()) {
42 thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId); 70 thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId);
diff --git a/common/mailpreprocessor.cpp b/common/mailpreprocessor.cpp
index 5c54fbc..253e8b4 100644
--- a/common/mailpreprocessor.cpp
+++ b/common/mailpreprocessor.cpp
@@ -104,9 +104,6 @@ static void updatedIndexedProperties(Sink::ApplicationDomain::Mail &mail, KMime:
104 mail.setExtractedBcc(getContactList(msg->bcc(true))); 104 mail.setExtractedBcc(getContactList(msg->bcc(true)));
105 mail.setExtractedDate(msg->date(true)->dateTime()); 105 mail.setExtractedDate(msg->date(true)->dateTime());
106 106
107 //The rest should never change, unless we didn't have the headers available initially.
108 auto messageId = msg->messageID(true)->identifier();
109
110 //Ensure the mssageId is unique. 107 //Ensure the mssageId is unique.
111 //If there already is one with the same id we'd have to assign a new message id, which probably doesn't make any sense. 108 //If there already is one with the same id we'd have to assign a new message id, which probably doesn't make any sense.
112 109
@@ -125,12 +122,21 @@ static void updatedIndexedProperties(Sink::ApplicationDomain::Mail &mail, KMime:
125 parentMessageId = inReplyTo.first(); 122 parentMessageId = inReplyTo.first();
126 } 123 }
127 } 124 }
125
126 //The rest should never change, unless we didn't have the headers available initially.
127 auto messageId = msg->messageID(true)->identifier();
128 if (messageId.isEmpty()) { 128 if (messageId.isEmpty()) {
129 auto tmp = KMime::Message::Ptr::create(); 129 //reuse an existing messageis (on modification)
130 auto header = tmp->messageID(true); 130 auto existing = mail.getMessageId();
131 header->generate("kube.kde.org"); 131 if (existing.isEmpty()) {
132 messageId = header->as7BitString(); 132 auto tmp = KMime::Message::Ptr::create();
133 SinkWarning() << "Message id is empty, generating one: " << messageId; 133 auto header = tmp->messageID(true);
134 header->generate("kube.kde.org");
135 messageId = header->as7BitString();
136 SinkWarning() << "Message id is empty, generating one: " << messageId;
137 } else {
138 messageId = existing;
139 }
134 } 140 }
135 141
136 mail.setExtractedMessageId(messageId); 142 mail.setExtractedMessageId(messageId);
diff --git a/common/modelresult.cpp b/common/modelresult.cpp
index 58703ab..95f4643 100644
--- a/common/modelresult.cpp
+++ b/common/modelresult.cpp
@@ -298,16 +298,16 @@ void ModelResult<T, Ptr>::add(const Ptr &value)
298 auto parent = createIndexFromId(id); 298 auto parent = createIndexFromId(id);
299 SinkTraceCtx(mLogCtx) << "Added entity " << childId << "id: " << value->identifier() << "parent: " << id; 299 SinkTraceCtx(mLogCtx) << "Added entity " << childId << "id: " << value->identifier() << "parent: " << id;
300 const auto keys = mTree[id]; 300 const auto keys = mTree[id];
301 int index = 0; 301 int idx = 0;
302 for (; index < keys.size(); index++) { 302 for (; idx < keys.size(); idx++) {
303 if (childId < keys.at(index)) { 303 if (childId < keys.at(idx)) {
304 break; 304 break;
305 } 305 }
306 } 306 }
307 // SinkTraceCtx(mLogCtx) << "Inserting rows " << index << parent; 307 // SinkTraceCtx(mLogCtx) << "Inserting rows " << index << parent;
308 beginInsertRows(parent, index, index); 308 beginInsertRows(parent, idx, idx);
309 mEntities.insert(childId, value); 309 mEntities.insert(childId, value);
310 mTree[id].insert(index, childId); 310 mTree[id].insert(idx, childId);
311 mParents.insert(childId, id); 311 mParents.insert(childId, id);
312 endInsertRows(); 312 endInsertRows();
313 // SinkTraceCtx(mLogCtx) << "Inserted rows " << mTree[id].size(); 313 // SinkTraceCtx(mLogCtx) << "Inserted rows " << mTree[id].size();
diff --git a/common/notification.cpp b/common/notification.cpp
index da31e20..20bc654 100644
--- a/common/notification.cpp
+++ b/common/notification.cpp
@@ -48,7 +48,7 @@ static QByteArray name(int type)
48 48
49QDebug operator<<(QDebug dbg, const Sink::Notification &n) 49QDebug operator<<(QDebug dbg, const Sink::Notification &n)
50{ 50{
51 dbg << "Notification(Type:" << name(n.type) << "Id, :" << n.id << ", Code:"; 51 dbg << "Notification(Type:" << name(n.type) << ", Id:" << n.id << ", Code:";
52 dbg << n.code; 52 dbg << n.code;
53 dbg << ", Message:" << n.message << ", Entities:" << n.entities << ")"; 53 dbg << ", Message:" << n.message << ", Entities:" << n.entities << ")";
54 return dbg.space(); 54 return dbg.space();
diff --git a/common/notifier.cpp b/common/notifier.cpp
index 1af65e9..1b7cbdb 100644
--- a/common/notifier.cpp
+++ b/common/notifier.cpp
@@ -49,6 +49,7 @@ public:
49 49
50 QList<QSharedPointer<ResourceAccess>> resourceAccess; 50 QList<QSharedPointer<ResourceAccess>> resourceAccess;
51 QList<std::function<void(const Notification &)>> handler; 51 QList<std::function<void(const Notification &)>> handler;
52 QSharedPointer<Sink::ResultEmitter<QSharedPointer<Sink::ApplicationDomain::SinkResource> > > mResourceEmitter;
52 QObject context; 53 QObject context;
53}; 54};
54 55
@@ -91,6 +92,9 @@ Notifier::Notifier(const Sink::Query &resourceQuery) : d(new Sink::Notifier::Pri
91 SinkTraceCtx(resourceCtx) << "Resource query complete"; 92 SinkTraceCtx(resourceCtx) << "Resource query complete";
92 }); 93 });
93 emitter->fetch({}); 94 emitter->fetch({});
95 if (resourceQuery.liveQuery()) {
96 d->mResourceEmitter = emitter;
97 }
94 result.first.exec(); 98 result.first.exec();
95} 99}
96 100
diff --git a/common/resource.cpp b/common/resource.cpp
index 32a92ca..804f0bf 100644
--- a/common/resource.cpp
+++ b/common/resource.cpp
@@ -56,10 +56,10 @@ class ResourceFactory::Private
56{ 56{
57public: 57public:
58 QByteArrayList capabilities; 58 QByteArrayList capabilities;
59 static QHash<QString, QPointer<ResourceFactory>> s_loadedFactories;
60}; 59};
61 60
62QHash<QString, QPointer<ResourceFactory>> ResourceFactory::Private::s_loadedFactories; 61typedef QHash<QString, QPointer<ResourceFactory>> FactoryRegistry;
62Q_GLOBAL_STATIC(FactoryRegistry, s_loadedFactories);
63 63
64ResourceFactory::ResourceFactory(QObject *parent, const QByteArrayList &capabilities) : QObject(parent), d(new ResourceFactory::Private) 64ResourceFactory::ResourceFactory(QObject *parent, const QByteArrayList &capabilities) : QObject(parent), d(new ResourceFactory::Private)
65{ 65{
@@ -73,7 +73,7 @@ ResourceFactory::~ResourceFactory()
73 73
74ResourceFactory *ResourceFactory::load(const QByteArray &resourceName) 74ResourceFactory *ResourceFactory::load(const QByteArray &resourceName)
75{ 75{
76 ResourceFactory *factory = Private::s_loadedFactories.value(resourceName); 76 ResourceFactory *factory = s_loadedFactories->value(resourceName);
77 if (factory) { 77 if (factory) {
78 return factory; 78 return factory;
79 } 79 }
@@ -96,7 +96,7 @@ ResourceFactory *ResourceFactory::load(const QByteArray &resourceName)
96 if (object) { 96 if (object) {
97 factory = qobject_cast<ResourceFactory *>(object); 97 factory = qobject_cast<ResourceFactory *>(object);
98 if (factory) { 98 if (factory) {
99 Private::s_loadedFactories.insert(resourceName, factory); 99 s_loadedFactories->insert(resourceName, factory);
100 //TODO: Instead of always loading both facades and adaptorfactories into the respective singletons, we could also leave this up to the caller. (ResourceFactory::loadFacades(...)) 100 //TODO: Instead of always loading both facades and adaptorfactories into the respective singletons, we could also leave this up to the caller. (ResourceFactory::loadFacades(...))
101 factory->registerFacades(resourceName, FacadeFactory::instance()); 101 factory->registerFacades(resourceName, FacadeFactory::instance());
102 factory->registerAdaptorFactories(resourceName, AdaptorFactoryRegistry::instance()); 102 factory->registerAdaptorFactories(resourceName, AdaptorFactoryRegistry::instance());
diff --git a/common/resourceaccess.cpp b/common/resourceaccess.cpp
index 808d892..35fa46c 100644
--- a/common/resourceaccess.cpp
+++ b/common/resourceaccess.cpp
@@ -232,7 +232,7 @@ KAsync::Job<void> ResourceAccess::Private::initializeSocket()
232ResourceAccess::ResourceAccess(const QByteArray &resourceInstanceIdentifier, const QByteArray &resourceType) 232ResourceAccess::ResourceAccess(const QByteArray &resourceInstanceIdentifier, const QByteArray &resourceType)
233 : ResourceAccessInterface(), d(new Private(resourceType, resourceInstanceIdentifier, this)) 233 : ResourceAccessInterface(), d(new Private(resourceType, resourceInstanceIdentifier, this))
234{ 234{
235 mResourceStatus = Sink::ApplicationDomain::OfflineStatus; 235 mResourceStatus = Sink::ApplicationDomain::NoStatus;
236 SinkTrace() << "Starting access"; 236 SinkTrace() << "Starting access";
237} 237}
238 238
diff --git a/common/resourcefacade.cpp b/common/resourcefacade.cpp
index dab6aed..0687bbc 100644
--- a/common/resourcefacade.cpp
+++ b/common/resourcefacade.cpp
@@ -24,6 +24,7 @@
24#include "store.h" 24#include "store.h"
25#include "resourceaccess.h" 25#include "resourceaccess.h"
26#include "resource.h" 26#include "resource.h"
27#include "facadefactory.h"
27 28
28using namespace Sink; 29using namespace Sink;
29 30
@@ -358,27 +359,50 @@ QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename ApplicationDomain
358 auto ctx = parentCtx.subContext("accounts"); 359 auto ctx = parentCtx.subContext("accounts");
359 auto runner = new LocalStorageQueryRunner<ApplicationDomain::SinkAccount>(query, mIdentifier, mTypeName, sConfigNotifier, ctx); 360 auto runner = new LocalStorageQueryRunner<ApplicationDomain::SinkAccount>(query, mIdentifier, mTypeName, sConfigNotifier, ctx);
360 auto monitoredResources = QSharedPointer<QSet<QByteArray>>::create(); 361 auto monitoredResources = QSharedPointer<QSet<QByteArray>>::create();
361 runner->setStatusUpdater([runner, monitoredResources, ctx](ApplicationDomain::SinkAccount &account) { 362 auto monitorResource = [monitoredResources, runner, ctx] (const QByteArray &accountIdentifier, const ApplicationDomain::SinkResource &resource, const ResourceAccess::Ptr &resourceAccess) {
362 Query query; 363 if (!monitoredResources->contains(resource.identifier())) {
364 auto ret = QObject::connect(resourceAccess.data(), &ResourceAccess::notification, runner->guard(), [resource, runner, resourceAccess, accountIdentifier, ctx](const Notification &notification) {
365 SinkTraceCtx(ctx) << "Received notification in facade: " << notification.type;
366 if (notification.type == Notification::Status) {
367 runner->statusChanged(accountIdentifier);
368 }
369 });
370 Q_ASSERT(ret);
371 monitoredResources->insert(resource.identifier());
372 }
373 };
374 runner->setStatusUpdater([this, runner, monitoredResources, ctx, monitorResource](ApplicationDomain::SinkAccount &account) {
375 Query query{Query::LiveQuery};
363 query.filter<ApplicationDomain::SinkResource::Account>(account.identifier()); 376 query.filter<ApplicationDomain::SinkResource::Account>(account.identifier());
364 query.request<ApplicationDomain::SinkResource::Account>() 377 query.request<ApplicationDomain::SinkResource::Account>()
365 .request<ApplicationDomain::SinkResource::Capabilities>(); 378 .request<ApplicationDomain::SinkResource::Capabilities>();
366 const auto resources = Store::read<ApplicationDomain::SinkResource>(query); 379 const auto resources = Store::read<ApplicationDomain::SinkResource>(query);
367 SinkTraceCtx(ctx) << "Found resource belonging to the account " << account.identifier() << " : " << resources; 380 SinkTraceCtx(ctx) << "Found resource belonging to the account " << account.identifier() << " : " << resources;
368 auto accountIdentifier = account.identifier(); 381 auto accountIdentifier = account.identifier();
382
383 //Monitor for new resources so they can be monitored as well
384 if (!runner->mResourceEmitter.contains(accountIdentifier)) {
385 auto facade = Sink::FacadeFactory::instance().getFacade<ApplicationDomain::SinkResource>();
386 Q_ASSERT(facade);
387
388 auto emitter = facade->load(query, ctx).second;
389 emitter->onAdded([=](const ApplicationDomain::SinkResource::Ptr &resource) {
390 auto resourceAccess = Sink::ResourceAccessFactory::instance().getAccess(resource->identifier(), ResourceConfig::getResourceType(resource->identifier()));
391 monitorResource(accountIdentifier, *resource, resourceAccess);
392 });
393 emitter->onModified([](const ApplicationDomain::SinkResource::Ptr &) {});
394 emitter->onRemoved([](const ApplicationDomain::SinkResource::Ptr &) {});
395 emitter->onInitialResultSetComplete([](const ApplicationDomain::SinkResource::Ptr &, bool) {});
396 emitter->onComplete([]() {});
397 emitter->fetch({});
398 runner->mResourceEmitter[accountIdentifier] = emitter;
399 }
400
369 QList<int> states; 401 QList<int> states;
402 //Gather all resources and ensure they are monitored
370 for (const auto &resource : resources) { 403 for (const auto &resource : resources) {
371 auto resourceAccess = ResourceAccessFactory::instance().getAccess(resource.identifier(), ResourceConfig::getResourceType(resource.identifier())); 404 auto resourceAccess = ResourceAccessFactory::instance().getAccess(resource.identifier(), ResourceConfig::getResourceType(resource.identifier()));
372 if (!monitoredResources->contains(resource.identifier())) { 405 monitorResource(accountIdentifier, resource, resourceAccess);
373 auto ret = QObject::connect(resourceAccess.data(), &ResourceAccess::notification, runner->guard(), [resource, runner, resourceAccess, accountIdentifier, ctx](const Notification &notification) {
374 SinkTraceCtx(ctx) << "Received notification in facade: " << notification.type;
375 if (notification.type == Notification::Status) {
376 runner->statusChanged(accountIdentifier);
377 }
378 });
379 Q_ASSERT(ret);
380 monitoredResources->insert(resource.identifier());
381 }
382 states << resourceAccess->getResourceStatus(); 406 states << resourceAccess->getResourceStatus();
383 } 407 }
384 const auto status = [&] { 408 const auto status = [&] {
@@ -391,7 +415,10 @@ QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename ApplicationDomain
391 if (states.contains(ApplicationDomain::OfflineStatus)) { 415 if (states.contains(ApplicationDomain::OfflineStatus)) {
392 return ApplicationDomain::OfflineStatus; 416 return ApplicationDomain::OfflineStatus;
393 } 417 }
394 return ApplicationDomain::ConnectedStatus; 418 if (states.contains(ApplicationDomain::ConnectedStatus)) {
419 return ApplicationDomain::ConnectedStatus;
420 }
421 return ApplicationDomain::NoStatus;
395 }(); 422 }();
396 account.setStatusStatus(status); 423 account.setStatusStatus(status);
397 }); 424 });
diff --git a/common/resourcefacade.h b/common/resourcefacade.h
index 76fadce..36049c4 100644
--- a/common/resourcefacade.h
+++ b/common/resourcefacade.h
@@ -65,6 +65,7 @@ public:
65 void setStatusUpdater(const std::function<void(DomainType &)> &); 65 void setStatusUpdater(const std::function<void(DomainType &)> &);
66 void statusChanged(const QByteArray &identifier); 66 void statusChanged(const QByteArray &identifier);
67 QObject *guard() const; 67 QObject *guard() const;
68 QMap<QByteArray, QSharedPointer<Sink::ResultEmitter<QSharedPointer<Sink::ApplicationDomain::SinkResource> > > > mResourceEmitter;
68 69
69private: 70private:
70 void updateStatus(DomainType &entity); 71 void updateStatus(DomainType &entity);
diff --git a/common/storage.h b/common/storage.h
index 8c129df..c39b904 100644
--- a/common/storage.h
+++ b/common/storage.h
@@ -198,9 +198,9 @@ public:
198 static QByteArray getTypeFromRevision(const Transaction &, qint64 revision); 198 static QByteArray getTypeFromRevision(const Transaction &, qint64 revision);
199 static void recordRevision(Transaction &, qint64 revision, const QByteArray &uid, const QByteArray &type); 199 static void recordRevision(Transaction &, qint64 revision, const QByteArray &uid, const QByteArray &type);
200 static void removeRevision(Transaction &, qint64 revision); 200 static void removeRevision(Transaction &, qint64 revision);
201 static void recordUid(DataStore::Transaction &transaction, const QByteArray &uid); 201 static void recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type);
202 static void removeUid(DataStore::Transaction &transaction, const QByteArray &uid); 202 static void removeUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type);
203 static void getUids(const Transaction &, const std::function<void(const QByteArray &uid)> &); 203 static void getUids(const QByteArray &type, const Transaction &, const std::function<void(const QByteArray &uid)> &);
204 204
205 bool exists() const; 205 bool exists() const;
206 206
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp
index 22e5ae3..5514e31 100644
--- a/common/storage/entitystore.cpp
+++ b/common/storage/entitystore.cpp
@@ -267,7 +267,7 @@ bool EntityStore::add(const QByteArray &type, ApplicationDomain::ApplicationDoma
267 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << entity.identifier() << newRevision; }); 267 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << entity.identifier() << newRevision; });
268 DataStore::setMaxRevision(d->transaction, newRevision); 268 DataStore::setMaxRevision(d->transaction, newRevision);
269 DataStore::recordRevision(d->transaction, newRevision, entity.identifier(), type); 269 DataStore::recordRevision(d->transaction, newRevision, entity.identifier(), type);
270 DataStore::recordUid(d->transaction, entity.identifier()); 270 DataStore::recordUid(d->transaction, entity.identifier(), type);
271 SinkTraceCtx(d->logCtx) << "Wrote entity: " << entity.identifier() << type << newRevision; 271 SinkTraceCtx(d->logCtx) << "Wrote entity: " << entity.identifier() << type << newRevision;
272 return true; 272 return true;
273} 273}
@@ -379,7 +379,7 @@ bool EntityStore::remove(const QByteArray &type, const Sink::ApplicationDomain::
379 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << uid << newRevision; }); 379 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << uid << newRevision; });
380 DataStore::setMaxRevision(d->transaction, newRevision); 380 DataStore::setMaxRevision(d->transaction, newRevision);
381 DataStore::recordRevision(d->transaction, newRevision, uid, type); 381 DataStore::recordRevision(d->transaction, newRevision, uid, type);
382 DataStore::removeUid(d->transaction, uid); 382 DataStore::removeUid(d->transaction, uid, type);
383 return true; 383 return true;
384} 384}
385 385
@@ -516,9 +516,8 @@ void EntityStore::readLatest(const QByteArray &type, const QByteArray &uid, cons
516{ 516{
517 auto db = DataStore::mainDatabase(d->getTransaction(), type); 517 auto db = DataStore::mainDatabase(d->getTransaction(), type);
518 db.findLatest(uid, 518 db.findLatest(uid,
519 [=](const QByteArray &key, const QByteArray &value) -> bool { 519 [=](const QByteArray &key, const QByteArray &value) {
520 callback(DataStore::uidFromKey(key), Sink::EntityBuffer(value.data(), value.size())); 520 callback(DataStore::uidFromKey(key), Sink::EntityBuffer(value.data(), value.size()));
521 return false;
522 }, 521 },
523 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during query: " << error.message << uid; }); 522 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during query: " << error.message << uid; });
524} 523}
@@ -639,7 +638,7 @@ ApplicationDomain::ApplicationDomainType EntityStore::readPrevious(const QByteAr
639 638
640void EntityStore::readAllUids(const QByteArray &type, const std::function<void(const QByteArray &uid)> callback) 639void EntityStore::readAllUids(const QByteArray &type, const std::function<void(const QByteArray &uid)> callback)
641{ 640{
642 DataStore::getUids(d->getTransaction(), callback); 641 DataStore::getUids(type, d->getTransaction(), callback);
643} 642}
644 643
645bool EntityStore::contains(const QByteArray &type, const QByteArray &uid) 644bool EntityStore::contains(const QByteArray &type, const QByteArray &uid)
@@ -653,7 +652,7 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid)
653 bool alreadyRemoved = false; 652 bool alreadyRemoved = false;
654 DataStore::mainDatabase(d->transaction, type) 653 DataStore::mainDatabase(d->transaction, type)
655 .findLatest(uid, 654 .findLatest(uid,
656 [&found, &alreadyRemoved](const QByteArray &key, const QByteArray &data) -> bool { 655 [&found, &alreadyRemoved](const QByteArray &key, const QByteArray &data) {
657 auto entity = GetEntity(data.data()); 656 auto entity = GetEntity(data.data());
658 if (entity && entity->metadata()) { 657 if (entity && entity->metadata()) {
659 auto metadata = GetMetadata(entity->metadata()->Data()); 658 auto metadata = GetMetadata(entity->metadata()->Data());
@@ -662,7 +661,6 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid)
662 alreadyRemoved = true; 661 alreadyRemoved = true;
663 } 662 }
664 } 663 }
665 return false;
666 }, 664 },
667 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to read old revision from storage: " << error.message; }); 665 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to read old revision from storage: " << error.message; });
668 if (!found) { 666 if (!found) {
diff --git a/common/storage_common.cpp b/common/storage_common.cpp
index 8603787..630dae9 100644
--- a/common/storage_common.cpp
+++ b/common/storage_common.cpp
@@ -156,19 +156,19 @@ void DataStore::removeRevision(DataStore::Transaction &transaction, qint64 revis
156 transaction.openDatabase("revisionType").remove(QByteArray::number(revision)); 156 transaction.openDatabase("revisionType").remove(QByteArray::number(revision));
157} 157}
158 158
159void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid) 159void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type)
160{ 160{
161 transaction.openDatabase("uids").write(uid, ""); 161 transaction.openDatabase(type + "uids").write(uid, "");
162} 162}
163 163
164void DataStore::removeUid(DataStore::Transaction &transaction, const QByteArray &uid) 164void DataStore::removeUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type)
165{ 165{
166 transaction.openDatabase("uids").remove(uid); 166 transaction.openDatabase(type + "uids").remove(uid);
167} 167}
168 168
169void DataStore::getUids(const Transaction &transaction, const std::function<void(const QByteArray &uid)> &callback) 169void DataStore::getUids(const QByteArray &type, const Transaction &transaction, const std::function<void(const QByteArray &uid)> &callback)
170{ 170{
171 transaction.openDatabase("uids").scan("", [&] (const QByteArray &key, const QByteArray &) { 171 transaction.openDatabase(type + "uids").scan("", [&] (const QByteArray &key, const QByteArray &) {
172 callback(key); 172 callback(key);
173 return true; 173 return true;
174 }); 174 });
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index f7999d1..58e3a9a 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -406,7 +406,7 @@ int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool
406 mdb_cursor_close(cursor); 406 mdb_cursor_close(cursor);
407 407
408 if (rc) { 408 if (rc) {
409 Error error(d->name.toLatin1() + d->db, getErrorCode(rc), QByteArray("Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); 409 Error error(d->name.toLatin1() + d->db, getErrorCode(rc), QByteArray("Error during scan. Key: ") + k + " : " + QByteArray(mdb_strerror(rc)));
410 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); 410 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
411 } 411 }
412 412
@@ -420,6 +420,11 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi
420 // Not an error. We rely on this to read nothing from non-existing databases. 420 // Not an error. We rely on this to read nothing from non-existing databases.
421 return; 421 return;
422 } 422 }
423 if (k.isEmpty()) {
424 Error error(d->name.toLatin1() + d->db, GenericError, QByteArray("Can't use findLatest with empty key."));
425 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
426 return;
427 }
423 428
424 int rc; 429 int rc;
425 MDB_val key; 430 MDB_val key;
@@ -441,25 +446,23 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi
441 if ((rc = mdb_cursor_get(cursor, &key, &data, op)) == 0) { 446 if ((rc = mdb_cursor_get(cursor, &key, &data, op)) == 0) {
442 // The first lookup will find a key that is equal or greather than our key 447 // The first lookup will find a key that is equal or greather than our key
443 if (QByteArray::fromRawData((char *)key.mv_data, key.mv_size).startsWith(k)) { 448 if (QByteArray::fromRawData((char *)key.mv_data, key.mv_size).startsWith(k)) {
444 bool advanced = false; 449 //Read next value until we no longer match
445 while (QByteArray::fromRawData((char *)key.mv_data, key.mv_size).startsWith(k)) { 450 while (QByteArray::fromRawData((char *)key.mv_data, key.mv_size).startsWith(k)) {
446 advanced = true;
447 MDB_cursor_op nextOp = MDB_NEXT; 451 MDB_cursor_op nextOp = MDB_NEXT;
448 rc = mdb_cursor_get(cursor, &key, &data, nextOp); 452 rc = mdb_cursor_get(cursor, &key, &data, nextOp);
449 if (rc) { 453 if (rc) {
450 break; 454 break;
451 } 455 }
452 } 456 }
453 if (advanced) { 457 //Now read the previous value, and that's the latest one
454 MDB_cursor_op prefOp = MDB_PREV; 458 MDB_cursor_op prefOp = MDB_PREV;
455 // We read past the end above, just take the last value 459 // We read past the end above, just take the last value
456 if (rc == MDB_NOTFOUND) { 460 if (rc == MDB_NOTFOUND) {
457 prefOp = MDB_LAST; 461 prefOp = MDB_LAST;
458 }
459 rc = mdb_cursor_get(cursor, &key, &data, prefOp);
460 foundValue = true;
461 resultHandler(QByteArray::fromRawData((char *)key.mv_data, key.mv_size), QByteArray::fromRawData((char *)data.mv_data, data.mv_size));
462 } 462 }
463 rc = mdb_cursor_get(cursor, &key, &data, prefOp);
464 foundValue = true;
465 resultHandler(QByteArray::fromRawData((char *)key.mv_data, key.mv_size), QByteArray::fromRawData((char *)data.mv_data, data.mv_size));
463 } 466 }
464 } 467 }
465 468
@@ -471,10 +474,10 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi
471 mdb_cursor_close(cursor); 474 mdb_cursor_close(cursor);
472 475
473 if (rc) { 476 if (rc) {
474 Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); 477 Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Error during find latest. Key: ") + k + " : " + QByteArray(mdb_strerror(rc)));
475 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); 478 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
476 } else if (!foundValue) { 479 } else if (!foundValue) {
477 Error error(d->name.toLatin1(), 1, QByteArray("Key: ") + k + " : No value found"); 480 Error error(d->name.toLatin1(), 1, QByteArray("Error during find latest. Key: ") + k + " : No value found");
478 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); 481 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
479 } 482 }
480 483
diff --git a/common/store.cpp b/common/store.cpp
index 4735113..1701a43 100644
--- a/common/store.cpp
+++ b/common/store.cpp
@@ -36,10 +36,34 @@
36#include "storage.h" 36#include "storage.h"
37#include "log.h" 37#include "log.h"
38 38
39#define ASSERT_ENUMS_MATCH(A, B) Q_STATIC_ASSERT_X(static_cast<int>(A) == static_cast<int>(B), "The enum values must match");
40
41//Ensure the copied enum matches
42typedef ModelResult<Sink::ApplicationDomain::Mail, Sink::ApplicationDomain::Mail::Ptr> MailModelResult;
43ASSERT_ENUMS_MATCH(Sink::Store::DomainObjectBaseRole, MailModelResult::DomainObjectBaseRole)
44ASSERT_ENUMS_MATCH(Sink::Store::ChildrenFetchedRole, MailModelResult::ChildrenFetchedRole)
45ASSERT_ENUMS_MATCH(Sink::Store::DomainObjectRole, MailModelResult::DomainObjectRole)
46ASSERT_ENUMS_MATCH(Sink::Store::StatusRole, MailModelResult::StatusRole)
47ASSERT_ENUMS_MATCH(Sink::Store::WarningRole, MailModelResult::WarningRole)
48ASSERT_ENUMS_MATCH(Sink::Store::ProgressRole, MailModelResult::ProgressRole)
49
39Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>) 50Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>)
40Q_DECLARE_METATYPE(QSharedPointer<Sink::ResourceAccessInterface>); 51Q_DECLARE_METATYPE(QSharedPointer<Sink::ResourceAccessInterface>);
41Q_DECLARE_METATYPE(std::shared_ptr<void>); 52Q_DECLARE_METATYPE(std::shared_ptr<void>);
42 53
54
55static bool sanityCheckQuery(const Sink::Query &query)
56{
57 for (const auto &id : query.ids()) {
58 if (id.isEmpty()) {
59 SinkError() << "Empty id in query.";
60 return false;
61 }
62 }
63 return true;
64}
65
66
43namespace Sink { 67namespace Sink {
44 68
45QString Store::storageLocation() 69QString Store::storageLocation()
@@ -138,6 +162,7 @@ static Log::Context getQueryContext(const Sink::Query &query, const QByteArray &
138template <class DomainType> 162template <class DomainType>
139QSharedPointer<QAbstractItemModel> Store::loadModel(const Query &query) 163QSharedPointer<QAbstractItemModel> Store::loadModel(const Query &query)
140{ 164{
165 Q_ASSERT(sanityCheckQuery(query));
141 auto ctx = getQueryContext(query, ApplicationDomain::getTypeName<DomainType>()); 166 auto ctx = getQueryContext(query, ApplicationDomain::getTypeName<DomainType>());
142 auto model = QSharedPointer<ModelResult<DomainType, typename DomainType::Ptr>>::create(query, query.requestedProperties, ctx); 167 auto model = QSharedPointer<ModelResult<DomainType, typename DomainType::Ptr>>::create(query, query.requestedProperties, ctx);
143 168
@@ -189,6 +214,10 @@ KAsync::Job<void> Store::create(const DomainType &domainObject)
189template <class DomainType> 214template <class DomainType>
190KAsync::Job<void> Store::modify(const DomainType &domainObject) 215KAsync::Job<void> Store::modify(const DomainType &domainObject)
191{ 216{
217 if (domainObject.changedProperties().isEmpty()) {
218 SinkLog() << "Nothing to modify: " << domainObject.identifier();
219 return KAsync::null();
220 }
192 SinkLog() << "Modify: " << domainObject; 221 SinkLog() << "Modify: " << domainObject;
193 // Potentially move to separate thread as well 222 // Potentially move to separate thread as well
194 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier()); 223 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
@@ -198,6 +227,10 @@ KAsync::Job<void> Store::modify(const DomainType &domainObject)
198template <class DomainType> 227template <class DomainType>
199KAsync::Job<void> Store::modify(const Query &query, const DomainType &domainObject) 228KAsync::Job<void> Store::modify(const Query &query, const DomainType &domainObject)
200{ 229{
230 if (domainObject.changedProperties().isEmpty()) {
231 SinkLog() << "Nothing to modify: " << domainObject.identifier();
232 return KAsync::null();
233 }
201 SinkLog() << "Modify: " << query << domainObject; 234 SinkLog() << "Modify: " << query << domainObject;
202 return fetchAll<DomainType>(query) 235 return fetchAll<DomainType>(query)
203 .each([=] (const typename DomainType::Ptr &entity) { 236 .each([=] (const typename DomainType::Ptr &entity) {
@@ -311,9 +344,14 @@ KAsync::Job<void> Store::synchronize(const Sink::Query &query)
311 344
312KAsync::Job<void> Store::synchronize(const Sink::SyncScope &scope) 345KAsync::Job<void> Store::synchronize(const Sink::SyncScope &scope)
313{ 346{
347 auto resourceFilter = scope.getResourceFilter();
348 //Filter resources by type by default
349 if (!resourceFilter.propertyFilter.contains(ApplicationDomain::SinkResource::Capabilities::name) && !scope.type().isEmpty()) {
350 resourceFilter.propertyFilter.insert(ApplicationDomain::SinkResource::Capabilities::name, Query::Comparator{scope.type(), Query::Comparator::Contains});
351 }
314 Sink::Query query; 352 Sink::Query query;
315 query.setFilter(scope.getResourceFilter()); 353 query.setFilter(resourceFilter);
316 SinkLog() << "Synchronizing: " << query; 354 SinkLog() << "Synchronizing all resource matching: " << query;
317 return fetchAll<ApplicationDomain::SinkResource>(query) 355 return fetchAll<ApplicationDomain::SinkResource>(query)
318 .template each([scope](const ApplicationDomain::SinkResource::Ptr &resource) -> KAsync::Job<void> { 356 .template each([scope](const ApplicationDomain::SinkResource::Ptr &resource) -> KAsync::Job<void> {
319 return synchronize(resource->identifier(), scope); 357 return synchronize(resource->identifier(), scope);
@@ -337,6 +375,7 @@ KAsync::Job<QList<typename DomainType::Ptr>> Store::fetchAll(const Sink::Query &
337template <class DomainType> 375template <class DomainType>
338KAsync::Job<QList<typename DomainType::Ptr>> Store::fetch(const Sink::Query &query, int minimumAmount) 376KAsync::Job<QList<typename DomainType::Ptr>> Store::fetch(const Sink::Query &query, int minimumAmount)
339{ 377{
378 Q_ASSERT(sanityCheckQuery(query));
340 auto model = loadModel<DomainType>(query); 379 auto model = loadModel<DomainType>(query);
341 auto list = QSharedPointer<QList<typename DomainType::Ptr>>::create(); 380 auto list = QSharedPointer<QList<typename DomainType::Ptr>>::create();
342 auto context = QSharedPointer<QObject>::create(); 381 auto context = QSharedPointer<QObject>::create();
@@ -388,6 +427,7 @@ DomainType Store::readOne(const Sink::Query &query)
388template <class DomainType> 427template <class DomainType>
389QList<DomainType> Store::read(const Sink::Query &query_) 428QList<DomainType> Store::read(const Sink::Query &query_)
390{ 429{
430 Q_ASSERT(sanityCheckQuery(query_));
391 auto query = query_; 431 auto query = query_;
392 query.setFlags(Query::SynchronousQuery); 432 query.setFlags(Query::SynchronousQuery);
393 433
diff --git a/common/store.h b/common/store.h
index 34e14df..3ad547e 100644
--- a/common/store.h
+++ b/common/store.h
@@ -75,12 +75,15 @@ KAsync::Job<void> SINK_EXPORT create(const DomainType &domainObject);
75 * Modify an entity. 75 * Modify an entity.
76 * 76 *
77 * This includes moving etc. since these are also simple settings on a property. 77 * This includes moving etc. since these are also simple settings on a property.
78 * Note that the modification will be dropped if there is no changedProperty on the domain object.
78 */ 79 */
79template <class DomainType> 80template <class DomainType>
80KAsync::Job<void> SINK_EXPORT modify(const DomainType &domainObject); 81KAsync::Job<void> SINK_EXPORT modify(const DomainType &domainObject);
81 82
82/** 83/**
83 * Modify a set of entities identified by @param query. 84 * Modify a set of entities identified by @param query.
85 *
86 * Note that the modification will be dropped if there is no changedProperty on the domain object.
84 */ 87 */
85template <class DomainType> 88template <class DomainType>
86KAsync::Job<void> SINK_EXPORT modify(const Query &query, const DomainType &domainObject); 89KAsync::Job<void> SINK_EXPORT modify(const Query &query, const DomainType &domainObject);
diff --git a/common/synchronizer.cpp b/common/synchronizer.cpp
index 3b32e68..46d3980 100644
--- a/common/synchronizer.cpp
+++ b/common/synchronizer.cpp
@@ -40,7 +40,7 @@ Synchronizer::Synchronizer(const Sink::ResourceContext &context)
40 mSyncStorage(Sink::storageLocation(), mResourceContext.instanceId() + ".synchronization", Sink::Storage::DataStore::DataStore::ReadWrite), 40 mSyncStorage(Sink::storageLocation(), mResourceContext.instanceId() + ".synchronization", Sink::Storage::DataStore::DataStore::ReadWrite),
41 mSyncInProgress(false) 41 mSyncInProgress(false)
42{ 42{
43 mCurrentState.push(ApplicationDomain::Status::OfflineStatus); 43 mCurrentState.push(ApplicationDomain::Status::NoStatus);
44 SinkTraceCtx(mLogCtx) << "Starting synchronizer: " << mResourceContext.resourceType << mResourceContext.instanceId(); 44 SinkTraceCtx(mLogCtx) << "Starting synchronizer: " << mResourceContext.resourceType << mResourceContext.instanceId();
45} 45}
46 46
@@ -344,8 +344,11 @@ void Synchronizer::setStatusFromResult(const KAsync::Error &error, const QString
344 } else if (error.errorCode == ApplicationDomain::LoginError) { 344 } else if (error.errorCode == ApplicationDomain::LoginError) {
345 //If we failed to login altough we could connect that indicates a problem with our setup. 345 //If we failed to login altough we could connect that indicates a problem with our setup.
346 setStatus(ApplicationDomain::ErrorStatus, s, requestId); 346 setStatus(ApplicationDomain::ErrorStatus, s, requestId);
347 } else if (error.errorCode == ApplicationDomain::ConnectionLostError) {
348 //We've lost the connection so we assume the connection to the server broke.
349 setStatus(ApplicationDomain::OfflineStatus, s, requestId);
347 } 350 }
348 //We don't know what kind of error this was, so we assume it's transient and don't change ou status. 351 //We don't know what kind of error this was, so we assume it's transient and don't change our status.
349 } else { 352 } else {
350 //An operation against the server worked, so we're probably online. 353 //An operation against the server worked, so we're probably online.
351 setStatus(ApplicationDomain::ConnectedStatus, s, requestId); 354 setStatus(ApplicationDomain::ConnectedStatus, s, requestId);
@@ -593,17 +596,13 @@ KAsync::Job<void> Synchronizer::replay(const QByteArray &type, const QByteArray
593 KAsync::Job<QByteArray> job = KAsync::null<QByteArray>(); 596 KAsync::Job<QByteArray> job = KAsync::null<QByteArray>();
594 //TODO This requires supporting every domain type here as well. Can we solve this better so we can do the dispatch somewhere centrally? 597 //TODO This requires supporting every domain type here as well. Can we solve this better so we can do the dispatch somewhere centrally?
595 if (type == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) { 598 if (type == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) {
596 auto folder = store().readEntity<ApplicationDomain::Folder>(key); 599 job = replay(store().readEntity<ApplicationDomain::Folder>(key), operation, oldRemoteId, modifiedProperties);
597 job = replay(folder, operation, oldRemoteId, modifiedProperties);
598 } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Mail>()) { 600 } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Mail>()) {
599 auto mail = store().readEntity<ApplicationDomain::Mail>(key); 601 job = replay(store().readEntity<ApplicationDomain::Mail>(key), operation, oldRemoteId, modifiedProperties);
600 job = replay(mail, operation, oldRemoteId, modifiedProperties);
601 } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) { 602 } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) {
602 auto mail = store().readEntity<ApplicationDomain::Contact>(key); 603 job = replay(store().readEntity<ApplicationDomain::Contact>(key), operation, oldRemoteId, modifiedProperties);
603 job = replay(mail, operation, oldRemoteId, modifiedProperties);
604 } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) { 604 } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) {
605 auto mail = store().readEntity<ApplicationDomain::Addressbook>(key); 605 job = replay(store().readEntity<ApplicationDomain::Addressbook>(key), operation, oldRemoteId, modifiedProperties);
606 job = replay(mail, operation, oldRemoteId, modifiedProperties);
607 } else { 606 } else {
608 SinkErrorCtx(mLogCtx) << "Replayed unknown type: " << type; 607 SinkErrorCtx(mLogCtx) << "Replayed unknown type: " << type;
609 } 608 }
diff --git a/dist/sink.spec b/dist/sink.spec
index f5132b7..4a4f50a 100644
--- a/dist/sink.spec
+++ b/dist/sink.spec
@@ -1,7 +1,7 @@
1 1
2Name: sink 2Name: sink
3Version: 0.3 3Version: 0.4.0
4Release: 10%{?dist} 4Release: 0%{?dist}
5Summary: sink 5Summary: sink
6 6
7Group: Applications/Desktop 7Group: Applications/Desktop
@@ -17,7 +17,7 @@ BuildRequires: kasync-devel
17BuildRequires: kf5-kcoreaddons-devel 17BuildRequires: kf5-kcoreaddons-devel
18BuildRequires: kf5-kcontacts-devel 18BuildRequires: kf5-kcontacts-devel
19BuildRequires: kf5-kmime-devel 19BuildRequires: kf5-kmime-devel
20BuildRequires: kimap2-devel 20BuildRequires: kimap2-devel >= 0.2
21BuildRequires: kdav2-devel 21BuildRequires: kdav2-devel
22BuildRequires: libcurl-devel 22BuildRequires: libcurl-devel
23BuildRequires: libgit2-devel 23BuildRequires: libgit2-devel
diff --git a/examples/davresource/CMakeLists.txt b/examples/davresource/CMakeLists.txt
index 7091edc..2351ecd 100644
--- a/examples/davresource/CMakeLists.txt
+++ b/examples/davresource/CMakeLists.txt
@@ -6,7 +6,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
6find_package(KPimKDAV2 REQUIRED) 6find_package(KPimKDAV2 REQUIRED)
7 7
8add_library(${PROJECT_NAME} SHARED davresource.cpp) 8add_library(${PROJECT_NAME} SHARED davresource.cpp)
9qt5_use_modules(${PROJECT_NAME} Core Network) 9target_link_libraries(${PROJECT_NAME} sink Qt5::Core Qt5::Network KPim::KDAV2)
10target_link_libraries(${PROJECT_NAME} sink KPim::KDAV2)
11 10
12install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) 11install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH})
diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp
index 465220f..22c502f 100644
--- a/examples/davresource/davresource.cpp
+++ b/examples/davresource/davresource.cpp
@@ -93,7 +93,7 @@ public:
93 QVector<QByteArray> ridList; 93 QVector<QByteArray> ridList;
94 for(const auto &f : addressbookList) { 94 for(const auto &f : addressbookList) {
95 const auto &rid = getRid(f); 95 const auto &rid = getRid(f);
96 SinkTrace() << "Found addressbook:" << rid; 96 SinkLog() << "Found addressbook:" << rid << f.displayName();
97 ridList.append(rid); 97 ridList.append(rid);
98 createAddressbook(f.displayName(), rid, ""); 98 createAddressbook(f.displayName(), rid, "");
99 } 99 }
@@ -138,19 +138,21 @@ public:
138 if (error) { 138 if (error) {
139 SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString(); 139 SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString();
140 } else { 140 } else {
141 synchronizeAddressbooks(collectionsFetchJob ->collections()); 141 synchronizeAddressbooks(collectionsFetchJob->collections());
142 } 142 }
143 }); 143 });
144 return job; 144 return job;
145 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) { 145 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) {
146 SinkLogCtx(mLogCtx) << "Synchronizing contacts."; 146 SinkLogCtx(mLogCtx) << "Synchronizing contacts.";
147 auto ridList = QSharedPointer<QByteArrayList>::create(); 147 auto ridList = QSharedPointer<QByteArrayList>::create();
148 auto total = QSharedPointer<int>::create(0);
149 auto progress = QSharedPointer<int>::create(0);
148 auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(mResourceUrl); 150 auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(mResourceUrl);
149 auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] { 151 auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] {
150 synchronizeAddressbooks(collectionsFetchJob ->collections()); 152 synchronizeAddressbooks(collectionsFetchJob ->collections());
151 return collectionsFetchJob->collections(); 153 return collectionsFetchJob->collections();
152 }) 154 })
153 .serialEach([this, ridList](const KDAV2::DavCollection &collection) { 155 .serialEach([=](const KDAV2::DavCollection &collection) {
154 auto collId = getRid(collection); 156 auto collId = getRid(collection);
155 const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId); 157 const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId);
156 auto ctag = collection.CTag().toLatin1(); 158 auto ctag = collection.CTag().toLatin1();
@@ -158,10 +160,11 @@ public:
158 SinkTraceCtx(mLogCtx) << "Syncing " << collId; 160 SinkTraceCtx(mLogCtx) << "Syncing " << collId;
159 auto cache = std::shared_ptr<KDAV2::EtagCache>(new KDAV2::EtagCache()); 161 auto cache = std::shared_ptr<KDAV2::EtagCache>(new KDAV2::EtagCache());
160 auto davItemsListJob = new KDAV2::DavItemsListJob(collection.url(), cache); 162 auto davItemsListJob = new KDAV2::DavItemsListJob(collection.url(), cache);
161 const QByteArray bufferType = ENTITY_TYPE_CONTACT;
162 QHash<QByteArray, Query::Comparator> mergeCriteria; 163 QHash<QByteArray, Query::Comparator> mergeCriteria;
163 auto colljob = runJob(davItemsListJob).then([davItemsListJob] { 164 auto colljob = runJob(davItemsListJob).then([=] {
164 return KAsync::value(davItemsListJob->items()); 165 const auto items = davItemsListJob->items();
166 *total = items.size();
167 return KAsync::value(items);
165 }) 168 })
166 .serialEach([=] (const KDAV2::DavItem &item) { 169 .serialEach([=] (const KDAV2::DavItem &item) {
167 QByteArray rid = getRid(item); 170 QByteArray rid = getRid(item);
@@ -175,13 +178,19 @@ public:
175 Sink::ApplicationDomain::Contact contact; 178 Sink::ApplicationDomain::Contact contact;
176 contact.setVcard(item.data()); 179 contact.setVcard(item.data());
177 contact.setAddressbook(addressbookLocalId); 180 contact.setAddressbook(addressbookLocalId);
178 createOrModify(bufferType, rid, contact, mergeCriteria); 181 createOrModify(ENTITY_TYPE_CONTACT, rid, contact, mergeCriteria);
179 return item; 182 return item;
180 }) 183 })
181 .then([this, ridList] (const KDAV2::DavItem &item) { 184 .then([=] (const KDAV2::DavItem &item) {
182 const auto rid = getRid(item); 185 const auto rid = getRid(item);
183 syncStore().writeValue(rid + "_etag", item.etag().toLatin1()); 186 syncStore().writeValue(rid + "_etag", item.etag().toLatin1());
184 ridList->append(rid); 187 ridList->append(rid);
188 *progress += 1;
189 reportProgress(*progress, *total, QByteArrayList{} << addressbookLocalId);
190 //commit every 5 contacts (so contacts start appearing in the UI)
191 if ((*progress % 5) == 0) {
192 commit();
193 }
185 return rid; 194 return rid;
186 }); 195 });
187 return itemjob; 196 return itemjob;
@@ -190,7 +199,7 @@ public:
190 return KAsync::value(rid); 199 return KAsync::value(rid);
191 } 200 }
192 }) 201 })
193 .then([this, collId, ctag] () { 202 .then([=] () {
194 syncStore().writeValue(collId + "_ctag", ctag); 203 syncStore().writeValue(collId + "_ctag", ctag);
195 }); 204 });
196 return colljob; 205 return colljob;
@@ -251,6 +260,7 @@ DavResourceFactory::DavResourceFactory(QObject *parent)
251 : Sink::ResourceFactory(parent, 260 : Sink::ResourceFactory(parent,
252 {Sink::ApplicationDomain::ResourceCapabilities::Contact::contact, 261 {Sink::ApplicationDomain::ResourceCapabilities::Contact::contact,
253 Sink::ApplicationDomain::ResourceCapabilities::Contact::addressbook, 262 Sink::ApplicationDomain::ResourceCapabilities::Contact::addressbook,
263 Sink::ApplicationDomain::ResourceCapabilities::Contact::storage
254 } 264 }
255 ) 265 )
256{ 266{
diff --git a/examples/dummyresource/CMakeLists.txt b/examples/dummyresource/CMakeLists.txt
index 2bbaa47..62e96f1 100644
--- a/examples/dummyresource/CMakeLists.txt
+++ b/examples/dummyresource/CMakeLists.txt
@@ -6,7 +6,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
6 6
7add_library(${PROJECT_NAME} SHARED resourcefactory.cpp domainadaptor.cpp dummystore.cpp) 7add_library(${PROJECT_NAME} SHARED resourcefactory.cpp domainadaptor.cpp dummystore.cpp)
8generate_flatbuffers(${PROJECT_NAME} dummycalendar) 8generate_flatbuffers(${PROJECT_NAME} dummycalendar)
9qt5_use_modules(${PROJECT_NAME} Core Network) 9target_link_libraries(${PROJECT_NAME} sink Qt5::Core Qt5::Network)
10target_link_libraries(${PROJECT_NAME} sink)
11 10
12install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) 11install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH})
diff --git a/examples/imapresource/CMakeLists.txt b/examples/imapresource/CMakeLists.txt
index 5d2d38b..f5f51f8 100644
--- a/examples/imapresource/CMakeLists.txt
+++ b/examples/imapresource/CMakeLists.txt
@@ -9,8 +9,7 @@ find_package(KIMAP2 0.2 REQUIRED)
9include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 9include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
10 10
11add_library(${PROJECT_NAME} SHARED imapresource.cpp imapserverproxy.cpp) 11add_library(${PROJECT_NAME} SHARED imapresource.cpp imapserverproxy.cpp)
12qt5_use_modules(${PROJECT_NAME} Core Network) 12target_link_libraries(${PROJECT_NAME} sink Qt5::Core Qt5::Network KF5::Mime KIMAP2)
13target_link_libraries(${PROJECT_NAME} sink KF5::Mime KIMAP2)
14 13
15install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) 14install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH})
16 15
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp
index 81c808b..3ae7fd7 100644
--- a/examples/imapresource/imapresource.cpp
+++ b/examples/imapresource/imapresource.cpp
@@ -511,6 +511,8 @@ public:
511 return {ApplicationDomain::LoginError, error.errorMessage}; 511 return {ApplicationDomain::LoginError, error.errorMessage};
512 case Imap::HostNotFoundError: 512 case Imap::HostNotFoundError:
513 return {ApplicationDomain::NoServerError, error.errorMessage}; 513 return {ApplicationDomain::NoServerError, error.errorMessage};
514 case Imap::ConnectionLost:
515 return {ApplicationDomain::ConnectionLostError, error.errorMessage};
514 default: 516 default:
515 return {ApplicationDomain::UnknownError, error.errorMessage}; 517 return {ApplicationDomain::UnknownError, error.errorMessage};
516 } 518 }
@@ -619,6 +621,15 @@ public:
619 621
620 KAsync::Job<QByteArray> replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE 622 KAsync::Job<QByteArray> replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
621 { 623 {
624 if (operation != Sink::Operation_Creation) {
625 if(oldRemoteId.isEmpty()) {
626 // return KAsync::error<QByteArray>("Tried to replay modification without old remoteId.");
627 qWarning() << "Tried to replay modification without old remoteId.";
628 // Since we can't recover from the situation we just skip over the revision.
629 // FIXME figure out how we can ever end up in this situation
630 return KAsync::null<QByteArray>();
631 }
632 }
622 auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort, &mSessionCache); 633 auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort, &mSessionCache);
623 auto login = imap->login(mUser, mPassword); 634 auto login = imap->login(mUser, mPassword);
624 KAsync::Job<QByteArray> job = KAsync::null<QByteArray>(); 635 KAsync::Job<QByteArray> job = KAsync::null<QByteArray>();
diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp
index 538105c..5c2e07c 100644
--- a/examples/imapresource/imapserverproxy.cpp
+++ b/examples/imapresource/imapserverproxy.cpp
@@ -61,6 +61,21 @@ const char* Imap::Capabilities::Namespace = "NAMESPACE";
61const char* Imap::Capabilities::Uidplus = "UIDPLUS"; 61const char* Imap::Capabilities::Uidplus = "UIDPLUS";
62const char* Imap::Capabilities::Condstore = "CONDSTORE"; 62const char* Imap::Capabilities::Condstore = "CONDSTORE";
63 63
64static int translateImapError(int error)
65{
66 switch (error) {
67 case KJob::UserDefinedError:
68 return Imap::ConnectionLost;
69 case KIMAP2::LoginJob::ErrorCode::ERR_HOST_NOT_FOUND:
70 return Imap::HostNotFoundError;
71 case KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT:
72 return Imap::CouldNotConnectError;
73 case KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED:
74 return Imap::SslHandshakeError;
75 }
76 return Imap::UnknownError;
77}
78
64template <typename T> 79template <typename T>
65static KAsync::Job<T> runJob(KJob *job, const std::function<T(KJob*)> &f) 80static KAsync::Job<T> runJob(KJob *job, const std::function<T(KJob*)> &f)
66{ 81{
@@ -69,7 +84,8 @@ static KAsync::Job<T> runJob(KJob *job, const std::function<T(KJob*)> &f)
69 SinkTrace() << "Job done: " << job->metaObject()->className(); 84 SinkTrace() << "Job done: " << job->metaObject()->className();
70 if (job->error()) { 85 if (job->error()) {
71 SinkWarning() << "Job failed: " << job->errorString() << job->metaObject()->className(); 86 SinkWarning() << "Job failed: " << job->errorString() << job->metaObject()->className();
72 future.setError(job->error(), job->errorString()); 87 auto proxyError = translateImapError(job->error());
88 future.setError(proxyError, job->errorString());
73 } else { 89 } else {
74 future.setValue(f(job)); 90 future.setValue(f(job));
75 future.setFinished(); 91 future.setFinished();
@@ -87,7 +103,8 @@ static KAsync::Job<void> runJob(KJob *job)
87 SinkTrace() << "Job done: " << job->metaObject()->className(); 103 SinkTrace() << "Job done: " << job->metaObject()->className();
88 if (job->error()) { 104 if (job->error()) {
89 SinkWarning() << "Job failed: " << job->errorString() << job->metaObject()->className(); 105 SinkWarning() << "Job failed: " << job->errorString() << job->metaObject()->className();
90 future.setError(job->error(), job->errorString()); 106 auto proxyError = translateImapError(job->error());
107 future.setError(proxyError, job->errorString());
91 } else { 108 } else {
92 future.setFinished(); 109 future.setFinished();
93 } 110 }
@@ -166,20 +183,6 @@ KAsync::Job<void> ImapServerProxy::login(const QString &username, const QString
166 // SinkTrace() << "Found personal namespaces: " << mNamespaces.personal; 183 // SinkTrace() << "Found personal namespaces: " << mNamespaces.personal;
167 // SinkTrace() << "Found shared namespaces: " << mNamespaces.shared; 184 // SinkTrace() << "Found shared namespaces: " << mNamespaces.shared;
168 // SinkTrace() << "Found user namespaces: " << mNamespaces.user; 185 // SinkTrace() << "Found user namespaces: " << mNamespaces.user;
169 }).then([=] (const KAsync::Error &error) {
170 if (error) {
171 switch (error.errorCode) {
172 case KIMAP2::LoginJob::ErrorCode::ERR_HOST_NOT_FOUND:
173 return KAsync::error(HostNotFoundError, "Host not found: " + error.errorMessage);
174 case KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT:
175 return KAsync::error(CouldNotConnectError, "Failed to connect: " + error.errorMessage);
176 case KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED:
177 return KAsync::error(SslHandshakeError, "Ssl handshake failed: " + error.errorMessage);
178 default:
179 return KAsync::error(error);
180 }
181 }
182 return KAsync::null();
183 }); 186 });
184} 187}
185 188
diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h
index 82f4f58..f9b854b 100644
--- a/examples/imapresource/imapserverproxy.h
+++ b/examples/imapresource/imapserverproxy.h
@@ -33,7 +33,9 @@ enum ErrorCode {
33 NoError, 33 NoError,
34 HostNotFoundError, 34 HostNotFoundError,
35 CouldNotConnectError, 35 CouldNotConnectError,
36 SslHandshakeError 36 SslHandshakeError,
37 ConnectionLost,
38 UnknownError
37}; 39};
38 40
39namespace Flags 41namespace Flags
diff --git a/examples/maildirresource/CMakeLists.txt b/examples/maildirresource/CMakeLists.txt
index a8f0359..690817e 100644
--- a/examples/maildirresource/CMakeLists.txt
+++ b/examples/maildirresource/CMakeLists.txt
@@ -6,8 +6,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
6find_package(KF5 COMPONENTS REQUIRED Mime) 6find_package(KF5 COMPONENTS REQUIRED Mime)
7 7
8add_library(${PROJECT_NAME} SHARED facade.cpp maildirresource.cpp libmaildir/maildir.cpp libmaildir/keycache.cpp) 8add_library(${PROJECT_NAME} SHARED facade.cpp maildirresource.cpp libmaildir/maildir.cpp libmaildir/keycache.cpp)
9qt5_use_modules(${PROJECT_NAME} Core Network) 9target_link_libraries(${PROJECT_NAME} sink Qt5::Core Qt5::Network KF5::Mime)
10target_link_libraries(${PROJECT_NAME} sink KF5::Mime)
11 10
12install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) 11install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH})
13 12
diff --git a/examples/mailtransportresource/CMakeLists.txt b/examples/mailtransportresource/CMakeLists.txt
index 03b89db..1c34676 100644
--- a/examples/mailtransportresource/CMakeLists.txt
+++ b/examples/mailtransportresource/CMakeLists.txt
@@ -10,13 +10,23 @@ include_directories(${CURL_INCLUDE_DIRS})
10 10
11 11
12add_library(${PROJECT_NAME} SHARED mailtransportresource.cpp mailtransport.cpp) 12add_library(${PROJECT_NAME} SHARED mailtransportresource.cpp mailtransport.cpp)
13qt5_use_modules(${PROJECT_NAME} Core Network) 13target_link_libraries(${PROJECT_NAME}
14target_link_libraries(${PROJECT_NAME} sink KF5::Mime ${CURL_LIBRARIES}) 14 sink
15 Qt5::Core
16 Qt5::Network
17 KF5::Mime
18 ${CURL_LIBRARIES}
19)
15install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) 20install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH})
16 21
17add_executable(sink_smtp_test smtptest.cpp mailtransport.cpp) 22add_executable(sink_smtp_test smtptest.cpp mailtransport.cpp)
18qt5_use_modules(sink_smtp_test Core Network) 23target_link_libraries(sink_smtp_test
19target_link_libraries(sink_smtp_test sink KF5::Mime ${CURL_LIBRARIES}) 24 sink
25 Qt5::Core
26 Qt5::Network
27 KF5::Mime
28 ${CURL_LIBRARIES}
29)
20install(TARGETS sink_smtp_test ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) 30install(TARGETS sink_smtp_test ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
21 31
22add_subdirectory(tests) 32add_subdirectory(tests)
diff --git a/sinksh/CMakeLists.txt b/sinksh/CMakeLists.txt
index bc487c1..cd12c16 100644
--- a/sinksh/CMakeLists.txt
+++ b/sinksh/CMakeLists.txt
@@ -19,6 +19,7 @@ set(sink_cli_SRCS
19 syntax_modules/sink_inspect.cpp 19 syntax_modules/sink_inspect.cpp
20 syntax_modules/sink_drop.cpp 20 syntax_modules/sink_drop.cpp
21 syntax_modules/sink_upgrade.cpp 21 syntax_modules/sink_upgrade.cpp
22 syntax_modules/sink_info.cpp
22 sinksh_utils.cpp 23 sinksh_utils.cpp
23 repl/repl.cpp 24 repl/repl.cpp
24 repl/replStates.cpp 25 repl/replStates.cpp
diff --git a/sinksh/syntax_modules/sink_info.cpp b/sinksh/syntax_modules/sink_info.cpp
new file mode 100644
index 0000000..aa515e6
--- /dev/null
+++ b/sinksh/syntax_modules/sink_info.cpp
@@ -0,0 +1,51 @@
1/*
2 * Copyright (C) 2017 Christian Mollekopf <mollekopf@kolabsys.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program 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
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include <QDebug>
21#include <QObject> // tr()
22
23#include "common/definitions.h"
24
25#include "sinksh_utils.h"
26#include "state.h"
27#include "syntaxtree.h"
28#include "sink_version.h"
29
30namespace SinkInfo
31{
32
33bool info(const QStringList &args, State &state)
34{
35 state.printLine(QString("Sink version: %1").arg(sink_VERSION_STRING));
36 state.printLine(QString("Storage location: %1").arg(Sink::storageLocation()));
37 state.printLine(QString("Data location: %1").arg(Sink::dataLocation()));
38 state.printLine(QString("Config location: %1").arg(Sink::configLocation()));
39 state.printLine(QString("Temporary file location: %1").arg(Sink::temporaryFileLocation()));
40 state.printLine(QString("Resource storage location: %1").arg(Sink::resourceStorageLocation("$RESOURCE")));
41 return false;
42}
43
44Syntax::List syntax()
45{
46 return Syntax::List() << Syntax{"info", QObject::tr("Shows general system info"), &SinkInfo::info, Syntax::NotInteractive};
47}
48
49REGISTER_SYNTAX(SinkInfo)
50
51}
diff --git a/sinksh/syntax_modules/sink_inspect.cpp b/sinksh/syntax_modules/sink_inspect.cpp
index a8a3805..da62250 100644
--- a/sinksh/syntax_modules/sink_inspect.cpp
+++ b/sinksh/syntax_modules/sink_inspect.cpp
@@ -19,8 +19,6 @@
19 19
20#include <QDebug> 20#include <QDebug>
21#include <QObject> // tr() 21#include <QObject> // tr()
22#include <QTimer>
23#include <QDir>
24 22
25#include "common/resource.h" 23#include "common/resource.h"
26#include "common/storage.h" 24#include "common/storage.h"
@@ -30,6 +28,7 @@
30#include "common/definitions.h" 28#include "common/definitions.h"
31#include "common/entitybuffer.h" 29#include "common/entitybuffer.h"
32#include "common/metadata_generated.h" 30#include "common/metadata_generated.h"
31#include "common/bufferutils.h"
33 32
34#include "sinksh_utils.h" 33#include "sinksh_utils.h"
35#include "state.h" 34#include "state.h"
@@ -41,7 +40,7 @@ namespace SinkInspect
41bool inspect(const QStringList &args, State &state) 40bool inspect(const QStringList &args, State &state)
42{ 41{
43 if (args.isEmpty()) { 42 if (args.isEmpty()) {
44 state.printError(QObject::tr("Options: $type [--resource $resource] [--db $db] [--filter $id] [--showinternal]")); 43 state.printError(QObject::tr("Options: [--resource $resource] ([--db $db] [--filter $id] [--showinternal] | [--validaterids $type])"));
45 } 44 }
46 auto options = SyntaxTree::parseOptions(args); 45 auto options = SyntaxTree::parseOptions(args);
47 auto resource = options.options.value("resource").value(0); 46 auto resource = options.options.value("resource").value(0);
@@ -49,6 +48,75 @@ bool inspect(const QStringList &args, State &state)
49 Sink::Storage::DataStore storage(Sink::storageLocation(), resource, Sink::Storage::DataStore::ReadOnly); 48 Sink::Storage::DataStore storage(Sink::storageLocation(), resource, Sink::Storage::DataStore::ReadOnly);
50 auto transaction = storage.createTransaction(Sink::Storage::DataStore::ReadOnly); 49 auto transaction = storage.createTransaction(Sink::Storage::DataStore::ReadOnly);
51 50
51 bool validateRids = options.options.contains("validaterids");
52 if (validateRids) {
53 if (options.options.value("validaterids").isEmpty()) {
54 state.printError(QObject::tr("Specify a type to validate."));
55 return false;
56 }
57 auto type = options.options.value("validaterids").first().toUtf8();
58 /*
59 * Try to find all rid's for all uid's.
60 * If we have entities without rid's that either means we have only created it locally or that we have a problem.
61 */
62 Sink::Storage::DataStore syncStore(Sink::storageLocation(), resource + ".synchronization", Sink::Storage::DataStore::ReadOnly);
63 auto syncTransaction = syncStore.createTransaction(Sink::Storage::DataStore::ReadOnly);
64
65 auto db = transaction.openDatabase(type + ".main",
66 [&] (const Sink::Storage::DataStore::Error &e) {
67 Q_ASSERT(false);
68 state.printError(e.message);
69 }, false);
70
71 auto ridMap = syncTransaction.openDatabase("localid.mapping." + type,
72 [&] (const Sink::Storage::DataStore::Error &e) {
73 Q_ASSERT(false);
74 state.printError(e.message);
75 }, false);
76
77 QHash<QByteArray, QByteArray> hash;
78
79 ridMap.scan("", [&] (const QByteArray &key, const QByteArray &data) {
80 hash.insert(key, data);
81 return true;
82 },
83 [&](const Sink::Storage::DataStore::Error &e) {
84 state.printError(e.message);
85 },
86 false);
87
88 QSet<QByteArray> uids;
89 db.scan("", [&] (const QByteArray &key, const QByteArray &data) {
90 uids.insert(Sink::Storage::DataStore::uidFromKey(key));
91 return true;
92 },
93 [&](const Sink::Storage::DataStore::Error &e) {
94 state.printError(e.message);
95 },
96 false);
97
98 int missing = 0;
99 for (const auto &uid : uids) {
100 if (!hash.remove(uid)) {
101 missing++;
102 qWarning() << "Failed to find RID for " << uid;
103 }
104 }
105 if (missing) {
106 qWarning() << "Found a total of " << missing << " missing rids";
107 }
108
109 //If we still have items in the hash it means we have rid mappings for entities
110 //that no longer exist.
111 if (!hash.isEmpty()) {
112 qWarning() << "Have rids left: " << hash.size();
113 } else if (!missing) {
114 qWarning() << "Everything is in order.";
115 }
116
117 return false;
118 }
119
52 auto dbs = options.options.value("db"); 120 auto dbs = options.options.value("db");
53 auto idFilter = options.options.value("filter"); 121 auto idFilter = options.options.value("filter");
54 bool showInternal = options.options.contains("showinternal"); 122 bool showInternal = options.options.contains("showinternal");
@@ -96,7 +164,10 @@ bool inspect(const QStringList &args, State &state)
96 state.printError("Read invalid buffer from disk: " + key); 164 state.printError("Read invalid buffer from disk: " + key);
97 } else { 165 } else {
98 const auto metadata = flatbuffers::GetRoot<Sink::Metadata>(buffer.metadataBuffer()); 166 const auto metadata = flatbuffers::GetRoot<Sink::Metadata>(buffer.metadataBuffer());
99 state.printLine("Key: " + key + " Operation: " + QString::number(metadata->operation())); 167 state.printLine("Key: " + key
168 + " Operation: " + QString::number(metadata->operation())
169 + " Replay: " + (metadata->replayToSource() ? "true" : "false")
170 + ((metadata->modifiedProperties() && metadata->modifiedProperties()->size() != 0) ? (" [" + Sink::BufferUtils::fromVector(*metadata->modifiedProperties()).join(", ")) + "]": ""));
100 } 171 }
101 } else { 172 } else {
102 state.printLine("Key: " + key + " Value: " + QString::fromUtf8(data)); 173 state.printLine("Key: " + key + " Value: " + QString::fromUtf8(data));
diff --git a/sinksh/syntax_modules/sink_show.cpp b/sinksh/syntax_modules/sink_show.cpp
index 391505a..7a4166f 100644
--- a/sinksh/syntax_modules/sink_show.cpp
+++ b/sinksh/syntax_modules/sink_show.cpp
@@ -18,18 +18,12 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */ 19 */
20 20
21#include <QCoreApplication>
22#include <QDebug> 21#include <QDebug>
23#include <QObject> // tr() 22#include <QObject> // tr()
24#include <QModelIndex> 23#include <QModelIndex>
25#include <QTime> 24#include <QTime>
26 25
27#include "common/resource.h"
28#include "common/storage.h" 26#include "common/storage.h"
29#include "common/resourceconfig.h"
30#include "common/log.h"
31#include "common/storage.h"
32#include "common/definitions.h"
33 27
34#include "sinksh_utils.h" 28#include "sinksh_utils.h"
35#include "state.h" 29#include "state.h"
@@ -41,25 +35,24 @@ namespace SinkShow
41bool show(const QStringList &args, State &state) 35bool show(const QStringList &args, State &state)
42{ 36{
43 if (args.isEmpty()) { 37 if (args.isEmpty()) {
44 state.printError(QObject::tr("Please provide at least one type to show (e.g. resource, ..")); 38 state.printError(QObject::tr("Options: $type --resource $resource --id $id"));
45 return false; 39 return false;
46 } 40 }
47 41
48 auto argList = args; 42 auto options = SyntaxTree::parseOptions(args);
49 if (argList.size() < 2 || !SinkshUtils::isValidStoreType(argList.at(0))) { 43
44 auto type = options.positionalArguments.isEmpty() ? QString{} : options.positionalArguments.first();
45 auto resource = options.options.value("resource");
46 auto id = options.options.value("id");
47
48 if (id.isEmpty() || resource.isEmpty() || !SinkshUtils::isValidStoreType(type)) {
50 state.printError(QObject::tr("Invalid command syntax. Supply type and resource at least.")); 49 state.printError(QObject::tr("Invalid command syntax. Supply type and resource at least."));
51 return false; 50 return false;
52 } 51 }
53 auto type = argList.takeFirst();
54 auto resource = argList.takeFirst();
55 bool queryForResourceOrAgent = argList.isEmpty();
56 52
57 Sink::Query query; 53 Sink::Query query;
58 if (queryForResourceOrAgent) { 54 query.resourceFilter(resource.first().toLatin1());
59 query.filter(resource.toLatin1()); 55 query.filter(id.first().toLatin1());
60 } else {
61 query.resourceFilter(resource.toLatin1());
62 }
63 56
64 QTime time; 57 QTime time;
65 time.start(); 58 time.start();
diff --git a/synchronizer/CMakeLists.txt b/synchronizer/CMakeLists.txt
index e049d64..2f8b128 100644
--- a/synchronizer/CMakeLists.txt
+++ b/synchronizer/CMakeLists.txt
@@ -7,6 +7,11 @@ set(sinksynchronizer_SRCS
7) 7)
8 8
9add_executable(${PROJECT_NAME} ${sinksynchronizer_SRCS}) 9add_executable(${PROJECT_NAME} ${sinksynchronizer_SRCS})
10target_link_libraries(${PROJECT_NAME} sink KAsync ${CMAKE_DL_LIBS}) 10target_link_libraries(${PROJECT_NAME}
11qt5_use_modules(${PROJECT_NAME} Core Network) 11 sink
12 Qt5::Core
13 Qt5::Network
14 KAsync
15 ${CMAKE_DL_LIBS}
16)
12install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) 17install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 2b3e7b1..6a757ca 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -9,12 +9,19 @@ include_directories(
9 ) 9 )
10 10
11add_definitions(-DTESTDATAPATH="${CMAKE_CURRENT_SOURCE_DIR}/data") 11add_definitions(-DTESTDATAPATH="${CMAKE_CURRENT_SOURCE_DIR}/data")
12add_definitions(-DTHREADTESTDATAPATH="${CMAKE_CURRENT_SOURCE_DIR}/threaddata")
12 13
13find_package(KF5 COMPONENTS REQUIRED Mime) 14find_package(KF5 COMPONENTS REQUIRED Mime)
14 15
15add_library(sink_test SHARED testimplementations.cpp getrssusage.cpp mailtest.cpp mailsynctest.cpp mailthreadtest.cpp) 16add_library(sink_test SHARED testimplementations.cpp getrssusage.cpp mailtest.cpp mailsynctest.cpp mailthreadtest.cpp)
16qt5_use_modules(sink_test Core Test Concurrent) 17target_link_libraries(sink_test
17target_link_libraries(sink_test sink libhawd KF5::Mime) 18 sink
19 libhawd
20 Qt5::Core
21 Qt5::Concurrent
22 Qt5::Test
23 KF5::Mime
24)
18 25
19add_executable(dbwriter dbwriter.cpp) 26add_executable(dbwriter dbwriter.cpp)
20target_link_libraries(dbwriter sink) 27target_link_libraries(dbwriter sink)
diff --git a/tests/SinkTest.cmake b/tests/SinkTest.cmake
index 03076e7..4eb8f43 100644
--- a/tests/SinkTest.cmake
+++ b/tests/SinkTest.cmake
@@ -17,8 +17,13 @@ macro(auto_tests)
17 add_executable(${_testname} ${_testname}.cpp) 17 add_executable(${_testname} ${_testname}.cpp)
18 add_test(${_testname} ${_testname}) 18 add_test(${_testname} ${_testname})
19 add_memcheck_test(${_testname} ${_testname}) 19 add_memcheck_test(${_testname} ${_testname})
20 qt5_use_modules(${_testname} Core Test Concurrent) 20 target_link_libraries(${_testname}
21 target_link_libraries(${_testname} sink libhawd sink_test) 21 sink libhawd
22 sink_test
23 Qt5::Core
24 Qt5::Concurrent
25 Qt5::Test
26 )
22 endforeach(_testname) 27 endforeach(_testname)
23endmacro(auto_tests) 28endmacro(auto_tests)
24 29
@@ -26,7 +31,13 @@ macro(manual_tests)
26 foreach(_testname ${ARGN}) 31 foreach(_testname ${ARGN})
27 add_executable(${_testname} ${_testname}.cpp) 32 add_executable(${_testname} ${_testname}.cpp)
28 add_memcheck_test(${_testname} ${_testname}) 33 add_memcheck_test(${_testname} ${_testname})
29 qt5_use_modules(${_testname} Core Test Concurrent) 34 target_link_libraries(${_testname}
30 target_link_libraries(${_testname} sink libhawd sink_test) 35 sink
36 libhawd
37 sink_test
38 Qt5::Core
39 Qt5::Concurrent
40 Qt5::Test
41 )
31 endforeach(_testname) 42 endforeach(_testname)
32endmacro(manual_tests) 43endmacro(manual_tests)
diff --git a/tests/accountstest.cpp b/tests/accountstest.cpp
index 0ab18ef..2eee9f9 100644
--- a/tests/accountstest.cpp
+++ b/tests/accountstest.cpp
@@ -139,7 +139,7 @@ private slots:
139 auto model = Sink::Store::loadModel<Sink::ApplicationDomain::SinkAccount>(query); 139 auto model = Sink::Store::loadModel<Sink::ApplicationDomain::SinkAccount>(query);
140 QTRY_COMPARE(model->rowCount(QModelIndex()), 1); 140 QTRY_COMPARE(model->rowCount(QModelIndex()), 1);
141 auto account = model->data(model->index(0, 0, QModelIndex()), Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::SinkAccount::Ptr>(); 141 auto account = model->data(model->index(0, 0, QModelIndex()), Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::SinkAccount::Ptr>();
142 QCOMPARE(account->getStatus(), static_cast<int>(Sink::ApplicationDomain::OfflineStatus)); 142 QCOMPARE(account->getStatus(), static_cast<int>(Sink::ApplicationDomain::NoStatus));
143 143
144 //Synchronize to connect 144 //Synchronize to connect
145 VERIFYEXEC(Sink::Store::synchronize(Query().resourceFilter(res.identifier()))); 145 VERIFYEXEC(Sink::Store::synchronize(Query().resourceFilter(res.identifier())));
@@ -148,6 +148,40 @@ private slots:
148 } 148 }
149 } 149 }
150 150
151 void testLoadAccountStatusLive()
152 {
153 using namespace Sink;
154 using namespace Sink::ApplicationDomain;
155
156 {
157 //Create a live query for all accounts
158 Sink::Query query;
159 query.setFlags(Query::LiveQuery);
160 query.request<Sink::ApplicationDomain::SinkAccount::Status>();
161
162 auto model = Sink::Store::loadModel<Sink::ApplicationDomain::SinkAccount>(query);
163
164 //Create the account
165 auto account = ApplicationDomainType::createEntity<SinkAccount>();
166 account.setAccountType("dummy");
167 account.setName("name");
168 VERIFYEXEC(Store::create(account));
169
170 auto res = Sink::ApplicationDomain::DummyResource::create(account.identifier());
171 VERIFYEXEC(Sink::Store::create(res));
172
173 //Ensure the account was created
174 QTRY_COMPARE(model->rowCount(QModelIndex()), 1);
175 auto retrievedAccount = model->data(model->index(0, 0, QModelIndex()), Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::SinkAccount::Ptr>();
176 QCOMPARE(retrievedAccount->getStatus(), static_cast<int>(Sink::ApplicationDomain::NoStatus));
177
178 //Synchronize to connect and ensure we receive the update
179 VERIFYEXEC(Sink::Store::synchronize(Query().resourceFilter(res.identifier())));
180
181 QTRY_COMPARE_WITH_TIMEOUT(model->data(model->index(0, 0, QModelIndex()), Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::SinkAccount::Ptr>()->getStatus(), static_cast<int>(Sink::ApplicationDomain::ConnectedStatus), 1000);
182 }
183 }
184
151}; 185};
152 186
153QTEST_GUILESS_MAIN(AccountsTest) 187QTEST_GUILESS_MAIN(AccountsTest)
diff --git a/tests/clientapitest.cpp b/tests/clientapitest.cpp
index 3955ebd..7e348c2 100644
--- a/tests/clientapitest.cpp
+++ b/tests/clientapitest.cpp
@@ -360,6 +360,8 @@ private slots:
360 auto event = Sink::ApplicationDomain::Event::createEntity<Sink::ApplicationDomain::Event>("dummyresource.instance1"); 360 auto event = Sink::ApplicationDomain::Event::createEntity<Sink::ApplicationDomain::Event>("dummyresource.instance1");
361 Sink::Store::create(event).exec().waitForFinished(); 361 Sink::Store::create(event).exec().waitForFinished();
362 QCOMPARE(facade->creations.size(), 1); 362 QCOMPARE(facade->creations.size(), 1);
363 //Modify something so the mdofication won't be dropped
364 event.setSummary("foobar");
363 Sink::Store::modify(event).exec().waitForFinished(); 365 Sink::Store::modify(event).exec().waitForFinished();
364 QCOMPARE(facade->modifications.size(), 1); 366 QCOMPARE(facade->modifications.size(), 1);
365 Sink::Store::remove(event).exec().waitForFinished(); 367 Sink::Store::remove(event).exec().waitForFinished();
diff --git a/tests/hawd/CMakeLists.txt b/tests/hawd/CMakeLists.txt
index 6ae5f13..7546920 100644
--- a/tests/hawd/CMakeLists.txt
+++ b/tests/hawd/CMakeLists.txt
@@ -27,8 +27,7 @@ set(SRCS
27 27
28add_library(lib${PROJECT_NAME} SHARED ${lib_SRCS}) 28add_library(lib${PROJECT_NAME} SHARED ${lib_SRCS})
29generate_export_header(lib${PROJECT_NAME} BASE_NAME HAWD EXPORT_FILE_NAME hawd_export.h) 29generate_export_header(lib${PROJECT_NAME} BASE_NAME HAWD EXPORT_FILE_NAME hawd_export.h)
30qt5_use_modules(lib${PROJECT_NAME} Core) 30target_link_libraries(lib${PROJECT_NAME} sink Qt5::Core)
31target_link_libraries(lib${PROJECT_NAME} sink)
32if (LIBGIT2_FOUND) 31if (LIBGIT2_FOUND)
33 target_link_libraries(lib${PROJECT_NAME} ${LIBGIT2_LIBRARIES}) 32 target_link_libraries(lib${PROJECT_NAME} ${LIBGIT2_LIBRARIES})
34endif(LIBGIT2_FOUND) 33endif(LIBGIT2_FOUND)
@@ -36,8 +35,7 @@ endif(LIBGIT2_FOUND)
36install(TARGETS lib${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) 35install(TARGETS lib${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
37 36
38add_executable(${PROJECT_NAME} ${SRCS}) 37add_executable(${PROJECT_NAME} ${SRCS})
39qt5_use_modules(${PROJECT_NAME} Core) 38target_link_libraries(${PROJECT_NAME} lib${PROJECT_NAME} Qt5::Core)
40target_link_libraries(${PROJECT_NAME} lib${PROJECT_NAME})
41if (LIBGIT2_FOUND) 39if (LIBGIT2_FOUND)
42 target_link_libraries(${PROJECT_NAME} ${LIBGIT2_LIBRARIES}) 40 target_link_libraries(${PROJECT_NAME} ${LIBGIT2_LIBRARIES})
43endif(LIBGIT2_FOUND) 41endif(LIBGIT2_FOUND)
diff --git a/tests/mailtest.cpp b/tests/mailtest.cpp
index cbb56d5..93d3ce3 100644
--- a/tests/mailtest.cpp
+++ b/tests/mailtest.cpp
@@ -328,6 +328,15 @@ void MailTest::testMarkMailAsRead()
328 return KAsync::null<void>(); 328 return KAsync::null<void>();
329 }); 329 });
330 VERIFYEXEC(job2); 330 VERIFYEXEC(job2);
331
332 //Verify we can mark the mail as unread again
333 {
334 auto readMail = Store::readOne<Mail>(Query{mail});
335 readMail.setUnread(true);
336 VERIFYEXEC(Store::modify(readMail));
337 VERIFYEXEC(ResourceControl::flushReplayQueue(mResourceInstanceIdentifier));
338 VERIFYEXEC(ResourceControl::inspect<Mail>(ResourceControl::Inspection::PropertyInspection(readMail, Mail::Unread::name, true)));
339 }
331} 340}
332 341
333void MailTest::testCreateDraft() 342void MailTest::testCreateDraft()
diff --git a/tests/mailthreadtest.cpp b/tests/mailthreadtest.cpp
index 741eb78..9ed5d83 100644
--- a/tests/mailthreadtest.cpp
+++ b/tests/mailthreadtest.cpp
@@ -21,6 +21,7 @@
21#include <QtTest> 21#include <QtTest>
22 22
23#include <QString> 23#include <QString>
24#include <QFile>
24#include <KMime/Message> 25#include <KMime/Message>
25 26
26#include "store.h" 27#include "store.h"
@@ -185,4 +186,67 @@ void MailThreadTest::testIndexInMixedOrder()
185 /* VERIFYEXEC(ResourceControl::flushReplayQueue(QByteArrayList() << mResourceInstanceIdentifier)); */ 186 /* VERIFYEXEC(ResourceControl::flushReplayQueue(QByteArrayList() << mResourceInstanceIdentifier)); */
186} 187}
187 188
189static QByteArray readMailFromFile(const QString &mailFile)
190{
191 QFile file(QLatin1String(THREADTESTDATAPATH) + QLatin1Char('/') + mailFile);
192 file.open(QIODevice::ReadOnly);
193 Q_ASSERT(file.isOpen());
194 return file.readAll();
195}
196
197static KMime::Message::Ptr readMail(const QString &mailFile)
198{
199 auto msg = KMime::Message::Ptr::create();
200 msg->setContent(readMailFromFile(mailFile));
201 msg->parse();
202 return msg;
203}
204
205void MailThreadTest::testRealWorldThread()
206{
207 auto folder = Folder::create(mResourceInstanceIdentifier);
208 folder.setName("folder");
209 VERIFYEXEC(Store::create(folder));
210
211 auto createMail = [this, folder] (KMime::Message::Ptr msg) {
212 auto mail = Mail::create(mResourceInstanceIdentifier);
213 mail.setMimeMessage(msg->encodedContent(true));
214 mail.setFolder(folder);
215 VERIFYEXEC(Store::create(mail));
216 };
217
218 createMail(readMail("thread1"));
219
220 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier));
221
222 auto query = Sink::StandardQueries::threadLeaders(folder);
223 query.resourceFilter(mResourceInstanceIdentifier);
224 query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>();
225
226 //Ensure we find the thread leader
227 Mail threadLeader = [&] {
228 auto mails = Store::read<Mail>(query);
229 Q_ASSERT(mails.size() == 1);
230 return mails.first();
231 }();
232
233 createMail(readMail("thread2"));
234 createMail(readMail("thread3"));
235 createMail(readMail("thread4"));
236 createMail(readMail("thread5"));
237 createMail(readMail("thread6"));
238 createMail(readMail("thread7"));
239 createMail(readMail("thread8")); //This mail is breaking the thread
240 VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier));
241
242 //Ensure the thread is complete
243 {
244 auto query = Sink::StandardQueries::completeThread(threadLeader);
245 query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>();
246
247 auto mails = Store::read<Mail>(query);
248 QCOMPARE(mails.size(), 8);
249 }
250}
251
188#include "mailthreadtest.moc" 252#include "mailthreadtest.moc"
diff --git a/tests/mailthreadtest.h b/tests/mailthreadtest.h
index 8730ec6..1c5c389 100644
--- a/tests/mailthreadtest.h
+++ b/tests/mailthreadtest.h
@@ -52,6 +52,7 @@ private slots:
52 52
53 void testListThreadLeader(); 53 void testListThreadLeader();
54 void testIndexInMixedOrder(); 54 void testIndexInMixedOrder();
55 void testRealWorldThread();
55}; 56};
56 57
57} 58}
diff --git a/tests/notificationtest.cpp b/tests/notificationtest.cpp
index f0d957e..c043f38 100644
--- a/tests/notificationtest.cpp
+++ b/tests/notificationtest.cpp
@@ -128,13 +128,41 @@ private slots:
128 VERIFYEXEC(Sink::Store::synchronize(newQuery)); 128 VERIFYEXEC(Sink::Store::synchronize(newQuery));
129 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 129 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1"));
130 130
131 QCOMPARE(status.size(), 3); 131 QTRY_COMPARE(status.size(), 3);
132 //Sync progress of item 132 //Sync progress of item
133 QCOMPARE(status.at(0), static_cast<int>(ApplicationDomain::SyncStatus::SyncInProgress)); 133 QCOMPARE(status.at(0), static_cast<int>(ApplicationDomain::SyncStatus::SyncInProgress));
134 QCOMPARE(status.at(1), static_cast<int>(ApplicationDomain::SyncStatus::SyncSuccess)); 134 QCOMPARE(status.at(1), static_cast<int>(ApplicationDomain::SyncStatus::SyncInProgress));
135 //Modification triggered during sync 135 //Modification triggered during sync
136 QCOMPARE(status.at(2), static_cast<int>(ApplicationDomain::SyncStatus::SyncSuccess)); 136 QCOMPARE(status.at(2), static_cast<int>(ApplicationDomain::SyncStatus::SyncSuccess));
137 } 137 }
138
139 void testNotifier()
140 {
141 QList<int> status;
142 Sink::Notifier notifier{Sink::Query{Sink::Query::LiveQuery}.resourceFilter("sink.dummy.instance2")};
143 notifier.registerHandler([&] (const Sink::Notification &notification) {
144 if (notification.type == Notification::Info) {
145 status << notification.code;
146 }
147 });
148
149 auto query = Query().resourceFilter("sink.dummy.instance2");
150 query.setType<ApplicationDomain::Mail>();
151 query.setFlags(Query::LiveQuery | Query::UpdateStatus);
152
153 auto resource = ApplicationDomain::ApplicationDomainType::createEntity<ApplicationDomain::SinkResource>("", "sink.dummy.instance2");
154 resource.setResourceType("sink.dummy");
155 VERIFYEXEC(Store::create(resource));
156
157 VERIFYEXEC(Sink::Store::synchronize(query));
158 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance2"));
159
160 QTRY_COMPARE(status.size(), 2);
161 //Sync progress of item
162 QCOMPARE(status.at(0), static_cast<int>(ApplicationDomain::SyncStatus::SyncInProgress));
163 QCOMPARE(status.at(1), static_cast<int>(ApplicationDomain::SyncStatus::SyncSuccess));
164 }
165
138}; 166};
139 167
140QTEST_MAIN(NotificationTest) 168QTEST_MAIN(NotificationTest)
diff --git a/tests/resourceconfigtest.cpp b/tests/resourceconfigtest.cpp
index df98a61..78a0d4c 100644
--- a/tests/resourceconfigtest.cpp
+++ b/tests/resourceconfigtest.cpp
@@ -91,7 +91,7 @@ private slots:
91 auto model = Sink::Store::loadModel<SinkResource>(query); 91 auto model = Sink::Store::loadModel<SinkResource>(query);
92 QTRY_COMPARE(model->rowCount(QModelIndex()), 1); 92 QTRY_COMPARE(model->rowCount(QModelIndex()), 1);
93 auto resource = model->data(model->index(0, 0, QModelIndex()), Sink::Store::DomainObjectRole).value<SinkResource::Ptr>(); 93 auto resource = model->data(model->index(0, 0, QModelIndex()), Sink::Store::DomainObjectRole).value<SinkResource::Ptr>();
94 QCOMPARE(resource->getStatus(), static_cast<int>(OfflineStatus)); 94 QCOMPARE(resource->getStatus(), static_cast<int>(NoStatus));
95 95
96 //Synchronize to connect 96 //Synchronize to connect
97 VERIFYEXEC(Sink::Store::synchronize(query)); 97 VERIFYEXEC(Sink::Store::synchronize(query));
diff --git a/tests/storagetest.cpp b/tests/storagetest.cpp
index 9e9bad9..3368549 100644
--- a/tests/storagetest.cpp
+++ b/tests/storagetest.cpp
@@ -542,6 +542,35 @@ private slots:
542 } 542 }
543 543
544 } 544 }
545
546 void testRecordUid()
547 {
548 Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite);
549 auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite);
550 Sink::Storage::DataStore::recordUid(transaction, "uid1", "type");
551 Sink::Storage::DataStore::recordUid(transaction, "uid2", "type");
552 Sink::Storage::DataStore::recordUid(transaction, "uid3", "type2");
553
554 {
555 QVector<QByteArray> uids;
556 Sink::Storage::DataStore::getUids("type", transaction, [&](const QByteArray &r) {
557 uids << r;
558 });
559 QVector<QByteArray> expected{{"uid1"}, {"uid2"}};
560 QCOMPARE(uids, expected);
561 }
562
563 Sink::Storage::DataStore::removeUid(transaction, "uid2", "type");
564
565 {
566 QVector<QByteArray> uids;
567 Sink::Storage::DataStore::getUids("type", transaction, [&](const QByteArray &r) {
568 uids << r;
569 });
570 QVector<QByteArray> expected{{"uid1"}};
571 QCOMPARE(uids, expected);
572 }
573 }
545}; 574};
546 575
547QTEST_MAIN(StorageTest) 576QTEST_MAIN(StorageTest)
diff --git a/tests/threaddata/thread1 b/tests/threaddata/thread1
new file mode 100644
index 0000000..8b72901
--- /dev/null
+++ b/tests/threaddata/thread1
@@ -0,0 +1,228 @@
1Return-Path: <kde-community-bounces@kde.org>
2Received: from imapb010.mykolab.com ([unix socket])
3 by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
4 Sun, 13 Aug 2017 11:50:30 +0200
5X-Sieve: CMU Sieve 2.4
6Received: from int-mx002.mykolab.com (unknown [10.9.13.2])
7 by imapb010.mykolab.com (Postfix) with ESMTPS id E79C512C084C4
8 for <christian@mailqueue.ch>; Sun, 13 Aug 2017 11:50:29 +0200 (CEST)
9Received: from mx.kolabnow.com (unknown [10.9.4.1])
10 by int-mx002.mykolab.com (Postfix) with ESMTPS id BE41F2329
11 for <christian@mailqueue.ch>; Sun, 13 Aug 2017 11:50:29 +0200 (CEST)
12X-Virus-Scanned: amavisd-new at mykolab.com
13Authentication-Results: ext-mx-in001.mykolab.com (amavisd-new);
14 dkim=pass (1024-bit key) header.d=kde.org
15X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0
16Received: from forward2-smtp.messagingengine.com (forward2-smtp.messagingengine.com [66.111.4.226])
17 by ext-mx-in001.mykolab.com (Postfix) with ESMTPS id 5614B11BB
18 for <christian@mailqueue.ch>; Sun, 13 Aug 2017 11:50:08 +0200 (CEST)
19Received: from mailredirect.nyi.internal (imap36.nyi.internal [10.202.2.86])
20 by mailforward.nyi.internal (Postfix) with ESMTP id E9026D1C6
21 for <christian@mailqueue.ch>; Sun, 13 Aug 2017 05:50:06 -0400 (EDT)
22Received: by mailredirect.nyi.internal (Postfix, from userid 501)
23 id D95328E3AC; Sun, 13 Aug 2017 05:50:06 -0400 (EDT)
24Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
25 by sloti36d2t28 (Cyrus fastmail-fmjessie44745-15358-git-fastmail-15358) with LMTPA;
26 Sun, 13 Aug 2017 05:50:06 -0400
27X-Cyrus-Session-Id: sloti36d2t28-2961984-1502617806-2-9300763073201928650
28X-Sieve: CMU Sieve 3.0
29X-Spam-known-sender: no
30X-Orig-Spam-score: 0.0
31X-Spam-hits: BAYES_20 -0.001, RCVD_IN_DNSWL_MED -2.3, RP_MATCHES_RCVD -0.001,
32 SPF_PASS -0.001, LANGUAGES en, BAYES_USED global, SA_VERSION 3.4.0
33X-Spam-source: IP='46.4.96.248', Host='postbox.kde.org', Country='DE', FromHeader='org',
34 MailFrom='org'
35X-Spam-charsets: plain='us-ascii'
36X-Attached: signature.asc
37X-Resolved-to: chrigi_1@fastmail.fm
38X-Delivered-to: chrigi_1@fastmail.fm
39X-Mail-from: kde-community-bounces@kde.org
40Received: from mx4 ([10.202.2.203])
41 by compute1.internal (LMTPProxy); Sun, 13 Aug 2017 05:50:06 -0400
42Authentication-Results: mx4.messagingengine.com;
43 dkim=pass (1024-bit rsa key sha256) header.d=kde.org header.i=@kde.org header.b=aAtxmD+3;
44 dmarc=none (p=none;has-list-id=yes) header.from=kde.org;
45 smime=temperror;
46 spf=pass smtp.mailfrom=kde-community-bounces@kde.org smtp.helo=postbox.kde.org
47Received-SPF: pass
48 (kde.org: 46.4.96.248 is authorized to use 'kde-community-bounces@kde.org' in 'mfrom' identity (mechanism 'mx' matched))
49 receiver=mx4.messagingengine.com;
50 identity=mailfrom;
51 envelope-from="kde-community-bounces@kde.org";
52 helo=postbox.kde.org;
53 client-ip=46.4.96.248
54Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
55 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
56 (No client certificate requested)
57 by mx4.messagingengine.com (Postfix) with ESMTPS
58 for <chrigi_1@fastmail.fm>; Sun, 13 Aug 2017 05:50:05 -0400 (EDT)
59DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
60 t=1502617803; bh=IzJP1FRz6gX2xTBV2xMDBc2KfNLK284LvmfZvBQHRwU=;
61 h=From:To:Subject:Date:Reply-To:List-Id:List-Unsubscribe:
62 List-Archive:List-Post:List-Help:List-Subscribe:From;
63 b=aAtxmD+3cXY913YngN6okQjxPwOn+T9Cw1Hl1NpSZ2E4VbNDeuQ9IVj6zCqeAZE6y
64 elk2GquLeHlXeLnygo2n5LQL6epM83pkS+1AWSHQI11mBDT5byLUrXX64hOSZ579jG
65 L6+jAJT8vcqnXZcGg5EjBQqYmFp4HLedW/xduUVg=
66X-Original-To: kde-community@kde.org
67X-Remote-Delivered-To: kde-community@localhost.kde.org
68Received-SPF: Neutral (access neither permitted nor denied) identity=mailfrom;
69 client-ip=85.214.75.115; helo=h2670809.stratoserver.net;
70 envelope-from=vkrause@kde.org; receiver=kde-community@kde.org
71Received: from h2670809.stratoserver.net (deltatauchi.de [85.214.75.115])
72 by postbox.kde.org (Postfix) with ESMTP id 61C21A308E
73 for <kde-community@kde.org>; Sun, 13 Aug 2017 09:49:38 +0000 (UTC)
74Received: from deltatauchi.de (ip5b403802.dynamic.kabel-deutschland.de
75 [91.64.56.2])
76 by h2670809.stratoserver.net (Postfix) with ESMTPSA id 521BBF1A0104
77 for <kde-community@kde.org>; Sun, 13 Aug 2017 11:49:07 +0200 (CEST)
78From: Volker Krause <vkrause@kde.org>
79To: kde-community@kde.org
80Subject: Telemetry Policy
81Date: Sun, 13 Aug 2017 11:47:28 +0200
82Message-ID: <2048912.XfIJe3ZSdj@vkpc5>
83Organization: KDE
84X-Face: rgzmh}R?iq<z7H#sc'l86vzjJ"{\d6`}N5x*9!HFBn`A^tnU?<Q%ruT(jt5PG1$td=GDXe
85 XsXW(lVZ%Z0.2|w-)y[+@HI})\pNZEMi/UY_D";
86 tt:5C'5&O9_xAqO!$HA8Ks-5}uMz%`D "2{s`Mt$}N]I`0UI=0;
87 '4v"!]XgBET9Q%cB?\vr#1=5X3,[a3k@083{n9H0m~Ey5_5xOb; @06MoJe"3/Rfe[eki
88MIME-Version: 1.0
89Content-Type: multipart/signed; boundary="nextPart1627232.ab0ruIHapE";
90 micalg="pgp-sha1"; protocol="application/pgp-signature"
91X-BeenThere: kde-community@kde.org
92X-Mailman-Version: 2.1.16
93Precedence: list
94Reply-To: informing about and discussing non-technical community topics
95 <kde-community@kde.org>
96List-Id: informing about and discussing non-technical community topics
97 <kde-community.kde.org>
98List-Unsubscribe: <https://mail.kde.org/mailman/options/kde-community>,
99 <mailto:kde-community-request@kde.org?subject=unsubscribe>
100List-Archive: <http://mail.kde.org/pipermail/kde-community/>
101List-Post: <mailto:kde-community@kde.org>
102List-Help: <mailto:kde-community-request@kde.org?subject=help>
103List-Subscribe: <https://mail.kde.org/mailman/listinfo/kde-community>,
104 <mailto:kde-community-request@kde.org?subject=subscribe>
105Errors-To: kde-community-bounces@kde.org
106Sender: "kde-community" <kde-community-bounces@kde.org>
107
108--nextPart1627232.ab0ruIHapE
109Content-Transfer-Encoding: 7Bit
110Content-Type: text/plain; charset="us-ascii"
111
112Hi,
113
114during the KUserFeedback BoF at Akademy there was quite some interest in
115collecting telemetry data in KDE applications. But before actually
116implementing that we agreed to define the rules under which we would want to
117do that. I've tried to put the input we collected during Akademy into proper
118wording below. What do you think? Did I miss anything?
119
120Regards,
121Volker
122
123
124# Telemetry Policy Draft
125
126Application telemetry data can be a valuable tool for tailoring our products
127to the needs of our users. The following rules define how KDE collects and
128uses such application telemetry data. As privacy is of utmost importance to
129us, the general rule of thumb is to err on the side of caution here. Privacy
130always trumps any need for telemetry data, no matter how legitimate.
131
132These rules apply to all products released by KDE.
133
134## Transparency
135
136We provide detailed information about the data that is going to be shared, in
137a way that:
138- is easy to understand
139- is precise and complete
140- is available locally without network connectivity
141
142Any changes or additions to the telemetry functionality of an application will
143be highlighted in the corresponding release announcement.
144
145## Control
146
147We give the user full control over what data they want to share with KDE. In
148particular:
149- application telemetry is always opt-in, that is off by default
150- application telemetry settings can be changed at any time, and are provided
151as prominent in the application interface as other application settings
152- applications honor system-wide telemetry settings where they exist (global
153"kill switch")
154- we provide detailed documentation about how to control the application
155telemetry system
156
157In order to ensure control over the data after it has been shared with KDE,
158applications will only transmit this data to KDE servers, that is servers
159under the full control of the KDE sysadmin team.
160
161We will provide a designated contact point for users who have concerns about
162the data they have shared with KDE. While we are willing to delete data a user
163no longer wants to have shared, it should be understood that the below rules
164are designed to make identification of data of a specific user impossible, and
165thus a deletion request impractical.
166
167## Anonymity
168
169We do not transmit data that could be used to identify a specific user. In
170particular:
171- we will not use any unique device, installation or user id
172- data is stripped of any unnecessary detail and downsampled appropriately
173before sharing to avoid fingerprinting
174- network addresses (which are exposed inevitably as part of the data
175transmission) are not stored together with the telemetry data, and must only
176be stored or used to the extend necessary for abuse counter-measures
177
178## Minimalism
179
180We only track the bare minimum of data necessary to answer specific questions,
181we do not collect data preemptively or for exploratory research. In
182particular, this means:
183- collected data must have a clear purpose
184- data is downsampled to the maximum extend possible at the source
185- relevant correlations between individual bits of data should be computed at
186the source whenever possible
187- data collection is stopped once corresponding question has been answered
188
189## Privacy
190
191We will never transmit anything containing user content, or even just hints at
192possible user content such as e.g. file names, URLs, etc.
193
194We will only ever track:
195- system information that are specific to the installation/environment, but
196independent of how the application/machine/installation is actually used
197- statistical usage data of an installation/application
198
199## Compliance
200
201KDE only releases products capable of acquiring telemetry data if compliance
202with these rules has been established by a public review on [kde-core-devel|
203kde-community]@kde.org from at least two reviewers. The review has to be
204repeated for every release if changes have been made to how/what data is
205collected.
206
207Received data is regularly reviewed for violations of these rules, in
208particular for data that is prone to fingerprinting. Should such violations be
209found, the affected data will be deleted, and data recording will be suspended
210until compliance with these rules has been established again. In order to
211enable reviewing of the data, every KDE contributor with a developer account
212will have access to all telemetry data gathered by any KDE product.
213
214--nextPart1627232.ab0ruIHapE
215Content-Type: application/pgp-signature; name="signature.asc"
216Content-Description: This is a digitally signed message part.
217Content-Transfer-Encoding: 7Bit
218
219-----BEGIN PGP SIGNATURE-----
220
221iF0EABECAB0WIQQAnu3FVHA48KjZ07R/lszWTRLSRwUCWZAgMAAKCRB/lszWTRLS
222Ry5WAJ9+8r8e7IFPh54YBsEkisE3+dNs8QCfY+0b0jcYPVP1HdpsTZVoh33JfhU=
223=v6cZ
224-----END PGP SIGNATURE-----
225
226--nextPart1627232.ab0ruIHapE--
227
228
diff --git a/tests/threaddata/thread2 b/tests/threaddata/thread2
new file mode 100644
index 0000000..4d90073
--- /dev/null
+++ b/tests/threaddata/thread2
@@ -0,0 +1,129 @@
1Return-Path: <kde-community-bounces@kde.org>
2Received: from imapb010.mykolab.com ([unix socket])
3 by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
4 Wed, 16 Aug 2017 09:15:00 +0200
5X-Sieve: CMU Sieve 2.4
6Received: from int-mx002.mykolab.com (unknown [10.9.13.2])
7 by imapb010.mykolab.com (Postfix) with ESMTPS id 1772014401C83
8 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 09:15:00 +0200 (CEST)
9Received: from mx.kolabnow.com (unknown [10.9.4.1])
10 by int-mx002.mykolab.com (Postfix) with ESMTPS id 01CCB2348
11 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 09:14:59 +0200 (CEST)
12X-Virus-Scanned: amavisd-new at mykolab.com
13Authentication-Results: ext-mx-in001.mykolab.com (amavisd-new);
14 dkim=pass (1024-bit key) header.d=kde.org
15X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0
16Received: from forward1-smtp.messagingengine.com (forward1-smtp.messagingengine.com [66.111.4.223])
17 by ext-mx-in001.mykolab.com (Postfix) with ESMTPS id 3BC6B11AC
18 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 09:14:42 +0200 (CEST)
19Received: from mailredirect.nyi.internal (imap36.nyi.internal [10.202.2.86])
20 by mailforward.nyi.internal (Postfix) with ESMTP id 9F77F12C6
21 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 03:14:41 -0400 (EDT)
22Received: by mailredirect.nyi.internal (Postfix, from userid 501)
23 id 8EF798E597; Wed, 16 Aug 2017 03:14:41 -0400 (EDT)
24Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
25 by sloti36d2t28 (Cyrus fastmail-fmjessie44745-15358-git-fastmail-15358) with LMTPA;
26 Wed, 16 Aug 2017 03:14:41 -0400
27X-Cyrus-Session-Id: sloti36d2t28-476506-1502867681-2-17694110317903435823
28X-Sieve: CMU Sieve 3.0
29X-Spam-known-sender: no
30X-Orig-Spam-score: 0.0
31X-Spam-hits: BAYES_20 -0.001, HEADER_FROM_DIFFERENT_DOMAINS 0.001,
32 RCVD_IN_DNSWL_MED -2.3, RP_MATCHES_RCVD -0.001, SPF_PASS -0.001,
33 LANGUAGES en, BAYES_USED global, SA_VERSION 3.4.0
34X-Spam-source: IP='46.4.96.248', Host='postbox.kde.org', Country='DE', FromHeader='ch',
35 MailFrom='org'
36X-Spam-charsets: plain='us-ascii'
37X-Resolved-to: chrigi_1@fastmail.fm
38X-Delivered-to: chrigi_1@fastmail.fm
39X-Mail-from: kde-community-bounces@kde.org
40Received: from mx4 ([10.202.2.203])
41 by compute1.internal (LMTPProxy); Wed, 16 Aug 2017 03:14:41 -0400
42Authentication-Results: mx4.messagingengine.com;
43 dkim=pass (1024-bit rsa key sha256) header.d=kde.org header.i=@kde.org header.b=cVfBDwjP;
44 dmarc=none (p=none;has-list-id=yes) header.from=fuchsnet.ch;
45 spf=pass smtp.mailfrom=kde-community-bounces@kde.org smtp.helo=postbox.kde.org
46Received-SPF: pass
47 (kde.org: 46.4.96.248 is authorized to use 'kde-community-bounces@kde.org' in 'mfrom' identity (mechanism 'mx' matched))
48 receiver=mx4.messagingengine.com;
49 identity=mailfrom;
50 envelope-from="kde-community-bounces@kde.org";
51 helo=postbox.kde.org;
52 client-ip=46.4.96.248
53Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
54 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
55 (No client certificate requested)
56 by mx4.messagingengine.com (Postfix) with ESMTPS
57 for <chrigi_1@fastmail.fm>; Wed, 16 Aug 2017 03:14:40 -0400 (EDT)
58DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
59 t=1502867678; bh=70oyTvxfLkdYUd1D8WFhrBEneI7DP4MY5KH1tM/AxUI=;
60 h=From:To:Subject:Date:In-Reply-To:References:Reply-To:List-Id:
61 List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:
62 From;
63 b=cVfBDwjPyB0OrVy5jQaU1YBZtx/95ktf4lpQDQddz0Udb+QkxzLzv6S3He6EjQIRs
64 nnEfVM/Y6V/Q9IHj+AYQckxyZxbXNOmfb9jOgU/R5bhPMkpstCvw/gQTD+LMGsFuSl
65 fCKdwg+KmAWmvBhoe+8Oa6BMR3KKViYziJgMTuwI=
66X-Original-To: kde-community@kde.org
67X-Remote-Delivered-To: kde-community@localhost.kde.org
68Received-SPF: None (no SPF record) identity=mailfrom;
69 client-ip=2a00:d70:0:e::317; helo=mxout017.mail.hostpoint.ch;
70 envelope-from=christian.loosli@fuchsnet.ch; receiver=kde-community@kde.org
71Received: from mxout017.mail.hostpoint.ch (mxout017.mail.hostpoint.ch
72 [IPv6:2a00:d70:0:e::317])
73 by postbox.kde.org (Postfix) with ESMTPS id AA196A3AC5
74 for <kde-community@kde.org>; Sun, 13 Aug 2017 10:18:17 +0000 (UTC)
75Received: from [10.0.2.45] (helo=asmtp012.mail.hostpoint.ch)
76 by mxout017.mail.hostpoint.ch with esmtp (Exim 4.89 (FreeBSD))
77 (envelope-from <christian.loosli@fuchsnet.ch>) id 1dgpyK-000DwH-Of
78 for kde-community@kde.org; Sun, 13 Aug 2017 12:18:16 +0200
79Received: from 77-56-19-119.dclient.hispeed.ch ([77.56.19.119]
80 helo=minixfox.localnet) by asmtp012.mail.hostpoint.ch with esmtpsa
81 (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.89 (FreeBSD))
82 (envelope-from <christian.loosli@fuchsnet.ch>) id 1dgpyK-000CRa-ML
83 for kde-community@kde.org; Sun, 13 Aug 2017 12:18:16 +0200
84X-Authenticated-Sender-Id: mail@fuchsnet.ch
85From: Christian Loosli <christian.loosli@fuchsnet.ch>
86To: kde-community@kde.org
87Subject: Re: Telemetry Policy
88Date: Sun, 13 Aug 2017 12:18:16 +0200
89Message-ID: <2990543.KVDkBByYO0@minixfox>
90User-Agent: KMail/5.2.3 (Linux/4.6.2-040602-generic; KDE/5.35.0; x86_64; ; )
91In-Reply-To: <2048912.XfIJe3ZSdj@vkpc5>
92References: <2048912.XfIJe3ZSdj@vkpc5>
93MIME-Version: 1.0
94Content-Transfer-Encoding: 7Bit
95Content-Type: text/plain; charset="us-ascii"
96X-Mailman-Approved-At: Wed, 16 Aug 2017 07:14:22 +0000
97X-BeenThere: kde-community@kde.org
98X-Mailman-Version: 2.1.16
99Precedence: list
100Reply-To: informing about and discussing non-technical community topics
101 <kde-community@kde.org>
102List-Id: informing about and discussing non-technical community topics
103 <kde-community.kde.org>
104List-Unsubscribe: <https://mail.kde.org/mailman/options/kde-community>,
105 <mailto:kde-community-request@kde.org?subject=unsubscribe>
106List-Archive: <http://mail.kde.org/pipermail/kde-community/>
107List-Post: <mailto:kde-community@kde.org>
108List-Help: <mailto:kde-community-request@kde.org?subject=help>
109List-Subscribe: <https://mail.kde.org/mailman/listinfo/kde-community>,
110 <mailto:kde-community-request@kde.org?subject=subscribe>
111Errors-To: kde-community-bounces@kde.org
112Sender: "kde-community" <kde-community-bounces@kde.org>
113
114Hi,
115
116thank you very much for this work, sounds great!
117
118Only point I have: maybe make sure that the opt-in / default settings are not
119only mandatory for application developers, but also for packagers /
120distributions.
121
122Some distributions have rather questionable views on privacy and by default
123sent information to third parties, so I would feel much more safe if they
124weren't allowed (in theory) to flick the switch in their package by default to
125"on" either.
126
127Kind regards,
128
129Christian
diff --git a/tests/threaddata/thread3 b/tests/threaddata/thread3
new file mode 100644
index 0000000..84db2b3
--- /dev/null
+++ b/tests/threaddata/thread3
@@ -0,0 +1,184 @@
1Return-Path: <kde-community-bounces@kde.org>
2Received: from imapb010.mykolab.com ([unix socket])
3 by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
4 Wed, 16 Aug 2017 09:33:42 +0200
5X-Sieve: CMU Sieve 2.4
6Received: from int-mx001.mykolab.com (unknown [10.9.13.1])
7 by imapb010.mykolab.com (Postfix) with ESMTPS id 5444D14414C34
8 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 09:33:42 +0200 (CEST)
9Received: from mx.kolabnow.com (unknown [10.9.4.2])
10 by int-mx001.mykolab.com (Postfix) with ESMTPS id 3DB4C114
11 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 09:33:42 +0200 (CEST)
12X-Virus-Scanned: amavisd-new at mykolab.com
13Authentication-Results: ext-mx-in002.mykolab.com (amavisd-new);
14 dkim=pass (1024-bit key) header.d=kde.org header.b=PXk+9Qyc;
15 dkim=pass (2048-bit key) header.d=gmail.com header.b=j1CN7DJ2
16X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0
17Received: from forward1-smtp.messagingengine.com (forward1-smtp.messagingengine.com [66.111.4.223])
18 by ext-mx-in002.mykolab.com (Postfix) with ESMTPS id DEAFCE9E
19 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 09:33:36 +0200 (CEST)
20Received: from mailredirect.nyi.internal (imap36.nyi.internal [10.202.2.86])
21 by mailforward.nyi.internal (Postfix) with ESMTP id 6AD87108D
22 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 03:33:35 -0400 (EDT)
23Received: by mailredirect.nyi.internal (Postfix, from userid 501)
24 id 444F38E597; Wed, 16 Aug 2017 03:33:35 -0400 (EDT)
25Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
26 by sloti36d2t28 (Cyrus fastmail-fmjessie44745-15358-git-fastmail-15358) with LMTPA;
27 Wed, 16 Aug 2017 03:33:35 -0400
28X-Cyrus-Session-Id: sloti36d2t28-546055-1502868815-2-14217351451016405562
29X-Sieve: CMU Sieve 3.0
30X-Spam-known-sender: no
31X-Orig-Spam-score: 0.0
32X-Spam-hits: BAYES_00 -1.9, FREEMAIL_FORGED_FROMDOMAIN 0.199, FREEMAIL_FROM 0.001,
33 HEADER_FROM_DIFFERENT_DOMAINS 0.001, RCVD_IN_DNSWL_MED -2.3,
34 RP_MATCHES_RCVD -0.001, SPF_PASS -0.001, LANGUAGES en, BAYES_USED global,
35 SA_VERSION 3.4.0
36X-Spam-source: IP='46.4.96.248', Host='postbox.kde.org', Country='DE', FromHeader='com',
37 MailFrom='org'
38X-Spam-charsets: plain='UTF-8'
39X-Resolved-to: chrigi_1@fastmail.fm
40X-Delivered-to: chrigi_1@fastmail.fm
41X-Mail-from: kde-community-bounces@kde.org
42Received: from mx1 ([10.202.2.200])
43 by compute1.internal (LMTPProxy); Wed, 16 Aug 2017 03:33:35 -0400
44Authentication-Results: mx1.messagingengine.com;
45 dkim=pass (1024-bit rsa key sha256) header.d=kde.org header.i=@kde.org header.b=PXk+9Qyc;
46 dkim=pass (2048-bit rsa key sha256) header.d=gmail.com header.i=@gmail.com header.b=j1CN7DJ2;
47 dmarc=pass header.from=gmail.com;
48 spf=pass smtp.mailfrom=kde-community-bounces@kde.org smtp.helo=postbox.kde.org;
49 x-google-dkim=pass (2048-bit rsa key) header.d=1e100.net header.i=@1e100.net header.b=nOWNMzab
50Received-SPF: pass
51 (kde.org: 46.4.96.248 is authorized to use 'kde-community-bounces@kde.org' in 'mfrom' identity (mechanism 'mx' matched))
52 receiver=mx1.messagingengine.com;
53 identity=mailfrom;
54 envelope-from="kde-community-bounces@kde.org";
55 helo=postbox.kde.org;
56 client-ip=46.4.96.248
57Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
58 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
59 (No client certificate requested)
60 by mx1.messagingengine.com (Postfix) with ESMTPS
61 for <chrigi_1@fastmail.fm>; Wed, 16 Aug 2017 03:33:34 -0400 (EDT)
62DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
63 t=1502868810; bh=dVFv3mcZvqPFeac2frbs+zJpMjYutwuTUR/aZqUTbZY=;
64 h=In-Reply-To:References:From:Date:Subject:To:Reply-To:List-Id:
65 List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:
66 From;
67 b=PXk+9Qyc+iRxwLPIHod4loutgNXu9pHl4peiPK0rI8Bl+4b02Cw0SUrzyf2JPqyDn
68 zuoxSnetdDbzoPnV1ep3yyHX+MhXiWvvc+PTKk15kIuBJYB77t+EJq3I/awvqG++Fa
69 d4Um24yPg/LUw5fFTsMuJ+Ra5MtpmFOmVbXrHDt0=
70X-Original-To: kde-community@kde.org
71X-Remote-Delivered-To: kde-community@localhost.kde.org
72Received-SPF: Pass (sender SPF authorized) identity=mailfrom;
73 client-ip=2607:f8b0:4001:c0b::22f; helo=mail-it0-x22f.google.com;
74 envelope-from=valorie.zimmerman@gmail.com; receiver=kde-community@kde.org
75Authentication-Results: postbox.kde.org; dkim=pass
76 reason="2048-bit key; unprotected key"
77 header.d=gmail.com header.i=@gmail.com header.b=j1CN7DJ2;
78 dkim-adsp=pass; dkim-atps=neutral
79Received: from mail-it0-x22f.google.com (mail-it0-x22f.google.com
80 [IPv6:2607:f8b0:4001:c0b::22f])
81 by postbox.kde.org (Postfix) with ESMTPS id 06F4BA0243
82 for <kde-community@kde.org>; Wed, 16 Aug 2017 07:33:19 +0000 (UTC)
83Received: by mail-it0-x22f.google.com with SMTP id 76so14155500ith.0
84 for <kde-community@kde.org>; Wed, 16 Aug 2017 00:33:19 -0700 (PDT)
85DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;
86 h=mime-version:in-reply-to:references:from:date:message-id:subject:to;
87 bh=FXMFEApKo547tDMnIu6insMVxFcMsw7/E/4fcI3MkkQ=;
88 b=j1CN7DJ2CYaCWqNWOR7Hpjah/U+OYATQhmN+zVkgRNbVJOMVW6B4hWmUihH5Nll4/G
89 YX5O5OQv6i2y1hAqT3R3iISGAz70o2gIWjq14Ea+zqM9ztCM/ZX4XGaBqdv4dHTAMyDh
90 mg556PB77JLPlwHtf2CsR9gTSAC2BAuY8lsTdBV7jVkLjCGdjaSPRxiyf2t4WbcVmiUt
91 yZzWB7QmtQA4JHQ8N/bJ2lEg8cTWSj8p9o4kSAF7HDZ4X7pXfQgAPEAs/DHf9LMNGiys
92 1xgAuYNxywGvtLaArQ+NXfgYH6VfRcFf7HFbMLs6yLyn63G9GLyUPHlHIgqWVAJrdn65
93 Nsow==
94X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
95 d=1e100.net; s=20161025;
96 h=x-gm-message-state:mime-version:in-reply-to:references:from:date
97 :message-id:subject:to;
98 bh=FXMFEApKo547tDMnIu6insMVxFcMsw7/E/4fcI3MkkQ=;
99 b=nOWNMzabDGsTEZISXzVD7236lDYmFHF2kAEUxMmDSvGYEcq1/FjZwj7w7SJT62S/pY
100 oS29tWyY7LE4I+Fq5E6D8H2sfMAfaCoYQ1J27ftPVClg/kmiTGRzxf2tcv6TR/v56+vD
101 pwgDEDwgZs1oM6IBFrJr65u2+0mlcmK3qsRHBdjoQLEbZMa+GugjcI2HILqtmpTS+NJi
102 HZcVfEgOwkyqgoZkgBsaBui+5OUpz/yqryOsYx7kQxCy6uZJIFCB0dsvk7COE8nG7LYa
103 0H8aRhVFxXRW76MUR6E67EhGMS+MS9F3DiXiUsWTn4yEZnC8cy76YPcPHVBBmGQ7CH0a
104 ScwA==
105X-Gm-Message-State: AHYfb5g0PFdyP1pw7TVZRMJqzU/nu12G3R2adj9OD2MzSxEew1QKnS99
106 U5MlthDVsG6C1f9Ak0fXNCtdI5w4CaBJhbc=
107X-Received: by 10.36.37.143 with SMTP id g137mr1056087itg.35.1502868798009;
108 Wed, 16 Aug 2017 00:33:18 -0700 (PDT)
109MIME-Version: 1.0
110Received: by 10.107.6.142 with HTTP; Wed, 16 Aug 2017 00:33:02 -0700 (PDT)
111In-Reply-To: <2990543.KVDkBByYO0@minixfox>
112References: <2048912.XfIJe3ZSdj@vkpc5> <2990543.KVDkBByYO0@minixfox>
113From: Valorie Zimmerman <valorie.zimmerman@gmail.com>
114Date: Wed, 16 Aug 2017 00:33:02 -0700
115Message-ID: <CACpu024EH1OeDqwL94QK33eq4sCGjKjwedcQDR_PWjprBevzfg@mail.gmail.com>
116Subject: Re: Telemetry Policy
117To: informing about and discussing non-technical community topics
118 <kde-community@kde.org>
119Content-Type: text/plain; charset="UTF-8"
120X-BeenThere: kde-community@kde.org
121X-Mailman-Version: 2.1.16
122Precedence: list
123Reply-To: informing about and discussing non-technical community topics
124 <kde-community@kde.org>
125List-Id: informing about and discussing non-technical community topics
126 <kde-community.kde.org>
127List-Unsubscribe: <https://mail.kde.org/mailman/options/kde-community>,
128 <mailto:kde-community-request@kde.org?subject=unsubscribe>
129List-Archive: <http://mail.kde.org/pipermail/kde-community/>
130List-Post: <mailto:kde-community@kde.org>
131List-Help: <mailto:kde-community-request@kde.org?subject=help>
132List-Subscribe: <https://mail.kde.org/mailman/listinfo/kde-community>,
133 <mailto:kde-community-request@kde.org?subject=subscribe>
134Errors-To: kde-community-bounces@kde.org
135Sender: "kde-community" <kde-community-bounces@kde.org>
136
137Hi all, Mozilla has done a lot of work on telemetry, and we might be
138able to use some of their findings. On this page:
139https://wiki.mozilla.org/Firefox/Data_Collection they break down the
140data they might possibly collect into four buckets - technical (such
141as crashes), user interaction, web activity, and sensitive (personal
142data).
143
144This bit might be relevant to our discussion: "Categories 1 & 2
145(Technical & Interaction data)
146Pre-Release & Release: Data may default on, provided the data is
147exclusively in these categories (it cannot be in any other category).
148In Release, an opt-out must be available for most types of Technical
149and Interaction data. "
150
151I think the entire page might be enlightening to this discussion. I
152believe our analysis of needs should be more fine-grained, and that
153some parts of what we need can be "default on" especially for
154pre-release testing. For releases, we can provide an opt-out.
155
156Other more sensitive data will need to be opt-in. I think it's a
157mistake to treat all the data we might want all in the same way.
158
159Valorie
160
161
162On Sun, Aug 13, 2017 at 3:18 AM, Christian Loosli
163<christian.loosli@fuchsnet.ch> wrote:
164> Hi,
165>
166> thank you very much for this work, sounds great!
167>
168> Only point I have: maybe make sure that the opt-in / default settings are not
169> only mandatory for application developers, but also for packagers /
170> distributions.
171>
172> Some distributions have rather questionable views on privacy and by default
173> sent information to third parties, so I would feel much more safe if they
174> weren't allowed (in theory) to flick the switch in their package by default to
175> "on" either.
176>
177> Kind regards,
178>
179> Christian
180
181
182
183--
184http://about.me/valoriez
diff --git a/tests/threaddata/thread4 b/tests/threaddata/thread4
new file mode 100644
index 0000000..492d64d
--- /dev/null
+++ b/tests/threaddata/thread4
@@ -0,0 +1,187 @@
1Return-Path: <kde-community-bounces@kde.org>
2Received: from imapb010.mykolab.com ([unix socket])
3 by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
4 Wed, 16 Aug 2017 14:15:52 +0200
5X-Sieve: CMU Sieve 2.4
6Received: from int-mx001.mykolab.com (unknown [10.9.13.1])
7 by imapb010.mykolab.com (Postfix) with ESMTPS id B5DFE145C97EA
8 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 14:15:52 +0200 (CEST)
9Received: from mx.kolabnow.com (unknown [10.9.4.3])
10 by int-mx001.mykolab.com (Postfix) with ESMTPS id 9430B114
11 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 14:15:52 +0200 (CEST)
12X-Virus-Scanned: amavisd-new at mykolab.com
13Authentication-Results: ext-mx-in003.mykolab.com (amavisd-new);
14 dkim=pass (1024-bit key) header.d=kde.org
15X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0
16Received: from forward1-smtp.messagingengine.com (forward1-smtp.messagingengine.com [66.111.4.223])
17 by ext-mx-in003.mykolab.com (Postfix) with ESMTPS id 87ADC292C
18 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 14:15:41 +0200 (CEST)
19Received: from mailredirect.nyi.internal (imap36.nyi.internal [10.202.2.86])
20 by mailforward.nyi.internal (Postfix) with ESMTP id 14E06F2B
21 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 08:15:41 -0400 (EDT)
22Received: by mailredirect.nyi.internal (Postfix, from userid 501)
23 id 02B668E597; Wed, 16 Aug 2017 08:15:40 -0400 (EDT)
24Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
25 by sloti36d2t28 (Cyrus fastmail-fmjessie44745-15358-git-fastmail-15358) with LMTPA;
26 Wed, 16 Aug 2017 08:15:40 -0400
27X-Cyrus-Session-Id: sloti36d2t28-920397-1502885740-5-10891205693403350257
28X-Sieve: CMU Sieve 3.0
29X-Spam-known-sender: no
30X-Orig-Spam-score: 0.0
31X-Spam-hits: BAYES_00 -1.9, RCVD_IN_DNSWL_MED -2.3, RP_MATCHES_RCVD -0.001,
32 SPF_PASS -0.001, LANGUAGES en, BAYES_USED global, SA_VERSION 3.4.0
33X-Spam-source: IP='46.4.96.248', Host='postbox.kde.org', Country='DE', FromHeader='org',
34 MailFrom='org'
35X-Spam-charsets: plain='utf-8'
36X-Attached: signature.asc
37X-Resolved-to: chrigi_1@fastmail.fm
38X-Delivered-to: chrigi_1@fastmail.fm
39X-Mail-from: kde-community-bounces@kde.org
40Received: from mx1 ([10.202.2.200])
41 by compute1.internal (LMTPProxy); Wed, 16 Aug 2017 08:15:40 -0400
42Authentication-Results: mx1.messagingengine.com;
43 dkim=pass (1024-bit rsa key sha256) header.d=kde.org header.i=@kde.org header.b=dcc9ZeF1;
44 dmarc=none (p=none;has-list-id=yes) header.from=kde.org;
45 smime=temperror;
46 spf=pass smtp.mailfrom=kde-community-bounces@kde.org smtp.helo=postbox.kde.org
47Received-SPF: pass
48 (kde.org: 46.4.96.248 is authorized to use 'kde-community-bounces@kde.org' in 'mfrom' identity (mechanism 'mx' matched))
49 receiver=mx1.messagingengine.com;
50 identity=mailfrom;
51 envelope-from="kde-community-bounces@kde.org";
52 helo=postbox.kde.org;
53 client-ip=46.4.96.248
54Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
55 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
56 (No client certificate requested)
57 by mx1.messagingengine.com (Postfix) with ESMTPS
58 for <chrigi_1@fastmail.fm>; Wed, 16 Aug 2017 08:15:40 -0400 (EDT)
59DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
60 t=1502885735; bh=SH/qVWnJJ/KE8PqQNaOwBRNoy7rIm5VobJE4/TZFZ9g=;
61 h=From:To:Subject:Date:In-Reply-To:References:Reply-To:List-Id:
62 List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:
63 From;
64 b=dcc9ZeF1EO5Q0C8mVOjhOITKyPmrCB9KGB4gKdTSfuxo4OZGKHg/xi7VH0/UDLYxy
65 Ni1GHXrJiD50yXOLDYICYr0XsDpYQaHmRQXGs6O6g/hIYxR2BCdqH1/5/NgNzPyjLH
66 5aKmEZt4LH8/JKYnv1UJCiKdhG2UQrs3fSg/ZMpM=
67X-Original-To: kde-community@kde.org
68X-Remote-Delivered-To: kde-community@localhost.kde.org
69Received-SPF: Neutral (access neither permitted nor denied) identity=mailfrom;
70 client-ip=85.214.75.115; helo=h2670809.stratoserver.net;
71 envelope-from=vkrause@kde.org; receiver=kde-community@kde.org
72Received: from h2670809.stratoserver.net (deltatauchi.de [85.214.75.115])
73 by postbox.kde.org (Postfix) with ESMTP id 7F686A0160
74 for <kde-community@kde.org>; Wed, 16 Aug 2017 12:15:15 +0000 (UTC)
75Received: from vkpc19.localnet (unknown [185.28.184.2])
76 by h2670809.stratoserver.net (Postfix) with ESMTPSA id 59DBAF1A0104
77 for <kde-community@kde.org>; Wed, 16 Aug 2017 14:14:44 +0200 (CEST)
78From: Volker Krause <vkrause@kde.org>
79To: kde-community@kde.org
80Subject: Re: Telemetry Policy
81Date: Wed, 16 Aug 2017 14:13:48 +0200
82Message-ID: <1942419.JquqIjZoWq@vkpc19>
83Organization: KDE
84In-Reply-To: <CACpu024EH1OeDqwL94QK33eq4sCGjKjwedcQDR_PWjprBevzfg@mail.gmail.com>
85References: <2048912.XfIJe3ZSdj@vkpc5> <2990543.KVDkBByYO0@minixfox>
86 <CACpu024EH1OeDqwL94QK33eq4sCGjKjwedcQDR_PWjprBevzfg@mail.gmail.com>
87MIME-Version: 1.0
88Content-Type: multipart/signed; boundary="nextPart3633370.DIlRsSa6NW";
89 micalg="pgp-sha1"; protocol="application/pgp-signature"
90X-BeenThere: kde-community@kde.org
91X-Mailman-Version: 2.1.16
92Precedence: list
93Reply-To: informing about and discussing non-technical community topics
94 <kde-community@kde.org>
95List-Id: informing about and discussing non-technical community topics
96 <kde-community.kde.org>
97List-Unsubscribe: <https://mail.kde.org/mailman/options/kde-community>,
98 <mailto:kde-community-request@kde.org?subject=unsubscribe>
99List-Archive: <http://mail.kde.org/pipermail/kde-community/>
100List-Post: <mailto:kde-community@kde.org>
101List-Help: <mailto:kde-community-request@kde.org?subject=help>
102List-Subscribe: <https://mail.kde.org/mailman/listinfo/kde-community>,
103 <mailto:kde-community-request@kde.org?subject=subscribe>
104Errors-To: kde-community-bounces@kde.org
105Sender: "kde-community" <kde-community-bounces@kde.org>
106
107--nextPart3633370.DIlRsSa6NW
108Content-Transfer-Encoding: 7Bit
109Content-Type: text/plain; charset="utf-8"
110
111On Wednesday, 16 August 2017 09:33:02 CEST Valorie Zimmerman wrote:
112> Hi all, Mozilla has done a lot of work on telemetry, and we might be
113> able to use some of their findings. On this page:
114> https://wiki.mozilla.org/Firefox/Data_Collection they break down the
115> data they might possibly collect into four buckets - technical (such
116> as crashes), user interaction, web activity, and sensitive (personal
117> data).
118
119without making it that explicit, we basically have the same four categories of
120data too, and explicitly exclude the use of category 3 and 4, ie user content/
121activity and personal data, only technical and interaction data are allowed to
122be used (category 1 and 2).
123
124> This bit might be relevant to our discussion: "Categories 1 & 2
125> (Technical & Interaction data)
126> Pre-Release & Release: Data may default on, provided the data is
127> exclusively in these categories (it cannot be in any other category).
128> In Release, an opt-out must be available for most types of Technical
129> and Interaction data. "
130>
131> I think the entire page might be enlightening to this discussion. I
132> believe our analysis of needs should be more fine-grained, and that
133> some parts of what we need can be "default on" especially for
134> pre-release testing. For releases, we can provide an opt-out.
135>
136> Other more sensitive data will need to be opt-in. I think it's a
137> mistake to treat all the data we might want all in the same way.
138
139This again brings up opt-out, which so far doesn't seem to have a chance for
140consensus. Can we defer this to when we have some more experience with the
141opt-in approach and how much participation we get with that? Or are people
142feeling this would too strongly limit what they are allowed to do in their
143applications?
144
145Seeing yesterday's blog from the Krita team (https://akapust1n.github.io/
1462017-08-15-sixth-blog-gsoc-2017/), I'd particularly be interested in their
147view on this.
148
149Regards,
150Volker
151
152> On Sun, Aug 13, 2017 at 3:18 AM, Christian Loosli
153>
154> <christian.loosli@fuchsnet.ch> wrote:
155> > Hi,
156> >
157> > thank you very much for this work, sounds great!
158> >
159> > Only point I have: maybe make sure that the opt-in / default settings are
160> > not only mandatory for application developers, but also for packagers /
161> > distributions.
162> >
163> > Some distributions have rather questionable views on privacy and by
164> > default
165> > sent information to third parties, so I would feel much more safe if they
166> > weren't allowed (in theory) to flick the switch in their package by
167> > default to "on" either.
168> >
169> > Kind regards,
170> >
171> > Christian
172
173
174
175--nextPart3633370.DIlRsSa6NW
176Content-Type: application/pgp-signature; name="signature.asc"
177Content-Description: This is a digitally signed message part.
178Content-Transfer-Encoding: 7Bit
179
180-----BEGIN PGP SIGNATURE-----
181
182iF0EABECAB0WIQQAnu3FVHA48KjZ07R/lszWTRLSRwUCWZQ2/AAKCRB/lszWTRLS
183R+niAKCpVjpRVPq455bnZlAVxpARkGWE/gCcCaBN1QAFz8Da6XIKJGY7iukaS3A=
184=ZSiq
185-----END PGP SIGNATURE-----
186
187--nextPart3633370.DIlRsSa6NW--
diff --git a/tests/threaddata/thread5 b/tests/threaddata/thread5
new file mode 100644
index 0000000..def438c
--- /dev/null
+++ b/tests/threaddata/thread5
@@ -0,0 +1,119 @@
1Return-Path: <kde-community-bounces@kde.org>
2Received: from imapb010.mykolab.com ([unix socket])
3 by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
4 Wed, 16 Aug 2017 15:30:55 +0200
5X-Sieve: CMU Sieve 2.4
6Received: from int-mx001.mykolab.com (unknown [10.9.13.1])
7 by imapb010.mykolab.com (Postfix) with ESMTPS id 2C73C14646FD6
8 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 15:30:55 +0200 (CEST)
9Received: from mx.kolabnow.com (unknown [10.9.4.2])
10 by int-mx001.mykolab.com (Postfix) with ESMTPS id 0324D114
11 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 15:30:54 +0200 (CEST)
12X-Virus-Scanned: amavisd-new at mykolab.com
13Authentication-Results: ext-mx-in002.mykolab.com (amavisd-new);
14 dkim=pass (1024-bit key) header.d=kde.org
15X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0
16Received: from forward1-smtp.messagingengine.com (forward1-smtp.messagingengine.com [66.111.4.223])
17 by ext-mx-in002.mykolab.com (Postfix) with ESMTPS id C91AA866
18 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 15:30:34 +0200 (CEST)
19Received: from mailredirect.nyi.internal (imap36.nyi.internal [10.202.2.86])
20 by mailforward.nyi.internal (Postfix) with ESMTP id 5C87A1858
21 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 09:30:33 -0400 (EDT)
22Received: by mailredirect.nyi.internal (Postfix, from userid 501)
23 id 4CC8B8E597; Wed, 16 Aug 2017 09:30:33 -0400 (EDT)
24Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
25 by sloti36d2t28 (Cyrus fastmail-fmjessie44745-15358-git-fastmail-15358) with LMTPA;
26 Wed, 16 Aug 2017 09:30:33 -0400
27X-Cyrus-Session-Id: sloti36d2t28-1026013-1502890233-2-11003035487755983862
28X-Sieve: CMU Sieve 3.0
29X-Spam-known-sender: no
30X-Orig-Spam-score: 0.0
31X-Spam-hits: BAYES_00 -1.9, HEADER_FROM_DIFFERENT_DOMAINS 0.001, RCVD_IN_DNSWL_MED -2.3,
32 RP_MATCHES_RCVD -0.001, SPF_PASS -0.001, LANGUAGES en, BAYES_USED global,
33 SA_VERSION 3.4.0
34X-Spam-source: IP='46.4.96.248', Host='postbox.kde.org', Country='DE', FromHeader='org',
35 MailFrom='org'
36X-Spam-charsets:
37X-Resolved-to: chrigi_1@fastmail.fm
38X-Delivered-to: chrigi_1@fastmail.fm
39X-Mail-from: kde-community-bounces@kde.org
40Received: from mx5 ([10.202.2.204])
41 by compute1.internal (LMTPProxy); Wed, 16 Aug 2017 09:30:33 -0400
42Authentication-Results: mx5.messagingengine.com;
43 dkim=pass (1024-bit rsa key sha256) header.d=kde.org header.i=@kde.org header.b=RolpJ4HJ;
44 dmarc=none (p=none;has-list-id=yes) header.from=valdyas.org;
45 spf=pass smtp.mailfrom=kde-community-bounces@kde.org smtp.helo=postbox.kde.org
46Received-SPF: pass
47 (kde.org: 46.4.96.248 is authorized to use 'kde-community-bounces@kde.org' in 'mfrom' identity (mechanism 'mx' matched))
48 receiver=mx5.messagingengine.com;
49 identity=mailfrom;
50 envelope-from="kde-community-bounces@kde.org";
51 helo=postbox.kde.org;
52 client-ip=46.4.96.248
53Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
54 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
55 (No client certificate requested)
56 by mx5.messagingengine.com (Postfix) with ESMTPS
57 for <chrigi_1@fastmail.fm>; Wed, 16 Aug 2017 09:30:32 -0400 (EDT)
58DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
59 t=1502890227; bh=z989fztQpyrmqEjIXZUifZvjlTrbUJ38xgYnpo5TWHY=;
60 h=Date:From:To:Subject:In-Reply-To:References:Reply-To:List-Id:
61 List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:
62 From;
63 b=RolpJ4HJf2ZKKk0rJkqOuvP1Ed5rERJZdvt2+FMMsImGArH3hWTlgl1qQewN3B81n
64 Nq4o83kvWrlw2Y0i0n/fd+NjBLY9wtDBeRslr06KZSnZe4vL6N8p15wkQIs+oUAvCL
65 01Uy2pFX/99m+u66rd5IH2Epkdj9iSn2S6U0S3Ew=
66X-Original-To: kde-community@kde.org
67X-Remote-Delivered-To: kde-community@localhost.kde.org
68Received-SPF: None (no SPF record) identity=mailfrom; client-ip=80.100.45.33;
69 helo=calcifer.valdyas.org; envelope-from=boud@valdyas.org;
70 receiver=kde-community@kde.org
71Received: from calcifer.valdyas.org (calcifer.xs4all.nl [80.100.45.33])
72 by postbox.kde.org (Postfix) with ESMTP id 6A134A0178
73 for <kde-community@kde.org>; Wed, 16 Aug 2017 13:30:15 +0000 (UTC)
74Received: by calcifer.valdyas.org (Postfix, from userid 1001)
75 id D3C2BC283D; Wed, 16 Aug 2017 15:30:14 +0200 (CEST)
76Date: Wed, 16 Aug 2017 15:30:14 +0200 (CEST)
77From: Boudewijn Rempt <boud@valdyas.org>
78To: informing about and discussing non-technical community topics
79 <kde-community@kde.org>
80Subject: Re: Telemetry Policy
81In-Reply-To: <1942419.JquqIjZoWq@vkpc19>
82Message-ID: <alpine.LNX.2.00.1708161528260.1363@calcifer.valdyas.org>
83References: <2048912.XfIJe3ZSdj@vkpc5> <2990543.KVDkBByYO0@minixfox>
84 <CACpu024EH1OeDqwL94QK33eq4sCGjKjwedcQDR_PWjprBevzfg@mail.gmail.com>
85 <1942419.JquqIjZoWq@vkpc19>
86User-Agent: Alpine 2.00 (LNX 1167 2008-08-23)
87MIME-Version: 1.0
88Content-Type: TEXT/PLAIN; charset=US-ASCII
89X-BeenThere: kde-community@kde.org
90X-Mailman-Version: 2.1.16
91Precedence: list
92Reply-To: informing about and discussing non-technical community topics
93 <kde-community@kde.org>
94List-Id: informing about and discussing non-technical community topics
95 <kde-community.kde.org>
96List-Unsubscribe: <https://mail.kde.org/mailman/options/kde-community>,
97 <mailto:kde-community-request@kde.org?subject=unsubscribe>
98List-Archive: <http://mail.kde.org/pipermail/kde-community/>
99List-Post: <mailto:kde-community@kde.org>
100List-Help: <mailto:kde-community-request@kde.org?subject=help>
101List-Subscribe: <https://mail.kde.org/mailman/listinfo/kde-community>,
102 <mailto:kde-community-request@kde.org?subject=subscribe>
103Errors-To: kde-community-bounces@kde.org
104Sender: "kde-community" <kde-community-bounces@kde.org>
105
106On Wed, 16 Aug 2017, Volker Krause wrote:
107
108> Seeing yesterday's blog from the Krita team (https://akapust1n.github.io/
109> 2017-08-15-sixth-blog-gsoc-2017/), I'd particularly be interested in their
110> view on this.
111
112I've pointed alexey at this thread, but there's a huge language barrier:
113he basically communicates through google translate. I've made it a firm
114condition of merging and operating the telemetry that we adhere to the
115KDE policy in every way. Even then, I still consider his work to be
116an experimental research project.
117
118--
119Boudewijn Rempt | http://www.krita.org, http://www.valdyas.org
diff --git a/tests/threaddata/thread6 b/tests/threaddata/thread6
new file mode 100644
index 0000000..5ff64ff
--- /dev/null
+++ b/tests/threaddata/thread6
@@ -0,0 +1,175 @@
1Return-Path: <kde-community-bounces@kde.org>
2Received: from imapb010.mykolab.com ([unix socket])
3 by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
4 Thu, 17 Aug 2017 01:47:27 +0200
5X-Sieve: CMU Sieve 2.4
6Received: from int-mx002.mykolab.com (unknown [10.9.13.2])
7 by imapb010.mykolab.com (Postfix) with ESMTPS id 2CC5214A68D1A
8 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 01:47:27 +0200 (CEST)
9Received: from mx.kolabnow.com (unknown [10.9.4.2])
10 by int-mx002.mykolab.com (Postfix) with ESMTPS id 13C82F44
11 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 01:47:27 +0200 (CEST)
12X-Virus-Scanned: amavisd-new at mykolab.com
13Authentication-Results: ext-mx-in002.mykolab.com (amavisd-new);
14 dkim=pass (1024-bit key) header.d=kde.org
15X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0
16Received: from forward1-smtp.messagingengine.com (forward1-smtp.messagingengine.com [66.111.4.223])
17 by ext-mx-in002.mykolab.com (Postfix) with ESMTPS id DA7D7211
18 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 01:47:15 +0200 (CEST)
19Received: from mailredirect.nyi.internal (imap36.nyi.internal [10.202.2.86])
20 by mailforward.nyi.internal (Postfix) with ESMTP id F078A4C
21 for <christian@mailqueue.ch>; Wed, 16 Aug 2017 19:47:14 -0400 (EDT)
22Received: by mailredirect.nyi.internal (Postfix, from userid 501)
23 id D2D7E8E231; Wed, 16 Aug 2017 19:47:14 -0400 (EDT)
24Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
25 by sloti36d2t28 (Cyrus fastmail-fmjessie44745-15358-git-fastmail-15358) with LMTPA;
26 Wed, 16 Aug 2017 19:47:14 -0400
27X-Cyrus-Session-Id: sloti36d2t28-1787481-1502927234-2-5475491779407099440
28X-Sieve: CMU Sieve 3.0
29X-Spam-known-sender: yes ("Address thomas.pfeiffer@kde.org in From header is in addressbook");
30 in-addressbook
31X-Orig-Spam-score: 0.0
32X-Spam-hits: BAYES_00 -1.9, RCVD_IN_DNSWL_MED -2.3, RCVD_IN_SORBS_SPAM 0.5,
33 RP_MATCHES_RCVD -0.001, SPF_PASS -0.001, LANGUAGES en, BAYES_USED global,
34 SA_VERSION 3.4.0
35X-Spam-source: IP='46.4.96.248', Host='postbox.kde.org', Country='DE', FromHeader='org',
36 MailFrom='org'
37X-Spam-charsets: plain='us-ascii'
38X-Resolved-to: chrigi_1@fastmail.fm
39X-Delivered-to: chrigi_1@fastmail.fm
40X-Mail-from: kde-community-bounces@kde.org
41Received: from mx6 ([10.202.2.205])
42 by compute1.internal (LMTPProxy); Wed, 16 Aug 2017 19:47:14 -0400
43Authentication-Results: mx6.messagingengine.com;
44 dkim=pass (1024-bit rsa key sha256) header.d=kde.org header.i=@kde.org header.b=jjcN/rDm;
45 dmarc=none (p=none;has-list-id=yes) header.from=kde.org;
46 spf=pass smtp.mailfrom=kde-community-bounces@kde.org smtp.helo=postbox.kde.org;
47 x-google-dkim=pass (2048-bit rsa key) header.d=1e100.net header.i=@1e100.net header.b=MDpKFUTu
48Received-SPF: pass
49 (kde.org: 46.4.96.248 is authorized to use 'kde-community-bounces@kde.org' in 'mfrom' identity (mechanism 'mx' matched))
50 receiver=mx6.messagingengine.com;
51 identity=mailfrom;
52 envelope-from="kde-community-bounces@kde.org";
53 helo=postbox.kde.org;
54 client-ip=46.4.96.248
55Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
56 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
57 (No client certificate requested)
58 by mx6.messagingengine.com (Postfix) with ESMTPS
59 for <chrigi_1@fastmail.fm>; Wed, 16 Aug 2017 19:47:13 -0400 (EDT)
60DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
61 t=1502927232; bh=ZPaDxaRw15uQ6A7HkCF2KoV4m+FrAkqde8P/U1SNqY8=;
62 h=From:To:Subject:Date:In-Reply-To:References:Reply-To:List-Id:
63 List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:
64 From;
65 b=jjcN/rDm5jM+2ttx4iyMXexUXHMS9OAzt1cfA461VOjTfg9ZPg+Kt1qCqUVzJNoSj
66 tXrKk69VVjb7tr4GNWJMKc2FAb5P33ndx6UC08kFDADMECoxSgwbHeKWdKCLE0KqOH
67 sCtYBZp0heUQzEztcQtjwtPuExHqivuLyYqZRvyM=
68X-Original-To: kde-community@kde.org
69X-Remote-Delivered-To: kde-community@localhost.kde.org
70Received-SPF: Pass (sender SPF authorized) identity=mailfrom;
71 client-ip=209.85.128.174; helo=mail-wr0-f174.google.com;
72 envelope-from=t.pfeiffer.ux@gmail.com; receiver=kde-community@kde.org
73Received: from mail-wr0-f174.google.com (mail-wr0-f174.google.com
74 [209.85.128.174])
75 by postbox.kde.org (Postfix) with ESMTPS id 6AB96A029E
76 for <kde-community@kde.org>; Wed, 16 Aug 2017 23:46:52 +0000 (UTC)
77Received: by mail-wr0-f174.google.com with SMTP id b65so29404863wrd.0
78 for <kde-community@kde.org>; Wed, 16 Aug 2017 16:46:52 -0700 (PDT)
79X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
80 d=1e100.net; s=20161025;
81 h=x-gm-message-state:from:to:subject:date:message-id:organization
82 :in-reply-to:references:mime-version:content-transfer-encoding;
83 bh=vOHtuq150Bi0dtLwrlvhmLXneJGQN+nwYPMD7ClfMTY=;
84 b=MDpKFUTuVW39/V5tI6WXpiuZNRgPorWVPEILVo6uTCaSPIWU4FPwx/FYFqYRnwLLZ4
85 JwLB2+R6USx5jpbjlgx7GDEuCAAGm+GI7GtyLRb0tZZMtXW7glpa2IuqLPTtIygXQSn4
86 nsSRysSlT02zZ26qbDXYeoWUOpn2CK2fmQ9l9q29GdTmC/+Ud4vfJqdW/nvnczqZVyyF
87 zUsQuOalp0VORBdSgDxDrtEA50pR+8TrnBu48u4OSigb4d6QgqZvPEYSPp7UWHmuEoBe
88 F92VN6efXYqb4tRUthsfokDw7l1TFhRB0g0UOl7BxXrRT54MGceiJ4fY8jVD+7+DN3aT
89 pD3g==
90X-Gm-Message-State: AHYfb5gfW1I+uGmtawofLSI0ZX4ZfkMah5Eyn73zmN/CEJ0d9ZDOFpsR
91 Y4FpRIYROX0uhR9L
92X-Received: by 10.28.11.131 with SMTP id 125mr45861wml.82.1502927211295;
93 Wed, 16 Aug 2017 16:46:51 -0700 (PDT)
94Received: from lenovo.localnet ([2a02:8071:31c0:f00:626c:66ff:fe3f:93eb])
95 by smtp.gmail.com with ESMTPSA id r70sm3132823wmb.35.2017.08.16.16.46.48
96 for <kde-community@kde.org>
97 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
98 Wed, 16 Aug 2017 16:46:49 -0700 (PDT)
99From: Thomas Pfeiffer <thomas.pfeiffer@kde.org>
100To: informing about and discussing non-technical community topics
101 <kde-community@kde.org>
102Subject: Re: Telemetry Policy
103Date: Thu, 17 Aug 2017 01:46:48 +0200
104Message-ID: <5231282.Ch11jfsTMl@lenovo>
105Organization: KDE
106In-Reply-To: <CACpu024EH1OeDqwL94QK33eq4sCGjKjwedcQDR_PWjprBevzfg@mail.gmail.com>
107References: <2048912.XfIJe3ZSdj@vkpc5> <2990543.KVDkBByYO0@minixfox>
108 <CACpu024EH1OeDqwL94QK33eq4sCGjKjwedcQDR_PWjprBevzfg@mail.gmail.com>
109MIME-Version: 1.0
110Content-Transfer-Encoding: 7Bit
111Content-Type: text/plain; charset="us-ascii"
112X-BeenThere: kde-community@kde.org
113X-Mailman-Version: 2.1.16
114Precedence: list
115Reply-To: informing about and discussing non-technical community topics
116 <kde-community@kde.org>
117List-Id: informing about and discussing non-technical community topics
118 <kde-community.kde.org>
119List-Unsubscribe: <https://mail.kde.org/mailman/options/kde-community>,
120 <mailto:kde-community-request@kde.org?subject=unsubscribe>
121List-Archive: <http://mail.kde.org/pipermail/kde-community/>
122List-Post: <mailto:kde-community@kde.org>
123List-Help: <mailto:kde-community-request@kde.org?subject=help>
124List-Subscribe: <https://mail.kde.org/mailman/listinfo/kde-community>,
125 <mailto:kde-community-request@kde.org?subject=subscribe>
126Errors-To: kde-community-bounces@kde.org
127Sender: "kde-community" <kde-community-bounces@kde.org>
128
129On Mittwoch, 16. August 2017 09:33:02 CEST Valorie Zimmerman wrote:
130> Hi all, Mozilla has done a lot of work on telemetry, and we might be
131> able to use some of their findings. On this page:
132> https://wiki.mozilla.org/Firefox/Data_Collection they break down the
133> data they might possibly collect into four buckets - technical (such
134> as crashes), user interaction, web activity, and sensitive (personal
135> data).
136>
137> This bit might be relevant to our discussion: "Categories 1 & 2
138> (Technical & Interaction data)
139> Pre-Release & Release: Data may default on, provided the data is
140> exclusively in these categories (it cannot be in any other category).
141> In Release, an opt-out must be available for most types of Technical
142> and Interaction data. "
143>
144> I think the entire page might be enlightening to this discussion. I
145> believe our analysis of needs should be more fine-grained, and that
146> some parts of what we need can be "default on" especially for
147> pre-release testing. For releases, we can provide an opt-out.
148
149Hi Valorie,
150Even if opt-out for some data is legally and even morally fine, it does not
151align with the values we communicate to our users:
152Unlike Mozilla's Mission, our Vision mentions privacy explicitly, and we're
153striving to make privacy our USP.
154
155Therefore I agree with others who replied in this thread: We should respect
156privacy unnecessarily much rather than too little.
157
158In the end, of course, it's a matter of how we present this opt-in. If it's an
159option buried in some settings dialog, we might as well not do it at all.
160
161If we, however - like Firefox does -, pfominently present that choice to users
162the first time they run one of our applications or desktop environment and try
163to make clear why that data collection is important for us, I don't see why we
164could not convince a relevant number of users to opt in.
165Sure, we'll get less data than with an opt-out scheme, but let's try it out
166first before we go for the option that carries a significant PR risk.
167
168> Other more sensitive data will need to be opt-in. I think it's a
169> mistake to treat all the data we might want all in the same way.
170
171Content (web activity for Mozilla) and personal information should not be opt-
172anything but not collected at all.
173
174Cheers,
175Thomas
diff --git a/tests/threaddata/thread7 b/tests/threaddata/thread7
new file mode 100644
index 0000000..b751d60
--- /dev/null
+++ b/tests/threaddata/thread7
@@ -0,0 +1,297 @@
1Return-Path: <kde-community-bounces@kde.org>
2Received: from imapb010.mykolab.com ([unix socket])
3 by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
4 Thu, 17 Aug 2017 17:40:53 +0200
5X-Sieve: CMU Sieve 2.4
6Received: from int-mx003.mykolab.com (unknown [10.9.13.3])
7 by imapb010.mykolab.com (Postfix) with ESMTPS id 467271505D03E
8 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 17:40:53 +0200 (CEST)
9Received: from mx.kolabnow.com (unknown [10.9.4.3])
10 by int-mx003.mykolab.com (Postfix) with ESMTPS id 2CC4FF16
11 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 17:40:53 +0200 (CEST)
12X-Virus-Scanned: amavisd-new at mykolab.com
13Authentication-Results: ext-mx-in003.mykolab.com (amavisd-new);
14 dkim=pass (1024-bit key) header.d=kde.org header.b=rysq5aPx;
15 dkim=fail (2048-bit key) reason="fail (message has been altered)"
16 header.d=gmail.com header.b=WiuysEuO
17X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0
18Received: from forward1-smtp.messagingengine.com (forward1-smtp.messagingengine.com [66.111.4.223])
19 by ext-mx-in003.mykolab.com (Postfix) with ESMTPS id 9C4BA2EB8
20 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 17:39:23 +0200 (CEST)
21Received: from mailredirect.nyi.internal (imap36.nyi.internal [10.202.2.86])
22 by mailforward.nyi.internal (Postfix) with ESMTP id 5138E1453
23 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 11:39:22 -0400 (EDT)
24Received: by mailredirect.nyi.internal (Postfix, from userid 501)
25 id 48C918E9B6; Thu, 17 Aug 2017 11:39:22 -0400 (EDT)
26Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
27 by sloti36d2t28 (Cyrus fastmail-fmjessie44745-15358-git-fastmail-15358) with LMTPA;
28 Thu, 17 Aug 2017 11:39:22 -0400
29X-Cyrus-Session-Id: sloti36d2t28-3156705-1502984362-2-3811956415179411272
30X-Sieve: CMU Sieve 3.0
31X-Spam-known-sender: no
32X-Orig-Spam-score: 0.0
33X-Spam-hits: BAYES_00 -1.9, HTML_MESSAGE 0.001, RCVD_IN_DNSWL_MED -2.3,
34 RP_MATCHES_RCVD -0.001, SPF_PASS -0.001, LANGUAGES en, BAYES_USED global,
35 SA_VERSION 3.4.0
36X-Spam-source: IP='46.4.96.248', Host='postbox.kde.org', Country='DE', FromHeader='org',
37 MailFrom='org'
38X-Spam-charsets: plain='us-ascii', html='us-ascii'
39X-Attached: signature.asc
40X-Resolved-to: chrigi_1@fastmail.fm
41X-Delivered-to: chrigi_1@fastmail.fm
42X-Mail-from: kde-community-bounces@kde.org
43Received: from mx3 ([10.202.2.202])
44 by compute1.internal (LMTPProxy); Thu, 17 Aug 2017 11:39:22 -0400
45Authentication-Results: mx3.messagingengine.com;
46 dkim=fail (message has been altered; 2048-bit rsa key sha256) header.d=gmail.com header.i=@gmail.com header.b=WiuysEuO;
47 dkim=pass (1024-bit rsa key sha256) header.d=kde.org header.i=@kde.org header.b=rysq5aPx;
48 dmarc=none (p=none;has-list-id=yes) header.from=kde.org;
49 spf=pass smtp.mailfrom=kde-community-bounces@kde.org smtp.helo=postbox.kde.org;
50 x-google-dkim=fail (message has been altered; 2048-bit rsa key) header.d=1e100.net header.i=@1e100.net header.b=eS2FiZD3
51Received-SPF: pass
52 (kde.org: 46.4.96.248 is authorized to use 'kde-community-bounces@kde.org' in 'mfrom' identity (mechanism 'mx' matched))
53 receiver=mx3.messagingengine.com;
54 identity=mailfrom;
55 envelope-from="kde-community-bounces@kde.org";
56 helo=postbox.kde.org;
57 client-ip=46.4.96.248
58Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
59 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
60 (No client certificate requested)
61 by mx3.messagingengine.com (Postfix) with ESMTPS
62 for <chrigi_1@fastmail.fm>; Thu, 17 Aug 2017 11:39:21 -0400 (EDT)
63DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
64 t=1502984359; bh=g7hsgd71OQgeOpLvXEjz16cF2X/6f5pmr2ujAF633tY=;
65 h=From:Date:Subject:In-Reply-To:To:References:Reply-To:List-Id:
66 List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:
67 From;
68 b=rysq5aPxwhI8i+Fx0jQHMx7aHC9RRMfjzmZTplBHjuND6qLLgZgMg2Tqpwgp1PnTR
69 zbpE07c5O50AqjjN74AqtaWdN7xCtsPb1taF9XjBDScI3wVcmiRZ5d88Sp9YI8rAzy
70 cY5uS6QDZfPt/BmonQqnc0oKpuuIlSp78R482qck=
71X-Original-To: kde-community@kde.org
72X-Remote-Delivered-To: kde-community@localhost.kde.org
73Received-SPF: Pass (sender SPF authorized) identity=mailfrom;
74 client-ip=2a00:1450:400c:c0c::22a; helo=mail-wr0-x22a.google.com;
75 envelope-from=mirko.mb.boehm@gmail.com; receiver=kde-community@kde.org
76Authentication-Results: postbox.kde.org; dkim=pass
77 reason="2048-bit key; unprotected key"
78 header.d=gmail.com header.i=@gmail.com header.b=WiuysEuO;
79 dkim-adsp=pass; dkim-atps=neutral
80Received: from mail-wr0-x22a.google.com (mail-wr0-x22a.google.com
81 [IPv6:2a00:1450:400c:c0c::22a])
82 by postbox.kde.org (Postfix) with ESMTPS id 6622AA028C;
83 Thu, 17 Aug 2017 15:38:56 +0000 (UTC)
84Received: by mail-wr0-x22a.google.com with SMTP id b65so47289023wrd.0;
85 Thu, 17 Aug 2017 08:38:56 -0700 (PDT)
86DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;
87 h=sender:from:message-id:mime-version:date:subject:in-reply-to:to
88 :references; bh=yh4e0FfFAXMmAqoicYQa52CLKXFU6SJTKLd0PdPDxRM=;
89 b=WiuysEuO59GFkLy+fKRSFayTM8LXWK3dVaVHDKfsw/XM8YKcLalwNWGVU0tsaDl+7P
90 r5MMLidc6O0gJKGftMdhWrjeFGY0aE5F5+2NKN1oEb2INbB/DrS9KaCVy1SWvHVu8zOo
91 t13omoGW6RIs5lgWrTLR1iwcwtfkWwO/+Ndy16U3/eYJSXeUPWHsVSXP0UgIS6IANt58
92 lNQhTgWBAJNViCxH/p5nIZYvLY5tGJPL6J46GaM7jUK4Ev6HUx4pUwGBsPB4hBezPm7h
93 mzA74izFW7jE6JklTciMAb0q7wEG15exVQbTEG54nvWMASrWx6mMAc+CWLh/T8vokpvT
94 Kvrw==
95X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
96 d=1e100.net; s=20161025;
97 h=x-gm-message-state:sender:from:message-id:mime-version:date:subject
98 :in-reply-to:to:references;
99 bh=yh4e0FfFAXMmAqoicYQa52CLKXFU6SJTKLd0PdPDxRM=;
100 b=eS2FiZD3QQ7P0mT2qKkYWf91vVTGXnIHKKzNdIJq+8JXN8WoXQ8IXmvQqSQFJa/w16
101 m9I0Yko7iv/JDAa7YTeJraBfImv9weknM3zpUNan8SltCFNXO8f4yylP1rGLn0RWbQ3Q
102 9NEpmYbS5dYQ79PMwy3zHxZEkUbsIFk8OlVSohdzpzGgtyU6nOjnBDULL14uHnouaz8+
103 TDss7L/vKlmrWOYdH+R8peCkFI6p3C69wpAEyNPGBaGyCRC+pebx6GGDBR63DFnyP464
104 VFQCUS4hPPTFaBPriqOF6xcWToacU40DVy3s2S/3EwHpUGWi+WMqQpPgqZYJh81unCDH
105 Hc7g==
106X-Gm-Message-State: AHYfb5hHmIopTFINsSS7+92/GpIh2jJhvpwp/cI1ajaDE2GLp/oZ6V7N
107 CCtnx8PQ3oXGOEKCRT4=
108X-Received: by 10.80.212.133 with SMTP id s5mr2195642edi.95.1502984335423;
109 Thu, 17 Aug 2017 08:38:55 -0700 (PDT)
110Received: from ?IPv6:2a02:8109:a4bf:e114:30bf:9873:2660:a4a8?
111 ([2a02:8109:a4bf:e114:30bf:9873:2660:a4a8])
112 by smtp.gmail.com with ESMTPSA id r29sm1790792edi.85.2017.08.17.08.38.52
113 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
114 Thu, 17 Aug 2017 08:38:52 -0700 (PDT)
115From: Mirko Boehm - KDE <mirko@kde.org>
116Message-Id: <B95D7D2A-23A9-4245-AAC0-8A3FAE330090@kde.org>
117Content-Type: multipart/signed;
118 boundary="Apple-Mail=_1637D59B-BBA3-401E-A7A5-3514665481AD";
119 protocol="application/pgp-signature"; micalg=pgp-sha1
120Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\))
121Date: Thu, 17 Aug 2017 17:38:52 +0200
122Subject: Re: Telemetry Policy
123In-Reply-To: <5231282.Ch11jfsTMl@lenovo>
124To: informing about and discussing non-technical community topics
125 <kde-community@kde.org>
126References: <2048912.XfIJe3ZSdj@vkpc5> <2990543.KVDkBByYO0@minixfox>
127 <CACpu024EH1OeDqwL94QK33eq4sCGjKjwedcQDR_PWjprBevzfg@mail.gmail.com>
128 <5231282.Ch11jfsTMl@lenovo>
129X-Mailer: Apple Mail (2.3273)
130X-BeenThere: kde-community@kde.org
131X-Mailman-Version: 2.1.16
132Precedence: list
133Reply-To: informing about and discussing non-technical community topics
134 <kde-community@kde.org>
135List-Id: informing about and discussing non-technical community topics
136 <kde-community.kde.org>
137List-Unsubscribe: <https://mail.kde.org/mailman/options/kde-community>,
138 <mailto:kde-community-request@kde.org?subject=unsubscribe>
139List-Archive: <http://mail.kde.org/pipermail/kde-community/>
140List-Post: <mailto:kde-community@kde.org>
141List-Help: <mailto:kde-community-request@kde.org?subject=help>
142List-Subscribe: <https://mail.kde.org/mailman/listinfo/kde-community>,
143 <mailto:kde-community-request@kde.org?subject=subscribe>
144Errors-To: kde-community-bounces@kde.org
145Sender: "kde-community" <kde-community-bounces@kde.org>
146
147
148--Apple-Mail=_1637D59B-BBA3-401E-A7A5-3514665481AD
149Content-Type: multipart/alternative;
150 boundary="Apple-Mail=_F49D9C7A-5DFA-4D78-8758-C0DB8C98E040"
151
152
153--Apple-Mail=_F49D9C7A-5DFA-4D78-8758-C0DB8C98E040
154Content-Transfer-Encoding: quoted-printable
155Content-Type: text/plain;
156 charset=us-ascii
157
158Hi,
159
160> On 17. Aug 2017, at 01:46, Thomas Pfeiffer <thomas.pfeiffer@kde.org> =
161wrote:
162>=20
163> Hi Valorie,
164> Even if opt-out for some data is legally and even morally fine, it =
165does not
166> align with the values we communicate to our users:
167> Unlike Mozilla's Mission, our Vision mentions privacy explicitly, and =
168we're
169> striving to make privacy our USP.
170
171We seem to assume a contradiction between telemetry and privacy. I =
172believe this is a knee-jerk reaction. We can implement telemetry in a =
173way that privacy is not violated. In fact, I would say that it follows =
174from our vision that we should do this.
175
176Cheers,
177
178Mirko.
179--
180Mirko Boehm | mirko@kde.org | KDE e.V.
181FSFE Fellowship Representative, FSFE Team Germany
182Qt Certified Specialist and Trainer
183Request a meeting: https://doodle.com/mirkoboehm
184
185
186--Apple-Mail=_F49D9C7A-5DFA-4D78-8758-C0DB8C98E040
187Content-Transfer-Encoding: quoted-printable
188Content-Type: text/html;
189 charset=us-ascii
190
191<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html =
192charset=3Dus-ascii"></head><body style=3D"word-wrap: break-word; =
193-webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" =
194class=3D"">Hi,&nbsp;<div class=3D""><br class=3D""><div><blockquote =
195type=3D"cite" class=3D""><div class=3D"">On 17. Aug 2017, at 01:46, =
196Thomas Pfeiffer &lt;<a href=3D"mailto:thomas.pfeiffer@kde.org" =
197class=3D"">thomas.pfeiffer@kde.org</a>&gt; wrote:</div><br =
198class=3D"Apple-interchange-newline"><div class=3D""><span =
199style=3D"font-family: Menlo-Regular; font-size: 11px; font-style: =
200normal; font-variant-caps: normal; font-weight: normal; letter-spacing: =
201normal; text-align: start; text-indent: 0px; text-transform: none; =
202white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
203float: none; display: inline !important;" class=3D"">Hi =
204Valorie,</span><br style=3D"font-family: Menlo-Regular; font-size: 11px; =
205font-style: normal; font-variant-caps: normal; font-weight: normal; =
206letter-spacing: normal; text-align: start; text-indent: 0px; =
207text-transform: none; white-space: normal; word-spacing: 0px; =
208-webkit-text-stroke-width: 0px;" class=3D""><span style=3D"font-family: =
209Menlo-Regular; font-size: 11px; font-style: normal; font-variant-caps: =
210normal; font-weight: normal; letter-spacing: normal; text-align: start; =
211text-indent: 0px; text-transform: none; white-space: normal; =
212word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: =
213inline !important;" class=3D"">Even if opt-out for some data is legally =
214and even morally fine, it does not<span =
215class=3D"Apple-converted-space">&nbsp;</span></span><br =
216style=3D"font-family: Menlo-Regular; font-size: 11px; font-style: =
217normal; font-variant-caps: normal; font-weight: normal; letter-spacing: =
218normal; text-align: start; text-indent: 0px; text-transform: none; =
219white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" =
220class=3D""><span style=3D"font-family: Menlo-Regular; font-size: 11px; =
221font-style: normal; font-variant-caps: normal; font-weight: normal; =
222letter-spacing: normal; text-align: start; text-indent: 0px; =
223text-transform: none; white-space: normal; word-spacing: 0px; =
224-webkit-text-stroke-width: 0px; float: none; display: inline =
225!important;" class=3D"">align with the values we communicate to our =
226users:</span><br style=3D"font-family: Menlo-Regular; font-size: 11px; =
227font-style: normal; font-variant-caps: normal; font-weight: normal; =
228letter-spacing: normal; text-align: start; text-indent: 0px; =
229text-transform: none; white-space: normal; word-spacing: 0px; =
230-webkit-text-stroke-width: 0px;" class=3D""><span style=3D"font-family: =
231Menlo-Regular; font-size: 11px; font-style: normal; font-variant-caps: =
232normal; font-weight: normal; letter-spacing: normal; text-align: start; =
233text-indent: 0px; text-transform: none; white-space: normal; =
234word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: =
235inline !important;" class=3D"">Unlike Mozilla's Mission, our Vision =
236mentions privacy explicitly, and we're<span =
237class=3D"Apple-converted-space">&nbsp;</span></span><br =
238style=3D"font-family: Menlo-Regular; font-size: 11px; font-style: =
239normal; font-variant-caps: normal; font-weight: normal; letter-spacing: =
240normal; text-align: start; text-indent: 0px; text-transform: none; =
241white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" =
242class=3D""><span style=3D"font-family: Menlo-Regular; font-size: 11px; =
243font-style: normal; font-variant-caps: normal; font-weight: normal; =
244letter-spacing: normal; text-align: start; text-indent: 0px; =
245text-transform: none; white-space: normal; word-spacing: 0px; =
246-webkit-text-stroke-width: 0px; float: none; display: inline =
247!important;" class=3D"">striving to make privacy our USP.</span><br =
248style=3D"font-family: Menlo-Regular; font-size: 11px; font-style: =
249normal; font-variant-caps: normal; font-weight: normal; letter-spacing: =
250normal; text-align: start; text-indent: 0px; text-transform: none; =
251white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" =
252class=3D""></div></blockquote><br class=3D""></div><div>We seem to =
253assume a contradiction between telemetry and privacy. I believe this is =
254a knee-jerk reaction. We can implement telemetry in a way that privacy =
255is not violated. In fact, I would say that it follows from our vision =
256that we should do this.</div><div><br =
257class=3D""></div><div>Cheers,</div><div><br =
258class=3D""></div><div>Mirko.</div><div class=3D"">
259<div style=3D"orphans: auto; text-align: start; text-indent: 0px; =
260widows: auto; word-wrap: break-word; -webkit-nbsp-mode: space; =
261-webkit-line-break: after-white-space;" class=3D""><div style=3D"color: =
262rgb(0, 0, 0); letter-spacing: normal; text-transform: none; white-space: =
263normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; orphans: =
264auto; text-align: start; text-indent: 0px; widows: auto; word-wrap: =
265break-word; -webkit-nbsp-mode: space; -webkit-line-break: =
266after-white-space;" class=3D"">--&nbsp;<br class=3D""></div><div =
267style=3D"orphans: auto; text-align: start; text-indent: 0px; widows: =
268auto; word-wrap: break-word; -webkit-nbsp-mode: space; =
269-webkit-line-break: after-white-space;" class=3D"">Mirko Boehm | <a =
270href=3D"mailto:mirko@kde.org" class=3D"">mirko@kde.org</a> | KDE e.V.<br =
271class=3D"">FSFE Fellowship Representative, FSFE Team Germany<br =
272class=3D"">Qt Certified Specialist and Trainer<br class=3D"">Request a =
273meeting: <a href=3D"https://doodle.com/mirkoboehm" =
274class=3D"">https://doodle.com/mirkoboehm</a><br class=3D""></div></div>
275</div>
276
277<br class=3D""></div></body></html>=
278
279--Apple-Mail=_F49D9C7A-5DFA-4D78-8758-C0DB8C98E040--
280
281--Apple-Mail=_1637D59B-BBA3-401E-A7A5-3514665481AD
282Content-Transfer-Encoding: 7bit
283Content-Disposition: attachment;
284 filename=signature.asc
285Content-Type: application/pgp-signature;
286 name=signature.asc
287Content-Description: Message signed with OpenPGP
288
289-----BEGIN PGP SIGNATURE-----
290Comment: GPGTools - http://gpgtools.org
291
292iEYEARECAAYFAlmVuIwACgkQYSSaITCTnKX82QCgxjyaXNsffHG/42ioAQrxjdCN
293D4kAn2Vv0q16buzjcRel1P144tLyqbr+
294=muZP
295-----END PGP SIGNATURE-----
296
297--Apple-Mail=_1637D59B-BBA3-401E-A7A5-3514665481AD--
diff --git a/tests/threaddata/thread8 b/tests/threaddata/thread8
new file mode 100644
index 0000000..b57daee
--- /dev/null
+++ b/tests/threaddata/thread8
@@ -0,0 +1,253 @@
1Return-Path: <kde-community-bounces@kde.org>
2Received: from imapb010.mykolab.com ([unix socket])
3 by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
4 Thu, 17 Aug 2017 18:40:51 +0200
5X-Sieve: CMU Sieve 2.4
6Received: from int-mx001.mykolab.com (unknown [10.9.13.1])
7 by imapb010.mykolab.com (Postfix) with ESMTPS id A9D23150D1CA9
8 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 18:40:51 +0200 (CEST)
9Received: from mx.kolabnow.com (unknown [10.9.4.1])
10 by int-mx001.mykolab.com (Postfix) with ESMTPS id 8FACC185
11 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 18:40:51 +0200 (CEST)
12X-Virus-Scanned: amavisd-new at mykolab.com
13Authentication-Results: ext-mx-in001.mykolab.com (amavisd-new);
14 dkim=pass (1024-bit key) header.d=kde.org
15X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0
16Received: from forward1-smtp.messagingengine.com (forward1-smtp.messagingengine.com [66.111.4.223])
17 by ext-mx-in001.mykolab.com (Postfix) with ESMTPS id 1B005169D
18 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 18:20:40 +0200 (CEST)
19Received: from mailredirect.nyi.internal (imap36.nyi.internal [10.202.2.86])
20 by mailforward.nyi.internal (Postfix) with ESMTP id B9C0FFD6
21 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 12:20:39 -0400 (EDT)
22Received: by mailredirect.nyi.internal (Postfix, from userid 501)
23 id A9EF58E9B6; Thu, 17 Aug 2017 12:20:39 -0400 (EDT)
24Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
25 by sloti36d2t28 (Cyrus fastmail-fmjessie44745-15358-git-fastmail-15358) with LMTPA;
26 Thu, 17 Aug 2017 12:20:39 -0400
27X-Cyrus-Session-Id: sloti36d2t28-3239059-1502986839-5-11465270081887081630
28X-Sieve: CMU Sieve 3.0
29X-Spam-known-sender: yes ("Address thomas.pfeiffer@kde.org in From header is in addressbook");
30 in-addressbook
31X-Orig-Spam-score: 0.0
32X-Spam-hits: BAYES_00 -1.9, HTML_MESSAGE 0.001, RCVD_IN_DNSWL_MED -2.3,
33 RCVD_IN_SORBS_SPAM 0.5, RP_MATCHES_RCVD -0.001, SPF_PASS -0.001,
34 LANGUAGES en, BAYES_USED global, SA_VERSION 3.4.0
35X-Spam-source: IP='46.4.96.248', Host='postbox.kde.org', Country='DE', FromHeader='org',
36 MailFrom='org'
37X-Spam-charsets: plain='utf-8', html='utf-8'
38X-Resolved-to: chrigi_1@fastmail.fm
39X-Delivered-to: chrigi_1@fastmail.fm
40X-Mail-from: kde-community-bounces@kde.org
41Received: from mx3 ([10.202.2.202])
42 by compute1.internal (LMTPProxy); Thu, 17 Aug 2017 12:20:39 -0400
43Authentication-Results: mx3.messagingengine.com;
44 dkim=pass (1024-bit rsa key sha256) header.d=kde.org header.i=@kde.org header.b=iaOusBVL;
45 dmarc=none (p=none;has-list-id=yes) header.from=kde.org;
46 spf=pass smtp.mailfrom=kde-community-bounces@kde.org smtp.helo=postbox.kde.org;
47 x-google-dkim=pass (2048-bit rsa key) header.d=1e100.net header.i=@1e100.net header.b=J3ZGQfFP
48Received-SPF: pass
49 (kde.org: 46.4.96.248 is authorized to use 'kde-community-bounces@kde.org' in 'mfrom' identity (mechanism 'mx' matched))
50 receiver=mx3.messagingengine.com;
51 identity=mailfrom;
52 envelope-from="kde-community-bounces@kde.org";
53 helo=postbox.kde.org;
54 client-ip=46.4.96.248
55Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
56 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
57 (No client certificate requested)
58 by mx3.messagingengine.com (Postfix) with ESMTPS
59 for <chrigi_1@fastmail.fm>; Thu, 17 Aug 2017 12:20:38 -0400 (EDT)
60DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
61 t=1502986836; bh=NN9eLPZMdRJe0stu0TDb+ROhjuNPhd/mDnhblsQ4F04=;
62 h=From:Subject:Date:References:To:In-Reply-To:Reply-To:List-Id:
63 List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:
64 From;
65 b=iaOusBVLSdEJElb2uoxf5ubKrt5iXH5zKZqAsGo/Ltor16eJ57YIP6QGSNn+L7fCO
66 QHgR+fL1/OCWmfEs80xz7ycwjVTdHSt8a9nP7EwwLfQFJ3b1bCs8hNFyLpwrlzH87p
67 6I1z36M4x53j3Yq7OU5DIWw7TieU2TaHCCClC1Cg=
68X-Original-To: kde-community@kde.org
69X-Remote-Delivered-To: kde-community@localhost.kde.org
70Received-SPF: Pass (sender SPF authorized) identity=mailfrom;
71 client-ip=209.85.128.181; helo=mail-wr0-f181.google.com;
72 envelope-from=t.pfeiffer.ux@gmail.com; receiver=kde-community@kde.org
73Received: from mail-wr0-f181.google.com (mail-wr0-f181.google.com
74 [209.85.128.181])
75 by postbox.kde.org (Postfix) with ESMTPS id A2098A0194
76 for <kde-community@kde.org>; Thu, 17 Aug 2017 16:20:19 +0000 (UTC)
77Received: by mail-wr0-f181.google.com with SMTP id f8so3729162wrf.3
78 for <kde-community@kde.org>; Thu, 17 Aug 2017 09:20:19 -0700 (PDT)
79X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
80 d=1e100.net; s=20161025;
81 h=x-gm-message-state:from:mime-version:subject:date:references:to
82 :in-reply-to:message-id;
83 bh=wicjkfxhAhrD+Lja6y+RkFl7BL7WSAOVLHUYZTaQM+E=;
84 b=J3ZGQfFPOcZSNOlqbFSZ/oBBPQSnoMN2pIBb5YlfFBYeCY2Rt6Xx0X0S/wET6IAcE6
85 ZILrUjwPh9q3Bjhx0x+CAGscD/sNJBosuBoVrE1ZFX2d8prqRz9D8fNeeCtuPnRgkDmm
86 EBW3JP5ifajIMbUnHPevV1W8er5VY1uqWW/z6lZu7iH1zabPs+5wS+X0M1xx71xBxTb1
87 Dx4jpLO/SRNSEIKZ0q1l0p6f9/9P9VScWbyDw7NeI1yj0GfRhNSP64dlQU3Z07vqaoKP
88 vfhpG0gFX/FEr0+MPz2r10v6LP1iACBlhOHwHZxYLTz/mNwvHvsLB6JWFoZ0FuwLRQFN
89 X47g==
90X-Gm-Message-State: AHYfb5hGX37YHJwSkL5Gin7U/eRe+E5RLYqxnYErKBibvkrRhrJDArNX
91 VyIneA7/u3wUDC0Wvl8=
92X-Received: by 10.223.176.5 with SMTP id f5mr3522751wra.194.1502986818721;
93 Thu, 17 Aug 2017 09:20:18 -0700 (PDT)
94Received: from [172.16.5.187] ([109.109.206.114])
95 by smtp.gmail.com with ESMTPSA id 5sm4544042wre.5.2017.08.17.09.20.17
96 for <kde-community@kde.org>
97 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
98 Thu, 17 Aug 2017 09:20:17 -0700 (PDT)
99From: Thomas Pfeiffer <thomas.pfeiffer@kde.org>
100Content-Type: multipart/alternative;
101 boundary="Apple-Mail=_AF2C4455-1CF7-489B-98CD-6BFD8687BF81"
102Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\))
103Subject: Re: Telemetry Policy
104Date: Thu, 17 Aug 2017 18:20:16 +0200
105References: <2048912.XfIJe3ZSdj@vkpc5> <2990543.KVDkBByYO0@minixfox>
106 <CACpu024EH1OeDqwL94QK33eq4sCGjKjwedcQDR_PWjprBevzfg@mail.gmail.com>
107 <5231282.Ch11jfsTMl@lenovo> <B95D7D2A-23A9-4245-AAC0-8A3FAE330090@kde.org>
108To: informing about and discussing non-technical community topics
109 <kde-community@kde.org>
110In-Reply-To: <B95D7D2A-23A9-4245-AAC0-8A3FAE330090@kde.org>
111Message-Id: <5A696707-744C-4035-A8FA-CA83EE8691D6@kde.org>
112X-Mailer: Apple Mail (2.3273)
113X-BeenThere: kde-community@kde.org
114X-Mailman-Version: 2.1.16
115Precedence: list
116Reply-To: informing about and discussing non-technical community topics
117 <kde-community@kde.org>
118List-Id: informing about and discussing non-technical community topics
119 <kde-community.kde.org>
120List-Unsubscribe: <https://mail.kde.org/mailman/options/kde-community>,
121 <mailto:kde-community-request@kde.org?subject=unsubscribe>
122List-Archive: <http://mail.kde.org/pipermail/kde-community/>
123List-Post: <mailto:kde-community@kde.org>
124List-Help: <mailto:kde-community-request@kde.org?subject=help>
125List-Subscribe: <https://mail.kde.org/mailman/listinfo/kde-community>,
126 <mailto:kde-community-request@kde.org?subject=subscribe>
127Errors-To: kde-community-bounces@kde.org
128Sender: "kde-community" <kde-community-bounces@kde.org>
129
130
131--Apple-Mail=_AF2C4455-1CF7-489B-98CD-6BFD8687BF81
132Content-Transfer-Encoding: quoted-printable
133Content-Type: text/plain;
134 charset=utf-8
135
136
137> On 17. Aug 2017, at 17:38, Mirko Boehm - KDE <mirko@kde.org> wrote:
138>=20
139> Hi,=20
140>=20
141>> On 17. Aug 2017, at 01:46, Thomas Pfeiffer <thomas.pfeiffer@kde.org =
142<mailto:thomas.pfeiffer@kde.org>> wrote:
143>>=20
144>> Hi Valorie,
145>> Even if opt-out for some data is legally and even morally fine, it =
146does not=20
147>> align with the values we communicate to our users:
148>> Unlike Mozilla's Mission, our Vision mentions privacy explicitly, and =
149we're=20
150>> striving to make privacy our USP.
151>=20
152> We seem to assume a contradiction between telemetry and privacy. I =
153believe this is a knee-jerk reaction. We can implement telemetry in a =
154way that privacy is not violated. In fact, I would say that it follows =
155from our vision that we should do this.
156>=20
157
158The problem is: I expect users to have the same knee-jerk reaction. I =
159don=E2=80=99t see us being able to explain to users that actually their =
160privacy is perfectly safe before they freak out.
161Privacy-minded Free Software users have freaked out in the past over =
162things which objectively speaking were not a huge deal.
163It=E2=80=99s emotion more than rational arguments
164
165
166--Apple-Mail=_AF2C4455-1CF7-489B-98CD-6BFD8687BF81
167Content-Transfer-Encoding: quoted-printable
168Content-Type: text/html;
169 charset=utf-8
170
171<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html =
172charset=3Dutf-8"></head><body style=3D"word-wrap: break-word; =
173-webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" =
174class=3D""><br class=3D""><div><blockquote type=3D"cite" class=3D""><div =
175class=3D"">On 17. Aug 2017, at 17:38, Mirko Boehm - KDE &lt;<a =
176href=3D"mailto:mirko@kde.org" class=3D"">mirko@kde.org</a>&gt; =
177wrote:</div><br class=3D"Apple-interchange-newline"><div class=3D""><meta =
178http-equiv=3D"Content-Type" content=3D"text/html charset=3Dus-ascii" =
179class=3D""><div style=3D"word-wrap: break-word; -webkit-nbsp-mode: =
180space; -webkit-line-break: after-white-space;" class=3D"">Hi,&nbsp;<div =
181class=3D""><br class=3D""><div class=3D""><blockquote type=3D"cite" =
182class=3D""><div class=3D"">On 17. Aug 2017, at 01:46, Thomas Pfeiffer =
183&lt;<a href=3D"mailto:thomas.pfeiffer@kde.org" =
184class=3D"">thomas.pfeiffer@kde.org</a>&gt; wrote:</div><br =
185class=3D"Apple-interchange-newline"><div class=3D""><span =
186style=3D"font-family: Menlo-Regular; font-size: 11px; font-style: =
187normal; font-variant-caps: normal; font-weight: normal; letter-spacing: =
188normal; text-align: start; text-indent: 0px; text-transform: none; =
189white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
190float: none; display: inline !important;" class=3D"">Hi =
191Valorie,</span><br style=3D"font-family: Menlo-Regular; font-size: 11px; =
192font-style: normal; font-variant-caps: normal; font-weight: normal; =
193letter-spacing: normal; text-align: start; text-indent: 0px; =
194text-transform: none; white-space: normal; word-spacing: 0px; =
195-webkit-text-stroke-width: 0px;" class=3D""><span style=3D"font-family: =
196Menlo-Regular; font-size: 11px; font-style: normal; font-variant-caps: =
197normal; font-weight: normal; letter-spacing: normal; text-align: start; =
198text-indent: 0px; text-transform: none; white-space: normal; =
199word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: =
200inline !important;" class=3D"">Even if opt-out for some data is legally =
201and even morally fine, it does not<span =
202class=3D"Apple-converted-space">&nbsp;</span></span><br =
203style=3D"font-family: Menlo-Regular; font-size: 11px; font-style: =
204normal; font-variant-caps: normal; font-weight: normal; letter-spacing: =
205normal; text-align: start; text-indent: 0px; text-transform: none; =
206white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" =
207class=3D""><span style=3D"font-family: Menlo-Regular; font-size: 11px; =
208font-style: normal; font-variant-caps: normal; font-weight: normal; =
209letter-spacing: normal; text-align: start; text-indent: 0px; =
210text-transform: none; white-space: normal; word-spacing: 0px; =
211-webkit-text-stroke-width: 0px; float: none; display: inline =
212!important;" class=3D"">align with the values we communicate to our =
213users:</span><br style=3D"font-family: Menlo-Regular; font-size: 11px; =
214font-style: normal; font-variant-caps: normal; font-weight: normal; =
215letter-spacing: normal; text-align: start; text-indent: 0px; =
216text-transform: none; white-space: normal; word-spacing: 0px; =
217-webkit-text-stroke-width: 0px;" class=3D""><span style=3D"font-family: =
218Menlo-Regular; font-size: 11px; font-style: normal; font-variant-caps: =
219normal; font-weight: normal; letter-spacing: normal; text-align: start; =
220text-indent: 0px; text-transform: none; white-space: normal; =
221word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: =
222inline !important;" class=3D"">Unlike Mozilla's Mission, our Vision =
223mentions privacy explicitly, and we're<span =
224class=3D"Apple-converted-space">&nbsp;</span></span><br =
225style=3D"font-family: Menlo-Regular; font-size: 11px; font-style: =
226normal; font-variant-caps: normal; font-weight: normal; letter-spacing: =
227normal; text-align: start; text-indent: 0px; text-transform: none; =
228white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" =
229class=3D""><span style=3D"font-family: Menlo-Regular; font-size: 11px; =
230font-style: normal; font-variant-caps: normal; font-weight: normal; =
231letter-spacing: normal; text-align: start; text-indent: 0px; =
232text-transform: none; white-space: normal; word-spacing: 0px; =
233-webkit-text-stroke-width: 0px; float: none; display: inline =
234!important;" class=3D"">striving to make privacy our USP.</span><br =
235style=3D"font-family: Menlo-Regular; font-size: 11px; font-style: =
236normal; font-variant-caps: normal; font-weight: normal; letter-spacing: =
237normal; text-align: start; text-indent: 0px; text-transform: none; =
238white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" =
239class=3D""></div></blockquote><br class=3D""></div><div class=3D"">We =
240seem to assume a contradiction between telemetry and privacy. I believe =
241this is a knee-jerk reaction. We can implement telemetry in a way that =
242privacy is not violated. In fact, I would say that it follows from our =
243vision that we should do this.</div><div class=3D""><br =
244class=3D""></div></div></div></div></blockquote><div><br =
245class=3D""></div>The problem is: I expect users to have the same =
246knee-jerk reaction. I don=E2=80=99t see us being able to explain to =
247users that actually their privacy is perfectly safe before they freak =
248out.</div><div>Privacy-minded Free Software users have freaked out in =
249the past over things which objectively speaking were not a huge =
250deal.</div><div>It=E2=80=99s emotion more than rational =
251arguments</div><br class=3D""></body></html>=
252
253--Apple-Mail=_AF2C4455-1CF7-489B-98CD-6BFD8687BF81--
diff --git a/tests/threaddata/thread9 b/tests/threaddata/thread9
new file mode 100644
index 0000000..c17e7fd
--- /dev/null
+++ b/tests/threaddata/thread9
@@ -0,0 +1,283 @@
1Return-Path: <kde-community-bounces@kde.org>
2Received: from imapb010.mykolab.com ([unix socket])
3 by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
4 Thu, 17 Aug 2017 18:30:41 +0200
5X-Sieve: CMU Sieve 2.4
6Received: from int-mx001.mykolab.com (unknown [10.9.13.1])
7 by imapb010.mykolab.com (Postfix) with ESMTPS id 92811150C3DE0
8 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 18:30:41 +0200 (CEST)
9Received: from mx.kolabnow.com (unknown [10.9.4.3])
10 by int-mx001.mykolab.com (Postfix) with ESMTPS id 7973B11D
11 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 18:30:41 +0200 (CEST)
12X-Virus-Scanned: amavisd-new at mykolab.com
13Authentication-Results: ext-mx-in003.mykolab.com (amavisd-new);
14 dkim=pass (1024-bit key) header.d=kde.org header.b=q4j4OOKP;
15 dkim=fail (2048-bit key) reason="fail (message has been altered)"
16 header.d=gmail.com header.b=DJRXq7Se
17X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0
18Received: from forward1-smtp.messagingengine.com (forward1-smtp.messagingengine.com [66.111.4.223])
19 by ext-mx-in003.mykolab.com (Postfix) with ESMTPS id 3E01E292D
20 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 18:30:29 +0200 (CEST)
21Received: from mailredirect.nyi.internal (imap36.nyi.internal [10.202.2.86])
22 by mailforward.nyi.internal (Postfix) with ESMTP id 89BC31A23
23 for <christian@mailqueue.ch>; Thu, 17 Aug 2017 12:30:28 -0400 (EDT)
24Received: by mailredirect.nyi.internal (Postfix, from userid 501)
25 id 6EC348E9B6; Thu, 17 Aug 2017 12:30:28 -0400 (EDT)
26Received: from compute1.internal (compute1.nyi.internal [10.202.2.41])
27 by sloti36d2t28 (Cyrus fastmail-fmjessie44745-15358-git-fastmail-15358) with LMTPA;
28 Thu, 17 Aug 2017 12:30:28 -0400
29X-Cyrus-Session-Id: sloti36d2t28-3239059-1502987428-2-12164706007640762698
30X-Sieve: CMU Sieve 3.0
31X-Spam-known-sender: no
32X-Orig-Spam-score: 0.0
33X-Spam-hits: BAYES_00 -1.9, HTML_MESSAGE 0.001, RCVD_IN_DNSWL_MED -2.3,
34 RP_MATCHES_RCVD -0.001, SPF_PASS -0.001, LANGUAGES en, BAYES_USED global,
35 SA_VERSION 3.4.0
36X-Spam-source: IP='46.4.96.248', Host='postbox.kde.org', Country='DE', FromHeader='org',
37 MailFrom='org'
38X-Spam-charsets: plain='UTF-8', html='UTF-8'
39X-Resolved-to: chrigi_1@fastmail.fm
40X-Delivered-to: chrigi_1@fastmail.fm
41X-Mail-from: kde-community-bounces@kde.org
42Received: from mx1 ([10.202.2.200])
43 by compute1.internal (LMTPProxy); Thu, 17 Aug 2017 12:30:28 -0400
44Authentication-Results: mx1.messagingengine.com;
45 dkim=fail (message has been altered; 2048-bit rsa key sha256) header.d=gmail.com header.i=@gmail.com header.b=DJRXq7Se;
46 dkim=pass (1024-bit rsa key sha256) header.d=kde.org header.i=@kde.org header.b=q4j4OOKP;
47 dmarc=none (p=none;has-list-id=yes) header.from=kde.org;
48 spf=pass smtp.mailfrom=kde-community-bounces@kde.org smtp.helo=postbox.kde.org;
49 x-google-dkim=fail (message has been altered; 2048-bit rsa key) header.d=1e100.net header.i=@1e100.net header.b=U7Pdj/LB
50Received-SPF: pass
51 (kde.org: 46.4.96.248 is authorized to use 'kde-community-bounces@kde.org' in 'mfrom' identity (mechanism 'mx' matched))
52 receiver=mx1.messagingengine.com;
53 identity=mailfrom;
54 envelope-from="kde-community-bounces@kde.org";
55 helo=postbox.kde.org;
56 client-ip=46.4.96.248
57Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
58 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
59 (No client certificate requested)
60 by mx1.messagingengine.com (Postfix) with ESMTPS
61 for <chrigi_1@fastmail.fm>; Thu, 17 Aug 2017 12:30:27 -0400 (EDT)
62DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
63 t=1502987424; bh=P5C3gzzCdP/NQgV0POjgD3g9Hpun4leANxLktFzWpbo=;
64 h=In-Reply-To:References:From:Date:Subject:To:Reply-To:List-Id:
65 List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:
66 From;
67 b=q4j4OOKPbM6MLGfTlM1WlnmVrh2PfQSKPYcLEoHUjBwoiu+oacbJb5cxmPkadvddx
68 MIYmJyog8F4NCNZCIi5vzNkit8vaUJgHws3pk+0uIFo9SdOBkFBfTXSGsDBWB2AdL5
69 wryEwxZKOqEDcECpTNEEmQykU3MYwLBw7sD+KJjY=
70X-Original-To: kde-community@kde.org
71X-Remote-Delivered-To: kde-community@localhost.kde.org
72Received-SPF: Pass (sender SPF authorized) identity=mailfrom;
73 client-ip=2607:f8b0:4003:c06::232; helo=mail-oi0-x232.google.com;
74 envelope-from=kexipl@gmail.com; receiver=kde-community@kde.org
75Authentication-Results: postbox.kde.org; dkim=pass
76 reason="2048-bit key; unprotected key"
77 header.d=gmail.com header.i=@gmail.com header.b=DJRXq7Se;
78 dkim-adsp=pass; dkim-atps=neutral
79Received: from mail-oi0-x232.google.com (mail-oi0-x232.google.com
80 [IPv6:2607:f8b0:4003:c06::232])
81 by postbox.kde.org (Postfix) with ESMTPS id A4F67A014D
82 for <kde-community@kde.org>; Thu, 17 Aug 2017 16:30:09 +0000 (UTC)
83Received: by mail-oi0-x232.google.com with SMTP id f11so71799456oic.0
84 for <kde-community@kde.org>; Thu, 17 Aug 2017 09:30:09 -0700 (PDT)
85DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;
86 h=mime-version:sender:in-reply-to:references:from:date:message-id
87 :subject:to; bh=P5C3gzzCdP/NQgV0POjgD3g9Hpun4leANxLktFzWpbo=;
88 b=DJRXq7SewhEpbavrA6kFk0TPbF9526gl1WpH2O4R7hpuM5tJVqLoT4b53UfmZyGeDw
89 pSdW599ZTY3WLsK29IZ5buua1TgJeSLgN+PWKfTJAFW7qAZaJo6pRIpqSgETEEk/BdMc
90 KtqYdBD/IkwUVx5LAuQikyNn1HrKbti/tbc/YiI23f5TRxfIQZb7DOvOaAi1bZO8jEFq
91 5EHEVcrjvIR2S4HHWxen9rZvGIotVN3womdK8b0t+Wx+Kt0qv06px9jNF0mTqLKhCJAz
92 los9Tpv/7RI0JiQyfPzl7kMQjU3i/pyA1u6b6t69ALfUQcjv25NcwhSaQbWIi9DN8rLg
93 Lc7g==
94X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
95 d=1e100.net; s=20161025;
96 h=x-gm-message-state:mime-version:sender:in-reply-to:references:from
97 :date:message-id:subject:to;
98 bh=P5C3gzzCdP/NQgV0POjgD3g9Hpun4leANxLktFzWpbo=;
99 b=U7Pdj/LBlwGbtXVpnpJudVk938b0fKTIRhK+Lc9IfB9zZhXh3FGwX5kWSUObEOT3hX
100 BXRk0cWvJYHpuGUnLXx/Wjen3j6283GHvDvPhfagyTZbGJohkRMEkxLwFh4ZsJ87M71t
101 pqLLayDjqDHj5jVuko5TDPTtRL8mjzPM7r0DQKu1GYkYtiNLE5JlGR9OsqK8ZH78Wkf8
102 PWUT2BD+mkOE03gFEYpTA0oQW1iwv+wN5xySzaUOlBVfxUUx69EOLnFuRthkQHXcnlGG
103 rchW44D/eiSVU7JWK1Tk2IKNK+ERiq2/zftSmKRzpbwfv6D8De0PZOJPyi89kS9t2I2L
104 ar9g==
105X-Gm-Message-State: AHYfb5g28N+JHV5G6R5j0X0hpMFpCnu/TuLNw/idrsMKyvGOUXdbQiIn
106 whIAqO9js0sL5H92k3yqqJIGhuicWA==
107X-Received: by 10.202.108.130 with SMTP id h124mr8149045oic.289.1502987407944;
108 Thu, 17 Aug 2017 09:30:07 -0700 (PDT)
109MIME-Version: 1.0
110Received: by 10.182.45.227 with HTTP; Thu, 17 Aug 2017 09:29:27 -0700 (PDT)
111In-Reply-To: <5A696707-744C-4035-A8FA-CA83EE8691D6@kde.org>
112References: <2048912.XfIJe3ZSdj@vkpc5> <2990543.KVDkBByYO0@minixfox>
113 <CACpu024EH1OeDqwL94QK33eq4sCGjKjwedcQDR_PWjprBevzfg@mail.gmail.com>
114 <5231282.Ch11jfsTMl@lenovo> <B95D7D2A-23A9-4245-AAC0-8A3FAE330090@kde.org>
115 <5A696707-744C-4035-A8FA-CA83EE8691D6@kde.org>
116From: Jaroslaw Staniek <staniek@kde.org>
117Date: Thu, 17 Aug 2017 18:29:27 +0200
118X-Google-Sender-Auth: LxL4QEJfN3UTITM2I0VbgyX7420
119Message-ID: <CAOj7QQ0WsTCfu9hoq+DTKMGTW=+KObo7HG2_YU1QZ6eOGwQbAQ@mail.gmail.com>
120Subject: Re: Telemetry Policy
121To: informing about and discussing non-technical community topics
122 <kde-community@kde.org>
123Content-Type: multipart/alternative; boundary="001a1142e7548d73010556f58604"
124X-BeenThere: kde-community@kde.org
125X-Mailman-Version: 2.1.16
126Precedence: list
127Reply-To: informing about and discussing non-technical community topics
128 <kde-community@kde.org>
129List-Id: informing about and discussing non-technical community topics
130 <kde-community.kde.org>
131List-Unsubscribe: <https://mail.kde.org/mailman/options/kde-community>,
132 <mailto:kde-community-request@kde.org?subject=unsubscribe>
133List-Archive: <http://mail.kde.org/pipermail/kde-community/>
134List-Post: <mailto:kde-community@kde.org>
135List-Help: <mailto:kde-community-request@kde.org?subject=help>
136List-Subscribe: <https://mail.kde.org/mailman/listinfo/kde-community>,
137 <mailto:kde-community-request@kde.org?subject=subscribe>
138Errors-To: kde-community-bounces@kde.org
139Sender: "kde-community" <kde-community-bounces@kde.org>
140
141--001a1142e7548d73010556f58604
142Content-Type: text/plain; charset="UTF-8"
143Content-Transfer-Encoding: quoted-printable
144
145On 17 August 2017 at 18:20, Thomas Pfeiffer <thomas.pfeiffer@kde.org> wrote=
146:
147
148>
149> On 17. Aug 2017, at 17:38, Mirko Boehm - KDE <mirko@kde.org> wrote:
150>
151> Hi,
152>
153> On 17. Aug 2017, at 01:46, Thomas Pfeiffer <thomas.pfeiffer@kde.org>
154> wrote:
155>
156> Hi Valorie,
157> Even if opt-out for some data is legally and even morally fine, it does n=
158ot
159>
160> align with the values we communicate to our users:
161> Unlike Mozilla's Mission, our Vision mentions privacy explicitly, and we'=
162re
163>
164> striving to make privacy our USP.
165>
166>
167> We seem to assume a contradiction between telemetry and privacy. I believ=
168e
169> this is a knee-jerk reaction. We can implement telemetry in a way that
170> privacy is not violated. In fact, I would say that it follows from our
171> vision that we should do this.
172>
173>
174> The problem is: I expect users to have the same knee-jerk reaction. I
175> don=E2=80=99t see us being able to explain to users that actually their p=
176rivacy is
177> perfectly safe before they freak out.
178> Privacy-minded Free Software users have freaked out in the past over
179> things which objectively speaking were not a huge deal.
180> It=E2=80=99s emotion more than rational arguments
181>
182>
183=E2=80=8BIt's hard to argue here or generalize to all app's communities. Kr=
184ita
185community for example is different than gcc community in these aspects.
186
187--=20
188regards, Jaroslaw Staniek
189
190KDE:
191: A world-wide network of software engineers, artists, writers, translators
192: and facilitators committed to Free Software development - http://kde.org
193Calligra Suite:
194: A graphic art and office suite - http://calligra.org
195Kexi:
196: A visual database apps builder - http://calligra.org/kexi
197Qt Certified Specialist:
198: http://www.linkedin.com/in/jstaniek
199
200--001a1142e7548d73010556f58604
201Content-Type: text/html; charset="UTF-8"
202Content-Transfer-Encoding: quoted-printable
203
204<div dir=3D"ltr"><div class=3D"gmail_default" style=3D"font-family:monospac=
205e,monospace;font-size:small"><br></div><div class=3D"gmail_extra"><br><div =
206class=3D"gmail_quote">On 17 August 2017 at 18:20, Thomas Pfeiffer <span dir=
207=3D"ltr">&lt;<a href=3D"mailto:thomas.pfeiffer@kde.org" target=3D"_blank">t=
208homas.pfeiffer@kde.org</a>&gt;</span> wrote:<br><blockquote class=3D"gmail_=
209quote" style=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1=
210ex"><div style=3D"word-wrap:break-word"><br><div><span class=3D""><blockquo=
211te type=3D"cite"><div>On 17. Aug 2017, at 17:38, Mirko Boehm - KDE &lt;<a h=
212ref=3D"mailto:mirko@kde.org" target=3D"_blank">mirko@kde.org</a>&gt; wrote:=
213</div><br class=3D"m_-9083494930210199564Apple-interchange-newline"><div><d=
214iv style=3D"word-wrap:break-word">Hi,=C2=A0<div><br><div><blockquote type=
215=3D"cite"><div>On 17. Aug 2017, at 01:46, Thomas Pfeiffer &lt;<a href=3D"ma=
216ilto:thomas.pfeiffer@kde.org" target=3D"_blank">thomas.pfeiffer@kde.org</a>=
217&gt; wrote:</div><br class=3D"m_-9083494930210199564Apple-interchange-newli=
218ne"><div><span style=3D"font-family:Menlo-Regular;font-size:11px;font-style=
219:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;t=
220ext-align:start;text-indent:0px;text-transform:none;white-space:normal;word=
221-spacing:0px;float:none;display:inline!important">Hi Valorie,</span><br sty=
222le=3D"font-family:Menlo-Regular;font-size:11px;font-style:normal;font-varia=
223nt-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;te=
224xt-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><spa=
225n style=3D"font-family:Menlo-Regular;font-size:11px;font-style:normal;font-=
226variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:sta=
227rt;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;=
228float:none;display:inline!important">Even if opt-out for some data is legal=
229ly and even morally fine, it does not<span class=3D"m_-9083494930210199564A=
230pple-converted-space">=C2=A0</span></span><br style=3D"font-family:Menlo-Re=
231gular;font-size:11px;font-style:normal;font-variant-caps:normal;font-weight=
232:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transfo=
233rm:none;white-space:normal;word-spacing:0px"><span style=3D"font-family:Men=
234lo-Regular;font-size:11px;font-style:normal;font-variant-caps:normal;font-w=
235eight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-tr=
236ansform:none;white-space:normal;word-spacing:0px;float:none;display:inline!=
237important">align with the values we communicate to our users:</span><br sty=
238le=3D"font-family:Menlo-Regular;font-size:11px;font-style:normal;font-varia=
239nt-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;te=
240xt-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><spa=
241n style=3D"font-family:Menlo-Regular;font-size:11px;font-style:normal;font-=
242variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:sta=
243rt;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;=
244float:none;display:inline!important">Unlike Mozilla&#39;s Mission, our Visi=
245on mentions privacy explicitly, and we&#39;re<span class=3D"m_-908349493021=
2460199564Apple-converted-space">=C2=A0</span></span><br style=3D"font-family:=
247Menlo-Regular;font-size:11px;font-style:normal;font-variant-caps:normal;fon=
248t-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text=
249-transform:none;white-space:normal;word-spacing:0px"><span style=3D"font-fa=
250mily:Menlo-Regular;font-size:11px;font-style:normal;font-variant-caps:norma=
251l;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px=
252;text-transform:none;white-space:normal;word-spacing:0px;float:none;display=
253:inline!important">striving to make privacy our USP.</span><br style=3D"fon=
254t-family:Menlo-Regular;font-size:11px;font-style:normal;font-variant-caps:n=
255ormal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent=
256:0px;text-transform:none;white-space:normal;word-spacing:0px"></div></block=
257quote><br></div><div>We seem to assume a contradiction between telemetry an=
258d privacy. I believe this is a knee-jerk reaction. We can implement telemet=
259ry in a way that privacy is not violated. In fact, I would say that it foll=
260ows from our vision that we should do this.</div><div><br></div></div></div=
261></div></blockquote><div><br></div></span>The problem is: I expect users to=
262 have the same knee-jerk reaction. I don=E2=80=99t see us being able to exp=
263lain to users that actually their privacy is perfectly safe before they fre=
264ak out.</div><div>Privacy-minded Free Software users have freaked out in th=
265e past over things which objectively speaking were not a huge deal.</div><d=
266iv>It=E2=80=99s emotion more than rational arguments</div><br></div></block=
267quote></div><br><div class=3D"gmail_default" style=3D"font-family:monospace=
268,monospace;font-size:small">=E2=80=8BIt&#39;s hard to argue here or general=
269ize to all app&#39;s communities. Krita community for example is different =
270than gcc community in these aspects.</div><div><br></div>-- <br><div class=
271=3D"gmail_signature" data-smartmail=3D"gmail_signature">regards, Jaroslaw S=
272taniek<br><br>KDE:<br>: A world-wide network of software engineers, artists=
273, writers, translators<br>: and facilitators committed to Free Software dev=
274elopment - <a href=3D"http://kde.org" target=3D"_blank">http://kde.org</a><=
275br>Calligra Suite:<br>: A graphic art and office suite - <a href=3D"http://=
276calligra.org" target=3D"_blank">http://calligra.org</a><br>Kexi:<br>: A vis=
277ual database apps builder - <a href=3D"http://calligra.org/kexi" target=3D"=
278_blank">http://calligra.org/kexi</a><br>Qt Certified Specialist:<br>: <a hr=
279ef=3D"http://www.linkedin.com/in/jstaniek" target=3D"_blank">http://www.lin=
280kedin.com/in/jstaniek</a></div>
281</div></div>
282
283--001a1142e7548d73010556f58604--