Implementing A Cookiejar for QtWebKit; QNetworkCookieJar Analysis

February 24, 2012 Leave a comment

As some of you may know already, I am working on the WildFox browser project which while it initially was going to fork the Mozilla code is now building a browser on top of QtWebKit. See the previous blog post for details on this decision. The WildFox project page and source can be found at www.mayaposch.com/wildfox.php.

One of the features I recently implemented was an advanced cookiejar for storing HTTP cookies. Why was this necessary, you may ask? QtWebKit does implement a cookiejar in QNetworkCookieJar, but even aside from the inability to save any of the cookies to disk, a quick look at its source code shows the following issues: a limit of 50 cookies, which is less than the 300 required by the current standard for HTTP cookies (RFC 2617) [1]. It also uses a basic QList to store the cookies, which requires it to search in linear time through every cookie to find ones for a specific URL and duplicate cookies when storing them.

In other words, the default implementation is unsuitable for any web browser. One thing it does do right, however, is the way it verifies domains. Due to the design of internet Top Level Domains (TLDs) it is impossible to algorithmically determine whether an internet URL is valid, or specifies a proper TLD.

The need to verify the domain is made clear when one imagines someone setting a cookie for the domain .com, which would then be a cookie valid for every website ending with the TLD .com. Obviously this can’t be allowed and the obvious approach would be to disallow single dot domains (.com, .org, .net). This doesn’t work for domains like .co.uk, however. Disallowing two dot domains would cause issues with the former, single dot type. Further there are more variations on this, such as URLs in the US where the public suffix can entail city.state.us style domains. Clearly the only way to do this verification is to use a look-up table. This can be found in Mozilla’s public suffix list [2].

What we need for a better QtWebKit cookiejar thus entails the following:

  • the ability to store cookies to disk.
  • storing at least 300 cookies.
  • quick look-ups of cookies based on their domain.

For this we recycle the existing functionality in Qt required to do the public suffix verification. The relevant files in Qt 4.8.0 are:

  • src/corelib/io/qtldurl.cpp
  • src/qurltlds_p.h

The former contains some basic routines to obtain the public suffix which we will expand upon and the latter contains the Public Suffix list processed in a more accessible format. The latter we’ll use almost as-is, with just the Qt namespace sections removed. The former has a major omission we’ll add. The functions we’ll keep from qtldurl.cpp are in renamed form:

  • containsTLDEntry(const QString &entry)
  • isEffectiveTLD(const QString &domain)
  • topLevelDomain(const QString &domain)

We add the following function:

QString getPublicDomain(const QString &domain) {
    QStringList sections = domain.toLower().split(QLatin1Char('.'), QString::SkipEmptyParts);
    if (sections.isEmpty())
        return QString();

    QString tld = "";
    for (int i = sections.count() - 1; i >= 0; --i) {
        tld.prepend(QLatin1Char('.') + sections.at(i));
        if (!isEffectiveTLD(tld.right(tld.size() - 1))) {
             return tld;
        }
    }

    return tld;
}

This allows us to obtain the public suffix plus the initial non-public (TLD) domain. For example, “http://www.slashdot.org” would be reduced to “.slashdot.org”. It is different from topLevelDomain() in that the latter would return just the public suffix, e.g. “.org” in the previous example, which is not desirable for our use.

With the domain verification taken care of, we move on to the next stage, which involves the data structure and storage method. To store cookies on disk we elect to use an SQLite database, as this is both efficient in terms of storage density, but also prevents disk fragmentation and allows for SQL-based look-ups instead of filesystem-based ones, as used to be common with older browsers. QtSQL comes with an SQLite driver. Do be sure to use the current version of Qt (4.8) as recently SQLite introduced journaling and the Qt 4.7.x libraries still use the old SQLite client.

For the in-memory data structure we use a QMultiMap. The rationale behind this is the key-based look-up based on the cookie domain. By taking the URL we’re seeking matching cookies for and obtaining its top domain (“.slashdot.org”) we can find any cookie in our data structure using this top domain as the key. This means we can search a large number of cookies in logarithmic (O(log N)) time for a match on the domain, a major improvement on the linear (O(N)) search of the default QList.

The link between the in-memory and on-disk storage is accomplished by the following rules:

  • All new cookies and cookie updates are stored in both in-memory and on-disk, except for session cookies, which are stored only in-memory.
  • Stored cookies are read into memory per-domain and on-demand.

