aboutsummaryrefslogtreecommitdiff
path: root/smartpensyncer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'smartpensyncer.cc')
-rw-r--r--smartpensyncer.cc214
1 files changed, 214 insertions, 0 deletions
diff --git a/smartpensyncer.cc b/smartpensyncer.cc
new file mode 100644
index 0000000..50304a3
--- /dev/null
+++ b/smartpensyncer.cc
@@ -0,0 +1,214 @@
+#include <QtCore/QBuffer>
+#include <QtCore/QScopedArrayPointer>
+#include <QtCore/QThread>
+#include <QtCore/QDebug>
+#include <QtGui/QDesktopServices>
+#include <quazip/quazipfile.h>
+#include "smartpensyncer.h"
+
+#define BUFFER_SIZE 16 * 1024
+
+namespace {
+static QString cleanFilename(QString s)
+{
+ static const QRegExp re("[^0-9A-Za-z-_]+");
+ return s.replace(re, "_");
+}
+
+static QDateTime getTimestampFileDate(const QString &path)
+{
+ QFileInfo info(path);
+ qDebug() << "Checking timestamp" << info.filePath();
+ if (info.exists()) {
+ return info.lastModified();
+ } else {
+ return QDateTime();
+ }
+}
+
+static void setTimestampFileDate(const QString &path)
+{
+ QFile f(path);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ qWarning() << "Could not set timestamp file:" << path;
+ return;
+ }
+ f.close();
+}
+
+void removeTimestampFile(const QString &path)
+{
+ QFile f(path);
+ if (!f.remove()) {
+ qWarning() << "Cannot remove timestamp file:" << path;
+ }
+}
+}
+
+SmartpenSyncer::SmartpenSyncer(const Smartpen::Address &addr, QObject *parent) :
+ QThread(parent), _addr(addr), _pen(new Smartpen(this)), _errored(false), _aborted(false)
+{
+}
+
+SmartpenSyncer::~SmartpenSyncer()
+{
+ if (isRunning()) {
+ _aborted = true;
+ wait();
+ }
+}
+
+Smartpen::Address SmartpenSyncer::penAddress() const
+{
+ return _addr;
+}
+
+void SmartpenSyncer::abort()
+{
+ _aborted = true;
+}
+
+void SmartpenSyncer::run()
+{
+ if (!_pen->connectToPen(_addr)) {
+ qWarning() << "Could not connect to pen with USB address: " << _addr;
+ _errored = true;
+ return;
+ }
+
+ _penName = _pen->getPenName();
+ qDebug() << "got pen name:" << _penName;
+
+ QVariantMap penInfo = _pen->getPenInfo();
+ if (penInfo.isEmpty()) {
+ qWarning() << "Could not get pen info";
+ _errored = true;
+ return;
+ }
+
+ _penSerial = penInfo["penserial"].toString();
+
+ _penDataDir.setPath(QDesktopServices::storageLocation(QDesktopServices::DataLocation) + "/" + _penName + ".pen");
+ if (!_penDataDir.exists()) {
+ if (!_penDataDir.mkpath(".")) {
+ qWarning() << "Cannot create pen data directory:" << _penDataDir.absolutePath();
+ }
+ }
+
+ if (!syncPen()) {
+ _errored = true;
+ }
+
+ _pen->disconnectFromPen();
+}
+
+bool SmartpenSyncer::syncPen()
+{
+ QDateTime lastSyncTime = getTimestampFileDate(_penDataDir.filePath(".lastsync"));
+ QList<Smartpen::ChangeReport> changes = _pen->getChangeList(lastSyncTime);
+
+ foreach(const Smartpen::ChangeReport &change, changes) {
+ qDebug() << "Synchronizing guid: " << change.guid << change.title;
+ if (!syncNotebook(change)) {
+ return false;
+ }
+ }
+
+ setTimestampFileDate(_penDataDir.filePath(".lastsync"));
+
+ return true;
+}
+
+bool SmartpenSyncer::syncNotebook(const Smartpen::ChangeReport &change)
+{
+ QDir notebookDir(_penDataDir.filePath(change.title + ".afd"));
+ if (!notebookDir.exists()) {
+ if (!notebookDir.mkpath(".")) {
+ qWarning() << "Cannot create notebook data directory:" << notebookDir.absolutePath();
+ }
+ }
+
+ setTimestampFileDate(notebookDir.filePath(".sync.lck"));
+
+ QDateTime lastSyncTime = getTimestampFileDate(notebookDir.filePath(".lastsync"));
+ QByteArray lspData = _pen->getLspData(change.guid, lastSyncTime);
+
+ if (!extractZip(lspData, notebookDir)) {
+ return false;
+ }
+
+ setTimestampFileDate(notebookDir.filePath(".lastsync"));
+ removeTimestampFile(notebookDir.filePath(".sync.lck"));
+
+ return true;
+}
+
+bool SmartpenSyncer::extractZip(QByteArray &zipData, QDir &dir)
+{
+ QBuffer zipBuffer(&zipData);
+ QuaZip zip(&zipBuffer);
+ QuaZipFile zipFile(&zip);
+
+ if (!zip.open(QuaZip::mdUnzip)) {
+ qWarning() << "Could not open zip file";
+ return false;
+ }
+
+ QScopedArrayPointer<char> buffer(new char[BUFFER_SIZE]);
+
+ for (bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) {
+ QString zipName = zip.getCurrentFileName();
+ if (!dir.absoluteFilePath(zipName).startsWith(dir.absolutePath())) {
+ qWarning() << "broken zip filename:" << zipName;
+ continue;
+ }
+ QFileInfo finfo(dir.filePath(zipName));
+ if (!dir.mkpath(finfo.path())) {
+ qWarning() << "cannot mkpath for:" << finfo.absoluteFilePath();
+ }
+
+ if (zipName.endsWith('/')) {
+ // Nothing to do
+ continue;
+ }
+
+ if (!zipFile.open(QIODevice::ReadOnly)) {
+ qWarning() << "cannot open zip file for reading:" << zipName;
+ continue;
+ }
+
+ QFile file(finfo.filePath());
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ qWarning() << "cannot open for writing:" << finfo.absoluteFilePath();
+ zipFile.close();
+ continue;
+ }
+
+ while (!zipFile.atEnd()) {
+ qint64 read = zipFile.read(buffer.data(), BUFFER_SIZE);
+ if (read <= 0) {
+ qWarning() << "short read on:" << zipName;
+ zipFile.close();
+ continue;
+ }
+ qint64 written = file.write(buffer.data(), read);
+ if (written != read) {
+ qWarning() << "short write on:" << file.fileName();
+ zipFile.close();
+ continue;
+ }
+ }
+
+ file.close();
+ zipFile.close();
+ }
+
+ buffer.reset();
+
+ if (zip.getZipError() == UNZ_OK) {
+ return true;
+ } else {
+ qWarning() << "Error while decompressing";
+ return false;
+ }
+}