Skip navigation.
KDE Developer's Journals

Using KNewStuffSecure

amantia's picture

As a follow-up to my last blog, here comes some detail about how to use KNewStuffSecure in your application. Shortly again, the idea behind it is to provide a way to upload and download digitaly signed resources, thus making it possible for the user to check the real
source as well as the integrity of the downloaded resources on one side, and
on the other side it allows automatization for processing the uploaded resources
on the server side.

Using KNewStuff itself is quite simple, and I tried to make KNewStuffSecure not
more complicated. The only extra requirement is to have gpg installed in your
path, and for uploading of course it is preferred if you already have a GPG key
that you can use for resource signing.

The first thing you must to is to subclass the KNewStuffSecure class and implement the installResource method, which is a pure virtual method in KNewStuffSecure. Here is an example of what you should put in a header file:

#include <knewstuff/knewstuffsecure.h>
class MyNewStuff: public KNewStuffSecure
{
  Q_OBJECT
 
public:
  MyNewStuff(const QString &type,  QWidget *parentWidget=0)
  :KNewStuffSecure(type, parentWidget){};
  ~MyNewStuff() {};
 
private:
  virtual void installResource();
};

In order to know what you should do in installResource() it is important to understand the structure of the resource you get via KNewStuffSecure. The resource is a gzipped tarball, let's call it resource.tar.gz. The resource.tar.gz contains three files:
data.tar.gz : another tarball containing the actual data to be installed. The
name is not fixed, it can be anything like cards.tgz,
greetings.tar.gz or whatever.
signature : holds the signature for data.tar.gz
md5sum: holds the MD5 sum for the data.tar.gz

In the implementation file you have to process the data.tar.gz only, the rest is
handled by KNewStuffSecure. If installing the resource means that you put the
downloaded file(s) from data.tar.gz into a directory, the implementation of
MyNewStuff looks like:

void MyNewStuff::installResource()
{
  bool ok = true;
  KTar tar(m_tarName, "application/x-gzip" );
  if (tar.open(IO_ReadOnly))
  {
    const KArchiveDirectory *directory = tar.directory();
    QString resDir =KGlobal::dirs()->saveLocation("data" ) + "appname/stuff/";
    directory->copyTo(resDir, true);
    tar.close();
  } else
  ok = false;
 
  if (!ok)
    KMessageBox::error(parentWidget(), i18n("There was an error with the downloaded resource tarball file. Possible causes are damaged archive or invalid directory structure in the archive." ), i18n("Resource Installation Error" ));
}

As you can see the name of the resource tarball you have to install is in
"m_tarName". The above code installs the files from m_tarName to
$KDEHOME/share/appname/stuff. Of course, you must provice the real appname there.

You are free to do other installation methods, depending on your needs. In some
cases it may be just enough to copy the resource tarball somewhere. This part
of the code depends completely on the type of the resource and your application.

Now how to initiate a download? You have to do three things:
- create a MyNewStuff object
- connect the signal installFinished() to a slot to do things what you want after the install is done
- call downloadResource() for the MyNewStuff object.

Example:

void MyApp::slotDownloadResource()
{
  if (!m_newStuff)
  {
    m_newStuff = new MyNewStuff("appname/resourcetype", this);
    connect(m_newStuff, SIGNAL(installFinished()), this, SLOT(slotResourceInstalled()));
  }
  m_newStuff->downloadResource();
}

Just a note: "appname/resourcetype" is in free form, it identifies the type of the resource. See the standard KNewStuff documentation for details.

That's all for downloading. Uploading is simple as well. You just have to create
the data.tar.gz (which is specific for your application) and call uploadResource(fileName), where fileName points to the created data tarball.
Example:

void MyApp::slotUploadResource()
{
  QString fileName = createUploadResource();
  if (!m_newStuff)
    m_newStuff = new MyNewStuff("application/resourcetype", this);
    m_newStuff->uploadResource(fileName);
  }
}

Here createUploadResource() creates the data tarball and returns the name with path to the created tarball.

That's all. I won't cover here the details about KNewStuff itself or what you should and can do on server side, for that read the KNewStuff API documentation
and see

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
amantia's picture

Tutorial page

The tutorial can be found on developer.kde.org.

richard dale's picture

Getters and setters for 'm_tarName'

Thanks for the writing the tutorial - maybe put it somewhere permanently. It makes so much difference if you can see example code and explanation, and don't have to puzzle it all out from the headers.

Wouldn't it be better to have KNewStuffSecure::tarName() and setTarName() methods instead of exposing the 'm_tarName' instance variable in the public api? It means that this class couldn't be used by the ruby bindings without special casing/futzing around, as they only normally work with method calls and enums, not instance variables. And a name like 'm_tarName' just looks dead wrong in ruby Sad

aseigo's picture

or a signal?

how about adding a signal that was emitted when the resource was available for installation .. something like "void resourceReceived(const QString&);" then the default implementation of installResource() could simply be { emit resourceReceived(m_tarName); } ...

this would allow usage of KNewStuffSecure without subclassing, while apps that needed something more sophisticated would still subclass away.

amantia's picture

Re: or a signal?

The problem is that there is a need to do some cleanup after the installResource finishes, like deleting the tarball file that the you just installed. If we use the signal method the user (programmer) must delete the temporary files or at least must call a KNewStuffSecure method to cleanup. I can imagine that many will forget about it and the downloaded resources will remain in /tmp forever (or until the system clears it).

aseigo's picture

tie it to knss lifetime?

why not just tie it to the lifetime of the KHotNewStuffSecure object? when KHNSS gets deleted, it cleans up after itself, so the forgetful programmer is covered.

then provide a KHotNewStuffSecure::contentInstalled() slot which can be called to delete it immediately, for the conscientious developer.

amantia's picture

It makes sense

But I'm not sure this is possible to do in 3.4.x.

aseigo's picture

should be BC

the changes should be binary compatible ... no new virtual methods, no new members, not removing any virtual methods... ?

i guess it depends on whether it's a bug or not though... which it probably isn't. just less convenient. so how about for 3.5? =)

sebien's picture

I agree

It would be much in the spirit of Qt programming to not oblige programers to subclass KNewStuffSecure and to not use member variable directly!

100% ok for th emodifications mentionned above.

amantia's picture

Re: Getters and setters for 'm_tarName'

I may put this onto developers.kde.org.
Regarding the getter method, yes I think that makes sense. It is visible in such cases that KNewStuffSecure was not written by start to be public, but a class inside Quanta... I will add a getter in 3.4.1, as I think this will be BC. A setter method is not needed though, as the filename is set internally in KNewStuffSecure and the user should not change it.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.