In addition to this I have implemented a cookie manager dialogue which allows one to look through and manage (delete) stored cookies. Expired cookies are automatically deleted the first time they are fetched from the database or before they’re stored. Blocking 3rd-party cookies is also very easy, with a comparison between the top domain and the cookie’s intended domain:

QString baseDomain = getPublicDomain(url.host());
if (skip3rd && (baseDomain != getPublicDomain(cookie.domain()))) {
     continue;
}

With this we got a relatively efficient cookie storage and retrieval mechanism with the ability to manage the set cookies. It can store an unlimited number of cookies and should remain efficient with look-ups even with over 10,000 cookies thanks to the logarithmic search of the QMultiMap.

Essential features still missing in the WildFox browser at this point are bookmarks and sessions. The next article on WildFox should be about the Chrome extension support I’m currently implementing, with as direct result XMarks bookmark synchronization support as well as the bookmarks feature. Stay tuned.

Maya

[1] http://www.ietf.org/rfc/rfc2617.txt
[2] http://publicsuffix.org/

Categories: HTTP, programming, Projects, Qt, WildFox Tags: , , ,

Surviving The Mozilla Build System, A Brief Guide

November 26, 2011 Leave a comment

Last year I did a couple of interviews for The Register and other sites regarding my WildFox project which in essence had the goal to add h.264 video support to Firefox using the GStreamer backend or FFmpeg. Due to circumstances I didn’t manage to do significant work on this project until a few months ago when I finally began the real modifications to the Firefox source.

As this article isn’t about the quality of the Mozilla source code, or the lack thereof, I won’t dwell on it too long. Suffice it to say that I found a lot of instances of NIH (‘Not Invented Here’) syndrome including the networking, smart pointer and threading sections. As my goal was to add Libav (recent fork of FFmpeg) support to the media backend of Firefox I became intimately familiar with these APIs as I discovered just how much of the code would be ripped out without causing adverse effects, and that the smart pointers do not work with anything but NSISupports-derived types.

Anyway, the build system… at first glance the Mozilla build system seems to use Makefiles, that is until you notice the .in extension indicating that they’re autoconfig templates. Or autobreak as lovingly called by a large section of the internet. After much trial and error I discovered that after putting my new Libav decoder & reader into /content/media/libav and the Libav includes into /media/libav of the source tree, creating a single Makefile with EXPORTS and an individual Makefile for each library of Libav (libavformat, libavcodec, etc.), I still had to edit a host of files to make it all work:

/layout/build
    Makefile.in

/toolkit
toolkit-makefiles.sh
    toolkit-tiers.mk

/content/media
    Makefile.in

After following the hints in the Mozilla documentation [1] I first discovered the files in /toolkit, and things finally began to work. Until I hit a few snags, like having to add a compiler flag to CXXFLAGS but there being no way to specify this in the Makefiles which didn’t get ignored for some reason. Libav is a C99 project and requires -D__STDC_CONSTANT_MACROS to be added to CXXFLAGS [2] to make it play nice with a C++ project. In the end I put this flag directly into the CXXFLAGS definition in the top of configure.in in the root folder. Ugly but it works.

At this point everything builds, the only thing I’m still stuck on is how to add the Libav’s LIB files to the linker flags. As usual the methods I have found do not work and even adding it to configure.in didn’t seem to do the trick.

To be quite honest I’m ready to give up on improving the Mozilla source. The changes required to bring it up to speed with proper project standards are just too daunting and severe to be handled just by me. As a fun comparison I started the WildFox-Mimic project a few days ago to investigate what it’d take to create a browser which looks and feels like Firefox, but uses Qt and the QtWebKit engine. The result is a modern, up to date browser with an HTML 5 video/audio backend which uses Qt’s Phonon which wraps around the OS’s media framework, be it DirectShow, GStreamer or something else. In other words it’s perfect. QtWebKit can also use the same NPAPI plugins Firefox uses, so Flash support is available out of the box. The Persona themes and JetPack add-ons can also be supported.

The result with WildFox-Mimic would be a browser with a codebase a fraction the size of the Firefox one, with most of the development and maintenance performed by the Qt and QtWebKit maintainers. This is the direction I think I’ll be heading towards.

Maya

[1] https://developer.mozilla.org/en/Adding_Files_to_the_Build
[2] http://libav.org/faq.html#I_0027m-using-libavutil-from-within-my-C_002b_002b-application-but-the-compiler-complains-about-_0027UINT64_005fC_0027-was-not-declared-in-this-scope

