Rationale

The libupnpp library, which provides the C++ interface for upmpdcli and upplay, used to rely on the venerable libupnp library (a.k.a pupnp: Portable UPnP) to provide the base UPnP protocol functionality.

Pupnp is very old and has suffered for a number of years from indifferent maintenance resulting in buggy releases being shipped by the Linux distributions (and endless trouble for me). For a few years now, I had to ship a patched version of an older release of the library with upmpdcli and upplay to work around the problem.

I did make an effort to help with a situation, and contributed a number of significant bug fixes to the package. See this, for example, or the Github pull requests. However, after a few years of suffering, I came to the conclusion that the situation was unsalvageable, and, at the beginning of 2020, I created a new library, named libnpupnp (new pupnp), based of the best and most specific parts of the old code. The changes:

  • The custom XML library (ixml) is not used any more (expat is used instead to parse XML when needed).

  • The HTTP server and client parts have respectively been replaced by libmicrohttpd and libcurl, two well-maintained and widely used libraries.

  • The retained code has been converted to C++, and most of the locally grown containers (lists etc.) have been replaced by STL objects.

  • Not even counting the ixml disparition (ixml was never a big issue), this brought the UPnP part of the library from around 24000 lines (C files only) to a manageable 13000 lines of C++, retaining the really UPnP-specific parts of the old library.

The current implementation has a C++ interface, because of the use of STL containers to replace XML DOM trees in a couple of entry points. It would be reasonably easy to add a pure C interface if someone saw a point to it. See the next section for a more detailed description of the changes.

Of course there is no chance that the new library is free of bugs, but I think that they will be easier to find and fix than previously.

In practise, the libupnpp layer has isolated the applications from these changes, and upmpdcli and upplay can still be built with libupnpp 0.17.x and libupnp. But the default is now to build the packages with libupnpp 0.18.x and libnpupnp.

New features

  • Support for providing service on multiple network interfaces.

Porting from PUPNP to NPUPNP

When creating the new library, the goal was to keep the API intact, as much as feasible.

However the complete suppression of libixml has forced changes where ixml elements were present in the interfaces (e.g. DOM trees). These elements have been replaced with C++ data elements.

There are relatively few changes, and the new code for interfacing NPUPNP is usually simpler than the old one (e.g. pass either an XML string or a C++ vector in place of a DOM tree).

Details follow.

Upnp_Action_Request.

struct Upnp_Action_Request (or its equivalent in libupnp 1.8) is used in the device interface to communicate an action request to the client code and retrieve the result. In libupnp, this structure has two IXML DOM tree members to transport the request and the response arguments. libnpupnp replaces these elements with vector<pair<string,string>>. The original XML request is also present in the structure, in case there are things in there which don’t make it into the args vector, and the client code can set an XML string in place of setting the response argument vector.

File_Info

The libupnp struct File_Info has a dynamically allocated ixml DOMString member for the content type value. In libupnp, this is replaced by an std::string.

libupnp has had a variety of ways for the client to set HTTP headers in the struct File_Info, so that the libupnp web server sets them in the HTTP response. libnpupnp uses request_headers and response_headers map and vector fields in the File_Info structure to communicate the request and response headers.

GENA methods

libupnp has an UpnpAcceptSubscriptionEx() call, which takes an IXML DOM tree as an argument to return the initial Property Set. libnpupnp replaces this with UpnpAcceptSubscriptionXML() which takes an XML std::string instead.

Same change for the UpnpNotifyEx() → UpnpNotifyXML() calls which are used to dispatch state change events.