[calligra/calligra/2.9] /: Fix to resource md5 generation

Stefano Bonicatti smjert at gmail.com
Mon Aug 10 13:29:45 UTC 2015


Git commit ab6f6c4a26664a468b50861dc9c24fa14c3bde27 by Stefano Bonicatti.
Committed on 10/08/2015 at 13:27.
Pushed by stefanobonicatti into branch 'calligra/2.9'.

Fix to resource md5 generation

Now the md5 will be calculated on the file data instead of the one in
memory that has been interpreted, to keep the result consistent.

Reactivated the code that warns the user and avoids to install resources
when they have a different md5 from the manifest one.

Reactivated the bundle edit button in Manage Resources.

Added KoHashGenerator, KoMD5Generator, KisMD5Generator,
KoHashGeneratorProvider classes.
The first is an abstract class which gives a base to implement an
hash generator that accepts a filename or a byte array to generate
its hash.
The second is an implementation of the first with the MD5 algorithm.
The third overrides the first on the function that generates the md5
from the filename, to support Krita resource bundles.
The fourth is the one that provides the correct generator when asked.
The hash generator identification is a string.

Added versioning of the bundle through a simple increasing integer
string.
All the bundles which doesn't have the version in their meta are
recreated and the md5 of each resource is recalculated.
An old version of the bundle is mantained (bundlename.bundle.old).

REVIEW: 122678
CCMAIL: kimageshop at kde.org

M  +2    -18   krita/image/brushengine/kis_paintop_preset.cpp
M  +0    -4    krita/image/brushengine/kis_paintop_preset.h
M  +0    -11   krita/libbrush/kis_abr_brush.cpp
M  +0    -5    krita/libbrush/kis_abr_brush_collection.cpp
M  +2    -24   krita/libbrush/kis_auto_brush.cpp
M  +0    -4    krita/libbrush/kis_auto_brush.h
M  +0    -13   krita/libbrush/kis_brush.cpp
M  +0    -1    krita/libbrush/kis_brush.h
M  +2    -0    krita/libbrush/kis_gbr_brush.cpp
M  +2    -0    krita/libbrush/kis_imagepipe_brush.cpp
M  +6    -2    krita/libbrush/kis_png_brush.cpp
M  +5    -1    krita/libbrush/kis_svg_brush.cpp
M  +3    -19   krita/plugins/extensions/dockers/tasksetdocker/taskset_resource.cpp
M  +1    -2    krita/plugins/extensions/dockers/tasksetdocker/taskset_resource.h
M  +0    -1    krita/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp
M  +133  -82   krita/plugins/extensions/resourcemanager/resourcebundle.cpp
M  +24   -4    krita/plugins/extensions/resourcemanager/resourcebundle.h
M  +14   -2    krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp
M  +3    -1    krita/plugins/extensions/resourcemanager/resourcebundle_manifest.h
M  +2    -1    krita/ui/CMakeLists.txt
M  +5    -0    krita/ui/kis_factory2.cc
A  +62   -0    krita/ui/kis_md5_generator.cpp     [License: GPL (v2+)]
A  +27   -0    krita/ui/kis_md5_generator.h     [License: GPL (v2+)]
M  +2    -12   krita/ui/kis_workspace_resource.cpp
M  +0    -4    krita/ui/kis_workspace_resource.h
M  +2    -0    libs/pigment/CMakeLists.txt
M  +0    -6    libs/pigment/resources/KoAbstractGradient.cpp
M  +0    -2    libs/pigment/resources/KoAbstractGradient.h
M  +3    -14   libs/pigment/resources/KoColorSet.cpp
M  +0    -4    libs/pigment/resources/KoColorSet.h
A  +30   -0    libs/pigment/resources/KoHashGenerator.h     [License: GPL (v2+)]
A  +58   -0    libs/pigment/resources/KoHashGeneratorProvider.cpp     [License: GPL (v2+)]
A  +43   -0    libs/pigment/resources/KoHashGeneratorProvider.h     [License: GPL (v2+)]
A  +56   -0    libs/pigment/resources/KoMD5Generator.cpp     [License: GPL (v2+)]
A  +34   -0    libs/pigment/resources/KoMD5Generator.h     [License: GPL (v2+)]
M  +2    -19   libs/pigment/resources/KoPattern.cpp
M  +0    -4    libs/pigment/resources/KoPattern.h
M  +16   -0    libs/pigment/resources/KoResource.cpp
M  +2    -2    libs/pigment/resources/KoResource.h
M  +3    -21   libs/pigment/resources/KoSegmentGradient.cpp
M  +0    -3    libs/pigment/resources/KoSegmentGradient.h
M  +2    -22   libs/pigment/resources/KoStopGradient.cpp
M  +0    -2    libs/pigment/resources/KoStopGradient.h

http://commits.kde.org/calligra/ab6f6c4a26664a468b50861dc9c24fa14c3bde27

diff --git a/krita/image/brushengine/kis_paintop_preset.cpp b/krita/image/brushengine/kis_paintop_preset.cpp
index 736699d..41b4827 100644
--- a/krita/image/brushengine/kis_paintop_preset.cpp
+++ b/krita/image/brushengine/kis_paintop_preset.cpp
@@ -26,7 +26,6 @@
 #include <QImageReader>
 #include <QDomDocument>
 #include <QBuffer>
-#include <QCryptographicHash>
 
 #include <KoInputDevice.h>
 
@@ -316,23 +315,6 @@ void KisPaintOpPreset::fromXML(const QDomElement& presetElt)
 
 }
 
-QByteArray KisPaintOpPreset::generateMD5() const
-{
-    QByteArray ba;
-    QBuffer buf(&ba);
-    buf.open(QBuffer::WriteOnly);
-    saveToDevice(&buf);
-    buf.close();
-
-    if (!ba.isEmpty()) {
-        QCryptographicHash md5(QCryptographicHash::Md5);
-        md5.addData(ba);
-        return md5.result();
-    }
-
-    return ba;
-}
-
 bool KisPaintOpPreset::saveToDevice(QIODevice *dev) const
 {
     QImageWriter writer(dev, "PNG");
@@ -355,6 +337,8 @@ bool KisPaintOpPreset::saveToDevice(QIODevice *dev) const
 
     m_d->dirtyPreset = false;
 
+    KoResource::saveToDevice(dev);
+
     return writer.write(img);
 
 }
diff --git a/krita/image/brushengine/kis_paintop_preset.h b/krita/image/brushengine/kis_paintop_preset.h
index 52caacc..2cb3181 100644
--- a/krita/image/brushengine/kis_paintop_preset.h
+++ b/krita/image/brushengine/kis_paintop_preset.h
@@ -103,10 +103,6 @@ public:
         bool m_isDirty;
     };
 
-protected:
-
-    virtual QByteArray generateMD5() const;
-
 private:
 
     struct Private;
diff --git a/krita/libbrush/kis_abr_brush.cpp b/krita/libbrush/kis_abr_brush.cpp
index ad717d4..8591fe3 100644
--- a/krita/libbrush/kis_abr_brush.cpp
+++ b/krita/libbrush/kis_abr_brush.cpp
@@ -78,17 +78,6 @@ void KisAbrBrush::setBrushTipImage(const QImage& image)
     setBrushType(MASK);
     setHasColor(false);
 