Categories: Projects, Qt, WildFox

Sharing Data Even More Universally; UDS Open Sourced

November 22, 2011 Leave a comment

A while ago (September 27th this year, to be precise) I announced[1] the Universal Data Share (UDS) project: a small utility using which people can share files with others without the need for a server or complicated software installs.

Since that time I have been testing the application with some friends and added a lot of bug fixes as well as some features, including:

  • Chunked downloading, to allow larger files to be transferred without the application dying on itself.
  • Downloads tab, with progress indicators and download folder.
  • Filesizes of the remote files reported in the client.
  • Much improved protocol.

The project page has been online for a while now [2]. You can find the most up to date binaries, GitHub link and protocol details there. Yes, that’s a GitHub link, as in the source being freely available. While I haven’t attached a formal license to it, I’d much appreciate it if people didn’t use significant parts of the source without attribution and send me any bug fixes or feature additions they may have produced.

Also important to note that the software is now essentially Beta-level quality, as in that it is feature-complete and relatively stable. Large-scale testing still has to be performed, naturally, and that’s where you people come into play :)

Maya

[1] http://mayaposch.wordpress.com/2011/09/27/qt-and-custom-networking-protocols-or-the-holy-grail-of-the-internet/
[2] http://www.mayaposch.com/uds.php

Categories: Projects, UDS Tags:

How To Really, Truly Use QThreads; The Full Explanation

November 1, 2011 8 comments

Threads in an operating system are a very simple thing. Write a function, maybe bundle it with some data and push it onto a newly created thread. Use a mutex or other method to safely communicate with the thread if necessary. Whether it are Win32, POSIX or other threads, they all basically work the same and are quite fool-proof. I’d venture to say that they’re at least a lot easier to use and handle than sockets :)

Those who have discovered the joys of the Qt framework may assume that threads in Qt (QThread) are just like this, but they would be both wrong and right. Wrong because years of wrong documentation from Trolltech/Nokia on QThread has caused countless people to use QThreads in a convoluted and highly inappropriate manner. Right because QThreads are in fact quite easy to use, as long as you ignore the incorrect official Qt documentation on QThread [1] and the myriad of wrongful methods being employed.

The main thing to keep in mind when using a QThread is that it’s not a thread. It’s a wrapper around a thread object. This wrapper provides the signals, slots and methods to easily use the thread object within a Qt project. This should immediately show why the recommended way of using QThreads in the documentation, namely to sub-class it and implement your own run() function, is very wrong. A QThread should be used much like a regular thread instance: prepare an object (QObject) class with all your desired functionality in it. Then create a new QThread instance, push the QObject onto it using moveToThread(QThread*) of the QObject instance and call start() on the QThread instance. That’s all. You set up the proper signal/slot connections to make it quit properly and such, and that’s all.

For a basic example, check this class declaration for the Worker class:

class Worker : public QObject {
    Q_OBJECT

public:
    Worker();
    ~Worker();

public slots:
    void process();

signals:
    void finished();
    void error(QString err);

private:
    // add your variables here
};

We add at least one public slot which will be used to trigger the instance and make it start processing data once the thread has started. Now, let’s see what the implementation for this basic class looks like.

// --- CONSTRUCTOR ---
Worker::Worker() {
    // you could copy data from constructor arguments to internal variables here.
}

// --- DECONSTRUCTOR ---
Worker::~Worker() {
    // free resources
}

// --- PROCESS ---
// Start processing data.
void Worker::process() {
    // allocate resources using new here
    qDebug("Hello World!");
    emit finished();
}

While this Worker class doesn’t do anything special, it nevertheless contains all the required elements. It starts processing when its main function, in this case process(), is called and when it is done it emits the signal finished() which will then be used to trigger the shutdown of the QThread instance it is contained in.

By the way, one extremely thing to note here is that you should NEVER allocate heap objects (using new) in the constructor of the QObject class as this allocation is then performed on the main thread and not on the new QThread instance, meaning that the newly created object is then owned by the main thread and not the QThread instance. This will make your code fail to work. Instead, allocate such resources in the main function slot such as process() in this case as when that is called the object will be on the new thread instance and thus it will own the resource.

Now, let’s see how to use this new construction by creating a new Worker instance and putting it on a QThread instance:

QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();

The connect() series here is the most crucial part. The first connect() line hooks up the error message signal from the worker to an error processing function in the main thread. The second connects the thread’s started() signal to the processing() slot in the worker, causing it to start.

Then the clean-up: when the worker instance emits finished(), as we did in the example, it will signal the thread to quit, i.e. shut down. We then mark the worker instance using the same finished() signal for deletion. Finally, to prevent nasty crashes because the thread hasn’t fully shut down yet when it is deleted, we connect the finished() of the thread (not the worker!) to its own deleteLater() slot. This will cause the thread to be deleted only after it has fully shut down.

I hope this small tutorial is of some use to people out there. It has taken me months to put together this method of doing things involving countless frustrating hours of debugging and Google searches which kept repeating the same wrong mantra of ‘sub-class QThread’. Constructive feedback on this tutorial is much appreciated. Emails to Nokia on the wrong QThreads documentation might be a good idea too as I would love for the myths surrounding QThread to finally be erased from this world :)

Maya

[1] http://doc.trolltech.com/4.7/qthread.html

Design Your Own Protocol In Five Minutes

October 3, 2011 Leave a comment

Among the most scary and official sounding terms in computing we find the word ‘protocol’. Its meaning really isn’t that scary, however. Just like when used in other contexts, all it means is a collection of agreements about how to go about something. In this case we’re talking about communication protocols, protocols which allow two or more devices and/or applications to communicate with each other.

Much like how humans have developed their own communication protocols, basically. We also do a handshake part during which we initialize the connection, whether it’s by smiling at each other, remarking on the beautiful/terrible weather or asking after something specific, depending on whether there was previous contact or not. Possible failure modes include getting ignored (Server Time-out), getting slapped in the face after a failed pick-up line (Connection Closed By Host) or interrupted by the girl’s muscular boyfriend (Connection Reset By Peer), as well as addressing the wrong person (Connection Denied).

After successfully establishing the connection, information is exchanged. For humans both during handshake and communication the form used for information exchange is a so-called language, a rather organic and informal set of syllables which when put into the right order (‘spelling’ and ‘grammar’) can be used to evoke understanding in the receiving party. To even get to this level, humans needed tens of thousands of years to evolve a series of grunts and other random noises into something coherent. Suffice it to say that human communication protocols are elaborate, imprecise, filled with misunderstandings and are a clear example of how not to design a communication protocol :)

Finally, ending the connection. Again, for humans this can take many forms, generally fails to result in a clean termination and can add many more minutes to a connection. Aren’t we glad now that we are designing a communication protocol for computers?

All joking aside, designing a communication protocol is fairly easy. The first choice we have to make is whether we want the protocol to be binary or text-based. Text-based protocols include the HTTP protocol, which is what we use to browse webpages with. Main benefit of it is that it’s easy for humans to write it out and debug it. Main disadvantage is that it’s less precise and exact in that generally you can’t parse it in one go, can’t instantly verify that it is valid as a whole and using the wrong text encoding can mess things up quite badly. You’ll quickly find that it’s a cumbersome and error-prone way to go about a communication protocol. It’s no wonder that they’re fairly rarely used, mostly with network applications for some reason.

Text-based protocols have the benefit of not being affected by endianness [1], which is the byte order used by a particular system. Little endian is what Intel and AMD processors use and mean that the least important (little) bits are placed at the front of a byte, while big endian is the opposite. This means that if we take the number 14 (hexidecimal 0x0E), in little endian a resulting four-byte integer looks like this: 0E 00 00 00, whereas with big endian it looks like: 00 00 00 0E. Confusing little endian with big or the other way around will lead to interpreting the number wrongly and making our small number of 14 into a much larger number of 917,504. Oops.

To solve this problem with binary protocols which might be used in mixed endian environments, we add a magic number to the front of the header, usually two bytes with known values. By reading those we know which endianness the data is in. One example is using ‘MM’ like in TIFF file headers to indicate big endian (MSB) and ‘ll’ to indicate little endian (LSB) byte order. We can then enter a different parsing routine, or swap the byte order while parsing.

Writing out the protocol itself is a fairly easy and in my experience fun task, but I may just be a tad crazy. It is made easiest when you know what the requirements for the protocol are, but in general we start with the endianness indicator if needed, then one or more indicators identifying the header as being what is expected. I generally use the name of my company followed by the protocol name. After that the data follows. Sections within the data have their own text headers to detect corruption. Where offsets aren’t fixed such as with text strings, an unsigned integer precedes the data to indicate the length of the segment.