-#if QT_VERSION >= 0x040700
-    QByteArray ba = QByteArray::fromRawData((const char*)image.constBits(), image.byteCount());
-#else
-    QByteArray ba = QByteArray::fromRawData((const char*)image.bits(), image.byteCount());
-#endif
-
-    QCryptographicHash md5(QCryptographicHash::Md5);
-    md5.addData(ba);
-    md5.addData(m_parentMD5);
-    setMD5(md5.result());
-
     KisBrush::setBrushTipImage(image);
 }
 
diff --git a/krita/libbrush/kis_abr_brush_collection.cpp b/krita/libbrush/kis_abr_brush_collection.cpp
index 554865b..4c759c3 100644
--- a/krita/libbrush/kis_abr_brush_collection.cpp
+++ b/krita/libbrush/kis_abr_brush_collection.cpp
@@ -32,7 +32,6 @@
 #include <QDebug>
 #include <QString>
 #include <QBuffer>
-#include <QCryptographicHash>
 
 #include <kis_debug.h>
 #include <klocale.h>
@@ -544,10 +543,6 @@ bool KisAbrBrushCollection::loadFromDevice(QIODevice *dev)
     qint32 layer_ID;
 
     QByteArray ba = dev->readAll();
-    QCryptographicHash md5(QCryptographicHash::Md5);
-    md5.addData(ba);
-    setMD5(md5.result());
-
     QBuffer buf(&ba);
     buf.open(QIODevice::ReadOnly);
     QDataStream abr(&buf);
diff --git a/krita/libbrush/kis_auto_brush.cpp b/krita/libbrush/kis_auto_brush.cpp
index 86cc379..e458940 100644
--- a/krita/libbrush/kis_auto_brush.cpp
+++ b/krita/libbrush/kis_auto_brush.cpp
@@ -28,7 +28,8 @@
 #include <QtConcurrentMap>
 #include <QByteArray>
 #include <QBuffer>
-#include <QCryptographicHash>
+#include <QFile>
+#include <QFileInfo>
 
 #include <KoColor.h>
 #include <KoColorSpace.h>
@@ -315,29 +316,6 @@ qreal KisAutoBrush::density() const
     return d->density;
 }
 
-QByteArray KisAutoBrush::generateMD5() const
-{
-    QByteArray ba;
-    if (!brushTipImage().isNull()) {
-#if QT_VERSION >= 0x040700
-        ba = QByteArray::fromRawData((const char*)brushTipImage().constBits(), brushTipImage().byteCount());
-#else
-        ba = QByteArray::fromRawData((const char*)brushTipImage().bits(), brushTipImage().byteCount());
-#endif
-    }
-    QCryptographicHash md5(QCryptographicHash::Md5);
-    md5.addData(ba);
-
-    QDomDocument doc;
-    QDomElement root = doc.createElement("autobrush");
-    toXML(doc, root);
-    doc.appendChild(root);
-    md5.addData(doc.toByteArray());
-
-    return md5.result();
-
-}
-
 qreal KisAutoBrush::randomness() const
 {
     return d->randomness;
diff --git a/krita/libbrush/kis_auto_brush.h b/krita/libbrush/kis_auto_brush.h
index c537697..d0ca4cd 100644
--- a/krita/libbrush/kis_auto_brush.h
+++ b/krita/libbrush/kis_auto_brush.h
@@ -77,10 +77,6 @@ public:
     qreal randomness() const;
     qreal density() const;
 
-protected:
-
-    virtual QByteArray generateMD5() const;
-
 private:
 
     QImage createBrushPreview();
diff --git a/krita/libbrush/kis_brush.cpp b/krita/libbrush/kis_brush.cpp
index efa78a4..ac12227 100644
--- a/krita/libbrush/kis_brush.cpp
+++ b/krita/libbrush/kis_brush.cpp
@@ -27,7 +27,6 @@
 #include <QFile>
 #include <QPoint>
 #include <QFileInfo>
-#include <QCryptographicHash>
 #include <QBuffer>
 
 #include <kis_debug.h>
@@ -334,18 +333,6 @@ void KisBrush::predefinedBrushToXML(const QString &type, QDomElement& e) const
     e.setAttribute("scale", QString::number(scale()));
 }
 
-QByteArray KisBrush::generateMD5() const
-{
-    if (!filename().isNull() && !filename().startsWith("bundle://") && QFileInfo(filename()).exists()) {
-        QFile f(filename());
-        f.open(QFile::ReadOnly);
-        QCryptographicHash md5(QCryptographicHash::Md5);
-        md5.addData(f.readAll());
-        return md5.result();
-    }
-    return QByteArray();
-}
-
 void KisBrush::toXML(QDomDocument& /*document*/ , QDomElement& element) const
 {
     element.setAttribute("BrushVersion", "2");
diff --git a/krita/libbrush/kis_brush.h b/krita/libbrush/kis_brush.h
index 6faf597..94a72e1 100644
--- a/krita/libbrush/kis_brush.h
+++ b/krita/libbrush/kis_brush.h
@@ -345,7 +345,6 @@ public:
     bool isPiercedApprox() const;
 
 protected:
-    virtual QByteArray generateMD5() const;
 
     void resetBoundary();
 
diff --git a/krita/libbrush/kis_gbr_brush.cpp b/krita/libbrush/kis_gbr_brush.cpp
index 37158f9..ddde235 100644
--- a/krita/libbrush/kis_gbr_brush.cpp
+++ b/krita/libbrush/kis_gbr_brush.cpp
@@ -390,6 +390,8 @@ bool KisGbrBrush::saveToDevice(QIODevice* dev) const
         return false;
     }
 
+    KoResource::saveToDevice(dev);
+
     return true;
 }
 
diff --git a/krita/libbrush/kis_imagepipe_brush.cpp b/krita/libbrush/kis_imagepipe_brush.cpp
index 9cffa37..6debdab 100644
--- a/krita/libbrush/kis_imagepipe_brush.cpp
+++ b/krita/libbrush/kis_imagepipe_brush.cpp
@@ -321,6 +321,8 @@ bool KisImagePipeBrush::saveToDevice(QIODevice* dev) const
     if (!dev->putChar('\n'))
         return false;
 
+    KoResource::saveToDevice(dev);
+
     // <gbr brushes>
     return m_d->brushesPipe.saveToDevice(dev);
 }
diff --git a/krita/libbrush/kis_png_brush.cpp b/krita/libbrush/kis_png_brush.cpp
index 358f8da..87ff5bc 100644
--- a/krita/libbrush/kis_png_brush.cpp
+++ b/krita/libbrush/kis_png_brush.cpp
@@ -23,7 +23,6 @@
 #include <QImageReader>
 #include <QByteArray>
 #include <QBuffer>
-#include <QCryptographicHash>
 
 KisPngBrush::KisPngBrush(const QString& filename)
     : KisBrush(filename)
@@ -109,7 +108,12 @@ bool KisPngBrush::save()
 
 bool KisPngBrush::saveToDevice(QIODevice *dev) const
 {
-    return brushTipImage().save(dev, "PNG");
+    if(brushTipImage().save(dev, "PNG")) {
+        KoResource::saveToDevice(dev);
+        return true;
+    }
+
+    return false;
 }
 
 QString KisPngBrush::defaultFileExtension() const