The basic protocol thus looks like follows:

ll/MM        uint8(2)
size        uint32
NYANKO        uint8(6)
UDS            uint8(3)
command        uint8(4)

To send a UDS protocol ‘LIST’ command to the server, we would use the following code, this one using a QByteArray:

QByteArray data;
data = "ll";
quint32 size = 19;
for (int i = sizeof(size); i > 0 ; --i) {
data.append((size >> (i * 8)) & 0xFF);
}

data += "NYANKOUDS";
data += "LIST";

Did I mention yet that bitwise operators are important? :) When dealing with low-level interactions such as communication protocols, they are invaluable and one’d do well to study them. Finally, I’d like to comment on the ‘size’ variable used. With network protocols it’s hard for the receiving socket to know when the end of the data has been reached. Putting the size of the whole data at the front of the header like this allows it to know exactly how much data still has to be received, when the data end has been reached and when a download is incomplete.

Parsing the protocol is essentially the opposite of putting it together. It’s done in a linear fashion, with checks for every value read. If done right it’s very robust and quite fool-proof.

Anyway, these are the basics of putting a communication protocol together. It’s very easy, absolutely not scary and even pretty fun :) Go give it a try some time.

Maya

[1] http://en.wikipedia.org/wiki/Endianness

Categories: programming, Protocols

Qt And Custom Networking Protocols, Or: The Holy Grail Of The Internet

September 27, 2011 3 comments

In the previous installment I talked about combining UPnP’s IGD features with Qt. This culminated for me in a few days of programming and debugging in order to get a functional program working.

Let’s be short about one thing: if you intend to use Qt with custom networking protocols, meaning something other than the standard HTTP, FTP and other such generic protocols, be aware that you shouldn’t use anything more of Qt than QTcpSocket and QTcpServer. QNetworkAccessManager doesn’t work unless you want to extend its range of protocols, while it’s easier to implement the protocol outside it. Avoid QByteArrays for received data and go as low-level as possible. Unsigned integers in Qt, such as quint8 are excellent to use with a standard C/C++ array. It makes data conversion so much easier, assuming you’re using a binary protocol, like I was.

Also brush up on your bitwise operators. You’ll need to know your way with the & AND operator for masking and the << and >> bitwise shifts, such as when converting an integer to bytes for adding them to an array:

QByteArray output;
for (quint32 i = sizeof(ourInt); i > 0; --i) {
    output.append((ourInt >> ((i - 1) * 8)) & 0xFF);
}

Earlier today I finished the first alpha build of the application. It was inspired by a recent XKCD comic:

File transfers

Why not have a basic application which allows one to just transfer files without any accounts, messing with NAT router forwardings, firewalls, setting up servers and other unpleasantness? With all of the raw power available, why can’t we just give someone a link (URI/URL) which allows that person to download a file straight from our own PC? It was the original concept behind the World Wide Web after all.

May I present to you, as the first stage in making this dream a reality, the Universal Data Share (UDS) application:

UDS

For those who want to give it a try, it’s available from here, as a Windows .exe: http://www.mayaposch.com/downloads/Universal_Data_Share.exe. It’s statically compiled against the Qt libraries, so no DLLs are required. UPX was used to compress the EXE (10 MB down to 3.25 MB).

Quick usage tutorial: after starting, go to Shares -> Go Online. This initiates the UPnP IGD discovery and port mapping. Port 11310 is hard-coded in this version. Under File you can add files to the local list. Under Remote you can copy the global (external) IP to the clipboard, which will then allow another person also running UDS to connect to you and obtain a list of your shares. These can then be downloaded via a menu option as well.

In the future I’d like to add URL-based downloads, maybe even add HTTP support so that it can act like a web server, allowing anyone with a browser to connect to it. Anyway, give it a whirl and let me know how it works for you. Feature requests are also quite welcome.

For the usual disclaimer, I’ll just say that this Alpha-level software. I have tested it for the past two days on my Windows 7 Enterprise system via the loopback network. The port mapping was not directly tested and may have issues with some NAT routers. I can not take responsibility for any damage which may be caused to routers, computers, files or other hardware or software. Use of this experimental software is completely at your own risk.

Now go have fun :)

Maya

Categories: programming, Qt, Software, UPnP

Setting Up MiniUPnPc With Qt

September 18, 2011 5 comments