diff --git a/krita/libbrush/kis_svg_brush.cpp b/krita/libbrush/kis_svg_brush.cpp
index 2934a36..ddaee57 100644
--- a/krita/libbrush/kis_svg_brush.cpp
+++ b/krita/libbrush/kis_svg_brush.cpp
@@ -98,7 +98,11 @@ bool KisSvgBrush::save()
 
 bool KisSvgBrush::saveToDevice(QIODevice *dev) const
 {
-    return (dev->write(m_svg.constData(), m_svg.size()) == m_svg.size());
+    if((dev->write(m_svg.constData(), m_svg.size()) == m_svg.size())) {
+        KoResource::saveToDevice(dev);
+        return true;
+    }
+    return false;
 }
 
 QString KisSvgBrush::defaultFileExtension() const
diff --git a/krita/plugins/extensions/dockers/tasksetdocker/taskset_resource.cpp b/krita/plugins/extensions/dockers/tasksetdocker/taskset_resource.cpp
index 6afc96f..5efe170 100644
--- a/krita/plugins/extensions/dockers/tasksetdocker/taskset_resource.cpp
+++ b/krita/plugins/extensions/dockers/tasksetdocker/taskset_resource.cpp
@@ -22,7 +22,6 @@
 #include <QTextStream>
 #include <QBuffer>
 #include <QByteArray>
-#include <QCryptographicHash>
 
 #include <kis_debug.h>
 
@@ -103,24 +102,6 @@ QStringList TasksetResource::actionList()
     return m_actions;
 }
 
-QByteArray TasksetResource::generateMD5() const
-{
-    QByteArray ba;
-    QBuffer buf(&ba);
-    buf.open(QBuffer::WriteOnly);
-    saveToDevice(&buf);
-    buf.close();
-
-    if (!ba.isEmpty()) {
-        QCryptographicHash md5(QCryptographicHash::Md5);
-        md5.addData(ba);
-        return md5.result();
-    }
-
-    return ba;
-
-}
-
 bool TasksetResource::saveToDevice(QIODevice *io) const
 {
 
@@ -137,6 +118,9 @@ bool TasksetResource::saveToDevice(QIODevice *io) const
 
     QTextStream textStream(io);
     doc.save(textStream, 4);
+
+    KoResource::saveToDevice(io);
+
     return true;
 }
 
diff --git a/krita/plugins/extensions/dockers/tasksetdocker/taskset_resource.h b/krita/plugins/extensions/dockers/tasksetdocker/taskset_resource.h
index 57ad287..19916ea 100644
--- a/krita/plugins/extensions/dockers/tasksetdocker/taskset_resource.h
+++ b/krita/plugins/extensions/dockers/tasksetdocker/taskset_resource.h
@@ -39,8 +39,7 @@ public:
     
     void setActionList(const QStringList actions);
     QStringList actionList();
-protected:
-    virtual QByteArray generateMD5() const;
+
 private:
 
     QStringList m_actions;
diff --git a/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp b/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp
index ae49cd7..95c2d42 100644
--- a/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp
+++ b/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp
@@ -70,7 +70,6 @@ DlgBundleManager::DlgBundleManager(KisActionManager* actionMgr, QWidget *parent)
 
     refreshListData();
 
-    m_ui->bnEditBundle->hide(); // this bunde editor is pretty broken. we can delete this line once fixed
     connect(m_ui->bnEditBundle, SIGNAL(clicked()), SLOT(editBundle()));
 
     connect(m_ui->importBundleButton, SIGNAL(clicked()), SLOT(slotImportResource()));
diff --git a/krita/plugins/extensions/resourcemanager/resourcebundle.cpp b/krita/plugins/extensions/resourcemanager/resourcebundle.cpp
index ba535d3..a6e2335 100644
--- a/krita/plugins/extensions/resourcemanager/resourcebundle.cpp
+++ b/krita/plugins/extensions/resourcemanager/resourcebundle.cpp
@@ -41,6 +41,8 @@
 #include <QStringList>
 #include <QMessageBox>
 
+#include <KoHashGeneratorProvider.h>
+#include <KoHashGenerator.h>
 #include <kis_resource_server_provider.h>
 #include <kis_workspace_resource.h>
 #include <kis_paintop_preset.h>
@@ -54,14 +56,14 @@
 
 
 ResourceBundle::ResourceBundle(QString const& fileName)
-    : KoResource(fileName)
+    : KoResource(fileName),
+      m_bundleVersion("1")
 {
     setName(QFileInfo(fileName).baseName());
 
     QString calligraVersion(CALLIGRA_VERSION_STRING);
     QString version;
 
-
 #ifdef CALLIGRA_GIT_SHA1_STRING
     QString gitVersion(CALLIGRA_GIT_SHA1_STRING);
     version = QString("%1 (git %2)").arg(calligraVersion).arg(gitVersion).toLatin1();
@@ -114,6 +116,7 @@ bool ResourceBundle::load()
             return false;
         }
 
+        bool versionFound = false;
         if (resourceStore->open("meta.xml")) {
             KoXmlDocument doc;
             if (!doc.setContent(resourceStore->device())) {
@@ -172,6 +175,10 @@ bool ResourceBundle::load()
                             m_metadata.insert(e.attribute("meta:name"), e.attribute("meta:value"));
                         }
                     }
+                    else if(e.tagName() == "meta:bundle-version") {
+                        m_metadata.insert("bundle-version", e.firstChild().toText().data());
+                        versionFound = true;
+                    }
                 }
             }
             resourceStore->close();
@@ -193,6 +200,12 @@ bool ResourceBundle::load()
             qWarning() << "Could not open preview.png";
         }
 
+        // If no version is found it's an old bundle with md5 hashes to fix, so we need to recreate the bundle
+        if(!versionFound) {
+            m_metadata.insert("bundle-version", "1");
+            recreateBundle(resourceStore);
+        }
+
         m_installed = true;
         setValid(true);
         setImage(m_thumbnail);
@@ -226,7 +239,7 @@ bool saveResourceToStore(KoResource *resource, KoStore *store, const QString &re
     QBuffer buf;
 
     QFileInfo fi(resource->filename());
-    if (fi.exists() && fi.isReadable() && !fi.isWritable()) {
+    if (fi.exists() && fi.isReadable()) {
 
         QFile f(resource->filename());
         if (!f.open(QFile::ReadOnly)) {
@@ -242,16 +255,8 @@ bool saveResourceToStore(KoResource *resource, KoStore *store, const QString &re
         buf.setBuffer(&ba);
     }
     else {
-
-        if (!buf.open(QBuffer::WriteOnly)) {
-            qWarning() << "Could not open buffer";
-            return false;
-        }
-        if (!resource->saveToDevice(&buf)) {
-            qWarning() << "Could not save resource to buffer";
-            return false;
-        }
-        buf.close();
+        qWarning() << "Could not find the resource " << resource->filename() << " or it isn't readable";
+        return false;
     }
 
     if (!buf.open(QBuffer::ReadOnly)) {
@@ -387,51 +392,10 @@ bool ResourceBundle::save()
         store->close();
     }
 
-    {
-        store->open("META-INF/manifest.xml");
-        QBuffer buf;
-        buf.open(QBuffer::WriteOnly);
-        m_manifest.save(&buf);
-        buf.close();
-        store->write(buf.data());
-        store->close();
-    }
+    saveManifest(store);
+
+    saveMetadata(store);
 
-    {
-        QBuffer buf;
-
-        store->open("meta.xml");
-        buf.open(QBuffer::WriteOnly);
-
-        KoXmlWriter metaWriter(&buf);
-        metaWriter.startDocument("office:document-meta");
-        metaWriter.startElement("meta:meta");
-
-        writeMeta("meta:generator", "generator", &metaWriter);
-        writeMeta("dc:author", "author", &metaWriter);
-        writeMeta("dc:title", "filename", &metaWriter);
-        writeMeta("dc:description", "description", &metaWriter);
-        writeMeta("meta:initial-creator", "author", &metaWriter);
-        writeMeta("dc:creator", "author", &metaWriter);
-        writeMeta("meta:creation-date", "created", &metaWriter);
-        writeMeta("meta:dc-date", "updated", &metaWriter);
-        writeUserDefinedMeta("email", &metaWriter);
-        writeUserDefinedMeta("license", &metaWriter);
-        writeUserDefinedMeta("website", &metaWriter);
-        foreach (const QString &tag, m_bundletags) {
-            metaWriter.startElement("meta:meta-userdefined");
-            metaWriter.addAttribute("meta:name", "tag");
-            metaWriter.addAttribute("meta:value", tag);
-            metaWriter.endElement();
-        }
-
-        metaWriter.endElement(); // meta:meta
-        metaWriter.endDocument();
-
-        buf.close();
-        store->write(buf.data());
-    }
-    store->close();
     store->finalize();
 
     return true;
@@ -728,17 +692,17 @@ bool ResourceBundle::install()
         }
     }
     m_installed = true;
-//    if(!md5Mismatch.isEmpty()){
-//        QString message = i18n("The following resources had mismatching MD5 sums. They may have gotten corrupted, for example, during download.");
-//        QMessageBox bundleFeedback;
-//        bundleFeedback.setIcon(QMessageBox::Warning);
-//        foreach (QString name, md5Mismatch) {
-//            message.append("\n");
-//            message.append(name);
-//        }
-//        bundleFeedback.setText(message);
-//        bundleFeedback.exec();
-//    }
+    if(!md5Mismatch.isEmpty()){
+        QString message = i18n("The following resources had mismatching MD5 sums. They may have gotten corrupted, for example, during download.");
+        QMessageBox bundleFeedback;
+        bundleFeedback.setIcon(QMessageBox::Warning);
+        foreach (QString name, md5Mismatch) {
+            message.append("\n");
+            message.append(name);
+        }
+        bundleFeedback.setText(message);
+        bundleFeedback.exec();
+    }
     return true;
 }
 
@@ -895,20 +859,6 @@ void ResourceBundle::setThumbnail(QString filename)
     }
 }
 