Recently I decided to give a feature of Universal Plug ‘n’ Play (UPnP) a whirl: Internet Gateway Device protocol (IGD) [1]. This is a feature which has become commonplace in network devices, specifically routers. Previously one would have to manually set port forwardings in the router’s administration panel in order to allow programs outside the LAN to talk to programs inside it. With UPnP’s IGD functionality the LAN-based program can now take care of those port mappings/forwardings itself. Many games, chat and other programs already use it. One could say that it is now an essential skill for anyone doing serious network programming.

If you need more than just IGD, you can go with one of the more expansive SDKs [2], but otherwise you should be perfectly happy with the extremely light-weight and easy to use MiniUPnPc library [3] which implements the IGD client functionality in ANSI C. This allows it to be used with a variety of languages, in this particular case C++.

The popular C++-based framework Qt doesn’t have native support for UPnP, thus one has to use an external library to add such support. I am currently using MiniUPnPc 1.6 with the Qt 4.7.4 libraries using the MinGW compiler.

Compiling MiniUPnPC is a quick and painless process. On Windows it’s as easy as executing the provided BAT file with MinGW in the PATH and within ten seconds you are left with an .a and .lib library. There’s also MSVC project support if you want to use MSVC, although I haven’t tested it yet.

At this point we can add the first bits of IGD support to our application. Most crucial is adding these parts to the top of the source file:

#define STATICLIB
#include <miniupnpc.h>

Next we have to initialize the network socket library, in this case Winsock2:

WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(nResult != NO_ERROR) {
    QMessageBox::critical(this, "Error", "WSAStartup() failed.");
    return;
}

With that out of the way, we can verify that all is working using the simple demo code below:

UPNPDev* devlist;
UPNPUrls urls;
IGDdatas data;
char lanaddr[64]; // IP address on the LAN
const char* multicastif = 0;
const char* minissdpdpath = 0;
int error;
devlist = upnpDiscover(1000, multicastif, minissdpdpath, 0, 0, &error);
if (error > 0) {
    QMessageBox::critical(this, "Error", "UPnP discovery failed: " + QString::number(error));
}
else {
    error = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
    freeUPNPDevlist(devlist);
    if (error == 0) {
        QMessageBox::critical(this, tr("Devices"), tr("No IGD found."));
    }
    else if (error == 1) {
        QMessageBox::critical(this, tr("Devices"), tr("A valid connected IGD has been found."));
    }
    else if (error == 2) {
        QMessageBox::critical(this, tr("Devices"), tr("A valid IGD has been found, but it reported as not connected."));
    }
    else if (error == 3) {
        QMessageBox::critical(this, tr("Devices"), tr("An UPnP device was found, but wasn't recognized as an IGD."));
    }
}

As defined in the source code, the error codes for the upnpDiscover() function are as follows:

#define UPNPDISCOVER_SUCCESS (0)
#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
#define UPNPDISCOVER_SOCKET_ERROR (-101)
#define UPNPDISCOVER_MEMORY_ERROR (-102)

Now, to get it all to compile so that we can test it. In our Qt project we need to add a few items to the Pro file. First, depending on whether the library file we intend to use is already in a known location (MinGW lib folder or Qt SDK’s lib folder) we may have to add its location using

LIBS += D:/dev/miniupnpc/miniupnpc.a

Personally I prefer to put the library files I use a lot in projects directly into the compiler’s lib folder. This means I only have to add the following to the Pro file for it:

LIBS += -lminiupnpc -liphlpapi

Note hereby that the library itself is called ‘libminiupnpc.a’, but we prefix with -l and omit the ‘lib’ and extension. Finally we must include the libiphlpapi.a library as it contains a number of functions used by MiniUPnPc’s network functionality.

At this point we should be able to compile the project and have it run successfully. If there’s a device on the network (router) with IGD (UPnP) enabled, it will respond to the discovery broadcast and be identified. You can verify the output using the commandline test application provided with MiniUPnPc ‘upnpc-static.exe’. Detailed sample code can be found in upnpc.c, which is the source for the test application. There isn’t much available in terms of documentation, but with some puzzling and liberal use of said sample source code, it shouldn’t be too hard to figure out how to do something.

Until next time,

Maya

[1] http://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol
[2] http://upnp.org/sdcps-and-certification/resources/sdks/
[3] http://miniupnp.free.fr/

Categories: MiniUPnP, programming, Qt, UPnP
Follow

Get every new post delivered to your Inbox.

Join 481 other followers