-
-QByteArray ResourceBundle::generateMD5() const
-{
-    QFile f(filename());
-    if (f.exists()) {
-        f.open(QFile::ReadOnly);
-        QByteArray ba = f.readAll();
-        QCryptographicHash md5(QCryptographicHash::Md5);
-        md5.addData(ba);
-        return md5.result();
-    }
-    return QByteArray();
-}
-
 void ResourceBundle::writeMeta(const char *metaTag, const QString &metaKey, KoXmlWriter *writer)
 {
     if (m_metadata.contains(metaKey)) {
@@ -932,3 +882,104 @@ void ResourceBundle::setInstalled(bool install)
 {
     m_installed = install;
 }
+
+void ResourceBundle::saveMetadata(QScopedPointer<KoStore> &store)
+{
+    QBuffer buf;
+
+    store->open("meta.xml");
+    buf.open(QBuffer::WriteOnly);
+
+    KoXmlWriter metaWriter(&buf);
+    metaWriter.startDocument("office:document-meta");
+    metaWriter.startElement("meta:meta");
+
+    writeMeta("meta:generator", "generator", &metaWriter);
+
+    metaWriter.startElement("meta:bundle-version");
+    metaWriter.addTextNode(m_bundleVersion.toUtf8());
+    metaWriter.endElement();
+
+    writeMeta("dc:author", "author", &metaWriter);
+    writeMeta("dc:title", "filename", &metaWriter);
+    writeMeta("dc:description", "description", &metaWriter);
+    writeMeta("meta:initial-creator", "author", &metaWriter);
+    writeMeta("dc:creator", "author", &metaWriter);
+    writeMeta("meta:creation-date", "created", &metaWriter);
+    writeMeta("meta:dc-date", "updated", &metaWriter);
+    writeUserDefinedMeta("email", &metaWriter);
+    writeUserDefinedMeta("license", &metaWriter);
+    writeUserDefinedMeta("website", &metaWriter);
+    foreach (const QString &tag, m_bundletags) {
+        metaWriter.startElement("meta:meta-userdefined");
+        metaWriter.addAttribute("meta:name", "tag");
+        metaWriter.addAttribute("meta:value", tag);
+        metaWriter.endElement();
+    }
+
+    metaWriter.endElement(); // meta:meta
+    metaWriter.endDocument();
+
+    buf.close();
+    store->write(buf.data());
+    store->close();
+}
+
+void ResourceBundle::saveManifest(QScopedPointer<KoStore> &store)
+{
+    store->open("META-INF/manifest.xml");
+    QBuffer buf;
+    buf.open(QBuffer::WriteOnly);
+    m_manifest.save(&buf);
+    buf.close();
+    store->write(buf.data());
+    store->close();
+}
+
+void ResourceBundle::recreateBundle(QScopedPointer<KoStore> &oldStore)
+{
+    // Save a copy of the unmodified bundle, so that if anything goes bad the user doesn't lose it
+    QFile file(filename());
+    file.copy(filename() + ".old");
+
+    QString newStoreName = filename() + ".tmp";
+    QScopedPointer<KoStore> store(KoStore::createStore(newStoreName, KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip));
+    KoHashGenerator *generator = KoHashGeneratorProvider::instance()->getGenerator("MD5");
+    ResourceBundleManifest newManifest;
+
+    addMeta("updated", QDate::currentDate().toString("dd/MM/yyyy"));
+
+    foreach(ResourceBundleManifest::ResourceReference ref, m_manifest.files()) {
+        oldStore->open(ref.resourcePath);
+        store->open(ref.resourcePath);
+
+        QByteArray data = oldStore->device()->readAll();
+        oldStore->close();
+        store->write(data);
+        store->close();
+        QByteArray result = generator->generateHash(data);
+        newManifest.addResource(ref.fileTypeName, ref.resourcePath, ref.tagList, result);
+    }
+
+    m_manifest = newManifest;
+
+    if (!m_thumbnail.isNull()) {
+        QByteArray byteArray;
+        QBuffer buffer(&byteArray);
+        m_thumbnail.save(&buffer, "PNG");
+        if (!store->open("preview.png")) qWarning() << "Could not open preview.png";
+        if (store->write(byteArray) != buffer.size()) qWarning() << "Could not write preview.png";
+        store->close();
+    }
+
+    saveManifest(store);
+    saveMetadata(store);
+
+    store->finalize();
+
+    // Remove the current bundle and then move the tmp one to be the correct one
+    file.setFileName(filename());
+    file.remove();
+    file.setFileName(newStoreName);
+    file.rename(filename());
+}
diff --git a/krita/plugins/extensions/resourcemanager/resourcebundle.h b/krita/plugins/extensions/resourcemanager/resourcebundle.h
index 7e3c8b9..8a9783d 100644
--- a/krita/plugins/extensions/resourcemanager/resourcebundle.h
+++ b/krita/plugins/extensions/resourcemanager/resourcebundle.h
@@ -28,6 +28,8 @@
 #include "KoResource.h"
 #include "resourcebundle_manifest.h"
 
+class KoStore;
+
 /**
  * @brief The ResourceBundle class
  * @details Describe the resource bundles as KoResources
@@ -107,12 +109,29 @@ public:
 
     void setThumbnail(QString);
 
-    QStringList resourceTypes();
-    QList<KoResource*> resources(const QString &resType);
+    /**
+     * @brief saveMetadata: saves bundle metadata
+     * @param store bundle where to save the metadata
+     */
+    void saveMetadata(QScopedPointer<KoStore> &store);
 
-protected:
+    /**
+     * @brief saveManifest: saves bundle manifest
+     * @param store bundle where to save the manifest
+     */
+    void saveManifest(QScopedPointer<KoStore> &store);
 
-    virtual QByteArray generateMD5() const;
+    /**
+     * @brief recreateBundle
+     * It recreates the bundle by copying the old bundle information to a new store
+     * and recalculating the md5 of each resource.
+     * @param oldStore the old store to be recreated.
+     */
+    void recreateBundle(QScopedPointer<KoStore> &oldStore);
+
+
+    QStringList resourceTypes();
+    QList<KoResource*> resources(const QString &resType);
 
 private:
 
@@ -131,6 +150,7 @@ private:
     QList<QByteArray> m_palettesMd5Installed;
     QList<QByteArray> m_workspacesMd5Installed;
     QList<QByteArray> m_presetsMd5Installed;
+    QString m_bundleVersion;
     
 };
 
diff --git a/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp b/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp
index b47c10a..aeb7300 100644
--- a/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp
+++ b/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp
@@ -22,6 +22,7 @@
 #include <QDomElement>
 #include <QDomNode>
 #include <QDomNodeList>
+#include <QForeachContainer>
 
 #include <KoXmlNS.h>
 #include <KoXmlReader.h>
@@ -178,7 +179,7 @@ bool ResourceBundleManifest::save(QIODevice *device)
 
 void ResourceBundleManifest::addResource(const QString &fileTypeName, const QString &fileName, const QStringList &fileTagList, const QByteArray &md5)
 {
-    ResourceReference ref(fileName, fileTagList, md5);
+    ResourceReference ref(fileName, fileTagList, fileTypeName, md5);
     if (!m_resources.contains(fileTypeName)) {
         m_resources[fileTypeName] = QMap<QString, ResourceReference>();
     }
@@ -203,7 +204,18 @@ QStringList ResourceBundleManifest::tags() const
 
 QList<ResourceBundleManifest::ResourceReference> ResourceBundleManifest::files(const QString &type) const
 {
-    if (!m_resources.contains(type)) {
+    // If no type is specified we return all the resources
+    if(type.isEmpty()) {
+        QList<ResourceReference> resources;
+        QList<QMap<QString, ResourceReference> >::iterator i;
+        QList<QMap<QString, ResourceReference> > values = m_resources.values();
+        for(i = values.begin(); i != values.end(); ++i) {
+            resources.append(i->values());
+        }
+
+        return resources;
+    }
+    else if (!m_resources.contains(type)) {
         return QList<ResourceBundleManifest::ResourceReference>();
     }
     return m_resources[type].values();
diff --git a/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.h b/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.h
index e4de7a2..1d3c292 100644
--- a/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.h
+++ b/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.h
@@ -30,14 +30,16 @@ public:
 
     struct ResourceReference {
 
-        ResourceReference(const QString &_resourcePath, const QList<QString> &_tagList, const QByteArray &_md5) {
+        ResourceReference(const QString &_resourcePath, const QList<QString> &_tagList, const QString &_fileTypeName, const QByteArray &_md5) {
             resourcePath = _resourcePath;
             tagList = _tagList;
+            fileTypeName = _fileTypeName;
             md5sum = _md5;
         }
 
         QString resourcePath;
         QList<QString> tagList;
+        QString fileTypeName;
         QByteArray md5sum;
     };
 
diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt
index 014b0d0..8f76575 100644
--- a/krita/ui/CMakeLists.txt
+++ b/krita/ui/CMakeLists.txt
@@ -327,6 +327,7 @@ set(kritaui_LIB_SRCS
     qtsingleapplication/qtsingleapplication.cpp
 
     widgets/kis_url_requester.cpp
+    kis_md5_generator.cpp
 )
 
 if(WIN32)
@@ -469,7 +470,7 @@ if(HAVE_OPENGL)
 
 endif()
 
-target_link_libraries(kritaui LINK_INTERFACE_LIBRARIES kritaimage kritalibbrush ${KDE4_KIO_LIBS} ${GL_INTERFACE_LIBRARIES} )
+target_link_libraries(kritaui LINK_INTERFACE_LIBRARIES kritaimage kritalibbrush pigmentcms ${KDE4_KIO_LIBS} ${GL_INTERFACE_LIBRARIES} )
 
 set_target_properties(kritaui
     PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION}
diff --git a/krita/ui/kis_factory2.cc b/krita/ui/kis_factory2.cc
index 6e2fd9d..d778bdd 100644
--- a/krita/ui/kis_factory2.cc
+++ b/krita/ui/kis_factory2.cc
@@ -32,12 +32,16 @@
 #include <kstandarddirs.h>
 #include <kiconloader.h>
 
+#include <KoHashGeneratorProvider.h>
+
 #include <kis_debug.h>
 
 #include "kis_aboutdata.h"
 
 #include "KisPart.h"
 
+#include "kis_md5_generator.h"
+
 
 KAboutData* KisFactory::s_aboutData = 0;
 KComponentData* KisFactory::s_componentData = 0;
@@ -45,6 +49,7 @@ KComponentData* KisFactory::s_componentData = 0;
 KisFactory::KisFactory()
 {
     (void)componentData();
+    KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator());
 }
 
 KisFactory::~KisFactory()
diff --git a/krita/ui/kis_md5_generator.cpp b/krita/ui/kis_md5_generator.cpp
new file mode 100644
index 0000000..a168fd4
--- /dev/null
+++ b/krita/ui/kis_md5_generator.cpp
@@ -0,0 +1,62 @@
+/*
+ *  Copyright (c) 2015 Stefano Bonicatti <smjert at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "kis_md5_generator.h"
+
+#include <QDebug>
+#include <KoStore.h>
+
+KisMD5Generator::KisMD5Generator()
+{
+
+}
+
+KisMD5Generator::~KisMD5Generator()
+{
+
+}
+
+QByteArray KisMD5Generator::generateHash(QString filename)
+{
+    QByteArray ba;
+    if(filename.startsWith("bundle://")) {
+        QString bn = filename.mid(9);
+        QString fn = bn.mid(bn.indexOf(":") + 1);
+        bn = bn.left(bn.indexOf(":"));
+
+        QScopedPointer<KoStore> resourceStore(KoStore::createStore(bn, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
+        if (!resourceStore || resourceStore->bad()) {
+            qWarning() << "Could not open store on bundle" << bn;
+            return ba;
+        }
+
+        if (resourceStore->isOpen()) resourceStore->close();
+
+        if (!resourceStore->open(fn)) {
+            qWarning() << "Could not open preset" << fn << "in bundle" << bn;
+            return ba;
+        }
+
+        ba = resourceStore->device()->readAll();
+
+        resourceStore->close();
+        return KoMD5Generator::generateHash(ba);
+
+    }
+
+    return KoMD5Generator::generateHash(filename);
+}
diff --git a/krita/ui/kis_md5_generator.h b/krita/ui/kis_md5_generator.h
new file mode 100644
index 0000000..ba6a97d
--- /dev/null
+++ b/krita/ui/kis_md5_generator.h
@@ -0,0 +1,27 @@
+/*
+ *  Copyright (c) 2015 Stefano Bonicatti <smjert at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include <KoMD5Generator.h>
+
+class KisMD5Generator : public KoMD5Generator
+{
+public:
+    KisMD5Generator();
+    ~KisMD5Generator();
+
+    QByteArray generateHash(QString filename);
+};
diff --git a/krita/ui/kis_workspace_resource.cpp b/krita/ui/kis_workspace_resource.cpp
index b71dc49..eec1d01 100644
--- a/krita/ui/kis_workspace_resource.cpp
+++ b/krita/ui/kis_workspace_resource.cpp
@@ -22,7 +22,6 @@
 #include <QFile>
 #include <QDomDocument>
 #include <QTextStream>
-#include <QCryptographicHash>
 
 
 #define WORKSPACE_VERSION 1
@@ -66,6 +65,8 @@ bool KisWorkspaceResource::saveToDevice(QIODevice *dev) const
     QTextStream textStream(dev);
     doc.save(textStream, 4);
 
+    KoResource::saveToDevice(dev);
+
     return true;
 
 }
@@ -126,14 +127,3 @@ QByteArray KisWorkspaceResource::dockerState()
 {
     return m_dockerState;
 }
-
-QByteArray KisWorkspaceResource::generateMD5() const
-{
-    if (!m_dockerState.isEmpty()) {
-        QCryptographicHash md5(QCryptographicHash::Md5);
-        md5.addData(m_dockerState);
-        return md5.result();
-    }
-
-    return m_dockerState;
-}
diff --git a/krita/ui/kis_workspace_resource.h b/krita/ui/kis_workspace_resource.h
index aefc57c..93399fd 100644
--- a/krita/ui/kis_workspace_resource.h
+++ b/krita/ui/kis_workspace_resource.h
@@ -40,10 +40,6 @@ public:
     void setDockerState(const QByteArray& state);
     QByteArray dockerState();
 
-protected:
-
-    virtual QByteArray generateMD5() const;
-
 private:
     QByteArray m_dockerState;
 };
diff --git a/libs/pigment/CMakeLists.txt b/libs/pigment/CMakeLists.txt
index 4f54815..4d0828d 100644
--- a/libs/pigment/CMakeLists.txt
+++ b/libs/pigment/CMakeLists.txt
@@ -73,6 +73,8 @@ set(pigmentcms_SRCS
     resources/KoColorSet.cpp
     resources/KoPattern.cpp
     resources/KoResource.cpp
+    resources/KoMD5Generator.cpp
+    resources/KoHashGeneratorProvider.cpp
     resources/KoStopGradient.cpp
     resources/KoSegmentGradient.cpp
 )
diff --git a/libs/pigment/resources/KoAbstractGradient.cpp b/libs/pigment/resources/KoAbstractGradient.cpp
index 800a490..28223e7 100644
--- a/libs/pigment/resources/KoAbstractGradient.cpp
+++ b/libs/pigment/resources/KoAbstractGradient.cpp
@@ -23,7 +23,6 @@
 
 #include <KoColor.h>
 
-#include <QCryptographicHash>
 #include <QBuffer>
 #include <QByteArray>
 
@@ -145,11 +144,6 @@ QImage KoAbstractGradient::generatePreview(int width, int height) const
     return image;
 }
 
-QByteArray KoAbstractGradient::generateMD5() const
-{
-    return QByteArray();
-}
-
 void KoAbstractGradient::updatePreview()
 {
     setImage(generatePreview(PREVIEW_WIDTH, PREVIEW_HEIGHT));
diff --git a/libs/pigment/resources/KoAbstractGradient.h b/libs/pigment/resources/KoAbstractGradient.h
index 167be69..5c815cc 100644
--- a/libs/pigment/resources/KoAbstractGradient.h
+++ b/libs/pigment/resources/KoAbstractGradient.h
@@ -77,8 +77,6 @@ public:
     void updatePreview();
 
     QImage generatePreview(int width, int height) const;
-protected:
-    virtual QByteArray generateMD5() const;
 
     KoAbstractGradient(const KoAbstractGradient &rhs);
 
diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp
index 47d3c6b..64527ec 100644
--- a/libs/pigment/resources/KoColorSet.cpp
+++ b/libs/pigment/resources/KoColorSet.cpp
@@ -27,7 +27,6 @@
 #include <QFile>
 #include <QTextStream>
 #include <QFileInfo>
-#include <QCryptographicHash>
 #include <QBuffer>
 #include <QByteArray>
 #include <QPainter>
@@ -118,9 +117,6 @@ bool KoColorSet::loadFromDevice(QIODevice *dev)
 
     Q_ASSERT(m_data.size() != 0);
 
-    QCryptographicHash md5(QCryptographicHash::Md5);
-    md5.addData(m_data);
-    setMD5(md5.result());
     return init();
 }
 
@@ -141,16 +137,6 @@ qint32 KoColorSet::nColors()
     return m_colors.count();
 }
 
-QByteArray KoColorSet::generateMD5() const
-{
-    if (!m_data.isEmpty()) {
-        QCryptographicHash md5(QCryptographicHash::Md5);
-        md5.addData(m_data);
-        return md5.result();
-    }
-    return QByteArray();
-}
-
 bool KoColorSet::saveToDevice(QIODevice *dev) const
 {
     QTextStream stream(dev);
@@ -165,6 +151,9 @@ bool KoColorSet::saveToDevice(QIODevice *dev) const
         else
             stream << entry.name << "\n";
     }
+
+    KoResource::saveToDevice(dev);
+
     return true;
 }
 
diff --git a/libs/pigment/resources/KoColorSet.h b/libs/pigment/resources/KoColorSet.h
index ffd81b8..d74d513 100644
--- a/libs/pigment/resources/KoColorSet.h
+++ b/libs/pigment/resources/KoColorSet.h
@@ -85,10 +85,6 @@ public:
     KoColorSetEntry getColor(quint32 index);
     qint32 nColors();
 
-protected:
-
-    virtual QByteArray generateMD5() const;
-
 private:
 
 
diff --git a/libs/pigment/resources/KoHashGenerator.h b/libs/pigment/resources/KoHashGenerator.h
new file mode 100644
index 0000000..db12959
--- /dev/null
+++ b/libs/pigment/resources/KoHashGenerator.h
@@ -0,0 +1,30 @@
+/*
+ *  Copyright (c) 2015 Stefano Bonicatti <smjert at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef KOHASHGENERATOR_H
+#define KOHASHGENERATOR_H
+
+#include <QByteArray>
+
+class KoHashGenerator
+{
+public:
+    virtual QByteArray generateHash(QString filename) = 0;
+    virtual QByteArray generateHash(const QByteArray &array) = 0;
+    virtual ~KoHashGenerator(){}
+};
+#endif
diff --git a/libs/pigment/resources/KoHashGeneratorProvider.cpp b/libs/pigment/resources/KoHashGeneratorProvider.cpp
new file mode 100644
index 0000000..f058130
--- /dev/null
+++ b/libs/pigment/resources/KoHashGeneratorProvider.cpp
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2015 Stefano Bonicatti <smjert at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "KoHashGeneratorProvider.h"
+
+#include <QMutexLocker>
+
+#include "KoMD5Generator.h"
+
+KoHashGeneratorProvider *KoHashGeneratorProvider::instance_var = 0;
+
+KoHashGeneratorProvider::KoHashGeneratorProvider()
+{
+    // Initialize default generators
+    hashGenerators.insert("MD5", new KoMD5Generator());
+}
+
+KoHashGeneratorProvider::~KoHashGeneratorProvider()
+{
+    qDeleteAll(hashGenerators);
+}
+
+KoHashGenerator *KoHashGeneratorProvider::getGenerator(QString algorithm)
+{
+    QMutexLocker locker(&mutex);
+    return hashGenerators.value(algorithm);
+}
+
+void KoHashGeneratorProvider::setGenerator(QString algorithm, KoHashGenerator *generator)
+{
+    if(hashGenerators.contains(algorithm)) {
+        hashGenerators[algorithm] = generator;
+    }
+    else
+        hashGenerators.insert(algorithm, generator);
+}
+
+KoHashGeneratorProvider *KoHashGeneratorProvider::instance()
+{
+    if(!instance_var)
+        instance_var = new KoHashGeneratorProvider();
+
+    return instance_var;
+}
diff --git a/libs/pigment/resources/KoHashGeneratorProvider.h b/libs/pigment/resources/KoHashGeneratorProvider.h
new file mode 100644
index 0000000..95654ed
--- /dev/null
+++ b/libs/pigment/resources/KoHashGeneratorProvider.h
@@ -0,0 +1,43 @@
+/*
+ *  Copyright (c) 2015 Stefano Bonicatti <smjert at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef KOHASHGENERATORPROVIDER_H
+#define KOHASHGENERATORPROVIDER_H
+
+#include <QHash>
+#include <QMutex>
+
+#include <pigment_export.h>
+
+class KoHashGenerator;
+
+class PIGMENTCMS_EXPORT KoHashGeneratorProvider
+{
+public:
+    KoHashGeneratorProvider();
+    ~KoHashGeneratorProvider();
+
+    KoHashGenerator *getGenerator(QString algorithm);
+    void setGenerator(QString algorithm, KoHashGenerator *generator);
+    static KoHashGeneratorProvider *instance();
+private:
+    static KoHashGeneratorProvider *instance_var;
+    QHash<QString, KoHashGenerator *> hashGenerators;
+    QMutex mutex;
+};
+
+#endif
diff --git a/libs/pigment/resources/KoMD5Generator.cpp b/libs/pigment/resources/KoMD5Generator.cpp
new file mode 100644
index 0000000..e95c7b0
--- /dev/null
+++ b/libs/pigment/resources/KoMD5Generator.cpp
@@ -0,0 +1,56 @@
+/*
+ *  Copyright (c) 2015 Stefano Bonicatti <smjert at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "KoMD5Generator.h"
+
+#include <QIODevice>
+#include <QFile>
+#include <QCryptographicHash>
+
+KoMD5Generator::KoMD5Generator()
+{
+
+}
+
+KoMD5Generator::~KoMD5Generator()
+{
+
+}
+
+QByteArray KoMD5Generator::generateHash(const QByteArray &array)
+{
+    if (!array.isEmpty()) {
+        QCryptographicHash md5(QCryptographicHash::Md5);
+        md5.addData(array);
+        return md5.result();
+    }
+
+    return array;
+}
+
+QByteArray KoMD5Generator::generateHash(QString filename)
+{
+    QByteArray result;
+
+    QFile f(filename);
+    if (f.exists() && f.open(QIODevice::ReadOnly)) {
+        QByteArray ba = f.readAll();
+        result = generateHash(ba);
+    }
+
+    return result;
+}
diff --git a/libs/pigment/resources/KoMD5Generator.h b/libs/pigment/resources/KoMD5Generator.h
new file mode 100644
index 0000000..7086abd
--- /dev/null
+++ b/libs/pigment/resources/KoMD5Generator.h
@@ -0,0 +1,34 @@
+/*
+ *  Copyright (c) 2015 Stefano Bonicatti <smjert at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef KOMD5GENERATOR_H
+#define KOMD5GENERATOR_H
+
+#include "KoHashGenerator.h"
+
+#include <pigment_export.h>
+
+class PIGMENTCMS_EXPORT KoMD5Generator : public KoHashGenerator
+{
+public:
+    KoMD5Generator();
+    virtual ~KoMD5Generator();
+    virtual QByteArray generateHash(QString filename);
+    virtual QByteArray generateHash(const QByteArray &array);
+};
+
+#endif
diff --git a/libs/pigment/resources/KoPattern.cpp b/libs/pigment/resources/KoPattern.cpp
index 1c1f521..0d0f02a 100644
--- a/libs/pigment/resources/KoPattern.cpp
+++ b/libs/pigment/resources/KoPattern.cpp
@@ -28,7 +28,6 @@
 
 #include <QFileInfo>
 #include <QDir>
-#include <QCryptographicHash>
 #include <QPoint>
 #include <QSize>
 #include <QImage>
@@ -156,6 +155,8 @@ bool KoPattern::savePatToDevice(QIODevice* dev) const
     if (wrote == -1)
         return false;
 
+    KoResource::saveToDevice(dev);
+
     return true;
 }
 
@@ -392,21 +393,3 @@ QImage KoPattern::pattern() const
     return m_pattern;
 }
 
-QByteArray KoPattern::generateMD5() const
-{
-    if (!pattern().isNull()) {
-        QImage im = m_pattern.convertToFormat(QImage::Format_ARGB32);
-#if QT_VERSION >= 0x040700
-        QByteArray ba = QByteArray::fromRawData((const char*)im.constBits(), im.byteCount());
-#else
-        QByteArray ba = QByteArray::fromRawData((const char*)im.bits(), im.byteCount());
-#endif
-
-        QCryptographicHash md5(QCryptographicHash::Md5);
-        md5.addData(ba);
-
-        return md5.result();
-    }
-    return QByteArray();
-}
-
diff --git a/libs/pigment/resources/KoPattern.h b/libs/pigment/resources/KoPattern.h
index 31acba4..0430a05 100644
--- a/libs/pigment/resources/KoPattern.h
+++ b/libs/pigment/resources/KoPattern.h
@@ -64,10 +64,6 @@ public:
      */
     QImage pattern() const;
 
-protected:
-
-    virtual QByteArray generateMD5() const;
-
 private:
 
     bool init(QByteArray& data);
diff --git a/libs/pigment/resources/KoResource.cpp b/libs/pigment/resources/KoResource.cpp
index 2b568bf..ae228eb 100644
--- a/libs/pigment/resources/KoResource.cpp
+++ b/libs/pigment/resources/KoResource.cpp
@@ -24,6 +24,9 @@
 #include <QDebug>
 #include <QImage>
 
+#include "KoHashGenerator.h"
+#include "KoHashGeneratorProvider.h"
+
 struct KoResource::Private {
     QString name;
     QString filename;
@@ -52,6 +55,13 @@ KoResource::KoResource(const KoResource &rhs)
 {
 }
 
+bool KoResource::saveToDevice(QIODevice *dev) const
+{
+    d->md5 = QByteArray();
+
+    return true;
+}
+
 QImage KoResource::image() const
 {
     return d->image;
@@ -75,6 +85,12 @@ void KoResource::setMD5(const QByteArray &md5)
     d->md5 = md5;
 }
 
+QByteArray KoResource::generateMD5() const
+{
+    KoHashGenerator *hashGenerator = KoHashGeneratorProvider::instance()->getGenerator("MD5");
+    return hashGenerator->generateHash(d->filename);
+}
+
 QString KoResource::filename() const
 {
     return d->filename;
diff --git a/libs/pigment/resources/KoResource.h b/libs/pigment/resources/KoResource.h
index 53a18e2..1529dd1 100644
--- a/libs/pigment/resources/KoResource.h
+++ b/libs/pigment/resources/KoResource.h
@@ -63,7 +63,7 @@ public:
      *@return true if saving the resource succeeded.
      */
     virtual bool save() = 0;
-    virtual bool saveToDevice(QIODevice* dev) const = 0;
+    virtual bool saveToDevice(QIODevice* dev) const;
 
     /**
      * @returns a QImage thumbnail image representing this resource.
@@ -100,7 +100,7 @@ public:
 protected:
 
     /// override generateMD5 and in your resource subclass
-    virtual QByteArray generateMD5() const = 0;
+    virtual QByteArray generateMD5() const;
 
     /// call this when the contents of the resource change so the md5 needs to be recalculated
     void setMD5(const QByteArray &md5);
diff --git a/libs/pigment/resources/KoSegmentGradient.cpp b/libs/pigment/resources/KoSegmentGradient.cpp
index 5387743..2e3c18f 100644
--- a/libs/pigment/resources/KoSegmentGradient.cpp
+++ b/libs/pigment/resources/KoSegmentGradient.cpp
@@ -28,7 +28,6 @@
 #include <QImage>
 #include <QTextStream>
 #include <QFile>
-#include <QCryptographicHash>
 #include <QByteArray>
 #include <QBuffer>
 
@@ -92,10 +91,6 @@ bool KoSegmentGradient::loadFromDevice(QIODevice *dev)
 {
     QByteArray data = dev->readAll();
 
-    QCryptographicHash md5(QCryptographicHash::Md5);
-    md5.addData(data);
-    setMD5(md5.result());
-
     QTextStream fileContent(data, QIODevice::ReadOnly);
     fileContent.setAutoDetectUnicode(true);
 
@@ -227,6 +222,9 @@ bool KoSegmentGradient::saveToDevice(QIODevice *dev) const
 
         fileContent << (int)segment->interpolation() << " " << (int)segment->colorInterpolation() << "\n";
     }
+
+    KoResource::saveToDevice(dev);
+
     return true;
 }
 
@@ -273,22 +271,6 @@ QString KoSegmentGradient::defaultFileExtension() const
     return QString(".ggr");
 }
 
-QByteArray KoSegmentGradient::generateMD5() const
-{
-    QBuffer buffer;
-    buffer.open(QIODevice::WriteOnly);
-    saveToDevice(&buffer);
-    QByteArray ba = buffer.buffer();
-
-    if (!ba.isEmpty()) {
-        QCryptographicHash md5(QCryptographicHash::Md5);
-        md5.addData(ba);
-        return md5.result();
-    }
-
-    return QByteArray();
-}
-
 KoGradientSegment::KoGradientSegment(int interpolationType, int colorInterpolationType, qreal startOffset, qreal middleOffset, qreal endOffset, const KoColor& startColor, const KoColor& endColor)
 {
     m_interpolator = 0;
diff --git a/libs/pigment/resources/KoSegmentGradient.h b/libs/pigment/resources/KoSegmentGradient.h
index bdd6baa..9b4b7a9 100644
--- a/libs/pigment/resources/KoSegmentGradient.h
+++ b/libs/pigment/resources/KoSegmentGradient.h
@@ -405,11 +405,8 @@ public:
     const QList<KoGradientSegment *>& segments() const;
 
 protected:
-
     KoSegmentGradient(const KoSegmentGradient &rhs);
 
-    virtual QByteArray generateMD5() const;
-
     inline void pushSegment(KoGradientSegment* segment) {
         m_segments.push_back(segment);
     }
diff --git a/libs/pigment/resources/KoStopGradient.cpp b/libs/pigment/resources/KoStopGradient.cpp
index dc851da..e24ab53 100644
--- a/libs/pigment/resources/KoStopGradient.cpp
+++ b/libs/pigment/resources/KoStopGradient.cpp
@@ -26,7 +26,6 @@
 #include <QFile>
 #include <QDomDocument>
 #include <QBuffer>
-#include <QCryptographicHash>
 
 #include <klocale.h>
 #include <kdebug.h>
@@ -72,10 +71,6 @@ bool KoStopGradient::loadFromDevice(QIODevice *dev)
     }
     QByteArray ba = dev->readAll();
 
-    QCryptographicHash md5(QCryptographicHash::Md5);
-    md5.addData(ba);
-    setMD5(md5.result());
-
     QBuffer buf(&ba);
     if (strExt == ".kgr") {
         loadKarbonGradient(&buf);
@@ -656,22 +651,7 @@ bool KoStopGradient::saveToDevice(QIODevice *dev) const
     stream << "</linearGradient>" << endl;
     stream << "</svg>" << endl;
 
-    return true;
-}
-
-QByteArray KoStopGradient::generateMD5() const
-{
-    QByteArray ba;
-    QBuffer buf(&ba);
-    buf.open(QBuffer::WriteOnly);
-    saveToDevice(&buf);
-    buf.close();
-
-    if (!ba.isEmpty()) {
-        QCryptographicHash md5(QCryptographicHash::Md5);
-        md5.addData(ba);
-        return md5.result();
-    }
+    KoResource::saveToDevice(dev);
 
-    return ba;
+    return true;
 }
diff --git a/libs/pigment/resources/KoStopGradient.h b/libs/pigment/resources/KoStopGradient.h
index 2e1e3d4..c3cce99 100644
--- a/libs/pigment/resources/KoStopGradient.h
+++ b/libs/pigment/resources/KoStopGradient.h
@@ -64,8 +64,6 @@ public:
 
 protected:
 
-    virtual QByteArray generateMD5() const;
-
     QList<KoGradientStop> m_stops;
     QPointF m_start;
     QPointF m_stop;


More information about the kimageshop mailing list