Update media-service-upnp to version 0.3.0 ( ca17a69 )
authorMark Ryan <mark.d.ryan@intel.com>
Wed, 14 Nov 2012 10:23:12 +0000 (11:23 +0100)
committerMark Ryan <mark.d.ryan@intel.com>
Wed, 14 Nov 2012 10:35:34 +0000 (11:35 +0100)
29 files changed:
AUTHORS
ChangeLog
Makefile.am
Makefile.in
aclocal.m4
build-aux/compile
build-aux/depcomp
configure
configure.ac
m4/compiler-flags.m4
packaging/media-service-upnp.changes
packaging/media-service-upnp.spec
src/async.c
src/async.h
src/chain-task.c [new file with mode: 0644]
src/chain-task.h [new file with mode: 0644]
src/client.h [new file with mode: 0644]
src/device.c
src/device.h
src/interface.h
src/media-service-upnp.c
src/props.c
src/props.h
src/settings.c
src/task.c
src/task.h
src/upnp.c
src/upnp.h
test/mediaconsole.py

diff --git a/AUTHORS b/AUTHORS
index 1bc6590..f788aae 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,4 +1,5 @@
 Mark Ryan (mark.d.ryan@intel.com)
 Ludovic Ferrandis (ludovic.ferrandis@intel.com)
 Sébastien Bianti (sebastien.bianti@intel.com)
-Regis Merlino (regis.merlino@intel.com)
\ No newline at end of file
+Regis Merlino (regis.merlino@intel.com)
+Christophe Guiraud (christophe.guiraud@intel.com)
index 496c626..ecae6d8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,63 @@
+version 0.3.0
+       - Implemented the Update method which allows server side object
+         meta data to be added, updated and deleted.
+
+               https://github.com/01org/media-service-upnp/pull/98
+
+       - Unsubscribe to service notifications when a context is lost.  This is
+         necessary to fix a bug reported by the UPnP test tool.
+
+       - Fixed a few problems with the API.txt file.
+
+
+version 0.2.1
+       - Added missing soup dependencies to the main Makefile.am.
+
+version 0.2.0
+       - Completed implementation of Upload.  It is now possible to
+         upload files to remote DMSs, both to specific directories and to
+         the AnyContainer ( if it is supported by your DMS ) .  Methods
+         have also been added to allow clients to cancel uploads and to
+         monitor their progress.
+               https://github.com/01org/media-service-upnp/issues/34
+       - DMS objects and containers can now be deleted.
+               https://github.com/01org/media-service-upnp/issues/35
+       - Containers can now be created on remote DMSs.
+               https://github.com/01org/media-service-upnp/issues/43
+       - New properties have been added to provide more information about
+         media objects exposed by DMSs.
+         - DLNAManaged indicates which actions can be performed on an object,
+           e.g., upload, delete, etc.
+               https://github.com/01org/media-service-upnp/issues/36
+          - Creator which indicates the creator of the content.
+          - CreateClass which identifies the type of objects that can be
+           created in a given container.
+               https://github.com/01org/media-service-upnp/issues/45
+       - The Artists property has been added to org.gnome.UPnP.MediaItem2.
+         Artists is an array of all the artists who worked on the object.
+       - The com.intel.UPnP.MediaDevice interface exposes some additional
+         properties that inform clients about the optional features supported
+         by DMSs.  These include:
+          - DLNACaps indicating the DLNA OCM operations supported by the server.
+          - FeatureList containing the list of features, e.g., BOOKMARK, EPG,
+           supported by the server.
+          - SortCaps and SortExt Caps indicating the sorting capabilities of the
+           server.
+          - SearchCaps indicating the searching capabilities of the server.
+         - See https://github.com/01org/media-service-upnp/issues/24 for more
+           details
+       - Some new methods and properties have been added to
+         com.intel.UPnP.MediaDevice for ContentSync:
+          - The System ID property
+                 https://github.com/01org/media-service-upnp/issues/38
+          - GetSystemResetToken
+                https://github.com/01org/media-service-upnp/issues/46
+        - The method com.intel.MediaServiceUPnP.PreferLocalAddresses has been
+         added.  This is useful for writing DMCs.  It allows DMCs to instruct
+         media-service-upnp to favour remote addresses.
+                https://github.com/01org/media-service-upnp/issues/21
+
+
 version 0.1.0
        - Implemented Upload
                https://github.com/01org/media-service-upnp/issues/34
index 1289a45..2559712 100644 (file)
@@ -2,8 +2,10 @@ INCLUDES = -DG_LOG_DOMAIN=\"MSU\"
 
 AM_CFLAGS =    $(GLIB_CFLAGS)                          \
                $(GIO_CFLAGS)                           \
+               $(GSSDP_CFLAGS)                         \
                $(GUPNP_CFLAGS)                         \
                $(GUPNPAV_CFLAGS)                       \
+               $(SOUP_CFLAGS)                          \
                -DSYS_CONFIG_DIR="\"$(sysconfdir)\""    \
                -include config.h
 
@@ -12,6 +14,7 @@ ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
 sysconf_DATA = media-service-upnp.conf
 
 media_service_upnp_sources =   src/async.c              \
+                               src/chain-task.c         \
                                src/device.c             \
                                src/error.c              \
                                src/media-service-upnp.c \
@@ -24,17 +27,19 @@ media_service_upnp_sources =        src/async.c              \
                                src/task.c               \
                                src/upnp.c
 
-media_service_upnp_headers =   src/async.h     \
-                               src/device.h    \
-                               src/error.h     \
-                               src/interface.h \
-                               src/log.h       \
-                               src/path.h      \
-                               src/props.h     \
-                               src/search.h    \
-                               src/settings.h  \
-                               src/sort.h      \
-                               src/task.h      \
+media_service_upnp_headers =   src/async.h             \
+                               src/chain-task.h        \
+                               src/client.h            \
+                               src/device.h            \
+                               src/error.h             \
+                               src/interface.h         \
+                               src/log.h               \
+                               src/path.h              \
+                               src/props.h             \
+                               src/search.h            \
+                               src/settings.h          \
+                               src/sort.h              \
+                               src/task.h              \
                                src/upnp.h
 
 
@@ -45,8 +50,10 @@ media_service_upnp_SOURCES = $(media_service_upnp_headers)   \
 
 media_service_upnp_LDADD =     $(GLIB_LIBS)    \
                                $(GIO_LIBS)     \
+                               $(GSSDP_LIBS)   \
                                $(GUPNP_LIBS)   \
-                               $(GUPNPAV_LIBS)
+                               $(GUPNPAV_LIBS) \
+                               $(SOUP_LIBS)
 
 
 dms_info_sources = test/dms-info.c
index 3d2bb4e..a0e4157 100644 (file)
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.11.3 from Makefile.am.
+# Makefile.in generated by automake 1.11.6 from Makefile.am.
 # @configure_input@
 
 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
 
 
 VPATH = @srcdir@
+am__make_dryrun = \
+  { \
+    am__dry=no; \
+    case $$MAKEFLAGS in \
+      *\\[\ \  ]*) \
+        echo 'am--echo: ; @echo "AM"  OK' | $(MAKE) -f - 2>/dev/null \
+          | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
+      *) \
+        for am__flg in $$MAKEFLAGS; do \
+          case $$am__flg in \
+            *=*|--*) ;; \
+            *n*) am__dry=yes; break;; \
+          esac; \
+        done;; \
+    esac; \
+    test $$am__dry = yes; \
+  }
 pkgdatadir = $(datadir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
 pkglibdir = $(libdir)/@PACKAGE@
@@ -67,15 +84,17 @@ dms_info_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 dms_info_LINK = $(CCLD) $(dms_info_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
        $(LDFLAGS) -o $@
 am__objects_2 =
-am__objects_3 = src/async.$(OBJEXT) src/device.$(OBJEXT) \
-       src/error.$(OBJEXT) src/media-service-upnp.$(OBJEXT) \
-       src/log.$(OBJEXT) src/path.$(OBJEXT) src/props.$(OBJEXT) \
-       src/search.$(OBJEXT) src/settings.$(OBJEXT) src/sort.$(OBJEXT) \
-       src/task.$(OBJEXT) src/upnp.$(OBJEXT)
+am__objects_3 = src/async.$(OBJEXT) src/chain-task.$(OBJEXT) \
+       src/device.$(OBJEXT) src/error.$(OBJEXT) \
+       src/media-service-upnp.$(OBJEXT) src/log.$(OBJEXT) \
+       src/path.$(OBJEXT) src/props.$(OBJEXT) src/search.$(OBJEXT) \
+       src/settings.$(OBJEXT) src/sort.$(OBJEXT) src/task.$(OBJEXT) \
+       src/upnp.$(OBJEXT)
 am_media_service_upnp_OBJECTS = $(am__objects_2) $(am__objects_3)
 media_service_upnp_OBJECTS = $(am_media_service_upnp_OBJECTS)
 media_service_upnp_DEPENDENCIES = $(am__DEPENDENCIES_1) \
        $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
        $(am__DEPENDENCIES_1)
 DEFAULT_INCLUDES = -I.@am__isrc@
 depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
@@ -102,6 +121,11 @@ am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
 am__v_GEN_0 = @echo "  GEN   " $@;
 SOURCES = $(dms_info_SOURCES) $(media_service_upnp_SOURCES)
 DIST_SOURCES = $(dms_info_SOURCES) $(media_service_upnp_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
 am__vpath_adj = case $$p in \
     $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
@@ -175,6 +199,8 @@ GIO_LIBS = @GIO_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_LIBS = @GLIB_LIBS@
 GREP = @GREP@
+GSSDP_CFLAGS = @GSSDP_CFLAGS@
+GSSDP_LIBS = @GSSDP_LIBS@
 GUPNPAV_CFLAGS = @GUPNPAV_CFLAGS@
 GUPNPAV_LIBS = @GUPNPAV_LIBS@
 GUPNP_CFLAGS = @GUPNP_CFLAGS@
@@ -260,14 +286,17 @@ with_log_type = @with_log_type@
 INCLUDES = -DG_LOG_DOMAIN=\"MSU\"
 AM_CFLAGS = $(GLIB_CFLAGS)                             \
                $(GIO_CFLAGS)                           \
+               $(GSSDP_CFLAGS)                         \
                $(GUPNP_CFLAGS)                         \
                $(GUPNPAV_CFLAGS)                       \
+               $(SOUP_CFLAGS)                          \
                -DSYS_CONFIG_DIR="\"$(sysconfdir)\""    \
                -include config.h
 
 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
 sysconf_DATA = media-service-upnp.conf
 media_service_upnp_sources = src/async.c                \
+                               src/chain-task.c         \
                                src/device.c             \
                                src/error.c              \
                                src/media-service-upnp.c \
@@ -280,17 +309,19 @@ media_service_upnp_sources = src/async.c           \
                                src/task.c               \
                                src/upnp.c
 
-media_service_upnp_headers = src/async.h       \
-                               src/device.h    \
-                               src/error.h     \
-                               src/interface.h \
-                               src/log.h       \
-                               src/path.h      \
-                               src/props.h     \
-                               src/search.h    \
-                               src/settings.h  \
-                               src/sort.h      \
-                               src/task.h      \
+media_service_upnp_headers = src/async.h               \
+                               src/chain-task.h        \
+                               src/client.h            \
+                               src/device.h            \
+                               src/error.h             \
+                               src/interface.h         \
+                               src/log.h               \
+                               src/path.h              \
+                               src/props.h             \
+                               src/search.h            \
+                               src/settings.h          \
+                               src/sort.h              \
+                               src/task.h              \
                                src/upnp.h
 
 media_service_upnp_SOURCES = $(media_service_upnp_headers)     \
@@ -298,8 +329,10 @@ media_service_upnp_SOURCES = $(media_service_upnp_headers) \
 
 media_service_upnp_LDADD = $(GLIB_LIBS)        \
                                $(GIO_LIBS)     \
+                               $(GSSDP_LIBS)   \
                                $(GUPNP_LIBS)   \
-                               $(GUPNPAV_LIBS)
+                               $(GUPNPAV_LIBS) \
+                               $(SOUP_LIBS)
 
 dms_info_sources = test/dms-info.c
 dms_info_SOURCES = $(dms_info_sources)
@@ -390,8 +423,11 @@ media-service-upnp.conf: $(top_builddir)/config.status $(srcdir)/media-service-u
        cd $(top_builddir) && $(SHELL) ./config.status $@
 install-binPROGRAMS: $(bin_PROGRAMS)
        @$(NORMAL_INSTALL)
-       test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
        @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+       if test -n "$$list"; then \
+         echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+         $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+       fi; \
        for p in $$list; do echo "$$p $$p"; done | \
        sed 's/$(EXEEXT)$$//' | \
        while read p p1; do if test -f $$p; \
@@ -446,6 +482,8 @@ src/$(DEPDIR)/$(am__dirstamp):
        @$(MKDIR_P) src/$(DEPDIR)
        @: > src/$(DEPDIR)/$(am__dirstamp)
 src/async.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/chain-task.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
 src/device.$(OBJEXT): src/$(am__dirstamp) \
        src/$(DEPDIR)/$(am__dirstamp)
 src/error.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
@@ -468,6 +506,7 @@ media-service-upnp$(EXEEXT): $(media_service_upnp_OBJECTS) $(media_service_upnp_
 mostlyclean-compile:
        -rm -f *.$(OBJEXT)
        -rm -f src/async.$(OBJEXT)
+       -rm -f src/chain-task.$(OBJEXT)
        -rm -f src/device.$(OBJEXT)
        -rm -f src/error.$(OBJEXT)
        -rm -f src/log.$(OBJEXT)
@@ -485,6 +524,7 @@ distclean-compile:
        -rm -f *.tab.c
 
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/async.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/chain-task.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/device.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/error.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/log.Po@am__quote@
@@ -529,8 +569,11 @@ test/dms_info-dms-info.obj: test/dms-info.c
 @am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dms_info_CFLAGS) $(CFLAGS) -c -o test/dms_info-dms-info.obj `if test -f 'test/dms-info.c'; then $(CYGPATH_W) 'test/dms-info.c'; else $(CYGPATH_W) '$(srcdir)/test/dms-info.c'; fi`
 install-dbussessionDATA: $(dbussession_DATA)
        @$(NORMAL_INSTALL)
-       test -z "$(dbussessiondir)" || $(MKDIR_P) "$(DESTDIR)$(dbussessiondir)"
        @list='$(dbussession_DATA)'; test -n "$(dbussessiondir)" || list=; \
+       if test -n "$$list"; then \
+         echo " $(MKDIR_P) '$(DESTDIR)$(dbussessiondir)'"; \
+         $(MKDIR_P) "$(DESTDIR)$(dbussessiondir)" || exit 1; \
+       fi; \
        for p in $$list; do \
          if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
          echo "$$d$$p"; \
@@ -547,8 +590,11 @@ uninstall-dbussessionDATA:
        dir='$(DESTDIR)$(dbussessiondir)'; $(am__uninstall_files_from_dir)
 install-pkgconfigDATA: $(pkgconfig_DATA)
        @$(NORMAL_INSTALL)
-       test -z "$(pkgconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)"
        @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+       if test -n "$$list"; then \
+         echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \
+         $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \
+       fi; \
        for p in $$list; do \
          if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
          echo "$$d$$p"; \
@@ -565,8 +611,11 @@ uninstall-pkgconfigDATA:
        dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
 install-sysconfDATA: $(sysconf_DATA)
        @$(NORMAL_INSTALL)
-       test -z "$(sysconfdir)" || $(MKDIR_P) "$(DESTDIR)$(sysconfdir)"
        @list='$(sysconf_DATA)'; test -n "$(sysconfdir)" || list=; \
+       if test -n "$$list"; then \
+         echo " $(MKDIR_P) '$(DESTDIR)$(sysconfdir)'"; \
+         $(MKDIR_P) "$(DESTDIR)$(sysconfdir)" || exit 1; \
+       fi; \
        for p in $$list; do \
          if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
          echo "$$d$$p"; \
@@ -732,7 +781,7 @@ distcheck: dist
        *.zip*) \
          unzip $(distdir).zip ;;\
        esac
-       chmod -R a-w $(distdir); chmod a+w $(distdir)
+       chmod -R a-w $(distdir); chmod u+w $(distdir)
        mkdir $(distdir)/_build
        mkdir $(distdir)/_inst
        chmod a-w $(distdir)
index 6ea7479..3c0d88c 100644 (file)
@@ -1,4 +1,4 @@
-# generated automatically by aclocal 1.11.3 -*- Autoconf -*-
+# generated automatically by aclocal 1.11.6 -*- Autoconf -*-
 
 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
 # 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
@@ -14,8 +14,8 @@
 
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
-m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.68],,
-[m4_warning([this file was generated for autoconf 2.68.
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
+[m4_warning([this file was generated for autoconf 2.69.
 You have another version of autoconf.  It may work, but is not guaranteed to.
 If you have problems, you may need to regenerate the build system entirely.
 To do so, use the procedure documented by the package, typically `autoreconf'.])])
@@ -198,7 +198,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
 [am__api_version='1.11'
 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
 dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.11.3], [],
+m4_if([$1], [1.11.6], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -214,7 +214,7 @@ m4_define([_AM_AUTOCONF_VERSION], [])
 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.11.3])dnl
+[AM_AUTOMAKE_VERSION([1.11.6])dnl
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
index b1f4749..862a14e 100755 (executable)
@@ -1,7 +1,7 @@
 #! /bin/sh
 # Wrapper for compilers which do not understand '-c -o'.
 
-scriptversion=2012-01-04.17; # UTC
+scriptversion=2012-03-05.13; # UTC
 
 # Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009, 2010, 2012 Free
 # Software Foundation, Inc.
@@ -79,6 +79,48 @@ func_file_conv ()
   esac
 }
 
+# func_cl_dashL linkdir
+# Make cl look for libraries in LINKDIR
+func_cl_dashL ()
+{
+  func_file_conv "$1"
+  if test -z "$lib_path"; then
+    lib_path=$file
+  else
+    lib_path="$lib_path;$file"
+  fi
+  linker_opts="$linker_opts -LIBPATH:$file"
+}
+
+# func_cl_dashl library
+# Do a library search-path lookup for cl
+func_cl_dashl ()
+{
+  lib=$1
+  found=no
+  save_IFS=$IFS
+  IFS=';'
+  for dir in $lib_path $LIB
+  do
+    IFS=$save_IFS
+    if $shared && test -f "$dir/$lib.dll.lib"; then
+      found=yes
+      lib=$dir/$lib.dll.lib
+      break
+    fi
+    if test -f "$dir/$lib.lib"; then
+      found=yes
+      lib=$dir/$lib.lib
+      break
+    fi
+  done
+  IFS=$save_IFS
+
+  if test "$found" != yes; then
+    lib=$lib.lib
+  fi
+}
+
 # func_cl_wrapper cl arg...
 # Adjust compile command to suit cl
 func_cl_wrapper ()
@@ -109,43 +151,34 @@ func_cl_wrapper ()
              ;;
          esac
          ;;
+       -I)
+         eat=1
+         func_file_conv "$2" mingw
+         set x "$@" -I"$file"
+         shift
+         ;;
        -I*)
          func_file_conv "${1#-I}" mingw
          set x "$@" -I"$file"
          shift
          ;;
+       -l)
+         eat=1
+         func_cl_dashl "$2"
+         set x "$@" "$lib"
+         shift
+         ;;
        -l*)
-         lib=${1#-l}
-         found=no
-         save_IFS=$IFS
-         IFS=';'
-         for dir in $lib_path $LIB
-         do
-           IFS=$save_IFS
-           if $shared && test -f "$dir/$lib.dll.lib"; then
-             found=yes
-             set x "$@" "$dir/$lib.dll.lib"
-             break
-           fi
-           if test -f "$dir/$lib.lib"; then
-             found=yes
-             set x "$@" "$dir/$lib.lib"
-             break
-           fi
-         done
-         IFS=$save_IFS
-
-         test "$found" != yes && set x "$@" "$lib.lib"
+         func_cl_dashl "${1#-l}"
+         set x "$@" "$lib"
          shift
          ;;
+       -L)
+         eat=1
+         func_cl_dashL "$2"
+         ;;
        -L*)
-         func_file_conv "${1#-L}"
-         if test -z "$lib_path"; then
-           lib_path=$file
-         else
-           lib_path="$lib_path;$file"
-         fi
-         linker_opts="$linker_opts -LIBPATH:$file"
+         func_cl_dashL "${1#-L}"
          ;;
        -static)
          shared=false
index bd0ac08..25a39e6 100755 (executable)
@@ -1,10 +1,10 @@
 #! /bin/sh
 # depcomp - compile a program generating dependencies as side-effects
 
-scriptversion=2011-12-04.11; # UTC
+scriptversion=2012-03-27.16; # UTC
 
 # Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010,
-# 2011 Free Software Foundation, Inc.
+# 2011, 2012 Free Software Foundation, Inc.
 
 # 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
@@ -28,7 +28,7 @@ scriptversion=2011-12-04.11; # UTC
 
 case $1 in
   '')
-     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
+     echo "$0: No command.  Try '$0 --help' for more information." 1>&2
      exit 1;
      ;;
   -h | --h*)
@@ -40,8 +40,8 @@ as side-effects.
 
 Environment variables:
   depmode     Dependency tracking mode.
-  source      Source file read by `PROGRAMS ARGS'.
-  object      Object file output by `PROGRAMS ARGS'.
+  source      Source file read by 'PROGRAMS ARGS'.
+  object      Object file output by 'PROGRAMS ARGS'.
   DEPDIR      directory where to store dependencies.
   depfile     Dependency file to output.
   tmpdepfile  Temporary file to use when outputting dependencies.
@@ -57,6 +57,12 @@ EOF
     ;;
 esac
 
+# A tabulation character.
+tab='  '
+# A newline character.
+nl='
+'
+
 if test -z "$depmode" || test -z "$source" || test -z "$object"; then
   echo "depcomp: Variables source, object and depmode must be set" 1>&2
   exit 1
@@ -102,6 +108,12 @@ if test "$depmode" = msvc7msys; then
    depmode=msvc7
 fi
 
+if test "$depmode" = xlc; then
+   # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency informations.
+   gccflag=-qmakedep=gcc,-MF
+   depmode=gcc
+fi
+
 case "$depmode" in
 gcc3)
 ## gcc 3 implements dependency tracking that does exactly what
@@ -156,15 +168,14 @@ gcc)
 ## The second -e expression handles DOS-style file names with drive letters.
   sed -e 's/^[^:]*: / /' \
       -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
-## This next piece of magic avoids the `deleted header file' problem.
+## This next piece of magic avoids the "deleted header file" problem.
 ## The problem is that when a header file which appears in a .P file
 ## is deleted, the dependency causes make to die (because there is
 ## typically no way to rebuild the header).  We avoid this by adding
 ## dummy dependencies for each header file.  Too bad gcc doesn't do
 ## this for us directly.
-  tr ' ' '
-' < "$tmpdepfile" |
-## Some versions of gcc put a space before the `:'.  On the theory
+  tr ' ' "$nl" < "$tmpdepfile" |
+## Some versions of gcc put a space before the ':'.  On the theory
 ## that the space means something, we add a space to the output as
 ## well.  hp depmode also adds that space, but also prefixes the VPATH
 ## to the object.  Take care to not repeat it in the output.
@@ -203,18 +214,15 @@ sgi)
     # clever and replace this with sed code, as IRIX sed won't handle
     # lines with more than a fixed number of characters (4096 in
     # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
-    # the IRIX cc adds comments like `#:fec' to the end of the
+    # the IRIX cc adds comments like '#:fec' to the end of the
     # dependency line.
-    tr ' ' '
-' < "$tmpdepfile" \
+    tr ' ' "$nl" < "$tmpdepfile" \
     | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
-    tr '
-' ' ' >> "$depfile"
+    tr "$nl" ' ' >> "$depfile"
     echo >> "$depfile"
 
     # The second pass generates a dummy entry for each header file.
-    tr ' ' '
-' < "$tmpdepfile" \
+    tr ' ' "$nl" < "$tmpdepfile" \
    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
    >> "$depfile"
   else
@@ -226,10 +234,17 @@ sgi)
   rm -f "$tmpdepfile"
   ;;
 
+xlc)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
 aix)
   # The C for AIX Compiler uses -M and outputs the dependencies
   # in a .u file.  In older versions, this file always lives in the
-  # current directory.  Also, the AIX compiler puts `$object:' at the
+  # current directory.  Also, the AIX compiler puts '$object:' at the
   # start of each line; $object doesn't have directory information.
   # Version 6 uses the directory in both cases.
   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
@@ -259,12 +274,11 @@ aix)
     test -f "$tmpdepfile" && break
   done
   if test -f "$tmpdepfile"; then
-    # Each line is of the form `foo.o: dependent.h'.
+    # Each line is of the form 'foo.o: dependent.h'.
     # Do two passes, one to just change these to
-    # `$object: dependent.h' and one to simply `dependent.h:'.
+    # '$object: dependent.h' and one to simply 'dependent.h:'.
     sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
-    # That's a tab and a space in the [].
-    sed -e 's,^.*\.[a-z]*:[     ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+    sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
   else
     # The sourcefile does not contain any dependencies, so just
     # store a dummy comment line, to avoid errors with the Makefile
@@ -275,23 +289,26 @@ aix)
   ;;
 
 icc)
-  # Intel's C compiler understands `-MD -MF file'.  However on
-  #    icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+  # Intel's C compiler anf tcc (Tiny C Compiler) understand '-MD -MF file'.
+  # However on
+  #    $CC -MD -MF foo.d -c -o sub/foo.o sub/foo.c
   # ICC 7.0 will fill foo.d with something like
   #    foo.o: sub/foo.c
   #    foo.o: sub/foo.h
-  # which is wrong.  We want:
+  # which is wrong.  We want
   #    sub/foo.o: sub/foo.c
   #    sub/foo.o: sub/foo.h
   #    sub/foo.c:
   #    sub/foo.h:
   # ICC 7.1 will output
   #    foo.o: sub/foo.c sub/foo.h
-  # and will wrap long lines using :
+  # and will wrap long lines using '\':
   #    foo.o: sub/foo.c ... \
   #     sub/foo.h ... \
   #     ...
-
+  # tcc 0.9.26 (FIXME still under development at the moment of writing)
+  # will emit a similar output, but also prepend the continuation lines
+  # with horizontal tabulation characters.
   "$@" -MD -MF "$tmpdepfile"
   stat=$?
   if test $stat -eq 0; then :
@@ -300,15 +317,21 @@ icc)
     exit $stat
   fi
   rm -f "$depfile"
-  # Each line is of the form `foo.o: dependent.h',
-  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+  # Each line is of the form 'foo.o: dependent.h',
+  # or 'foo.o: dep1.h dep2.h \', or ' dep3.h dep4.h \'.
   # Do two passes, one to just change these to
-  # `$object: dependent.h' and one to simply `dependent.h:'.
-  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
-  # Some versions of the HPUX 10.20 sed can't process this invocation
-  # correctly.  Breaking it into two sed invocations is a workaround.
-  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
-    sed -e 's/$/ :/' >> "$depfile"
+  # '$object: dependent.h' and one to simply 'dependent.h:'.
+  sed -e "s/^[ $tab][ $tab]*/  /" -e "s,^[^:]*:,$object :," \
+    < "$tmpdepfile" > "$depfile"
+  sed '
+    s/[ '"$tab"'][ '"$tab"']*/ /g
+    s/^ *//
+    s/ *\\*$//
+    s/^[^:]*: *//
+    /^$/d
+    /:$/d
+    s/$/ :/
+  ' < "$tmpdepfile" >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
 
@@ -344,7 +367,7 @@ hp2)
   done
   if test -f "$tmpdepfile"; then
     sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
-    # Add `dependent.h:' lines.
+    # Add 'dependent.h:' lines.
     sed -ne '2,${
               s/^ *//
               s/ \\*$//
@@ -359,9 +382,9 @@ hp2)
 
 tru64)
    # The Tru64 compiler uses -MD to generate dependencies as a side
-   # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+   # effect.  'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
    # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
-   # dependencies in `foo.d' instead, so we check for that too.
+   # dependencies in 'foo.d' instead, so we check for that too.
    # Subdirectories are respected.
    dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
    test "x$dir" = "x$object" && dir=
@@ -407,8 +430,7 @@ tru64)
    done
    if test -f "$tmpdepfile"; then
       sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
-      # That's a tab and a space in the [].
-      sed -e 's,^.*\.[a-z]*:[   ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+      sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
    else
       echo "#dummy" > "$depfile"
    fi
@@ -443,11 +465,11 @@ msvc7)
   p
 }' | $cygpath_u | sort -u | sed -n '
 s/ /\\ /g
-s/\(.*\)/      \1 \\/p
+s/\(.*\)/'"$tab"'\1 \\/p
 s/.\(.*\) \\/\1:/
 H
 $ {
-  s/.*/        /
+  s/.*/'"$tab"'/
   G
   p
 }' >> "$depfile"
@@ -478,7 +500,7 @@ dashmstdout)
     shift
   fi
 
-  # Remove `-o $object'.
+  # Remove '-o $object'.
   IFS=" "
   for arg
   do
@@ -498,15 +520,14 @@ dashmstdout)
   done
 
   test -z "$dashmflag" && dashmflag=-M
-  # Require at least two characters before searching for `:'
+  # Require at least two characters before searching for ':'
   # in the target name.  This is to cope with DOS-style filenames:
-  # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+  # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
   "$@" $dashmflag |
-    sed 's:^[  ]*[^: ][^:][^:]*\:[    ]*:'"$object"'\: :' > "$tmpdepfile"
+    sed 's:^['"$tab"' ]*[^:'"$tab"' ][^:][^:]*\:['"$tab"' ]*:'"$object"'\: :' > "$tmpdepfile"
   rm -f "$depfile"
   cat < "$tmpdepfile" > "$depfile"
-  tr ' ' '
-' < "$tmpdepfile" | \
+  tr ' ' "$nl" < "$tmpdepfile" | \
 ## Some versions of the HPUX 10.20 sed can't process this invocation
 ## correctly.  Breaking it into two sed invocations is a workaround.
     sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
@@ -562,8 +583,7 @@ makedepend)
   # makedepend may prepend the VPATH from the source file name to the object.
   # No need to regex-escape $object, excess matching of '.' is harmless.
   sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
-  sed '1,2d' "$tmpdepfile" | tr ' ' '
-' | \
+  sed '1,2d' "$tmpdepfile" | tr ' ' "$nl" | \
 ## Some versions of the HPUX 10.20 sed can't process this invocation
 ## correctly.  Breaking it into two sed invocations is a workaround.
     sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
@@ -583,7 +603,7 @@ cpp)
     shift
   fi
 
-  # Remove `-o $object'.
+  # Remove '-o $object'.
   IFS=" "
   for arg
   do
@@ -652,8 +672,8 @@ msvisualcpp)
   sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
   rm -f "$depfile"
   echo "$object : \\" > "$depfile"
-  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::     \1 \\:p' >> "$depfile"
-  echo "       " >> "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
+  echo "$tab" >> "$depfile"
   sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
index 9cad819..9c07211 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,13 +1,11 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for media-service-upnp 0.1.0.
+# Generated by GNU Autoconf 2.69 for media-service-upnp 0.3.0.
 #
 # Report bugs to <https://github.com/01org/media-service-upnp/issues/new>.
 #
 #
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
-# Foundation, Inc.
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
 #
 #
 # This configure script is free software; the Free Software Foundation
@@ -136,6 +134,31 @@ export LANGUAGE
 # CDPATH.
 (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
 
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -169,7 +192,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
 else
   exitcode=1; echo positional parameters were not saved.
 fi
-test x\$exitcode = x0 || exit 1"
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
   as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
   as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
   eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
@@ -214,21 +238,25 @@ IFS=$as_save_IFS
 
 
       if test "x$CONFIG_SHELL" != x; then :
-  # We cannot yet assume a decent shell, so we have to provide a
-       # neutralization value for shells without unset; and this also
-       # works around shells that cannot unset nonexistent variables.
-       # Preserve -v and -x to the replacement shell.
-       BASH_ENV=/dev/null
-       ENV=/dev/null
-       (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
-       export CONFIG_SHELL
-       case $- in # ((((
-         *v*x* | *x*v* ) as_opts=-vx ;;
-         *v* ) as_opts=-v ;;
-         *x* ) as_opts=-x ;;
-         * ) as_opts= ;;
-       esac
-       exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
 fi
 
     if test x$as_have_required = xno; then :
@@ -332,6 +360,14 @@ $as_echo X"$as_dir" |
 
 
 } # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
 # as_fn_append VAR VALUE
 # ----------------------
 # Append the text in VALUE to the end of the definition contained in VAR. Take
@@ -453,6 +489,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
   chmod +x "$as_me.lineno" ||
     { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
 
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
   # Don't try to exec as it changes $[0], causing all sort of problems
   # (the dirname of $[0] is not the place where we might find the
   # original and so on.  Autoconf is especially sensitive to this).
@@ -487,16 +527,16 @@ if (echo >conf$$.file) 2>/dev/null; then
     # ... but there are two gotchas:
     # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
     # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
-    # In both cases, we have to default to `cp -p'.
+    # In both cases, we have to default to `cp -pR'.
     ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
-      as_ln_s='cp -p'
+      as_ln_s='cp -pR'
   elif ln conf$$.file conf$$ 2>/dev/null; then
     as_ln_s=ln
   else
-    as_ln_s='cp -p'
+    as_ln_s='cp -pR'
   fi
 else
-  as_ln_s='cp -p'
+  as_ln_s='cp -pR'
 fi
 rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
 rmdir conf$$.dir 2>/dev/null
@@ -508,28 +548,8 @@ else
   as_mkdir_p=false
 fi
 
-if test -x / >/dev/null 2>&1; then
-  as_test_x='test -x'
-else
-  if ls -dL / >/dev/null 2>&1; then
-    as_ls_L_option=L
-  else
-    as_ls_L_option=
-  fi
-  as_test_x='
-    eval sh -c '\''
-      if test -d "$1"; then
-       test -d "$1/.";
-      else
-       case $1 in #(
-       -*)set "./$1";;
-       esac;
-       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
-       ???[sx]*):;;*)false;;esac;fi
-    '\'' sh
-  '
-fi
-as_executable_p=$as_test_x
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
 
 # Sed expression to map a string onto a valid CPP name.
 as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
@@ -561,8 +581,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='media-service-upnp'
 PACKAGE_TARNAME='media-service-upnp'
-PACKAGE_VERSION='0.1.0'
-PACKAGE_STRING='media-service-upnp 0.1.0'
+PACKAGE_VERSION='0.3.0'
+PACKAGE_STRING='media-service-upnp 0.3.0'
 PACKAGE_BUGREPORT='https://github.com/01org/media-service-upnp/issues/new'
 PACKAGE_URL='https://01.org/dleyna/'
 
@@ -624,6 +644,8 @@ GUPNPAV_LIBS
 GUPNPAV_CFLAGS
 GUPNP_LIBS
 GUPNP_CFLAGS
+GSSDP_LIBS
+GSSDP_CFLAGS
 GIO_LIBS
 GIO_CFLAGS
 GLIB_LIBS
@@ -748,6 +770,8 @@ GLIB_CFLAGS
 GLIB_LIBS
 GIO_CFLAGS
 GIO_LIBS
+GSSDP_CFLAGS
+GSSDP_LIBS
 GUPNP_CFLAGS
 GUPNP_LIBS
 GUPNPAV_CFLAGS
@@ -1210,8 +1234,6 @@ target=$target_alias
 if test "x$host_alias" != x; then
   if test "x$build_alias" = x; then
     cross_compiling=maybe
-    $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
-    If a cross compiler is detected then cross compile mode will be used" >&2
   elif test "x$build_alias" != "x$host_alias"; then
     cross_compiling=yes
   fi
@@ -1297,7 +1319,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures media-service-upnp 0.1.0 to adapt to many kinds of systems.
+\`configure' configures media-service-upnp 0.3.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1364,7 +1386,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of media-service-upnp 0.1.0:";;
+     short | recursive ) echo "Configuration of media-service-upnp 0.3.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1410,6 +1432,9 @@ Some influential environment variables:
   GLIB_LIBS   linker flags for GLIB, overriding pkg-config
   GIO_CFLAGS  C compiler flags for GIO, overriding pkg-config
   GIO_LIBS    linker flags for GIO, overriding pkg-config
+  GSSDP_CFLAGS
+              C compiler flags for GSSDP, overriding pkg-config
+  GSSDP_LIBS  linker flags for GSSDP, overriding pkg-config
   GUPNP_CFLAGS
               C compiler flags for GUPNP, overriding pkg-config
   GUPNP_LIBS  linker flags for GUPNP, overriding pkg-config
@@ -1488,10 +1513,10 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-media-service-upnp configure 0.1.0
-generated by GNU Autoconf 2.68
+media-service-upnp configure 0.3.0
+generated by GNU Autoconf 2.69
 
-Copyright (C) 2010 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
 This configure script is free software; the Free Software Foundation
 gives unlimited permission to copy, distribute and modify it.
 _ACEOF
@@ -1765,7 +1790,8 @@ int
 main ()
 {
 static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)];
-test_array [0] = 0
+test_array [0] = 0;
+return test_array [0];
 
   ;
   return 0;
@@ -1875,7 +1901,7 @@ $as_echo "$ac_try_echo"; } >&5
         test ! -s conftest.err
        } && test -s conftest$ac_exeext && {
         test "$cross_compiling" = yes ||
-        $as_test_x conftest$ac_exeext
+        test -x conftest$ac_exeext
        }; then :
   ac_retval=0
 else
@@ -1964,8 +1990,8 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by media-service-upnp $as_me 0.1.0, which was
-generated by GNU Autoconf 2.68.  Invocation command line was
+It was created by media-service-upnp $as_me 0.3.0, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
 
@@ -2388,7 +2414,7 @@ case $as_dir/ in #((
     # by default.
     for ac_prog in ginstall scoinst install; do
       for ac_exec_ext in '' $ac_executable_extensions; do
-       if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+       if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
          if test $ac_prog = install &&
            grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
            # AIX install.  It has an incompatible calling convention.
@@ -2557,7 +2583,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_STRIP="${ac_tool_prefix}strip"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -2597,7 +2623,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_STRIP="strip"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -2648,7 +2674,7 @@ do
   test -z "$as_dir" && as_dir=.
     for ac_prog in mkdir gmkdir; do
         for ac_exec_ext in '' $ac_executable_extensions; do
-          { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+          as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
           case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
             'mkdir (GNU coreutils) '* | \
             'mkdir (coreutils) '* | \
@@ -2701,7 +2727,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_AWK="$ac_prog"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -2787,7 +2813,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='media-service-upnp'
- VERSION='0.1.0'
+ VERSION='0.3.0'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -2919,6 +2945,7 @@ AM_BACKSLASH='\'
                CFLAGS+=" -Wno-overlength-strings"
 
                CFLAGS+=" -DG_DISABLE_DEPRECATED"
+               CFLAGS+=" -DGLIB_DISABLE_DEPRECATION_WARNINGS"
        fi
 
        CFLAGS+=" -Wno-format-extra-args"
@@ -2955,7 +2982,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_CC="${ac_tool_prefix}gcc"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -2995,7 +3022,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_CC="gcc"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -3048,7 +3075,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_CC="${ac_tool_prefix}cc"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -3089,7 +3116,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
        ac_prog_rejected=yes
        continue
@@ -3147,7 +3174,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -3191,7 +3218,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_CC="$ac_prog"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -3637,8 +3664,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <stdarg.h>
 #include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+struct stat;
 /* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
 struct buf { int x; };
 FILE * (*rcsopen) (struct buf *, struct stat *, int);
@@ -4070,7 +4096,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -4113,7 +4139,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -4438,6 +4464,97 @@ $as_echo "yes" >&6; }
 fi
 
 pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GSSDP" >&5
+$as_echo_n "checking for GSSDP... " >&6; }
+
+if test -n "$GSSDP_CFLAGS"; then
+    pkg_cv_GSSDP_CFLAGS="$GSSDP_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gssdp-1.0 >= 0.13.0\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gssdp-1.0 >= 0.13.0") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GSSDP_CFLAGS=`$PKG_CONFIG --cflags "gssdp-1.0 >= 0.13.0" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GSSDP_LIBS"; then
+    pkg_cv_GSSDP_LIBS="$GSSDP_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gssdp-1.0 >= 0.13.0\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gssdp-1.0 >= 0.13.0") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GSSDP_LIBS=`$PKG_CONFIG --libs "gssdp-1.0 >= 0.13.0" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               GSSDP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gssdp-1.0 >= 0.13.0" 2>&1`
+        else
+               GSSDP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gssdp-1.0 >= 0.13.0" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$GSSDP_PKG_ERRORS" >&5
+
+       as_fn_error $? "Package requirements (gssdp-1.0 >= 0.13.0) were not met:
+
+$GSSDP_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables GSSDP_CFLAGS
+and GSSDP_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+       { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables GSSDP_CFLAGS
+and GSSDP_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+       GSSDP_CFLAGS=$pkg_cv_GSSDP_CFLAGS
+       GSSDP_LIBS=$pkg_cv_GSSDP_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+
+pkg_failed=no
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GUPNP" >&5
 $as_echo_n "checking for GUPNP... " >&6; }
 
@@ -4445,12 +4562,12 @@ if test -n "$GUPNP_CFLAGS"; then
     pkg_cv_GUPNP_CFLAGS="$GUPNP_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gupnp-1.0 >= 0.17.2\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "gupnp-1.0 >= 0.17.2") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gupnp-1.0 >= 0.19.1\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gupnp-1.0 >= 0.19.1") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_GUPNP_CFLAGS=`$PKG_CONFIG --cflags "gupnp-1.0 >= 0.17.2" 2>/dev/null`
+  pkg_cv_GUPNP_CFLAGS=`$PKG_CONFIG --cflags "gupnp-1.0 >= 0.19.1" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -4462,12 +4579,12 @@ if test -n "$GUPNP_LIBS"; then
     pkg_cv_GUPNP_LIBS="$GUPNP_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gupnp-1.0 >= 0.17.2\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "gupnp-1.0 >= 0.17.2") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gupnp-1.0 >= 0.19.1\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gupnp-1.0 >= 0.19.1") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_GUPNP_LIBS=`$PKG_CONFIG --libs "gupnp-1.0 >= 0.17.2" 2>/dev/null`
+  pkg_cv_GUPNP_LIBS=`$PKG_CONFIG --libs "gupnp-1.0 >= 0.19.1" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -4488,14 +4605,14 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-               GUPNP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gupnp-1.0 >= 0.17.2" 2>&1`
+               GUPNP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gupnp-1.0 >= 0.19.1" 2>&1`
         else
-               GUPNP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gupnp-1.0 >= 0.17.2" 2>&1`
+               GUPNP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gupnp-1.0 >= 0.19.1" 2>&1`
         fi
        # Put the nasty error message in config.log where it belongs
        echo "$GUPNP_PKG_ERRORS" >&5
 
-       as_fn_error $? "Package requirements (gupnp-1.0 >= 0.17.2) were not met:
+       as_fn_error $? "Package requirements (gupnp-1.0 >= 0.19.1) were not met:
 
 $GUPNP_PKG_ERRORS
 
@@ -4536,12 +4653,12 @@ if test -n "$GUPNPAV_CFLAGS"; then
     pkg_cv_GUPNPAV_CFLAGS="$GUPNPAV_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gupnp-av-1.0\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "gupnp-av-1.0") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gupnp-av-1.0 >= 0.11.1\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gupnp-av-1.0 >= 0.11.1") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_GUPNPAV_CFLAGS=`$PKG_CONFIG --cflags "gupnp-av-1.0" 2>/dev/null`
+  pkg_cv_GUPNPAV_CFLAGS=`$PKG_CONFIG --cflags "gupnp-av-1.0 >= 0.11.1" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -4553,12 +4670,12 @@ if test -n "$GUPNPAV_LIBS"; then
     pkg_cv_GUPNPAV_LIBS="$GUPNPAV_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gupnp-av-1.0\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "gupnp-av-1.0") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gupnp-av-1.0 >= 0.11.1\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gupnp-av-1.0 >= 0.11.1") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_GUPNPAV_LIBS=`$PKG_CONFIG --libs "gupnp-av-1.0" 2>/dev/null`
+  pkg_cv_GUPNPAV_LIBS=`$PKG_CONFIG --libs "gupnp-av-1.0 >= 0.11.1" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -4579,14 +4696,14 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-               GUPNPAV_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gupnp-av-1.0" 2>&1`
+               GUPNPAV_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gupnp-av-1.0 >= 0.11.1" 2>&1`
         else
-               GUPNPAV_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gupnp-av-1.0" 2>&1`
+               GUPNPAV_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gupnp-av-1.0 >= 0.11.1" 2>&1`
         fi
        # Put the nasty error message in config.log where it belongs
        echo "$GUPNPAV_PKG_ERRORS" >&5
 
-       as_fn_error $? "Package requirements (gupnp-av-1.0) were not met:
+       as_fn_error $? "Package requirements (gupnp-av-1.0 >= 0.11.1) were not met:
 
 $GUPNPAV_PKG_ERRORS
 
@@ -4627,12 +4744,12 @@ if test -n "$SOUP_CFLAGS"; then
     pkg_cv_SOUP_CFLAGS="$SOUP_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsoup-2.4\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libsoup-2.4") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsoup-2.4 >= 2.28.2\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libsoup-2.4 >= 2.28.2") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_SOUP_CFLAGS=`$PKG_CONFIG --cflags "libsoup-2.4" 2>/dev/null`
+  pkg_cv_SOUP_CFLAGS=`$PKG_CONFIG --cflags "libsoup-2.4 >= 2.28.2" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -4644,12 +4761,12 @@ if test -n "$SOUP_LIBS"; then
     pkg_cv_SOUP_LIBS="$SOUP_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsoup-2.4\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libsoup-2.4") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsoup-2.4 >= 2.28.2\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libsoup-2.4 >= 2.28.2") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_SOUP_LIBS=`$PKG_CONFIG --libs "libsoup-2.4" 2>/dev/null`
+  pkg_cv_SOUP_LIBS=`$PKG_CONFIG --libs "libsoup-2.4 >= 2.28.2" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -4670,14 +4787,14 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-               SOUP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsoup-2.4" 2>&1`
+               SOUP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsoup-2.4 >= 2.28.2" 2>&1`
         else
-               SOUP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsoup-2.4" 2>&1`
+               SOUP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsoup-2.4 >= 2.28.2" 2>&1`
         fi
        # Put the nasty error message in config.log where it belongs
        echo "$SOUP_PKG_ERRORS" >&5
 
-       as_fn_error $? "Package requirements (libsoup-2.4) were not met:
+       as_fn_error $? "Package requirements (libsoup-2.4 >= 2.28.2) were not met:
 
 $SOUP_PKG_ERRORS
 
@@ -4866,7 +4983,7 @@ do
     for ac_prog in grep ggrep; do
     for ac_exec_ext in '' $ac_executable_extensions; do
       ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
-      { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+      as_fn_executable_p "$ac_path_GREP" || continue
 # Check for GNU ac_path_GREP and select it if it is found.
   # Check for GNU $ac_path_GREP
 case `"$ac_path_GREP" --version 2>&1` in
@@ -4932,7 +5049,7 @@ do
     for ac_prog in egrep; do
     for ac_exec_ext in '' $ac_executable_extensions; do
       ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
-      { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+      as_fn_executable_p "$ac_path_EGREP" || continue
 # Check for GNU ac_path_EGREP and select it if it is found.
   # Check for GNU $ac_path_EGREP
 case `"$ac_path_EGREP" --version 2>&1` in
@@ -5146,60 +5263,60 @@ else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-#include <stdbool.h>
-#ifndef bool
- "error: bool is not defined"
-#endif
-#ifndef false
- "error: false is not defined"
-#endif
-#if false
- "error: false is not 0"
-#endif
-#ifndef true
- "error: true is not defined"
-#endif
-#if true != 1
- "error: true is not 1"
-#endif
-#ifndef __bool_true_false_are_defined
- "error: __bool_true_false_are_defined is not defined"
-#endif
-
-       struct s { _Bool s: 1; _Bool t; } s;
-
-       char a[true == 1 ? 1 : -1];
-       char b[false == 0 ? 1 : -1];
-       char c[__bool_true_false_are_defined == 1 ? 1 : -1];
-       char d[(bool) 0.5 == true ? 1 : -1];
-       /* See body of main program for 'e'.  */
-       char f[(_Bool) 0.0 == false ? 1 : -1];
-       char g[true];
-       char h[sizeof (_Bool)];
-       char i[sizeof s.t];
-       enum { j = false, k = true, l = false * true, m = true * 256 };
-       /* The following fails for
-          HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */
-       _Bool n[m];
-       char o[sizeof n == m * sizeof n[0] ? 1 : -1];
-       char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1];
-       /* Catch a bug in an HP-UX C compiler.  See
-          http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
-          http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
-        */
-       _Bool q = true;
-       _Bool *pq = &q;
+             #include <stdbool.h>
+             #ifndef bool
             "error: bool is not defined"
+             #endif
+             #ifndef false
             "error: false is not defined"
+             #endif
+             #if false
             "error: false is not 0"
+             #endif
+             #ifndef true
             "error: true is not defined"
+             #endif
+             #if true != 1
             "error: true is not 1"
+             #endif
+             #ifndef __bool_true_false_are_defined
             "error: __bool_true_false_are_defined is not defined"
+             #endif
+
+             struct s { _Bool s: 1; _Bool t; } s;
+
+             char a[true == 1 ? 1 : -1];
+             char b[false == 0 ? 1 : -1];
+             char c[__bool_true_false_are_defined == 1 ? 1 : -1];
+             char d[(bool) 0.5 == true ? 1 : -1];
+             /* See body of main program for 'e'.  */
+             char f[(_Bool) 0.0 == false ? 1 : -1];
+             char g[true];
+             char h[sizeof (_Bool)];
+             char i[sizeof s.t];
+             enum { j = false, k = true, l = false * true, m = true * 256 };
+             /* The following fails for
+                HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */
+             _Bool n[m];
+             char o[sizeof n == m * sizeof n[0] ? 1 : -1];
+             char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1];
+             /* Catch a bug in an HP-UX C compiler.  See
+                http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
+                http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
+              */
+             _Bool q = true;
+             _Bool *pq = &q;
 
 int
 main ()
 {
 
-       bool e = &s;
-       *pq |= q;
-       *pq |= ! q;
-       /* Refer to every declared value, to avoid compiler optimizations.  */
-       return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l
-               + !m + !n + !o + !p + !q + !pq);
+             bool e = &s;
+             *pq |= q;
+             *pq |= ! q;
+             /* Refer to every declared value, to avoid compiler optimizations.  */
+             return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l
+                     + !m + !n + !o + !p + !q + !pq);
 
   ;
   return 0;
@@ -5214,7 +5331,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5
 $as_echo "$ac_cv_header_stdbool_h" >&6; }
-ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default"
+   ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default"
 if test "x$ac_cv_type__Bool" = xyes; then :
 
 cat >>confdefs.h <<_ACEOF
@@ -5224,6 +5341,7 @@ _ACEOF
 
 fi
 
+
 if test $ac_cv_header_stdbool_h = yes; then
 
 $as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h
@@ -6107,16 +6225,16 @@ if (echo >conf$$.file) 2>/dev/null; then
     # ... but there are two gotchas:
     # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
     # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
-    # In both cases, we have to default to `cp -p'.
+    # In both cases, we have to default to `cp -pR'.
     ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
-      as_ln_s='cp -p'
+      as_ln_s='cp -pR'
   elif ln conf$$.file conf$$ 2>/dev/null; then
     as_ln_s=ln
   else
-    as_ln_s='cp -p'
+    as_ln_s='cp -pR'
   fi
 else
-  as_ln_s='cp -p'
+  as_ln_s='cp -pR'
 fi
 rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
 rmdir conf$$.dir 2>/dev/null
@@ -6176,28 +6294,16 @@ else
   as_mkdir_p=false
 fi
 
-if test -x / >/dev/null 2>&1; then
-  as_test_x='test -x'
-else
-  if ls -dL / >/dev/null 2>&1; then
-    as_ls_L_option=L
-  else
-    as_ls_L_option=
-  fi
-  as_test_x='
-    eval sh -c '\''
-      if test -d "$1"; then
-       test -d "$1/.";
-      else
-       case $1 in #(
-       -*)set "./$1";;
-       esac;
-       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
-       ???[sx]*):;;*)false;;esac;fi
-    '\'' sh
-  '
-fi
-as_executable_p=$as_test_x
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
 
 # Sed expression to map a string onto a valid CPP name.
 as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
@@ -6218,8 +6324,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by media-service-upnp $as_me 0.1.0, which was
-generated by GNU Autoconf 2.68.  Invocation command line was
+This file was extended by media-service-upnp $as_me 0.3.0, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
   CONFIG_HEADERS  = $CONFIG_HEADERS
@@ -6285,11 +6391,11 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-media-service-upnp config.status 0.1.0
-configured by $0, generated by GNU Autoconf 2.68,
+media-service-upnp config.status 0.3.0
+configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
-Copyright (C) 2010 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
 This config.status script is free software; the Free Software Foundation
 gives unlimited permission to copy, distribute and modify it."
 
@@ -6380,7 +6486,7 @@ fi
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 if \$ac_cs_recheck; then
-  set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
   shift
   \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
   CONFIG_SHELL='$SHELL'
index a977494..3eb0a84 100644 (file)
@@ -1,7 +1,7 @@
 AC_PREREQ([2.66])
 
 AC_INIT([media-service-upnp],
-       [0.1.0],
+       [0.3.0],
        [https://github.com/01org/media-service-upnp/issues/new],
        ,
        [https://01.org/dleyna/])
@@ -33,9 +33,10 @@ PKG_PROG_PKG_CONFIG(0.16)
 PKG_CHECK_MODULES([DBUS], [dbus-1])
 PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.26.1])
 PKG_CHECK_MODULES([GIO], [gio-2.0 >= 2.26.1])
-PKG_CHECK_MODULES([GUPNP], [gupnp-1.0 >= 0.17.2])
-PKG_CHECK_MODULES([GUPNPAV], [gupnp-av-1.0])
-PKG_CHECK_MODULES([SOUP], [libsoup-2.4])
+PKG_CHECK_MODULES([GSSDP], [gssdp-1.0 >= 0.13.0])
+PKG_CHECK_MODULES([GUPNP], [gupnp-1.0 >= 0.19.1])
+PKG_CHECK_MODULES([GUPNPAV], [gupnp-av-1.0 >= 0.11.1])
+PKG_CHECK_MODULES([SOUP], [libsoup-2.4 >= 2.28.2])
 
 # Checks for header files.
 AC_CHECK_HEADERS([stdlib.h string.h syslog.h])
index 248a50a..885b81a 100644 (file)
@@ -47,6 +47,7 @@ AC_DEFUN_ONCE([MSU_COMPILER_FLAGS], [
                CFLAGS+=" -Wno-overlength-strings"
 
                CFLAGS+=" -DG_DISABLE_DEPRECATED"
+               CFLAGS+=" -DGLIB_DISABLE_DEPRECATION_WARNINGS"
        fi
 
        CFLAGS+=" -Wno-format-extra-args"
index d6f2bd7..8b74ac0 100644 (file)
@@ -1,2 +1,5 @@
+* Wed Nov 14 11:19:45 CET 2012 - Mark Ryan <mark.d.ryan@intel.com>
+- Submit version 0.3.0 ( ca17a69 ) of media-service-upnp
+
 * Mon Sep 24 14:46:57 CEST 2012 - Mark Ryan <mark.d.ryan@intel.com>
-- Submit version 0.10.0 ( d56381a) of media-service-upnp
+- Submit version 0.1.0 ( d56381a ) of media-service-upnp
index a52fa07..2cb1fe8 100644 (file)
@@ -7,7 +7,7 @@
 
 Name:       media-service-upnp
 Summary:    A high level API for discovering, browsing and searching digital media servers.
-Version:    0.1.0
+Version:    0.3.0
 Release:    0
 Group:      System/Libraries
 License:    LGPLv2+
@@ -19,7 +19,9 @@ BuildRequires:  pkgconfig(dbus-1)
 BuildRequires:  pkgconfig(glib-2.0)
 BuildRequires:  pkgconfig(gio-2.0)
 BuildRequires:  pkgconfig(gupnp-1.0)
+BuildRequires:  pkgconfig(gssdp-1.0)
 BuildRequires:  pkgconfig(gupnp-av-1.0)
+BuildRequires:  pkgconfig(libsoup-2.4)
 
 
 %description
index 9e2d8d0..7f6c440 100644 (file)
 #include "log.h"
 
 msu_async_cb_data_t *msu_async_cb_data_new(msu_task_t *task,
-                                          msu_upnp_task_complete_t cb,
-                                          void *user_data)
+                                          msu_upnp_task_complete_t cb)
 {
        msu_async_cb_data_t *cb_data = g_new0(msu_async_cb_data_t, 1);
 
        cb_data->type = task->type;
        cb_data->task = task;
        cb_data->cb = cb;
-       cb_data->user_data = user_data;
 
        return cb_data;
 }
@@ -62,6 +60,13 @@ void msu_async_cb_data_delete(msu_async_cb_data_t *cb_data)
                        g_free(cb_data->ut.upload.root_path);
                        g_free(cb_data->ut.upload.mime_type);
                        break;
+               case MSU_TASK_CREATE_CONTAINER:
+                       g_free(cb_data->ut.create_container.root_path);
+                       break;
+               case MSU_TASK_UPDATE_OBJECT:
+                       g_free(cb_data->ut.update.current_tag_value);
+                       g_free(cb_data->ut.update.new_tag_value);
+                       break;
                default:
                        break;
                }
@@ -78,8 +83,7 @@ gboolean msu_async_complete_task(gpointer user_data)
        MSU_LOG_DEBUG("Enter. Error %p", (void *) cb_data->error);
        MSU_LOG_DEBUG_NL();
 
-       cb_data->cb(cb_data->task, cb_data->result, cb_data->error,
-                   cb_data->user_data);
+       cb_data->cb(cb_data->task, cb_data->result, cb_data->error);
        msu_async_cb_data_delete(cb_data);
 
        return FALSE;
index 43db7a9..0bc4a8c 100644 (file)
@@ -60,6 +60,8 @@ struct msu_async_get_all_t_ {
        guint32 filter_mask;
        const gchar *protocol_info;
        gboolean need_child_count;
+       gboolean device_object;
+       msu_device_t *device;
 };
 
 typedef struct msu_async_upload_t_ msu_async_upload_t;
@@ -70,11 +72,22 @@ struct msu_async_upload_t_ {
        msu_device_t *device;
 };
 
+typedef struct msu_async_create_container_t_ msu_async_create_container_t;
+struct msu_async_create_container_t_ {
+       gchar *root_path;
+};
+
+typedef struct msu_async_update_t_ msu_async_update_t;
+struct msu_async_update_t_ {
+       gchar *current_tag_value;
+       gchar *new_tag_value;
+       GHashTable *map;
+};
+
 struct msu_async_cb_data_t_ {
        msu_task_type_t type;
        msu_task_t *task;
        msu_upnp_task_complete_t cb;
-       void *user_data;
        GVariant *result;
        GError *error;
        GUPnPServiceProxyAction *action;
@@ -87,12 +100,13 @@ struct msu_async_cb_data_t_ {
                msu_async_get_prop_t get_prop;
                msu_async_get_all_t get_all;
                msu_async_upload_t upload;
+               msu_async_create_container_t create_container;
+               msu_async_update_t update;
        } ut;
 };
 
 msu_async_cb_data_t *msu_async_cb_data_new(msu_task_t *task,
-                                         msu_upnp_task_complete_t cb,
-                                         void *user_data);
+                                         msu_upnp_task_complete_t cb);
 void msu_async_cb_data_delete(msu_async_cb_data_t *cb_data);
 gboolean msu_async_complete_task(gpointer user_data);
 void msu_async_task_cancelled(GCancellable *cancellable, gpointer user_data);
diff --git a/src/chain-task.c b/src/chain-task.c
new file mode 100644 (file)
index 0000000..27f99fb
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * media-service-upnp
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Ludovic Ferrandis <ludovic.ferrandis@intel.com>
+ *
+ */
+
+#include <glib.h>
+
+#include "chain-task.h"
+#include "device.h"
+#include "log.h"
+
+typedef struct msu_chain_task_atom_t_ msu_chain_task_atom_t;
+struct msu_chain_task_atom_t_ {
+       GUPnPServiceProxyActionCallback callback;
+       GUPnPServiceProxyAction *p_action;
+       GDestroyNotify free_func;
+       gpointer user_data;
+       msu_chain_task_action t_action;
+       msu_device_t *device;
+};
+
+struct msu_chain_task_t_ {
+       msu_chain_task_end end_func;
+       gpointer end_data;
+       GList *task_list;
+       msu_chain_task_atom_t *current;
+       gboolean canceled;
+       guint idle_id;
+};
+
+static void prv_free_atom(msu_chain_task_atom_t *atom)
+{
+       if (atom->free_func != NULL)
+               atom->free_func(atom->user_data);
+
+       g_free(atom);
+}
+
+static gboolean prv_idle_end_func(gpointer user_data)
+{
+       msu_chain_task_t *chain = (msu_chain_task_t *)user_data;
+
+       chain->end_func(chain, chain->end_data);
+       return FALSE;
+}
+
+static gboolean prv_idle_next_task(gpointer user_data)
+{
+       msu_chain_task_t *chain = (msu_chain_task_t *) user_data;
+       GList *head = chain->task_list;
+
+       chain->task_list = g_list_remove_link(chain->task_list, head);
+       g_list_free_1(head);
+
+       chain->idle_id = 0;
+       msu_chain_task_start(chain);
+
+       return FALSE;
+}
+
+static void prv_next_task(msu_chain_task_t *chain)
+{
+       chain->idle_id = g_idle_add(prv_idle_next_task, chain);
+}
+
+gboolean msu_chain_task_is_canceled(msu_chain_task_t *chain)
+{
+       return chain->canceled;
+}
+
+msu_device_t *msu_chain_task_get_device(msu_chain_task_t *chain)
+{
+       if ((chain != NULL) && (chain->current != NULL))
+               return chain->current->device;
+
+       return NULL;
+}
+
+gpointer *msu_chain_task_get_user_data(msu_chain_task_t *chain)
+{
+       if ((chain != NULL) && (chain->current != NULL))
+               return chain->current->user_data;
+
+       return NULL;
+}
+
+void msu_chain_task_cancel(msu_chain_task_t *chain)
+{
+       msu_device_t *device;
+       msu_device_context_t *context;
+
+       if (chain->idle_id) {
+               g_source_remove(chain->idle_id);
+               chain->idle_id = 0;
+       }
+
+       device = msu_chain_task_get_device(chain);
+       context = msu_device_get_context(device, NULL);
+
+       if (chain->current->p_action) {
+               gupnp_service_proxy_cancel_action(context->service_proxy,
+                                                 chain->current->p_action);
+               chain->current->p_action = 0;
+       }
+
+       chain->canceled = TRUE;
+}
+
+void msu_chain_task_begin_action_cb(GUPnPServiceProxy *proxy,
+                                   GUPnPServiceProxyAction *action,
+                                   gpointer user_data)
+{
+       msu_chain_task_t *chain = (msu_chain_task_t *) user_data;
+       msu_chain_task_atom_t *current;
+
+       if (chain != NULL) {
+               current = chain->current;
+
+               if (chain->current != NULL) {
+                       current->callback(proxy, action, current->user_data);
+                       chain->current->p_action = NULL;
+               }
+
+               prv_next_task(chain);
+       }
+}
+
+void msu_chain_task_start(msu_chain_task_t *chain)
+{
+       gboolean failed;
+
+       if ((chain->task_list != NULL) && (!chain->canceled)) {
+               chain->current = chain->task_list->data;
+               chain->current->p_action = chain->current->t_action(chain,
+                                                                   &failed);
+
+               if (failed)
+                       chain->canceled = TRUE;
+
+               if (chain->current->p_action == NULL &&
+                   chain->current->callback == NULL)
+                       prv_next_task(chain);
+
+       } else {
+               if (chain->end_func)
+                       chain->idle_id = g_idle_add(prv_idle_end_func, chain);
+       }
+}
+
+void msu_chain_task_add(msu_chain_task_t *chain,
+                       msu_chain_task_action action,
+                       msu_device_t *device,
+                       GUPnPServiceProxyActionCallback action_cb,
+                       GDestroyNotify free_func,
+                       gpointer cb_user_data)
+{
+       msu_chain_task_atom_t *atom;
+
+       atom = g_new0(msu_chain_task_atom_t, 1);
+
+       atom->t_action = action;
+       atom->callback = action_cb;
+       atom->free_func = free_func;
+       atom->user_data = cb_user_data;
+       atom->device = device;
+
+       chain->task_list = g_list_append(chain->task_list, atom);
+}
+
+void msu_chain_task_delete(msu_chain_task_t *chain)
+{
+       g_list_free_full(chain->task_list, (GDestroyNotify)prv_free_atom);
+       chain->task_list = NULL;
+       g_free(chain);
+}
+
+msu_chain_task_t *msu_chain_task_new(msu_chain_task_end end_func,
+                                    gpointer end_data)
+{
+       msu_chain_task_t *chain;
+
+       chain = g_new0(msu_chain_task_t, 1);
+       chain->end_func = end_func;
+       chain->end_data = end_data;
+
+       return chain;
+}
diff --git a/src/chain-task.h b/src/chain-task.h
new file mode 100644 (file)
index 0000000..39b6470
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * media-service-upnp
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Ludovic Ferrandis <ludovic.ferrandis@intel.com>
+ *
+ */
+
+#ifndef MSU_CHAIN_TASK_H__
+#define MSU_CHAIN_TASK_H__
+
+#include <libgupnp/gupnp-service-proxy.h>
+#include "async.h"
+
+typedef struct msu_chain_task_t_ msu_chain_task_t;
+
+typedef GUPnPServiceProxyAction * (*msu_chain_task_action)
+                               (msu_chain_task_t *chain, gboolean *failed);
+
+typedef void (*msu_chain_task_end)(msu_chain_task_t *chain, gpointer data);
+
+msu_chain_task_t *msu_chain_task_new(msu_chain_task_end end_func,
+                                    gpointer end_data);
+
+void msu_chain_task_delete(msu_chain_task_t *chain);
+
+void msu_chain_task_add(msu_chain_task_t *chain,
+                       msu_chain_task_action action,
+                       msu_device_t *device,
+                       GUPnPServiceProxyActionCallback action_cb,
+                       GDestroyNotify free_func,
+                       gpointer cb_user_data);
+
+void msu_chain_task_start(msu_chain_task_t *chain);
+
+void msu_chain_task_begin_action_cb(GUPnPServiceProxy *proxy,
+                                   GUPnPServiceProxyAction *action,
+                                   gpointer user_data);
+
+void msu_chain_task_cancel(msu_chain_task_t *chain);
+gboolean msu_chain_task_is_canceled(msu_chain_task_t *chain);
+
+msu_device_t *msu_chain_task_get_device(msu_chain_task_t *chain);
+gpointer *msu_chain_task_get_user_data(msu_chain_task_t *chain);
+
+#endif /* MSU_CHAIN_TASK_H__ */
diff --git a/src/client.h b/src/client.h
new file mode 100644 (file)
index 0000000..aab6f7a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * media-service-upnp
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Regis Merlino <regis.merlino@intel.com>
+ *
+ */
+
+#ifndef MSU_CLIENT_H__
+#define MSU_CLIENT_H__
+
+#include <glib.h>
+
+typedef struct msu_client_t_ msu_client_t;
+struct msu_client_t_ {
+       guint id;
+       gchar *protocol_info;
+       gboolean prefer_local_addresses;
+};
+
+
+#endif /* MSU_CLIENT_H__ */
index 0400238..871daf6 100644 (file)
@@ -24,6 +24,7 @@
 #include <libgupnp/gupnp-error.h>
 #include <libsoup/soup.h>
 
+#include "chain-task.h"
 #include "device.h"
 #include "error.h"
 #include "interface.h"
 
 #define MSU_SYSTEM_UPDATE_VAR "SystemUpdateID"
 #define MSU_CONTAINER_UPDATE_VAR "ContainerUpdateIDs"
+#define MEDIA_SERVER_DEVICE_TYPE "urn:schemas-upnp-org:device:MediaServer:"
+
+#define MSU_UPLOAD_STATUS_IN_PROGRESS "IN_PROGRESS"
+#define MSU_UPLOAD_STATUS_CANCELLED "CANCELLED"
+#define MSU_UPLOAD_STATUS_ERROR "ERROR"
+#define MSU_UPLOAD_STATUS_COMPLETED "COMPLETED"
 
 typedef gboolean (*msu_device_count_cb_t)(msu_async_cb_data_t *cb_data,
                                          gint count);
@@ -49,17 +56,32 @@ struct msu_device_object_builder_t_ {
        gboolean needs_child_count;
 };
 
+typedef struct msu_device_upload_job_t_ msu_device_upload_job_t;
+
 typedef struct msu_device_upload_t_ msu_device_upload_t;
 struct msu_device_upload_t_ {
        SoupSession *soup_session;
        SoupMessage *msg;
        GMappedFile *mapped_file;
+       const gchar *status;
+       guint64 bytes_uploaded;
+       guint64 bytes_to_upload;
 };
 
-typedef struct msu_device_upload_job_t_ msu_device_upload_job_t;
 struct msu_device_upload_job_t_ {
        gint upload_id;
        msu_device_t *device;
+       guint remove_idle;
+};
+
+/* Private structure used in chain task */
+typedef struct prv_new_device_ct_t_ prv_new_device_ct_t;
+struct prv_new_device_ct_t_ {
+       msu_device_t *dev;
+       GDBusConnection *connection;
+       const GDBusSubtreeVTable *vtable;
+       void *user_data;
+       GHashTable *property_map;
 };
 
 static void prv_get_child_count(msu_async_cb_data_t *cb_data,
@@ -74,6 +96,11 @@ static void prv_system_update_cb(GUPnPServiceProxy *proxy,
                                GValue *value,
                                gpointer user_data);
 static void prv_msu_device_upload_delete(gpointer up);
+static void prv_msu_upload_job_delete(gpointer up);
+static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
+                            msu_device_t *device,
+                            GCancellable *cancellable,
+                            msu_async_cb_data_t *cb_data);
 
 static void prv_msu_device_object_builder_delete(void *dob)
 {
@@ -118,6 +145,8 @@ static void prv_msu_context_delete(gpointer context)
                                                MSU_CONTAINER_UPDATE_VAR,
                                                prv_container_update_cb,
                                                ctx->device);
+                       gupnp_service_proxy_set_subscribed(ctx->service_proxy,
+                                                          FALSE);
                }
 
                if (ctx->device_proxy)
@@ -158,6 +187,8 @@ void msu_device_delete(void *device)
        msu_device_t *dev = device;
 
        if (dev) {
+               dev->shutting_down = TRUE;
+               g_hash_table_unref(dev->upload_jobs);
                g_hash_table_unref(dev->uploads);
 
                if (dev->timeout_id)
@@ -169,6 +200,10 @@ void msu_device_delete(void *device)
 
                g_ptr_array_unref(dev->contexts);
                g_free(dev->path);
+               g_variant_unref(dev->search_caps);
+               g_variant_unref(dev->sort_caps);
+               g_variant_unref(dev->sort_ext_caps);
+               g_variant_unref(dev->feature_list);
                g_free(dev);
        }
 }
@@ -229,17 +264,31 @@ static void prv_system_update_cb(GUPnPServiceProxy *proxy,
                                 GValue *value,
                                 gpointer user_data)
 {
+       GVariantBuilder *array;
+       GVariant *val;
        msu_device_t *device = user_data;
+       guint suid = g_value_get_uint(value);
+
+       MSU_LOG_DEBUG("System Update %u", suid);
+
+       device->system_update_id = suid;
 
-       MSU_LOG_DEBUG("System Update %u", g_value_get_uint(value));
+       array = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+       g_variant_builder_add(array, "{sv}", MSU_INTERFACE_SYSTEM_UPDATE_ID,
+                             g_variant_new_uint32(suid));
+       val = g_variant_new("(s@a{sv}as)", MSU_INTERFACE_MEDIA_DEVICE,
+                           g_variant_builder_end(array),
+                           NULL);
 
        (void) g_dbus_connection_emit_signal(device->connection,
-                       NULL,
-                       device->path,
-                       MSU_INTERFACE_MEDIA_DEVICE,
-                       MSU_INTERFACE_SYSTEM_UPDATE,
-                       g_variant_new("(u)", g_value_get_uint(value)),
-                       NULL);
+                                            NULL,
+                                            device->path,
+                                            MSU_INTERFACE_PROPERTIES,
+                                            MSU_INTERFACE_PROPERTIES_CHANGED,
+                                            val,
+                                            NULL);
+
+       g_variant_builder_unref(array);
 }
 
 static gboolean prv_re_enable_subscription(gpointer user_data)
@@ -283,7 +332,7 @@ void msu_device_subscribe_to_contents_change(msu_device_t *device)
 {
        msu_device_context_t *context;
 
-       context = msu_device_get_context(device);
+       context = msu_device_get_context(device, NULL);
 
        MSU_LOG_DEBUG("Subscribe for events on context: %s",
                      context->ip_address);
@@ -293,6 +342,7 @@ void msu_device_subscribe_to_contents_change(msu_device_t *device)
                                G_TYPE_UINT,
                                prv_system_update_cb,
                                device);
+
        gupnp_service_proxy_add_notify(context->service_proxy,
                                MSU_CONTAINER_UPDATE_VAR,
                                G_TYPE_STRING,
@@ -308,60 +358,437 @@ void msu_device_subscribe_to_contents_change(msu_device_t *device)
                                context);
 }
 
-gboolean msu_device_new(GDBusConnection *connection,
-                       GUPnPDeviceProxy *proxy,
-                       const gchar *ip_address,
-                       const GDBusSubtreeVTable *vtable,
-                       void *user_data,
-                       guint counter,
-                       msu_device_t **device)
+static void prv_feature_list_add_feature(gchar* root_path,
+                                        GUPnPFeature *feature,
+                                        GVariantBuilder *vb)
 {
-       msu_device_t *dev = g_new0(msu_device_t, 1);
-       guint flags;
-       guint id;
-       GString *new_path = NULL;
+       GVariantBuilder vbo;
+       GVariant *var_obj;
+       const char *name;
+       const char *version;
+       const char *obj_id;
+       gchar **obj;
+       gchar **saved;
+       gchar *path;
 
-       MSU_LOG_DEBUG("Enter");
+       name = gupnp_feature_get_name(feature);
+       version = gupnp_feature_get_version(feature);
+       obj_id = gupnp_feature_get_object_ids(feature);
 
-       dev->connection = connection;
-       dev->contexts = g_ptr_array_new_with_free_func(prv_msu_context_delete);
-       msu_device_append_new_context(dev, ip_address, proxy);
+       g_variant_builder_init(&vbo, G_VARIANT_TYPE("ao"));
 
-       msu_device_subscribe_to_contents_change(dev);
+       if (obj_id != NULL && *obj_id) {
+               obj = g_strsplit(obj_id, ",", 0);
+               saved = obj;
 
-       new_path = g_string_new("");
-       g_string_printf(new_path, "%s/%u", MSU_SERVER_PATH, counter);
+               while (obj && *obj) {
+                       path = msu_path_from_id(root_path, *obj);
+                       g_variant_builder_add(&vbo, "o", path);
+                       g_free(path);
+                       obj++;
+               }
 
-       MSU_LOG_DEBUG("Server Path %s", new_path->str);
+               g_strfreev(saved);
+       }
 
-       flags = G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES;
-       id =  g_dbus_connection_register_subtree(connection, new_path->str,
-                                                vtable, flags, user_data, NULL,
-                                                NULL);
-       if (!id)
+       var_obj = g_variant_builder_end(&vbo);
+       g_variant_builder_add(vb, "(ss@ao)", name, version, var_obj);
+}
+
+static void prv_get_feature_list_analyze(msu_device_t *device, gchar *result)
+{
+       GUPnPFeatureListParser *parser;
+       GUPnPFeature *feature;
+       GList *list;
+       GList *item;
+       GError *error = NULL;
+       GVariantBuilder vb;
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       char *str;
+#endif
+       parser = gupnp_feature_list_parser_new();
+       list = gupnp_feature_list_parser_parse_text(parser, result, &error);
+
+       if (error != NULL) {
+               MSU_LOG_WARNING("GetFeatureList parsing failed: %s",
+                               error->message);
+               goto on_exit;
+       }
+
+       g_variant_builder_init(&vb, G_VARIANT_TYPE("a(ssao)"));
+       item = list;
+
+       while (item != NULL) {
+               feature = (GUPnPFeature *) item->data;
+               prv_feature_list_add_feature(device->path, feature, &vb);
+               g_object_unref(feature);
+               item = g_list_next(item);
+       }
+
+       device->feature_list = g_variant_ref_sink(g_variant_builder_end(&vb));
+
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       str = g_variant_print(device->feature_list, FALSE);
+       MSU_LOG_DEBUG("%s = %s", MSU_INTERFACE_PROP_SV_FEATURE_LIST, str);
+       g_free(str);
+#endif
+
+on_exit:
+       g_list_free(list);
+       g_object_unref(parser);
+
+       if (error != NULL)
+               g_error_free(error);
+}
+
+static void prv_get_feature_list_cb(GUPnPServiceProxy *proxy,
+                                   GUPnPServiceProxyAction *action,
+                                   gpointer user_data)
+{
+       gchar *result = NULL;
+       GError *error = NULL;
+       prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
+
+       if (!gupnp_service_proxy_end_action(proxy, action, &error,
+                                           "FeatureList", G_TYPE_STRING,
+                                           &result, NULL)) {
+               MSU_LOG_WARNING("GetFeatureList operation failed: %s",
+                               error->message);
                goto on_error;
+       }
 
-       dev->path = g_string_free(new_path, FALSE);
-       dev->id = id;
+       MSU_LOG_DEBUG("GetFeatureList result: %s", result);
 
-       dev->uploads = g_hash_table_new_full(g_int_hash, g_int_equal, g_free,
-                                            prv_msu_device_upload_delete);
+       prv_get_feature_list_analyze(priv_t->dev, result);
 
-       *device = dev;
+on_error:
+       if (error != NULL)
+               g_error_free(error);
 
-       MSU_LOG_DEBUG("Exit with SUCCESS");
+       g_free(result);
+}
 
-       return TRUE;
+static GUPnPServiceProxyAction *prv_get_feature_list(msu_chain_task_t *chain,
+                                                    gboolean *failed)
+{
+       msu_device_t *device;
+       msu_device_context_t *context;
+
+       device = msu_chain_task_get_device(chain);
+       context = msu_device_get_context(device, NULL);
+       *failed = FALSE;
+
+       return gupnp_service_proxy_begin_action(context->service_proxy,
+                                               "GetFeatureList",
+                                               msu_chain_task_begin_action_cb,
+                                               chain, NULL);
+}
+
+static void prv_get_sort_ext_capabilities_analyze(msu_device_t *device,
+                                                 gchar *result)
+{
+       gchar **caps;
+       gchar **saved;
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       gchar *props;
+#endif
+       GVariantBuilder sort_ext_caps_vb;
+
+       g_variant_builder_init(&sort_ext_caps_vb, G_VARIANT_TYPE("as"));
+
+       caps = g_strsplit(result, ",", 0);
+       saved = caps;
+
+       while (caps && *caps) {
+               g_variant_builder_add(&sort_ext_caps_vb, "s", *caps);
+               caps++;
+       }
+
+       g_strfreev(saved);
+
+       device->sort_ext_caps = g_variant_ref_sink(g_variant_builder_end(
+                                                       &sort_ext_caps_vb));
+
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       props = g_variant_print(device->sort_ext_caps, FALSE);
+       MSU_LOG_DEBUG("%s = %s", MSU_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES,
+                                props);
+       g_free(props);
+#endif
+}
+
+static void prv_get_sort_ext_capabilities_cb(GUPnPServiceProxy *proxy,
+                                          GUPnPServiceProxyAction *action,
+                                          gpointer user_data)
+{
+       gchar *result = NULL;
+       GError *error = NULL;
+       prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
+
+       if (!gupnp_service_proxy_end_action(proxy, action, &error,
+                                           "SortExtensionCaps",
+                                           G_TYPE_STRING, &result, NULL)) {
+               MSU_LOG_WARNING(
+                       "GetSortExtensionCapabilities operation failed: %s",
+                       error->message);
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("GetSortExtensionCapabilities result: %s", result);
+
+       prv_get_sort_ext_capabilities_analyze(priv_t->dev, result);
 
 on_error:
-       if (new_path)
-               g_string_free(new_path, TRUE);
 
-       msu_device_delete(dev);
+       if (error)
+               g_error_free(error);
 
-       MSU_LOG_DEBUG("Exit with FAIL");
+       g_free(result);
+}
 
-       return FALSE;
+static GUPnPServiceProxyAction *prv_get_sort_ext_capabilities(
+                                                       msu_chain_task_t *chain,
+                                                       gboolean *failed)
+{
+       msu_device_t *device;
+       msu_device_context_t *context;
+
+       device = msu_chain_task_get_device(chain);
+       context = msu_device_get_context(device, NULL);
+       *failed = FALSE;
+
+       return gupnp_service_proxy_begin_action(context->service_proxy,
+                                               "GetSortExtensionCapabilities",
+                                               msu_chain_task_begin_action_cb,
+                                               chain, NULL);
+}
+
+static void prv_get_capabilities_analyze(GHashTable *property_map,
+                                        gchar *result,
+                                        GVariant **variant)
+{
+       gchar **caps;
+       gchar **saved;
+       gchar *prop_name;
+       GVariantBuilder caps_vb;
+
+       g_variant_builder_init(&caps_vb, G_VARIANT_TYPE("as"));
+
+       caps = g_strsplit(result, ",", 0);
+       saved = caps;
+
+       while (caps && *caps) {
+               prop_name = g_hash_table_lookup(property_map, *caps);
+
+               if (prop_name)
+                       g_variant_builder_add(&caps_vb, "s", prop_name);
+
+               caps++;
+       }
+
+       g_strfreev(saved);
+
+       *variant = g_variant_ref_sink(g_variant_builder_end(&caps_vb));
+
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       prop_name = g_variant_print(*variant, FALSE);
+       MSU_LOG_DEBUG("%s = %s", "   Variant", prop_name);
+       g_free(prop_name);
+#endif
+}
+
+static void prv_get_sort_capabilities_cb(GUPnPServiceProxy *proxy,
+                                          GUPnPServiceProxyAction *action,
+                                          gpointer user_data)
+{
+       gchar *result = NULL;
+       GError *error = NULL;
+       prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
+
+       if (!gupnp_service_proxy_end_action(proxy, action, &error, "SortCaps",
+                                           G_TYPE_STRING, &result, NULL)) {
+               MSU_LOG_WARNING("GetSortCapabilities operation failed: %s",
+                               error->message);
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("GetSortCapabilities result: %s", result);
+
+       prv_get_capabilities_analyze(priv_t->property_map, result,
+                                    &priv_t->dev->sort_caps);
+
+on_error:
+
+       if (error)
+               g_error_free(error);
+
+       g_free(result);
+}
+
+static GUPnPServiceProxyAction *prv_get_sort_capabilities(
+                                       msu_chain_task_t *chain,
+                                       gboolean *failed)
+{
+       msu_device_t *device;
+       msu_device_context_t *context;
+
+       device = msu_chain_task_get_device(chain);
+       context = msu_device_get_context(device, NULL);
+       *failed = FALSE;
+
+       return gupnp_service_proxy_begin_action(context->service_proxy,
+                                               "GetSortCapabilities",
+                                               msu_chain_task_begin_action_cb,
+                                               chain, NULL);
+}
+
+static void prv_get_search_capabilities_cb(GUPnPServiceProxy *proxy,
+                                          GUPnPServiceProxyAction *action,
+                                          gpointer user_data)
+{
+       gchar *result = NULL;
+       GError *error = NULL;
+       prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
+
+       if (!gupnp_service_proxy_end_action(proxy, action, &error, "SearchCaps",
+                                           G_TYPE_STRING, &result, NULL)) {
+               MSU_LOG_WARNING("GetSearchCapabilities operation failed: %s",
+                               error->message);
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("GetSearchCapabilities result: %s", result);
+
+       prv_get_capabilities_analyze(priv_t->property_map, result,
+                                    &priv_t->dev->search_caps);
+
+on_error:
+
+       if (error)
+               g_error_free(error);
+
+       g_free(result);
+}
+
+static GUPnPServiceProxyAction *prv_get_search_capabilities(
+                                       msu_chain_task_t *chain,
+                                       gboolean *failed)
+{
+       msu_device_t *device;
+       msu_device_context_t *context;
+
+       device = msu_chain_task_get_device(chain);
+       context = msu_device_get_context(device, NULL);
+       *failed = FALSE;
+
+       return gupnp_service_proxy_begin_action(context->service_proxy,
+                                               "GetSearchCapabilities",
+                                               msu_chain_task_begin_action_cb,
+                                               chain, NULL);
+}
+
+static GUPnPServiceProxyAction *prv_subscribe(msu_chain_task_t *chain,
+                                             gboolean *failed)
+{
+       msu_device_t *device;
+
+       device = msu_chain_task_get_device(chain);
+       msu_device_subscribe_to_contents_change(device);
+
+       *failed = FALSE;
+
+       return NULL;
+}
+
+static GUPnPServiceProxyAction *prv_declare(msu_chain_task_t *chain,
+                                           gboolean *failed)
+{
+       guint flags;
+       guint id;
+       msu_device_t *device;
+       prv_new_device_ct_t *priv_t;
+
+       device = msu_chain_task_get_device(chain);
+
+       priv_t = (prv_new_device_ct_t *) msu_chain_task_get_user_data(chain);
+
+       flags = G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES;
+       id =  g_dbus_connection_register_subtree(priv_t->connection,
+                                                device->path,
+                                                priv_t->vtable,
+                                                flags,
+                                                priv_t->user_data,
+                                                NULL, NULL);
+       if (id) {
+               device->id = id;
+
+               device->uploads = g_hash_table_new_full(g_int_hash, g_int_equal,
+                                               g_free,
+                                               prv_msu_device_upload_delete);
+               device->upload_jobs =
+                       g_hash_table_new_full(g_int_hash, g_int_equal,
+                                             g_free,
+                                             prv_msu_upload_job_delete);
+
+       } else
+               MSU_LOG_ERROR("g_dbus_connection_register_subtree FAILED");
+
+       *failed = (!id);
+
+       return NULL;
+}
+
+msu_device_t *msu_device_new(GDBusConnection *connection,
+                            GUPnPDeviceProxy *proxy,
+                            const gchar *ip_address,
+                            const GDBusSubtreeVTable *vtable,
+                            void *user_data,
+                            GHashTable *property_map,
+                            guint counter,
+                            msu_chain_task_t *chain)
+{
+       msu_device_t *dev;
+       prv_new_device_ct_t *priv_t;
+       gchar *new_path;
+
+       MSU_LOG_DEBUG("New Device on %s", ip_address);
+
+       new_path = g_strdup_printf("%s/%u", MSU_SERVER_PATH, counter);
+       MSU_LOG_DEBUG("Server Path %s", new_path);
+
+       dev = g_new0(msu_device_t, 1);
+       priv_t = g_new0(prv_new_device_ct_t, 1);
+
+       dev->connection = connection;
+       dev->contexts = g_ptr_array_new_with_free_func(prv_msu_context_delete);
+       dev->path = new_path;
+
+       priv_t->dev = dev;
+       priv_t->connection = connection;
+       priv_t->vtable = vtable;
+       priv_t->user_data = user_data;
+       priv_t->property_map = property_map;
+
+       msu_device_append_new_context(dev, ip_address, proxy);
+
+       msu_chain_task_add(chain, prv_get_search_capabilities, dev,
+                          prv_get_search_capabilities_cb, NULL, priv_t);
+
+       msu_chain_task_add(chain, prv_get_sort_capabilities, dev,
+                          prv_get_sort_capabilities_cb, NULL, priv_t);
+
+       msu_chain_task_add(chain, prv_get_sort_ext_capabilities, dev,
+                          prv_get_sort_ext_capabilities_cb, NULL, priv_t);
+
+       msu_chain_task_add(chain, prv_get_feature_list, dev,
+                          prv_get_feature_list_cb, NULL, priv_t);
+MSU_LOG_DEBUG("AFTER");
+       msu_chain_task_add(chain, prv_subscribe, dev, NULL, NULL, NULL);
+       msu_chain_task_add(chain, prv_declare, dev, NULL, g_free, priv_t);
+
+       msu_chain_task_start(chain);
+
+       return dev;
 }
 
 void msu_device_append_new_context(msu_device_t *device,
@@ -395,19 +822,26 @@ msu_device_t *msu_device_from_path(const gchar *path, GHashTable *device_list)
        return retval;
 }
 
-msu_device_context_t *msu_device_get_context(msu_device_t *device)
+msu_device_context_t *msu_device_get_context(msu_device_t *device,
+                                            msu_client_t *client)
 {
        msu_device_context_t *context;
        unsigned int i;
        const char ip4_local_prefix[] = "127.0.0.";
+       gboolean prefer_local;
+       gboolean is_local;
+
+       prefer_local = (client && client->prefer_local_addresses);
 
        for (i = 0; i < device->contexts->len; ++i) {
                context = g_ptr_array_index(device->contexts, i);
 
-               if (!strncmp(context->ip_address, ip4_local_prefix,
-                            sizeof(ip4_local_prefix) - 1) ||
-                   !strcmp(context->ip_address, "::1") ||
-                   !strcmp(context->ip_address, "0:0:0:0:0:0:0:1"))
+               is_local = (!strncmp(context->ip_address, ip4_local_prefix,
+                                               sizeof(ip4_local_prefix) - 1) ||
+                           !strcmp(context->ip_address, "::1") ||
+                           !strcmp(context->ip_address, "0:0:0:0:0:0:0:1"));
+
+               if (prefer_local == is_local)
                        break;
        }
 
@@ -627,8 +1061,8 @@ no_complete:
        MSU_LOG_DEBUG("Exit");
 }
 
-void msu_device_get_children(msu_device_t *device,  msu_task_t *task,
-                            msu_async_cb_data_t *cb_data,
+void msu_device_get_children(msu_device_t *device, msu_client_t *client,
+                            msu_task_t *task, msu_async_cb_data_t *cb_data,
                             const gchar *upnp_filter, const gchar *sort_by,
                             GCancellable *cancellable)
 {
@@ -636,7 +1070,7 @@ void msu_device_get_children(msu_device_t *device,  msu_task_t *task,
 
        MSU_LOG_DEBUG("Enter");
 
-       context = msu_device_get_context(device);
+       context = msu_device_get_context(device, client);
 
        cb_data->action =
                gupnp_service_proxy_begin_action(context->service_proxy,
@@ -769,349 +1203,337 @@ static void prv_get_all(GUPnPDIDLLiteParser *parser,
        }
 }
 
-static gboolean prv_get_all_child_count_cb(msu_async_cb_data_t *cb_data,
-                                      gint count)
+static gboolean prv_device_subscribed(msu_device_t *device)
 {
-       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+       msu_device_context_t *context;
+       unsigned int i;
+       gboolean subscribed = FALSE;
 
-       msu_props_add_child_count(cb_task_data->vb, count);
-       cb_data->result = g_variant_ref_sink(g_variant_builder_end(
-                                                    cb_task_data->vb));
-       return TRUE;
+       for (i = 0; i < device->contexts->len; ++i) {
+               context = g_ptr_array_index(device->contexts, i);
+               if (context->subscribed) {
+                       subscribed = TRUE;
+                       break;
+               }
+       }
+
+       return subscribed;
 }
 
-static void prv_get_all_ms2spec_props_cb(GUPnPServiceProxy *proxy,
-                                        GUPnPServiceProxyAction *action,
-                                        gpointer user_data)
+static void prv_system_update_id_for_prop_cb(GUPnPServiceProxy *proxy,
+                                   GUPnPServiceProxyAction *action,
+                                   gpointer user_data)
 {
        GError *upnp_error = NULL;
-       gchar *result = NULL;
-       GUPnPDIDLLiteParser *parser = NULL;
+       guint id;
        msu_async_cb_data_t *cb_data = user_data;
-       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
 
        MSU_LOG_DEBUG("Enter");
 
-       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
-                                           &upnp_error,
-                                           "Result", G_TYPE_STRING,
-                                           &result, NULL)) {
-               MSU_LOG_WARNING("Browse operation failed: %s",
-                             upnp_error->message);
+       if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+                                           "Id", G_TYPE_UINT,
+                                           &id,
+                                           NULL)) {
+               MSU_LOG_ERROR("Unable to retrieve ServiceUpdateID: %s %s",
+                              g_quark_to_string(upnp_error->domain),
+                              upnp_error->message);
 
                cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_OPERATION_FAILED,
-                                            "Browse operation failed: %s",
-                                            upnp_error->message);
-               goto on_error;
+                               MSU_ERROR_OPERATION_FAILED,
+                               "Unable to retrieve ServiceUpdateID: %s",
+                               upnp_error->message);
+
+               goto on_complete;
        }
 
-       MSU_LOG_DEBUG("GetMS2SpecProps result: %s", result);
+       cb_data->result = g_variant_ref_sink(g_variant_new_uint32(id));
 
-       parser = gupnp_didl_lite_parser_new();
+on_complete:
 
-       g_signal_connect(parser, "object-available" , cb_task_data->prop_func,
-                        cb_data);
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
 
-       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
-               if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
-                       MSU_LOG_WARNING("Property not defined for object");
+       if (upnp_error)
+               g_error_free(upnp_error);
 
-                       cb_data->error =
-                               g_error_new(MSU_ERROR,
-                                           MSU_ERROR_UNKNOWN_PROPERTY,
-                                           "Property not defined for object");
-               } else {
-                       MSU_LOG_WARNING("Unable to parse results of browse: %s",
-                                     upnp_error->message);
+       MSU_LOG_DEBUG("Exit");
+}
 
-                       cb_data->error =
-                               g_error_new(MSU_ERROR,
-                                           MSU_ERROR_OPERATION_FAILED,
-                                           "Unable to parse results of "
-                                           "browse: %s",
-                                           upnp_error->message);
-               }
-               goto on_error;
-       }
+static void prv_get_system_update_id_for_prop(GUPnPServiceProxy *proxy,
+                                    msu_device_t *device,
+                                    GCancellable *cancellable,
+                                    msu_async_cb_data_t *cb_data)
+{
+       guint suid;
 
-       if (cb_data->error)
-               goto on_error;
+       MSU_LOG_DEBUG("Enter");
 
-       if (cb_task_data->need_child_count) {
-               MSU_LOG_DEBUG("Need Child Count");
+       if (prv_device_subscribed(device)) {
+               suid = device->system_update_id;
 
-               prv_get_child_count(cb_data, prv_get_all_child_count_cb,
-                       cb_data->id);
+               cb_data->result = g_variant_ref_sink(
+                                       g_variant_new_uint32(suid));
 
-               goto no_complete;
-       } else {
-               cb_data->result = g_variant_ref_sink(g_variant_builder_end(
-                                                            cb_task_data->vb));
-       }
-
-on_error:
+               (void) g_idle_add(msu_async_complete_task, cb_data);
 
-       (void) g_idle_add(msu_async_complete_task, cb_data);
-       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+               goto on_complete;
+       }
 
-no_complete:
+       gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
+                                        prv_system_update_id_for_prop_cb,
+                                        cb_data,
+                                        NULL);
 
-       if (upnp_error)
-               g_error_free(upnp_error);
+       cb_data->proxy = proxy;
 
-       if (parser)
-               g_object_unref(parser);
+       cb_data->cancel_id =
+       g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
 
-       g_free(result);
+on_complete:
 
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_all_ms2spec_props(msu_device_context_t *context,
-                                     GCancellable *cancellable,
-                                     msu_async_cb_data_t *cb_data)
+static void prv_system_update_id_for_props_cb(GUPnPServiceProxy *proxy,
+                                   GUPnPServiceProxyAction *action,
+                                   gpointer user_data)
 {
+       GError *upnp_error = NULL;
+       guint id;
+       msu_async_cb_data_t *cb_data = user_data;
        msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
-       msu_task_t *task = cb_data->task;
-       msu_task_get_props_t *task_data = &task->ut.get_props;
-
-       MSU_LOG_DEBUG("Enter called");
-
-       if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, task_data->interface_name))
-               cb_task_data->prop_func = G_CALLBACK(prv_get_container);
-       else if (!strcmp(MSU_INTERFACE_MEDIA_ITEM, task_data->interface_name))
-               cb_task_data->prop_func = G_CALLBACK(prv_get_item);
-       else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT, task_data->interface_name))
-               cb_task_data->prop_func = G_CALLBACK(prv_get_object);
-       else  if (!strcmp("", task_data->interface_name))
-               cb_task_data->prop_func = G_CALLBACK(prv_get_all);
-       else {
-               MSU_LOG_WARNING("Interface is unknown.");
 
-               cb_data->error =
-                       g_error_new(MSU_ERROR, MSU_ERROR_UNKNOWN_INTERFACE,
-                                   "Interface is unknown.");
-               goto on_error;
-       }
+       MSU_LOG_DEBUG("Enter");
 
-       cb_data->action = gupnp_service_proxy_begin_action(
-               context->service_proxy, "Browse",
-               prv_get_all_ms2spec_props_cb, cb_data,
-               "ObjectID", G_TYPE_STRING, cb_data->id,
-               "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
-               "Filter", G_TYPE_STRING, "*",
-               "StartingIndex", G_TYPE_INT, 0,
-               "RequestedCount", G_TYPE_INT, 0,
-               "SortCriteria", G_TYPE_STRING,
-               "", NULL);
+       if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+                                           "Id", G_TYPE_UINT,
+                                           &id,
+                                           NULL)) {
+               MSU_LOG_ERROR("Unable to retrieve ServiceUpdateID: %s %s",
+                              g_quark_to_string(upnp_error->domain),
+                              upnp_error->message);
 
-       cb_data->proxy = context->service_proxy;
+               cb_data->error = g_error_new(MSU_ERROR,
+                               MSU_ERROR_OPERATION_FAILED,
+                               "Unable to retrieve ServiceUpdateID: %s",
+                               upnp_error->message);
 
-       cb_data->cancel_id =
-               g_cancellable_connect(cancellable,
-                                     G_CALLBACK(msu_async_task_cancelled),
-                                     cb_data, NULL);
-       cb_data->cancellable = cancellable;
+               goto on_complete;
+       }
 
-       MSU_LOG_DEBUG("Exit with SUCCESS");
+       g_variant_builder_add(cb_task_data->vb, "{sv}",
+                             MSU_SYSTEM_UPDATE_VAR,
+                             g_variant_new_uint32(id));
 
-       return;
+       cb_data->result = g_variant_ref_sink(g_variant_builder_end(
+                                               cb_task_data->vb));
 
-on_error:
+on_complete:
 
-       (void) g_idle_add(msu_async_complete_task, cb_data);
+       if (!cb_data->error)
+               prv_get_sr_token_for_props(proxy, cb_task_data->device,
+                                          cb_data->cancellable, cb_data);
+       else {
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+               g_cancellable_disconnect(cb_data->cancellable,
+                                        cb_data->cancel_id);
+       }
 
-       MSU_LOG_DEBUG("Exit with FAIL");
+       if (upnp_error)
+               g_error_free(upnp_error);
 
-       return;
+       MSU_LOG_DEBUG("Exit");
 }
 
-void msu_device_get_all_props(msu_device_t *device,  msu_task_t *task,
-                             msu_async_cb_data_t *cb_data,
-                             gboolean root_object,
-                             GCancellable *cancellable)
+static void prv_get_system_update_id_for_props(GUPnPServiceProxy *proxy,
+                                    msu_device_t *device,
+                                    GCancellable *cancellable,
+                                    msu_async_cb_data_t *cb_data)
 {
        msu_async_get_all_t *cb_task_data;
-       msu_task_get_props_t *task_data = &task->ut.get_props;
-       msu_device_context_t *context;
+       guint suid;
 
        MSU_LOG_DEBUG("Enter");
 
-       context = msu_device_get_context(device);
-       cb_task_data = &cb_data->ut.get_all;
+       if (prv_device_subscribed(device)) {
+               suid = device->system_update_id;
 
-       cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+               cb_task_data = &cb_data->ut.get_all;
 
-       if (!strcmp(task_data->interface_name, MSU_INTERFACE_MEDIA_DEVICE)) {
-               if (root_object) {
-                       msu_props_add_device(
-                               (GUPnPDeviceInfo *) context->device_proxy,
-                               cb_task_data->vb);
+               g_variant_builder_add(cb_task_data->vb, "{sv}",
+                                     MSU_SYSTEM_UPDATE_VAR,
+                                     g_variant_new_uint32(suid));
 
-                       cb_data->result =
-                               g_variant_ref_sink(g_variant_builder_end(
-                                                          cb_task_data->vb));
-               } else {
-                       cb_data->error =
-                               g_error_new(MSU_ERROR,
-                                           MSU_ERROR_UNKNOWN_INTERFACE,
-                                           "Interface is only valid on "
-                                           "root objects.");
-               }
+               prv_get_sr_token_for_props(proxy, device, cancellable, cb_data);
 
-               (void) g_idle_add(msu_async_complete_task, cb_data);
+               goto on_complete;
+       }
 
-       } else if (strcmp(task_data->interface_name, "")) {
-               prv_get_all_ms2spec_props(context, cancellable, cb_data);
-       } else {
-               if (root_object)
-                       msu_props_add_device(
-                               (GUPnPDeviceInfo *) context->device_proxy,
-                               cb_task_data->vb);
+       gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
+                                        prv_system_update_id_for_props_cb,
+                                        cb_data,
+                                        NULL);
 
-               prv_get_all_ms2spec_props(context, cancellable, cb_data);
-       }
+       cb_data->proxy = proxy;
+
+       cb_data->cancel_id =
+       g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+on_complete:
 
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_object_property(GUPnPDIDLLiteParser *parser,
-                                   GUPnPDIDLLiteObject *object,
-                                   gpointer user_data)
+static int prv_get_media_server_version(msu_device_t *device)
 {
-       msu_async_cb_data_t *cb_data = user_data;
-       msu_task_t *task = cb_data->task;
-       msu_task_get_prop_t *task_data = &task->ut.get_prop;
-       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
+       msu_device_context_t *context;
+       const char *device_type;
+       const char *version;
 
-       if (cb_data->result)
+       context = msu_device_get_context(device, NULL);
+       device_type = gupnp_device_info_get_device_type((GUPnPDeviceInfo *)
+                                                       context->device_proxy);
+
+       if (strncmp(device_type, MEDIA_SERVER_DEVICE_TYPE,
+                                       sizeof(MEDIA_SERVER_DEVICE_TYPE) - 1))
                goto on_error;
 
-       cb_data->result = msu_props_get_object_prop(task_data->prop_name,
-                                                   cb_task_data->root_path,
-                                                   object);
+       version = device_type + sizeof(MEDIA_SERVER_DEVICE_TYPE) - 1;
+
+       return atoi(version);
 
 on_error:
 
-       return;
+       return -1;
 }
 
-static void prv_get_item_property(GUPnPDIDLLiteParser *parser,
-                                 GUPnPDIDLLiteObject *object,
-                                 gpointer user_data)
+static void prv_service_reset_for_prop_cb(GUPnPServiceProxy *proxy,
+                                         GUPnPServiceProxyAction *action,
+                                         gpointer user_data)
 {
+       GError *upnp_error = NULL;
+       gchar *token = NULL;
        msu_async_cb_data_t *cb_data = user_data;
-       msu_task_t *task = cb_data->task;
-       msu_task_get_prop_t *task_data = &task->ut.get_prop;
-       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
 
-       if (cb_data->result)
-               goto on_error;
+       MSU_LOG_DEBUG("Enter");
 
-       cb_data->result = msu_props_get_item_prop(task_data->prop_name,
-                                                 cb_task_data->root_path,
-                                                 object,
-                                                 cb_task_data->protocol_info);
+       if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+                                           "ResetToken", G_TYPE_STRING,
+                                           &token,
+                                           NULL)) {
+               MSU_LOG_ERROR("Unable to retrieve ServiceResetToken: %s %s",
+                               g_quark_to_string(upnp_error->domain),
+                                upnp_error->message);
 
-on_error:
 
-       return;
-}
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "GetServiceResetToken failed: %s",
+                                            upnp_error->message);
 
-static void prv_get_container_property(GUPnPDIDLLiteParser *parser,
-                                      GUPnPDIDLLiteObject *object,
-                                      gpointer user_data)
-{
-       msu_async_cb_data_t *cb_data = user_data;
-       msu_task_t *task = cb_data->task;
-       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+               goto on_complete;
+       }
 
-       if (cb_data->result)
-               goto on_error;
+       cb_data->result = g_variant_ref_sink(g_variant_new_string(token));
 
-       cb_data->result = msu_props_get_container_prop(task_data->prop_name,
-                                                      object);
+       g_free(token);
 
-on_error:
+       MSU_LOG_DEBUG("Service Reset %s", token);
 
-       return;
+on_complete:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_all_property(GUPnPDIDLLiteParser *parser,
-                                GUPnPDIDLLiteObject *object,
-                                gpointer user_data)
+static void prv_get_sr_token_for_prop(GUPnPServiceProxy *proxy,
+                            msu_device_t *device,
+                            GCancellable *cancellable,
+                            msu_async_cb_data_t *cb_data)
 {
-       msu_async_cb_data_t *cb_data = user_data;
-
-       prv_get_object_property(parser, object, user_data);
+       MSU_LOG_DEBUG("Enter");
 
-       if (cb_data->result)
-               goto on_error;
+       if (prv_get_media_server_version(device) < 3) {
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_UNKNOWN_PROPERTY,
+                                            "Unknown property");
 
-       if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
-               prv_get_container_property(parser, object, user_data);
-       else
-               prv_get_item_property(parser, object, user_data);
+               (void) g_idle_add(msu_async_complete_task, cb_data);
 
-on_error:
+               goto on_error;
+       }
 
-       return;
-}
+       gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
+                                        prv_service_reset_for_prop_cb,
+                                        cb_data,
+                                        NULL);
 
-static gboolean prv_get_child_count_cb(msu_async_cb_data_t *cb_data,
-                                  gint count)
-{
-       MSU_LOG_DEBUG("Enter");
+       cb_data->proxy = proxy;
 
-       MSU_LOG_DEBUG("Count %d", count);
+       cb_data->cancel_id = g_cancellable_connect(cancellable,
+                                       G_CALLBACK(msu_async_task_cancelled),
+                                       cb_data, NULL);
+       cb_data->cancellable = cancellable;
 
-       cb_data->result =  g_variant_ref_sink(
-               g_variant_new_uint32((guint) count));
+on_error:
 
        MSU_LOG_DEBUG("Exit");
-
-       return TRUE;
 }
 
-static void prv_count_children_cb(GUPnPServiceProxy *proxy,
-                                 GUPnPServiceProxyAction *action,
-                                 gpointer user_data)
+static void prv_service_reset_for_props_cb(GUPnPServiceProxy *proxy,
+                                         GUPnPServiceProxyAction *action,
+                                         gpointer user_data)
 {
-       msu_device_count_data_t *count_data = user_data;
-       msu_async_cb_data_t *cb_data = count_data->cb_data;
        GError *upnp_error = NULL;
-       gint count;
-       gboolean complete = FALSE;
+       gchar *token = NULL;
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_get_all_t *cb_task_data;
 
        MSU_LOG_DEBUG("Enter");
 
-       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
-                                           &upnp_error,
-                                           "TotalMatches", G_TYPE_INT,
-                                           &count,
+       if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+                                           "ResetToken", G_TYPE_STRING,
+                                           &token,
                                            NULL)) {
-               MSU_LOG_WARNING("Browse operation failed: %s",
+               MSU_LOG_ERROR("Unable to retrieve ServiceResetToken: %s %s",
+                             g_quark_to_string(upnp_error->domain),
                              upnp_error->message);
 
                cb_data->error = g_error_new(MSU_ERROR,
                                             MSU_ERROR_OPERATION_FAILED,
-                                            "Browse operation failed: %s",
+                                            "GetServiceResetToken failed: %s",
                                             upnp_error->message);
-               goto on_error;
+
+               goto on_complete;
        }
 
-       complete = count_data->cb(cb_data, count);
+       cb_task_data = &cb_data->ut.get_all;
+       g_variant_builder_add(cb_task_data->vb, "{sv}",
+                             MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN,
+                             g_variant_new_string(token));
 
-on_error:
+       cb_data->result = g_variant_ref_sink(g_variant_builder_end(
+                                               cb_task_data->vb));
 
-       g_free(user_data);
+       g_free(token);
 
-       if (cb_data->error || complete) {
-               (void) g_idle_add(msu_async_complete_task, cb_data);
-               g_cancellable_disconnect(cb_data->cancellable,
-                                        cb_data->cancel_id);
-       }
+       MSU_LOG_DEBUG("Service Reset %s", token);
+
+on_complete:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
 
        if (upnp_error)
                g_error_free(upnp_error);
@@ -1119,50 +1541,70 @@ on_error:
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_child_count(msu_async_cb_data_t *cb_data,
-                               msu_device_count_cb_t cb, const gchar *id)
+static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
+                            msu_device_t *device,
+                            GCancellable *cancellable,
+                            msu_async_cb_data_t *cb_data)
 {
-       msu_device_count_data_t *count_data;
+       msu_async_get_all_t *cb_task_data;
 
        MSU_LOG_DEBUG("Enter");
 
-       prv_msu_device_count_data_new(cb_data, cb, &count_data);
-       cb_data->action =
-               gupnp_service_proxy_begin_action(cb_data->proxy,
-                                                "Browse",
-                                                prv_count_children_cb,
-                                                count_data,
-                                                "ObjectID", G_TYPE_STRING, id,
+       if (prv_get_media_server_version(device) < 3) {
+               cb_task_data = &cb_data->ut.get_all;
 
-                                                "BrowseFlag", G_TYPE_STRING,
-                                                "BrowseDirectChildren",
+               cb_data->result = g_variant_ref_sink(g_variant_builder_end(
+                                                       cb_task_data->vb));
 
-                                                "Filter", G_TYPE_STRING, "",
+               goto on_complete; /* No error here, just skip the property */
+       }
 
-                                                "StartingIndex", G_TYPE_INT,
-                                                0,
+       gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
+                                        prv_service_reset_for_props_cb,
+                                        cb_data,
+                                        NULL);
 
-                                                "RequestedCount", G_TYPE_INT,
-                                                1,
+       cb_data->proxy = proxy;
 
-                                                "SortCriteria", G_TYPE_STRING,
-                                                "",
+       cb_data->cancel_id = g_cancellable_connect(cancellable,
+                                       G_CALLBACK(msu_async_task_cancelled),
+                                       cb_data, NULL);
+       cb_data->cancellable = cancellable;
 
-                                                NULL);
+on_complete:
 
-       MSU_LOG_DEBUG("Exit with SUCCESS");
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+
+       MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
-                                   GUPnPServiceProxyAction *action,
-                                   gpointer user_data)
+static gboolean prv_get_all_child_count_cb(msu_async_cb_data_t *cb_data,
+                                      gint count)
+{
+       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+
+       msu_props_add_child_count(cb_task_data->vb, count);
+       if (cb_task_data->device_object)
+               prv_get_system_update_id_for_props(cb_data->proxy,
+                                                  cb_task_data->device,
+                                                  cb_data->cancellable,
+                                                  cb_data);
+       else
+               cb_data->result = g_variant_ref_sink(g_variant_builder_end(
+                                                    cb_task_data->vb));
+
+       return !cb_task_data->device_object;
+}
+
+static void prv_get_all_ms2spec_props_cb(GUPnPServiceProxy *proxy,
+                                        GUPnPServiceProxyAction *action,
+                                        gpointer user_data)
 {
        GError *upnp_error = NULL;
        gchar *result = NULL;
        GUPnPDIDLLiteParser *parser = NULL;
        msu_async_cb_data_t *cb_data = user_data;
-       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
-       msu_task_get_prop_t *task_data = &cb_data->task->ut.get_prop;
+       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
 
        MSU_LOG_DEBUG("Enter");
 
@@ -1180,7 +1622,7 @@ static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
                goto on_error;
        }
 
-       MSU_LOG_DEBUG("GetMS2SpecProp result: %s", result);
+       MSU_LOG_DEBUG("GetMS2SpecProps result: %s", result);
 
        parser = gupnp_didl_lite_parser_new();
 
@@ -1209,30 +1651,34 @@ static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
                goto on_error;
        }
 
-       if (!cb_data->result) {
-               MSU_LOG_WARNING("Property not defined for object");
+       if (cb_data->error)
+               goto on_error;
 
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_UNKNOWN_PROPERTY,
-                                            "Property not defined for object");
-       }
+       if (cb_task_data->need_child_count) {
+               MSU_LOG_DEBUG("Need Child Count");
 
-on_error:
+               prv_get_child_count(cb_data, prv_get_all_child_count_cb,
+                       cb_data->id);
 
-       if (cb_data->error && !strcmp(task_data->prop_name,
-                                     MSU_INTERFACE_PROP_CHILD_COUNT)) {
-               MSU_LOG_DEBUG("ChildCount not supported by server");
+               goto no_complete;
+       } else if (cb_data->task->type == MSU_TASK_GET_ALL_PROPS &&
+                                               cb_task_data->device_object) {
+               prv_get_system_update_id_for_props(proxy, cb_task_data->device,
+                                               cb_data->cancellable, cb_data);
 
-               g_error_free(cb_data->error);
-               cb_data->error = NULL;
-               prv_get_child_count(cb_data, prv_get_child_count_cb,
-                                   cb_data->id);
+               goto no_complete;
        } else {
-               (void) g_idle_add(msu_async_complete_task, cb_data);
-               g_cancellable_disconnect(cb_data->cancellable,
-                                        cb_data->cancel_id);
+               cb_data->result = g_variant_ref_sink(g_variant_builder_end(
+                                                            cb_task_data->vb));
        }
 
+on_error:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+no_complete:
+
        if (upnp_error)
                g_error_free(upnp_error);
 
@@ -1244,62 +1690,43 @@ on_error:
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_ms2spec_prop(msu_device_context_t *context,
-                                msu_prop_map_t *prop_map,
-                                msu_task_get_prop_t *task_data,
-                                GCancellable *cancellable,
-                                msu_async_cb_data_t *cb_data)
+static void prv_get_all_ms2spec_props(msu_device_context_t *context,
+                                     GCancellable *cancellable,
+                                     msu_async_cb_data_t *cb_data)
 {
-       msu_async_get_prop_t *cb_task_data;
-       const gchar *filter;
-
-       MSU_LOG_DEBUG("Enter");
-
-       cb_task_data = &cb_data->ut.get_prop;
-
-       if (!prop_map) {
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_UNKNOWN_PROPERTY,
-                                            "Unknown property");
-               goto on_error;
-       }
+       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+       msu_task_t *task = cb_data->task;
+       msu_task_get_props_t *task_data = &task->ut.get_props;
 
-       filter = prop_map->filter ? prop_map->upnp_prop_name : "";
+       MSU_LOG_DEBUG("Enter called");
 
-       if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
-               cb_task_data->prop_func =
-                       G_CALLBACK(prv_get_container_property);
-       } else if (!strcmp(MSU_INTERFACE_MEDIA_ITEM,
-                          task_data->interface_name)) {
-               cb_task_data->prop_func = G_CALLBACK(prv_get_item_property);
-       } else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT,
-                          task_data->interface_name)) {
-               cb_task_data->prop_func = G_CALLBACK(prv_get_object_property);
-       } else  if (!strcmp("", task_data->interface_name)) {
-               cb_task_data->prop_func = G_CALLBACK(prv_get_all_property);
-       } else {
-               MSU_LOG_WARNING("Interface is unknown.%s",
-                             task_data->interface_name);
+       if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, task_data->interface_name))
+               cb_task_data->prop_func = G_CALLBACK(prv_get_container);
+       else if (!strcmp(MSU_INTERFACE_MEDIA_ITEM, task_data->interface_name))
+               cb_task_data->prop_func = G_CALLBACK(prv_get_item);
+       else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT, task_data->interface_name))
+               cb_task_data->prop_func = G_CALLBACK(prv_get_object);
+       else  if (!strcmp("", task_data->interface_name))
+               cb_task_data->prop_func = G_CALLBACK(prv_get_all);
+       else {
+               MSU_LOG_WARNING("Interface is unknown.");
 
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_UNKNOWN_INTERFACE,
-                                            "Interface is unknown.");
+               cb_data->error =
+                       g_error_new(MSU_ERROR, MSU_ERROR_UNKNOWN_INTERFACE,
+                                   "Interface is unknown.");
                goto on_error;
        }
 
        cb_data->action = gupnp_service_proxy_begin_action(
                context->service_proxy, "Browse",
-               prv_get_ms2spec_prop_cb,
-               cb_data,
+               prv_get_all_ms2spec_props_cb, cb_data,
                "ObjectID", G_TYPE_STRING, cb_data->id,
-               "BrowseFlag", G_TYPE_STRING,
-               "BrowseMetadata",
-               "Filter", G_TYPE_STRING, filter,
+               "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
+               "Filter", G_TYPE_STRING, "*",
                "StartingIndex", G_TYPE_INT, 0,
                "RequestedCount", G_TYPE_INT, 0,
                "SortCriteria", G_TYPE_STRING,
-               "",
-               NULL);
+               "", NULL);
 
        cb_data->proxy = context->service_proxy;
 
@@ -1322,202 +1749,1285 @@ on_error:
        return;
 }
 
-void msu_device_get_prop(msu_device_t *device,  msu_task_t *task,
-                        msu_async_cb_data_t *cb_data,
-                        msu_prop_map_t *prop_map, gboolean root_object,
-                        GCancellable *cancellable)
+void msu_device_get_all_props(msu_device_t *device,  msu_client_t *client,
+                             msu_task_t *task, msu_async_cb_data_t *cb_data,
+                             gboolean root_object,
+                             GCancellable *cancellable)
 {
-       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+       msu_async_get_all_t *cb_task_data;
+       msu_task_get_props_t *task_data = &task->ut.get_props;
        msu_device_context_t *context;
 
        MSU_LOG_DEBUG("Enter");
 
-       context = msu_device_get_context(device);
+       context = msu_device_get_context(device, client);
+       cb_task_data = &cb_data->ut.get_all;
+
+       cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+       cb_task_data->device_object = root_object;
+       cb_task_data->device = device;
 
        if (!strcmp(task_data->interface_name, MSU_INTERFACE_MEDIA_DEVICE)) {
                if (root_object) {
-                       cb_data->result =
-                               msu_props_get_device_prop(
-                                       (GUPnPDeviceInfo *)
-                                       context->device_proxy,
-                                       task_data->prop_name);
-                       if (!cb_data->result)
-                               cb_data->error = g_error_new(
-                                       MSU_ERROR,
-                                       MSU_ERROR_UNKNOWN_PROPERTY,
-                                       "Unknown property");
+                       msu_props_add_device(
+                               (GUPnPDeviceInfo *) context->device_proxy,
+                               device,
+                               cb_task_data->vb);
+
+                       prv_get_system_update_id_for_props(
+                                                       context->service_proxy,
+                                                       device,
+                                                       cancellable,
+                                                       cb_data);
                } else {
                        cb_data->error =
                                g_error_new(MSU_ERROR,
                                            MSU_ERROR_UNKNOWN_INTERFACE,
-                                           "Interface is unknown.");
-               }
+                                           "Interface is only valid on "
+                                           "root objects.");
 
-               (void) g_idle_add(msu_async_complete_task, cb_data);
+                       (void) g_idle_add(msu_async_complete_task, cb_data);
+               }
 
        } else if (strcmp(task_data->interface_name, "")) {
-               prv_get_ms2spec_prop(context, prop_map, &task->ut.get_prop,
-                                    cancellable, cb_data);
+               cb_task_data->device_object = FALSE;
+               prv_get_all_ms2spec_props(context, cancellable, cb_data);
        } else {
                if (root_object)
-                       cb_data->result = msu_props_get_device_prop(
-                               (GUPnPDeviceInfo *)
-                               context->device_proxy,
-                               task_data->prop_name);
+                       msu_props_add_device(
+                               (GUPnPDeviceInfo *) context->device_proxy,
+                               device,
+                               cb_task_data->vb);
 
-               if (cb_data->result)
-                       (void) g_idle_add(msu_async_complete_task, cb_data);
-               else
-                       prv_get_ms2spec_prop(context, prop_map,
-                                            &task->ut.get_prop, cancellable,
-                                            cb_data);
+               prv_get_all_ms2spec_props(context, cancellable, cb_data);
        }
 
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_found_target(GUPnPDIDLLiteParser *parser,
-                            GUPnPDIDLLiteObject *object,
-                            gpointer user_data)
+static void prv_get_object_property(GUPnPDIDLLiteParser *parser,
+                                   GUPnPDIDLLiteObject *object,
+                                   gpointer user_data)
 {
        msu_async_cb_data_t *cb_data = user_data;
-       msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
-       const char *id;
-       const char *parent_path;
-       gchar *path = NULL;
-       gboolean have_child_count;
-       msu_device_object_builder_t *builder;
+       msu_task_t *task = cb_data->task;
+       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
 
-       MSU_LOG_DEBUG("Enter");
+       if (cb_data->result)
+               goto on_error;
 
-       builder = g_new0(msu_device_object_builder_t, 1);
+       cb_data->result = msu_props_get_object_prop(task_data->prop_name,
+                                                   cb_task_data->root_path,
+                                                   object);
 
-       id = gupnp_didl_lite_object_get_parent_id(object);
+on_error:
 
-       if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
-               parent_path = cb_task_data->root_path;
-       } else {
-               path = msu_path_from_id(cb_task_data->root_path, id);
-               parent_path = path;
-       }
+       return;
+}
 
-       builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+static void prv_get_item_property(GUPnPDIDLLiteParser *parser,
+                                 GUPnPDIDLLiteObject *object,
+                                 gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_task_t *task = cb_data->task;
+       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
 
-       if (!msu_props_add_object(builder->vb, object, cb_task_data->root_path,
-                                 parent_path, cb_task_data->filter_mask))
+       if (cb_data->result)
                goto on_error;
 
-       if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
-               msu_props_add_container(builder->vb,
-                                       (GUPnPDIDLLiteContainer *) object,
-                                       cb_task_data->filter_mask,
-                                       &have_child_count);
-
-               if (!have_child_count && (cb_task_data->filter_mask &
-                                         MSU_UPNP_MASK_PROP_CHILD_COUNT)) {
-                       builder->needs_child_count = TRUE;
-                       builder->id = g_strdup(
-                               gupnp_didl_lite_object_get_id(object));
-                       cb_task_data->need_child_count = TRUE;
-               }
-       } else {
-               msu_props_add_item(builder->vb,
-                                  object,
-                                  cb_task_data->root_path,
-                                  cb_task_data->filter_mask,
-                                  cb_task_data->protocol_info);
-       }
-
-       g_ptr_array_add(cb_task_data->vbs, builder);
-       g_free(path);
-
-       MSU_LOG_DEBUG("Exit with SUCCESS");
-
-       return;
+       cb_data->result = msu_props_get_item_prop(task_data->prop_name,
+                                                 cb_task_data->root_path,
+                                                 object,
+                                                 cb_task_data->protocol_info);
 
 on_error:
 
-       g_free(path);
-       prv_msu_device_object_builder_delete(builder);
-
-       MSU_LOG_DEBUG("Exit with FAIL");
+       return;
 }
 
-static void prv_search_cb(GUPnPServiceProxy *proxy,
-                         GUPnPServiceProxyAction *action,
-                         gpointer user_data)
-{
-       gchar *result = NULL;
+static void prv_get_container_property(GUPnPDIDLLiteParser *parser,
+                                      GUPnPDIDLLiteObject *object,
+                                      gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_task_t *task = cb_data->task;
+       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+
+       if (cb_data->result)
+               goto on_error;
+
+       cb_data->result = msu_props_get_container_prop(task_data->prop_name,
+                                                      object);
+
+on_error:
+
+       return;
+}
+
+static void prv_get_all_property(GUPnPDIDLLiteParser *parser,
+                                GUPnPDIDLLiteObject *object,
+                                gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+
+       prv_get_object_property(parser, object, user_data);
+
+       if (cb_data->result)
+               goto on_error;
+
+       if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
+               prv_get_container_property(parser, object, user_data);
+       else
+               prv_get_item_property(parser, object, user_data);
+
+on_error:
+
+       return;
+}
+
+static gboolean prv_get_child_count_cb(msu_async_cb_data_t *cb_data,
+                                  gint count)
+{
+       MSU_LOG_DEBUG("Enter");
+
+       MSU_LOG_DEBUG("Count %d", count);
+
+       cb_data->result =  g_variant_ref_sink(
+               g_variant_new_uint32((guint) count));
+
+       MSU_LOG_DEBUG("Exit");
+
+       return TRUE;
+}
+
+static void prv_count_children_cb(GUPnPServiceProxy *proxy,
+                                 GUPnPServiceProxyAction *action,
+                                 gpointer user_data)
+{
+       msu_device_count_data_t *count_data = user_data;
+       msu_async_cb_data_t *cb_data = count_data->cb_data;
+       GError *upnp_error = NULL;
+       gint count;
+       gboolean complete = FALSE;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           "TotalMatches", G_TYPE_INT,
+                                           &count,
+                                           NULL)) {
+               MSU_LOG_WARNING("Browse operation failed: %s",
+                             upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Browse operation failed: %s",
+                                            upnp_error->message);
+               goto on_error;
+       }
+
+       complete = count_data->cb(cb_data, count);
+
+on_error:
+
+       g_free(user_data);
+
+       if (cb_data->error || complete) {
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+               g_cancellable_disconnect(cb_data->cancellable,
+                                        cb_data->cancel_id);
+       }
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_get_child_count(msu_async_cb_data_t *cb_data,
+                               msu_device_count_cb_t cb, const gchar *id)
+{
+       msu_device_count_data_t *count_data;
+
+       MSU_LOG_DEBUG("Enter");
+
+       prv_msu_device_count_data_new(cb_data, cb, &count_data);
+       cb_data->action =
+               gupnp_service_proxy_begin_action(cb_data->proxy,
+                                                "Browse",
+                                                prv_count_children_cb,
+                                                count_data,
+                                                "ObjectID", G_TYPE_STRING, id,
+
+                                                "BrowseFlag", G_TYPE_STRING,
+                                                "BrowseDirectChildren",
+
+                                                "Filter", G_TYPE_STRING, "",
+
+                                                "StartingIndex", G_TYPE_INT,
+                                                0,
+
+                                                "RequestedCount", G_TYPE_INT,
+                                                1,
+
+                                                "SortCriteria", G_TYPE_STRING,
+                                                "",
+
+                                                NULL);
+
+       MSU_LOG_DEBUG("Exit with SUCCESS");
+}
+
+static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
+                                   GUPnPServiceProxyAction *action,
+                                   gpointer user_data)
+{
+       GError *upnp_error = NULL;
+       gchar *result = NULL;
+       GUPnPDIDLLiteParser *parser = NULL;
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
+       msu_task_get_prop_t *task_data = &cb_data->task->ut.get_prop;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           "Result", G_TYPE_STRING,
+                                           &result, NULL)) {
+               MSU_LOG_WARNING("Browse operation failed: %s",
+                             upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Browse operation failed: %s",
+                                            upnp_error->message);
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("GetMS2SpecProp result: %s", result);
+
+       parser = gupnp_didl_lite_parser_new();
+
+       g_signal_connect(parser, "object-available" , cb_task_data->prop_func,
+                        cb_data);
+
+       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
+               if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
+                       MSU_LOG_WARNING("Property not defined for object");
+
+                       cb_data->error =
+                               g_error_new(MSU_ERROR,
+                                           MSU_ERROR_UNKNOWN_PROPERTY,
+                                           "Property not defined for object");
+               } else {
+                       MSU_LOG_WARNING("Unable to parse results of browse: %s",
+                                     upnp_error->message);
+
+                       cb_data->error =
+                               g_error_new(MSU_ERROR,
+                                           MSU_ERROR_OPERATION_FAILED,
+                                           "Unable to parse results of "
+                                           "browse: %s",
+                                           upnp_error->message);
+               }
+               goto on_error;
+       }
+
+       if (!cb_data->result) {
+               MSU_LOG_WARNING("Property not defined for object");
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_UNKNOWN_PROPERTY,
+                                            "Property not defined for object");
+       }
+
+on_error:
+
+       if (cb_data->error && !strcmp(task_data->prop_name,
+                                     MSU_INTERFACE_PROP_CHILD_COUNT)) {
+               MSU_LOG_DEBUG("ChildCount not supported by server");
+
+               g_error_free(cb_data->error);
+               cb_data->error = NULL;
+               prv_get_child_count(cb_data, prv_get_child_count_cb,
+                                   cb_data->id);
+       } else {
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+               g_cancellable_disconnect(cb_data->cancellable,
+                                        cb_data->cancel_id);
+       }
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       if (parser)
+               g_object_unref(parser);
+
+       g_free(result);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_get_ms2spec_prop(msu_device_context_t *context,
+                                msu_prop_map_t *prop_map,
+                                msu_task_get_prop_t *task_data,
+                                GCancellable *cancellable,
+                                msu_async_cb_data_t *cb_data)
+{
+       msu_async_get_prop_t *cb_task_data;
+       const gchar *filter;
+
+       MSU_LOG_DEBUG("Enter");
+
+       cb_task_data = &cb_data->ut.get_prop;
+
+       if (!prop_map) {
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_UNKNOWN_PROPERTY,
+                                            "Unknown property");
+               goto on_error;
+       }
+
+       filter = prop_map->filter ? prop_map->upnp_prop_name : "";
+
+       if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
+               cb_task_data->prop_func =
+                       G_CALLBACK(prv_get_container_property);
+       } else if (!strcmp(MSU_INTERFACE_MEDIA_ITEM,
+                          task_data->interface_name)) {
+               cb_task_data->prop_func = G_CALLBACK(prv_get_item_property);
+       } else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT,
+                          task_data->interface_name)) {
+               cb_task_data->prop_func = G_CALLBACK(prv_get_object_property);
+       } else  if (!strcmp("", task_data->interface_name)) {
+               cb_task_data->prop_func = G_CALLBACK(prv_get_all_property);
+       } else {
+               MSU_LOG_WARNING("Interface is unknown.%s",
+                             task_data->interface_name);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_UNKNOWN_INTERFACE,
+                                            "Interface is unknown.");
+               goto on_error;
+       }
+
+       cb_data->action = gupnp_service_proxy_begin_action(
+               context->service_proxy, "Browse",
+               prv_get_ms2spec_prop_cb,
+               cb_data,
+               "ObjectID", G_TYPE_STRING, cb_data->id,
+               "BrowseFlag", G_TYPE_STRING,
+               "BrowseMetadata",
+               "Filter", G_TYPE_STRING, filter,
+               "StartingIndex", G_TYPE_INT, 0,
+               "RequestedCount", G_TYPE_INT, 0,
+               "SortCriteria", G_TYPE_STRING,
+               "",
+               NULL);
+
+       cb_data->proxy = context->service_proxy;
+
+       cb_data->cancel_id =
+               g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+       MSU_LOG_DEBUG("Exit with SUCCESS");
+
+       return;
+
+on_error:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+
+       MSU_LOG_DEBUG("Exit with FAIL");
+
+       return;
+}
+
+void msu_device_get_prop(msu_device_t *device, msu_client_t *client,
+                        msu_task_t *task, msu_async_cb_data_t *cb_data,
+                        msu_prop_map_t *prop_map, gboolean root_object,
+                        GCancellable *cancellable)
+{
+       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+       msu_device_context_t *context;
+       gboolean complete = FALSE;
+
+       MSU_LOG_DEBUG("Enter");
+
+       context = msu_device_get_context(device, client);
+
+       if (!strcmp(task_data->interface_name, MSU_INTERFACE_MEDIA_DEVICE)) {
+               if (root_object) {
+                       if (!strcmp(task_data->prop_name,
+                               MSU_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
+                               prv_get_system_update_id_for_prop(
+                                                       context->service_proxy,
+                                                       device,
+                                                       cancellable,
+                                                       cb_data);
+                       } else if (!strcmp(task_data->prop_name,
+                                 MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN)) {
+                               prv_get_sr_token_for_prop(
+                                                       context->service_proxy,
+                                                       device,
+                                                       cancellable,
+                                                       cb_data);
+                       } else {
+                               cb_data->result =
+                                       msu_props_get_device_prop(
+                                               (GUPnPDeviceInfo *)
+                                               context->device_proxy,
+                                               device,
+                                               task_data->prop_name);
+
+                               if (!cb_data->result)
+                                       cb_data->error = g_error_new(
+                                               MSU_ERROR,
+                                               MSU_ERROR_UNKNOWN_PROPERTY,
+                                               "Unknown property");
+
+                               (void) g_idle_add(msu_async_complete_task,
+                                                 cb_data);
+                       }
+
+               } else {
+                       cb_data->error =
+                               g_error_new(MSU_ERROR,
+                                           MSU_ERROR_UNKNOWN_INTERFACE,
+                                           "Interface is unknown.");
+
+                       (void) g_idle_add(msu_async_complete_task, cb_data);
+               }
+
+       } else if (strcmp(task_data->interface_name, "")) {
+               prv_get_ms2spec_prop(context, prop_map, &task->ut.get_prop,
+                                    cancellable, cb_data);
+       } else {
+               if (root_object) {
+                       if (!strcmp(task_data->prop_name,
+                               MSU_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
+                               prv_get_system_update_id_for_prop(
+                                                       context->service_proxy,
+                                                       device,
+                                                       cancellable,
+                                                       cb_data);
+                               complete = TRUE;
+                       } else if (!strcmp(task_data->prop_name,
+                                 MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN)) {
+                               prv_get_sr_token_for_prop(
+                                                       context->service_proxy,
+                                                       device,
+                                                       cancellable,
+                                                       cb_data);
+                               complete = TRUE;
+                       } else {
+                               cb_data->result = msu_props_get_device_prop(
+                                       (GUPnPDeviceInfo *)
+                                       context->device_proxy,
+                                       device,
+                                       task_data->prop_name);
+                               if (cb_data->result) {
+                                       (void) g_idle_add(
+                                                       msu_async_complete_task,
+                                                       cb_data);
+                                       complete = TRUE;
+                               }
+                       }
+               }
+
+               if (!complete)
+                       prv_get_ms2spec_prop(context, prop_map,
+                                            &task->ut.get_prop, cancellable,
+                                            cb_data);
+       }
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_found_target(GUPnPDIDLLiteParser *parser,
+                            GUPnPDIDLLiteObject *object,
+                            gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
+       const char *id;
+       const char *parent_path;
+       gchar *path = NULL;
+       gboolean have_child_count;
+       msu_device_object_builder_t *builder;
+
+       MSU_LOG_DEBUG("Enter");
+
+       builder = g_new0(msu_device_object_builder_t, 1);
+
+       id = gupnp_didl_lite_object_get_parent_id(object);
+
+       if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
+               parent_path = cb_task_data->root_path;
+       } else {
+               path = msu_path_from_id(cb_task_data->root_path, id);
+               parent_path = path;
+       }
+
+       builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+
+       if (!msu_props_add_object(builder->vb, object, cb_task_data->root_path,
+                                 parent_path, cb_task_data->filter_mask))
+               goto on_error;
+
+       if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
+               msu_props_add_container(builder->vb,
+                                       (GUPnPDIDLLiteContainer *) object,
+                                       cb_task_data->filter_mask,
+                                       &have_child_count);
+
+               if (!have_child_count && (cb_task_data->filter_mask &
+                                         MSU_UPNP_MASK_PROP_CHILD_COUNT)) {
+                       builder->needs_child_count = TRUE;
+                       builder->id = g_strdup(
+                               gupnp_didl_lite_object_get_id(object));
+                       cb_task_data->need_child_count = TRUE;
+               }
+       } else {
+               msu_props_add_item(builder->vb,
+                                  object,
+                                  cb_task_data->root_path,
+                                  cb_task_data->filter_mask,
+                                  cb_task_data->protocol_info);
+       }
+
+       g_ptr_array_add(cb_task_data->vbs, builder);
+       g_free(path);
+
+       MSU_LOG_DEBUG("Exit with SUCCESS");
+
+       return;
+
+on_error:
+
+       g_free(path);
+       prv_msu_device_object_builder_delete(builder);
+
+       MSU_LOG_DEBUG("Exit with FAIL");
+}
+
+static void prv_search_cb(GUPnPServiceProxy *proxy,
+                         GUPnPServiceProxyAction *action,
+                         gpointer user_data)
+{
+       gchar *result = NULL;
+       GUPnPDIDLLiteParser *parser = NULL;
+       GError *upnp_error = NULL;
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           "Result", G_TYPE_STRING,
+                                           &result,
+                                           "TotalMatches", G_TYPE_INT,
+                                           &cb_task_data->max_count,
+                                           NULL)) {
+
+               MSU_LOG_WARNING("Search operation failed %s",
+                             upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Search operation failed: %s",
+                                            upnp_error->message);
+               goto on_error;
+       }
+
+       parser = gupnp_didl_lite_parser_new();
+
+       cb_task_data->vbs = g_ptr_array_new_with_free_func(
+               prv_msu_device_object_builder_delete);
+
+       g_signal_connect(parser, "object-available" ,
+                        G_CALLBACK(prv_found_target), cb_data);
+
+       MSU_LOG_DEBUG("Server Search result: %s", result);
+
+       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
+               && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
+               MSU_LOG_WARNING("Unable to parse results of search: %s",
+                             upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Unable to parse results of "
+                                            "search: %s", upnp_error->message);
+               goto on_error;
+       }
+
+       if (cb_task_data->need_child_count) {
+               MSU_LOG_DEBUG("Need to retrieve child count");
+
+               if (cb_data->task->multiple_retvals)
+                       cb_task_data->get_children_cb =
+                               prv_get_search_ex_result;
+               else
+                       cb_task_data->get_children_cb = prv_get_children_result;
+               prv_retrieve_child_count_for_list(cb_data);
+               goto no_complete;
+       } else {
+               if (cb_data->task->multiple_retvals)
+                       prv_get_search_ex_result(cb_data);
+               else
+                       prv_get_children_result(cb_data);
+       }
+
+on_error:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+no_complete:
+
+       if (parser)
+               g_object_unref(parser);
+
+       g_free(result);
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_device_search(msu_device_t *device, msu_client_t *client,
+                      msu_task_t *task, msu_async_cb_data_t *cb_data,
+                      const gchar *upnp_filter, const gchar *upnp_query,
+                      const gchar *sort_by, GCancellable *cancellable)
+{
+       msu_device_context_t *context;
+
+       MSU_LOG_DEBUG("Enter");
+
+       context = msu_device_get_context(device, client);
+
+       cb_data->action = gupnp_service_proxy_begin_action(
+               context->service_proxy, "Search",
+               prv_search_cb,
+               cb_data,
+               "ContainerID", G_TYPE_STRING, cb_data->id,
+               "SearchCriteria", G_TYPE_STRING, upnp_query,
+               "Filter", G_TYPE_STRING, upnp_filter,
+               "StartingIndex", G_TYPE_INT, task->ut.search.start,
+               "RequestedCount", G_TYPE_INT, task->ut.search.count,
+               "SortCriteria", G_TYPE_STRING, sort_by,
+               NULL);
+
+       cb_data->proxy = context->service_proxy;
+
+       cb_data->cancel_id =
+               g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_get_resource(GUPnPDIDLLiteParser *parser,
+                            GUPnPDIDLLiteObject *object,
+                            gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_task_t *task = cb_data->task;
+       msu_task_get_resource_t *task_data = &task->ut.resource;
+       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+
+       MSU_LOG_DEBUG("Enter");
+
+       msu_props_add_resource(cb_task_data->vb, object,
+                              cb_task_data->filter_mask,
+                              task_data->protocol_info);
+}
+
+void msu_device_get_resource(msu_device_t *device, msu_client_t *client,
+                            msu_task_t *task, msu_async_cb_data_t *cb_data,
+                            const gchar *upnp_filter,
+                            GCancellable *cancellable)
+{
+       msu_async_get_all_t *cb_task_data;
+       msu_device_context_t *context;
+
+       context = msu_device_get_context(device, client);
+       cb_task_data = &cb_data->ut.get_all;
+
+       cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+       cb_task_data->prop_func = G_CALLBACK(prv_get_resource);
+       cb_task_data->device_object = FALSE;
+
+       cb_data->action = gupnp_service_proxy_begin_action(
+               context->service_proxy, "Browse",
+               prv_get_all_ms2spec_props_cb, cb_data,
+               "ObjectID", G_TYPE_STRING, cb_data->id,
+               "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
+               "Filter", G_TYPE_STRING, upnp_filter,
+               "StartingIndex", G_TYPE_INT, 0,
+               "RequestedCount", G_TYPE_INT, 0,
+               "SortCriteria", G_TYPE_STRING,
+               "", NULL);
+
+       cb_data->proxy = context->service_proxy;
+
+       cb_data->cancel_id =
+               g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static gchar *prv_create_new_container_didl(const gchar *parent_id,
+                                           msu_task_t *task)
+{
+       GUPnPDIDLLiteWriter *writer;
+       GUPnPDIDLLiteObject *item;
+       GUPnPDIDLLiteContainer *container;
+       gchar *retval;
+       GVariantIter iter;
+       GVariant *child_type;
+       const gchar *actual_type;
+
+       writer = gupnp_didl_lite_writer_new(NULL);
+       item = GUPNP_DIDL_LITE_OBJECT(
+                               gupnp_didl_lite_writer_add_container(writer));
+       container = GUPNP_DIDL_LITE_CONTAINER(item);
+
+       gupnp_didl_lite_object_set_id(item, "");
+       gupnp_didl_lite_object_set_title(item,
+                                       task->ut.create_container.display_name);
+       gupnp_didl_lite_object_set_parent_id(item, parent_id);
+       actual_type = msu_props_media_spec_to_upnp_class(
+                                               task->ut.create_container.type);
+       gupnp_didl_lite_object_set_upnp_class(item, actual_type);
+       gupnp_didl_lite_object_set_restricted(item, FALSE);
+       gupnp_didl_lite_object_set_dlna_managed(item, GUPNP_OCM_FLAGS_UPLOAD);
+
+       g_variant_iter_init(&iter, task->ut.create_container.child_types);
+       while ((child_type = g_variant_iter_next_value(&iter))) {
+               actual_type = msu_props_media_spec_to_upnp_class(
+                                       g_variant_get_string(child_type, NULL));
+               if (actual_type != NULL)
+                       gupnp_didl_lite_container_add_create_class(container,
+                                                                  actual_type);
+               g_variant_unref(child_type);
+       }
+
+       retval = gupnp_didl_lite_writer_get_string(writer);
+
+       g_object_unref(item);
+       g_object_unref(writer);
+
+       return retval;
+}
+
+static gchar *prv_create_upload_didl(const gchar *parent_id, msu_task_t *task,
+                                    const gchar *object_class,
+                                    const gchar *mime_type)
+{
+       GUPnPDIDLLiteWriter *writer;
+       GUPnPDIDLLiteObject *item;
+       gchar *retval;
+       GUPnPProtocolInfo *protocol_info;
+       GUPnPDIDLLiteResource *res;
+
+       writer = gupnp_didl_lite_writer_new(NULL);
+       item = GUPNP_DIDL_LITE_OBJECT(gupnp_didl_lite_writer_add_item(writer));
+
+       gupnp_didl_lite_object_set_id(item, "");
+       gupnp_didl_lite_object_set_title(item, task->ut.upload.display_name);
+       gupnp_didl_lite_object_set_parent_id(item, parent_id);
+       gupnp_didl_lite_object_set_upnp_class(item, object_class);
+       gupnp_didl_lite_object_set_restricted(item, FALSE);
+
+       protocol_info = gupnp_protocol_info_new();
+       gupnp_protocol_info_set_mime_type(protocol_info, mime_type);
+       gupnp_protocol_info_set_protocol(protocol_info, "*");
+       gupnp_protocol_info_set_network(protocol_info, "*");
+
+       res = gupnp_didl_lite_object_add_resource(item);
+       gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
+
+       /* TODO: Need to compute DLNA Profile */
+
+       retval = gupnp_didl_lite_writer_get_string(writer);
+
+       g_object_unref(res);
+       g_object_unref(protocol_info);
+       g_object_unref(item);
+       g_object_unref(writer);
+
+       return retval;
+}
+
+static void prv_extract_import_uri(GUPnPDIDLLiteParser *parser,
+                                  GUPnPDIDLLiteObject *object,
+                                  gpointer user_data)
+{
+       gchar **import_uri = user_data;
+       GList *resources;
+       GList *ptr;
+       GUPnPDIDLLiteResource *res;
+       const gchar *uri;
+
+       if (!*import_uri) {
+               resources = gupnp_didl_lite_object_get_resources(object);
+               ptr = resources;
+               while (ptr) {
+                       res = ptr->data;
+                       if (!*import_uri) {
+                               uri = gupnp_didl_lite_resource_get_import_uri(
+                                       res);
+                               if (uri)
+                                       *import_uri = g_strdup(uri);
+                       }
+                       g_object_unref(res);
+                       ptr = ptr->next;
+               }
+
+               g_list_free(resources);
+       }
+}
+
+static void prv_upload_delete_cb(GUPnPServiceProxy *proxy,
+                                GUPnPServiceProxyAction *action,
+                                gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+
+       MSU_LOG_DEBUG("Enter");
+
+       (void) gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                             NULL, NULL);
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_msu_upload_job_delete(gpointer up_job)
+{
+       msu_device_upload_job_t *upload_job = up_job;
+
+       if (up_job) {
+               if (upload_job->remove_idle)
+                       (void) g_source_remove(upload_job->remove_idle);
+
+               g_free(upload_job);
+       }
+}
+
+static gboolean prv_remove_update_job(gpointer user_data)
+{
+       msu_device_upload_job_t *upload_job = user_data;
+       msu_device_upload_t *upload;
+
+       upload = g_hash_table_lookup(upload_job->device->uploads,
+                                    &upload_job->upload_id);
+       if (upload) {
+               g_hash_table_remove(upload_job->device->uploads,
+                                   &upload_job->upload_id);
+
+               MSU_LOG_DEBUG("Removing Upload Object: %d",
+                       upload_job->upload_id);
+       }
+
+       upload_job->remove_idle = 0;
+       g_hash_table_remove(upload_job->device->upload_jobs,
+                           &upload_job->upload_id);
+
+       return FALSE;
+}
+
+static void prv_generate_upload_update(msu_device_upload_job_t *upload_job,
+                                      msu_device_upload_t *upload)
+{
+       GVariant *args;
+
+       args = g_variant_new("(ustt)", upload_job->upload_id, upload->status,
+                            upload->bytes_uploaded, upload->bytes_to_upload);
+
+       MSU_LOG_DEBUG(
+               "Emitting: %s (%u %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT")"
+               " on %s",
+               MSU_INTERFACE_UPLOAD_UPDATE, upload_job->upload_id,
+               upload->status, upload->bytes_uploaded,
+               upload->bytes_to_upload, upload_job->device->path);
+
+       (void) g_dbus_connection_emit_signal(upload_job->device->connection,
+                                            NULL,
+                                            upload_job->device->path,
+                                            MSU_INTERFACE_MEDIA_DEVICE,
+                                            MSU_INTERFACE_UPLOAD_UPDATE,
+                                            args,
+                                            NULL);
+}
+
+static void prv_post_finished(SoupSession *session, SoupMessage *msg,
+                             gpointer user_data)
+{
+       msu_device_upload_job_t *upload_job = user_data;
+       msu_device_upload_t *upload;
+       gint *upload_id;
+
+       MSU_LOG_DEBUG("Enter");
+
+       MSU_LOG_DEBUG("Upload %u finished.  Code %u Message %s",
+                     upload_job->upload_id, msg->status_code,
+                     msg->reason_phrase);
+
+       /* This is clumsy but we need to distinguish between two cases:
+          1. We cancel because the process is exitting.
+          2. We cancel because a client has called CancelUpload.
+
+          We could use custom SOUP error messages to distinguish the cases
+          but device->shutting_down seemed less hacky.
+
+          We need this check as if we are shutting down it is
+          dangerous to manipulate uploads as we are being called from its
+          destructor.
+       */
+
+       if (upload_job->device->shutting_down) {
+               MSU_LOG_DEBUG("Device shutting down. Cancelling Upload");
+               goto on_error;
+       }
+
+       upload = g_hash_table_lookup(upload_job->device->uploads,
+                                    &upload_job->upload_id);
+       if (upload) {
+               upload_job->remove_idle =
+                       g_timeout_add(30000, prv_remove_update_job, user_data);
+
+               if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
+                       upload->status = MSU_UPLOAD_STATUS_COMPLETED;
+                       upload->bytes_uploaded = upload->bytes_to_upload;
+               } else if (msg->status_code == SOUP_STATUS_CANCELLED) {
+                       upload->status = MSU_UPLOAD_STATUS_CANCELLED;
+               } else {
+                       upload->status = MSU_UPLOAD_STATUS_ERROR;
+               }
+
+               MSU_LOG_DEBUG("Upload Status: %s", upload->status);
+
+               prv_generate_upload_update(upload_job, upload);
+
+               g_object_unref(upload->msg);
+               upload->msg = NULL;
+
+               g_object_unref(upload->soup_session);
+               upload->soup_session = NULL;
+
+               g_mapped_file_unref(upload->mapped_file);
+               upload->mapped_file = NULL;
+
+               upload_id = g_new(gint, 1);
+               *upload_id = upload_job->upload_id;
+
+               g_hash_table_insert(upload_job->device->upload_jobs, upload_id,
+                                   upload_job);
+
+               upload_job = NULL;
+       }
+
+on_error:
+
+       prv_msu_upload_job_delete(upload_job);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_msu_device_upload_delete(gpointer up)
+{
+       msu_device_upload_t *upload = up;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (upload) {
+               if (upload->msg) {
+                       soup_session_cancel_message(upload->soup_session,
+                                                   upload->msg,
+                                                   SOUP_STATUS_CANCELLED);
+                       g_object_unref(upload->msg);
+               }
+
+               if (upload->soup_session)
+                       g_object_unref(upload->soup_session);
+
+               if (upload->mapped_file)
+                       g_mapped_file_unref(upload->mapped_file);
+
+               g_free(upload);
+       }
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_post_bytes_written(SoupMessage *msg, SoupBuffer *chunk,
+                                  gpointer user_data)
+{
+       msu_device_upload_t *upload = user_data;
+
+       upload->bytes_uploaded += chunk->length;
+       if (upload->bytes_uploaded > upload->bytes_to_upload)
+               upload->bytes_uploaded = upload->bytes_to_upload;
+}
+
+static msu_device_upload_t *prv_msu_device_upload_new(const gchar *file_path,
+                                                     const gchar *import_uri,
+                                                     const gchar *mime_type,
+                                                     GError **error)
+{
+       const char *body;
+       gsize body_length;
+       msu_device_upload_t *upload;
+
+       MSU_LOG_DEBUG("Enter");
+
+       upload = g_new0(msu_device_upload_t, 1);
+
+       upload->mapped_file = g_mapped_file_new(file_path, FALSE, NULL);
+       if (!upload->mapped_file) {
+               MSU_LOG_WARNING("Unable to map %s into memory", file_path);
+
+               *error = g_error_new(MSU_ERROR, MSU_ERROR_IO,
+                                    "Unable to map %s into memory",
+                                    file_path);
+               goto on_error;
+       }
+
+       body = g_mapped_file_get_contents(upload->mapped_file);
+       body_length = g_mapped_file_get_length(upload->mapped_file);
+
+       upload->soup_session = soup_session_async_new();
+       upload->msg = soup_message_new("POST", import_uri);
+
+       if (!upload->msg) {
+               MSU_LOG_WARNING("Invalid URI %s", import_uri);
+
+               *error = g_error_new(MSU_ERROR, MSU_ERROR_BAD_RESULT,
+                                    "Invalid URI %s", import_uri);
+               goto on_error;
+       }
+       upload->status = MSU_UPLOAD_STATUS_IN_PROGRESS;
+       upload->bytes_to_upload = body_length;
+
+       soup_message_headers_set_expectations(upload->msg->request_headers,
+                                             SOUP_EXPECTATION_CONTINUE);
+
+       soup_message_set_request(upload->msg, mime_type, SOUP_MEMORY_STATIC,
+                                body, body_length);
+       g_signal_connect(upload->msg, "wrote-body-data",
+                        G_CALLBACK(prv_post_bytes_written), upload);
+
+       MSU_LOG_DEBUG("Exit with Success");
+
+       return upload;
+
+on_error:
+
+       prv_msu_device_upload_delete(upload);
+
+       MSU_LOG_WARNING("Exit with Fail");
+
+       return NULL;
+}
+
+static void prv_create_container_cb(GUPnPServiceProxy *proxy,
+                GUPnPServiceProxyAction *action,
+                gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_create_container_t *cb_task_data;
+       GError *upnp_error = NULL;
+       gchar *result = NULL;
+       gchar *object_id = NULL;
+       gchar *object_path;
+
+       MSU_LOG_DEBUG("Enter");
+
+       cb_task_data = &cb_data->ut.create_container;
+
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           "ObjectID", G_TYPE_STRING,
+                                           &object_id,
+                                           "Result", G_TYPE_STRING,
+                                           &result,
+                                           NULL)) {
+               MSU_LOG_ERROR("Create Object operation failed: %s",
+                                                       upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Create Object operation "
+                                            " failed: %s",
+                                            upnp_error->message);
+               goto on_error;
+       }
+
+       object_path = msu_path_from_id(cb_task_data->root_path, object_id);
+       cb_data->result = g_variant_ref_sink(g_variant_new_object_path(
+                                                               object_path));
+       g_free(object_path);
+
+on_error:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+       if (object_id)
+               g_free(object_id);
+
+       if (result)
+               g_free(result);
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_create_object_cb(GUPnPServiceProxy *proxy,
+                                GUPnPServiceProxyAction *action,
+                                gpointer user_data)
+{
+       gchar *result = NULL;
        GUPnPDIDLLiteParser *parser = NULL;
        GError *upnp_error = NULL;
        msu_async_cb_data_t *cb_data = user_data;
-       msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
+       msu_async_upload_t *cb_task_data = &cb_data->ut.upload;
+       gchar *import_uri = NULL;
+       gchar *object_id = NULL;
+       gboolean delete_needed = FALSE;
+       GVariant *out_params[2];
+       gchar *object_path;
+       msu_device_upload_t *upload;
+       gint *upload_id;
+       msu_device_upload_job_t *upload_job;
 
        MSU_LOG_DEBUG("Enter");
 
        if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
                                            &upnp_error,
+                                           "ObjectID", G_TYPE_STRING,
+                                           &object_id,
                                            "Result", G_TYPE_STRING,
                                            &result,
-                                           "TotalMatches", G_TYPE_INT,
-                                           &cb_task_data->max_count,
                                            NULL)) {
+               MSU_LOG_WARNING("Create Object operation failed: %s",
+                               upnp_error->message);
 
-               MSU_LOG_WARNING("Search operation failed %s",
-                             upnp_error->message);
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Create Object operation "
+                                            " failed: %s",
+                                            upnp_error->message);
+               goto on_error;
+       }
+
+       delete_needed = TRUE;
+       parser = gupnp_didl_lite_parser_new();
+
+       g_signal_connect(parser, "object-available" ,
+                        G_CALLBACK(prv_extract_import_uri), &import_uri);
+
+       MSU_LOG_DEBUG("Create Object Result: %s", result);
+
+       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
+               && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
+
+               MSU_LOG_WARNING("Unable to parse results of CreateObject: %s",
+                               upnp_error->message);
 
                cb_data->error = g_error_new(MSU_ERROR,
                                             MSU_ERROR_OPERATION_FAILED,
-                                            "Search operation failed: %s",
+                                            "Unable to parse results of "
+                                            "CreateObject: %s",
                                             upnp_error->message);
                goto on_error;
        }
 
-       parser = gupnp_didl_lite_parser_new();
+       if (!import_uri) {
+               MSU_LOG_WARNING("Missing Import URI");
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Missing Import URI");
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("Import URI %s", import_uri);
+
+       upload = prv_msu_device_upload_new(cb_data->task->ut.upload.file_path,
+                                          import_uri, cb_task_data->mime_type,
+                                          &cb_data->error);
+       if (!upload)
+               goto on_error;
+
+       upload_job = g_new0(msu_device_upload_job_t, 1);
+       upload_job->device = cb_task_data->device;
+       upload_job->upload_id = (gint) cb_task_data->device->upload_id;
+
+       soup_session_queue_message(upload->soup_session, upload->msg,
+                                  prv_post_finished, upload_job);
+       g_object_ref(upload->msg);
+
+       upload_id = g_new(gint, 1);
+       *upload_id = upload_job->upload_id;
+       g_hash_table_insert(cb_task_data->device->uploads, upload_id, upload);
+
+       object_path = msu_path_from_id(cb_task_data->root_path, object_id);
 
-       cb_task_data->vbs = g_ptr_array_new_with_free_func(
-               prv_msu_device_object_builder_delete);
+       MSU_LOG_DEBUG("Upload ID %u", *upload_id);
+       MSU_LOG_DEBUG("Object ID %s", object_id);
+       MSU_LOG_DEBUG("Object Path %s", object_path);
 
-       g_signal_connect(parser, "object-available" ,
-                        G_CALLBACK(prv_found_target), cb_data);
+       out_params[0] = g_variant_new_uint32(*upload_id);
+       out_params[1] = g_variant_new_object_path(object_path);
+       cb_data->result = g_variant_ref_sink(g_variant_new_tuple(out_params,
+                                                                2));
 
-       MSU_LOG_DEBUG("Server Search result: %s", result);
+       ++cb_task_data->device->upload_id;
+       if (cb_task_data->device->upload_id > G_MAXINT)
+               cb_task_data->device->upload_id = 0;
 
-       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
-               && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
-               MSU_LOG_WARNING("Unable to parse results of search: %s",
-                             upnp_error->message);
+       g_free(object_path);
 
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_OPERATION_FAILED,
-                                            "Unable to parse results of "
-                                            "search: %s", upnp_error->message);
-               goto on_error;
-       }
+on_error:
 
-       if (cb_task_data->need_child_count) {
-               MSU_LOG_DEBUG("Need to retrieve child count");
+       if (cb_data->error && delete_needed) {
+               MSU_LOG_WARNING("Upload failed deleting created object "
+                               "with id %s", object_id);
 
-               if (cb_data->task->multiple_retvals)
-                       cb_task_data->get_children_cb =
-                               prv_get_search_ex_result;
-               else
-                       cb_task_data->get_children_cb = prv_get_children_result;
-               prv_retrieve_child_count_for_list(cb_data);
-               goto no_complete;
+               cb_data->action = gupnp_service_proxy_begin_action(
+                       cb_data->proxy, "DestroyObject", prv_upload_delete_cb,
+                       cb_data, "ObjectID", G_TYPE_STRING,
+                       object_id, NULL);
        } else {
-               if (cb_data->task->multiple_retvals)
-                       prv_get_search_ex_result(cb_data);
-               else
-                       prv_get_children_result(cb_data);
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+               g_cancellable_disconnect(cb_data->cancellable,
+                                        cb_data->cancel_id);
        }
 
-on_error:
-
-       (void) g_idle_add(msu_async_complete_task, cb_data);
-       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
-
-no_complete:
+       g_free(object_id);
+       g_free(import_uri);
 
        if (parser)
                g_object_unref(parser);
@@ -1530,30 +3040,35 @@ no_complete:
        MSU_LOG_DEBUG("Exit");
 }
 
-void msu_device_search(msu_device_t *device,  msu_task_t *task,
-                      msu_async_cb_data_t *cb_data,
-                      const gchar *upnp_filter, const gchar *upnp_query,
-                      const gchar *sort_by, GCancellable *cancellable)
+void msu_device_upload(msu_device_t *device, msu_client_t *client,
+                      msu_task_t *task, const gchar *parent_id,
+                      msu_async_cb_data_t *cb_data, GCancellable *cancellable)
 {
        msu_device_context_t *context;
+       gchar *didl;
+       msu_async_upload_t *cb_task_data;
 
        MSU_LOG_DEBUG("Enter");
+       MSU_LOG_DEBUG("Uploading file to %s", parent_id);
+
+       context = msu_device_get_context(device, client);
+       cb_task_data = &cb_data->ut.upload;
+
+       didl = prv_create_upload_didl(parent_id, task,
+                                     cb_task_data->object_class,
+                                     cb_task_data->mime_type);
 
-       context = msu_device_get_context(device);
+       MSU_LOG_DEBUG("DIDL: %s", didl);
 
        cb_data->action = gupnp_service_proxy_begin_action(
-               context->service_proxy, "Search",
-               prv_search_cb,
-               cb_data,
-               "ContainerID", G_TYPE_STRING, cb_data->id,
-               "SearchCriteria", G_TYPE_STRING, upnp_query,
-               "Filter", G_TYPE_STRING, upnp_filter,
-               "StartingIndex", G_TYPE_INT, task->ut.search.start,
-               "RequestedCount", G_TYPE_INT, task->ut.search.count,
-               "SortCriteria", G_TYPE_STRING, sort_by,
+               context->service_proxy, "CreateObject",
+               prv_create_object_cb, cb_data,
+               "ContainerID", G_TYPE_STRING, parent_id,
+               "Elements", G_TYPE_STRING, didl,
                NULL);
 
        cb_data->proxy = context->service_proxy;
+       cb_task_data->device = device;
 
        cb_data->cancel_id =
                g_cancellable_connect(cancellable,
@@ -1561,369 +3076,482 @@ void msu_device_search(msu_device_t *device,  msu_task_t *task,
                                      cb_data, NULL);
        cb_data->cancellable = cancellable;
 
+       g_free(didl);
+
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_resource(GUPnPDIDLLiteParser *parser,
-                            GUPnPDIDLLiteObject *object,
-                            gpointer user_data)
+gboolean msu_device_get_upload_status(msu_device_t *device,
+                                     msu_task_t *task, GError **error)
 {
-       msu_async_cb_data_t *cb_data = user_data;
-       msu_task_t *task = cb_data->task;
-       msu_task_get_resource_t *task_data = &task->ut.resource;
-       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+       msu_device_upload_t *upload;
+       gboolean retval = FALSE;
+       GVariant *out_params[3];
+       guint upload_id;
 
        MSU_LOG_DEBUG("Enter");
 
-       msu_props_add_resource(cb_task_data->vb, object,
-                              cb_task_data->filter_mask,
-                              task_data->protocol_info);
+       upload_id = task->ut.upload_action.upload_id;
+
+       upload = g_hash_table_lookup(device->uploads, &upload_id);
+       if (!upload) {
+               *error = g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                    "Unknown Upload ID %u ", upload_id);
+               goto on_error;
+       }
+
+       out_params[0] = g_variant_new_string(upload->status);
+       out_params[1] = g_variant_new_uint64(upload->bytes_uploaded);
+       out_params[2] = g_variant_new_uint64(upload->bytes_to_upload);
+
+       MSU_LOG_DEBUG("Upload ( %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT" )",
+                     upload->status, upload->bytes_uploaded,
+                     upload->bytes_to_upload);
+
+       task->result = g_variant_ref_sink(g_variant_new_tuple(out_params, 3));
+
+       retval = TRUE;
+
+on_error:
+
+       MSU_LOG_DEBUG("Exit");
+
+       return retval;
 }
 
-void msu_device_get_resource(msu_device_t *device,  msu_task_t *task,
-                            msu_async_cb_data_t *cb_data,
-                            const gchar *upnp_filter,
-                            GCancellable *cancellable)
+void msu_device_get_upload_ids(msu_device_t *device, msu_task_t *task)
 {
-       msu_async_get_all_t *cb_task_data;
-       msu_device_context_t *context;
-
-       context = msu_device_get_context(device);
-       cb_task_data = &cb_data->ut.get_all;
+       GVariantBuilder vb;
+       GHashTableIter iter;
+       gpointer key;
 
-       cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
-       cb_task_data->prop_func = G_CALLBACK(prv_get_resource);
+       MSU_LOG_DEBUG("Enter");
 
-       cb_data->action = gupnp_service_proxy_begin_action(
-               context->service_proxy, "Browse",
-               prv_get_all_ms2spec_props_cb, cb_data,
-               "ObjectID", G_TYPE_STRING, cb_data->id,
-               "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
-               "Filter", G_TYPE_STRING, upnp_filter,
-               "StartingIndex", G_TYPE_INT, 0,
-               "RequestedCount", G_TYPE_INT, 0,
-               "SortCriteria", G_TYPE_STRING,
-               "", NULL);
+       g_variant_builder_init(&vb, G_VARIANT_TYPE("au"));
 
-       cb_data->proxy = context->service_proxy;
+       g_hash_table_iter_init(&iter, device->uploads);
+       while (g_hash_table_iter_next(&iter, &key, NULL))
+               g_variant_builder_add(&vb, "u", (guint32) (*((gint *) key)));
 
-       cb_data->cancel_id =
-               g_cancellable_connect(cancellable,
-                                     G_CALLBACK(msu_async_task_cancelled),
-                                     cb_data, NULL);
-       cb_data->cancellable = cancellable;
+       task->result = g_variant_ref_sink(g_variant_builder_end(&vb));
 
        MSU_LOG_DEBUG("Exit");
 }
 
-static gchar *prv_create_upload_didl(const gchar *parent_id, msu_task_t *task,
-                                    const gchar *object_class,
-                                    const gchar *mime_type)
+gboolean msu_device_cancel_upload(msu_device_t *device, msu_task_t *task,
+                                 GError **error)
 {
-       GUPnPDIDLLiteWriter *writer;
-       GUPnPDIDLLiteObject *item;
-       gchar *retval;
-       GUPnPProtocolInfo *protocol_info;
-       GUPnPDIDLLiteResource *res;
+       msu_device_upload_t *upload;
+       gboolean retval = FALSE;
+       guint upload_id;
 
-       writer = gupnp_didl_lite_writer_new(NULL);
-       item = GUPNP_DIDL_LITE_OBJECT(gupnp_didl_lite_writer_add_item(writer));
+       MSU_LOG_DEBUG("Enter");
 
-       gupnp_didl_lite_object_set_id(item, "");
-       gupnp_didl_lite_object_set_title(item, task->ut.upload.display_name);
-       gupnp_didl_lite_object_set_parent_id(item, parent_id);
-       gupnp_didl_lite_object_set_upnp_class(item, object_class);
-       gupnp_didl_lite_object_set_restricted(item, FALSE);
+       upload_id = task->ut.upload_action.upload_id;
 
-       protocol_info = gupnp_protocol_info_new();
-       gupnp_protocol_info_set_mime_type(protocol_info, mime_type);
-       gupnp_protocol_info_set_protocol(protocol_info, "*");
-       gupnp_protocol_info_set_network(protocol_info, "*");
+       upload = g_hash_table_lookup(device->uploads, &upload_id);
+       if (!upload) {
+               *error = g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                    "Unknown Upload ID %u ", upload_id);
+               goto on_error;
+       }
 
-       res = gupnp_didl_lite_object_add_resource(item);
-       gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
+       if (upload->msg) {
+               soup_session_cancel_message(upload->soup_session, upload->msg,
+                                           SOUP_STATUS_CANCELLED);
+               MSU_LOG_DEBUG("Cancelling Upload %u ", upload_id);
+       }
 
-       /* TODO: Need to compute DLNA Profile */
+       retval = TRUE;
 
-       retval = gupnp_didl_lite_writer_get_string(writer);
+on_error:
 
-       g_object_unref(res);
-       g_object_unref(protocol_info);
-       g_object_unref(item);
-       g_object_unref(writer);
+       MSU_LOG_DEBUG("Exit");
 
        return retval;
 }
 
-static void prv_extract_import_uri(GUPnPDIDLLiteParser *parser,
-                                  GUPnPDIDLLiteObject *object,
-                                  gpointer user_data)
+static void prv_destroy_object_cb(GUPnPServiceProxy *proxy,
+                                 GUPnPServiceProxyAction *action,
+                                 gpointer user_data)
 {
-       gchar **import_uri = user_data;
-       GList *resources;
-       GList *ptr;
-       GUPnPDIDLLiteResource *res;
-       const gchar *uri;
+       GError *upnp_error = NULL;
+       msu_async_cb_data_t *cb_data = user_data;
 
-       if (!*import_uri) {
-               ptr = resources = gupnp_didl_lite_object_get_resources(object);
-               while (ptr) {
-                       res = ptr->data;
-                       if (!*import_uri) {
-                               uri = gupnp_didl_lite_resource_get_import_uri(
-                                       res);
-                               if (uri)
-                                       *import_uri = g_strdup(uri);
-                       }
-                       g_object_unref(res);
-                       ptr = ptr->next;
-               }
+       MSU_LOG_DEBUG("Enter");
 
-               g_list_free(resources);
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           NULL)) {
+               MSU_LOG_WARNING("Destroy Object operation failed: %s",
+                               upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Destroy Object operation "
+                                            " failed: %s",
+                                            upnp_error->message);
        }
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_device_delete_object(msu_device_t *device, msu_client_t *client,
+                             msu_task_t *task,
+                             msu_async_cb_data_t *cb_data,
+                             GCancellable *cancellable)
+{
+       msu_device_context_t *context;
+
+       MSU_LOG_DEBUG("Enter");
+
+       context = msu_device_get_context(device, client);
+
+       cb_data->action = gupnp_service_proxy_begin_action(
+                               context->service_proxy, "DestroyObject",
+                               prv_destroy_object_cb, cb_data,
+                               "ObjectID", G_TYPE_STRING, cb_data->id,
+                               NULL);
+
+       cb_data->proxy = context->service_proxy;
+
+       cb_data->cancel_id = g_cancellable_connect(cancellable,
+                                       G_CALLBACK(msu_async_task_cancelled),
+                                       cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_device_create_container(msu_device_t *device, msu_client_t *client,
+                                msu_task_t *task,
+                                const gchar *parent_id,
+                                msu_async_cb_data_t *cb_data,
+                                GCancellable *cancellable)
+{
+       msu_device_context_t *context;
+       gchar *didl;
+
+       MSU_LOG_DEBUG("Enter");
+
+       context = msu_device_get_context(device, client);
+
+       didl = prv_create_new_container_didl(parent_id, task);
+
+       MSU_LOG_DEBUG("DIDL: %s", didl);
+
+       cb_data->action = gupnp_service_proxy_begin_action(
+                               context->service_proxy, "CreateObject",
+                               prv_create_container_cb, cb_data,
+                               "ContainerID", G_TYPE_STRING, parent_id,
+                               "Elements", G_TYPE_STRING, didl,
+                               NULL);
+
+       cb_data->proxy = context->service_proxy;
+
+       cb_data->cancel_id =
+               g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+       g_free(didl);
+
+       MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_upload_delete_cb(GUPnPServiceProxy *proxy,
-                                GUPnPServiceProxyAction *action,
-                                gpointer user_data)
+static void prv_update_object_update_cb(GUPnPServiceProxy *proxy,
+                                       GUPnPServiceProxyAction *action,
+                                       gpointer user_data)
 {
+       GError *upnp_error = NULL;
        msu_async_cb_data_t *cb_data = user_data;
 
        MSU_LOG_DEBUG("Enter");
 
-       (void) gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
-                                             NULL, NULL);
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           NULL)) {
+               MSU_LOG_WARNING("Update Object operation failed: %s",
+                               upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Update Object operation "
+                                            " failed: %s",
+                                            upnp_error->message);
+       }
+
        (void) g_idle_add(msu_async_complete_task, cb_data);
        g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
 
+       if (upnp_error)
+               g_error_free(upnp_error);
+
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_post_finished(SoupSession *session, SoupMessage *msg,
-                             gpointer user_data)
+static gchar *prv_get_current_xml_fragment(GUPnPDIDLLiteObject *object,
+                                          guint32 mask)
 {
-       msu_device_upload_job_t *upload_job = user_data;
+       gchar *retval = NULL;
+
+       if (mask & MSU_UPNP_MASK_PROP_DISPLAY_NAME)
+               retval = gupnp_didl_lite_object_get_title_xml_string(object);
+       else if (mask & MSU_UPNP_MASK_PROP_ALBUM)
+               retval = gupnp_didl_lite_object_get_album_xml_string(object);
+       else if (mask & MSU_UPNP_MASK_PROP_DATE)
+               retval = gupnp_didl_lite_object_get_date_xml_string(object);
+       else if (mask & MSU_UPNP_MASK_PROP_TYPE)
+               retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
+                       object);
+       else if (mask & MSU_UPNP_MASK_PROP_TRACK_NUMBER)
+               retval = gupnp_didl_lite_object_get_track_number_xml_string(
+                       object);
+       else if (mask & MSU_UPNP_MASK_PROP_ARTISTS)
+               retval = gupnp_didl_lite_object_get_artists_xml_string(object);
 
-       MSU_LOG_DEBUG("Enter");
+       return retval;
+}
 
-       MSU_LOG_DEBUG("Upload %u finished.  Code %u Message %s",
-                     upload_job->upload_id, msg->status_code,
-                     msg->reason_phrase);
+static gchar *prv_get_new_xml_fragment(GUPnPDIDLLiteObject *object,
+                                      guint32 mask,
+                                      GVariant *value)
+{
+       GUPnPDIDLLiteContributor *artist;
+       const gchar *artist_name;
+       const gchar *upnp_class;
+       GVariantIter viter;
+       gchar *retval = NULL;
+
+       if (mask & MSU_UPNP_MASK_PROP_DISPLAY_NAME) {
+               gupnp_didl_lite_object_set_title(object,
+                       g_variant_get_string(value, NULL));
+
+               retval = gupnp_didl_lite_object_get_title_xml_string(object);
+       } else if (mask & MSU_UPNP_MASK_PROP_ALBUM) {
+               gupnp_didl_lite_object_set_album(object,
+                       g_variant_get_string(value, NULL));
+
+               retval = gupnp_didl_lite_object_get_album_xml_string(object);
+       } else if (mask & MSU_UPNP_MASK_PROP_DATE) {
+               gupnp_didl_lite_object_set_date(object,
+                       g_variant_get_string(value, NULL));
+
+               retval = gupnp_didl_lite_object_get_date_xml_string(object);
+       } else if (mask & MSU_UPNP_MASK_PROP_TYPE) {
+               upnp_class = msu_props_media_spec_to_upnp_class(
+                                       g_variant_get_string(value, NULL));
+
+               gupnp_didl_lite_object_set_upnp_class(object, upnp_class);
+
+               retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
+                       object);
+       } else if (mask & MSU_UPNP_MASK_PROP_TRACK_NUMBER) {
+               gupnp_didl_lite_object_set_track_number(object,
+                                       g_variant_get_int32(value));
+
+               retval = gupnp_didl_lite_object_get_track_number_xml_string(
+                       object);
+       } else if (mask & MSU_UPNP_MASK_PROP_ARTISTS) {
+               gupnp_didl_lite_object_unset_artists(object);
+
+               (void) g_variant_iter_init(&viter, value);
+
+               while (g_variant_iter_next(&viter, "&s", &artist_name)) {
+                       artist = gupnp_didl_lite_object_add_artist(object);
+
+                       gupnp_didl_lite_contributor_set_name(artist,
+                                                            artist_name);
+               }
 
-       if (msg->status_code != SOUP_STATUS_CANCELLED)
-               g_hash_table_remove(upload_job->device->uploads,
-                                   &upload_job->upload_id);
-       g_free(upload_job);
+               retval = gupnp_didl_lite_object_get_artists_xml_string(object);
+       }
 
-       MSU_LOG_DEBUG("Exit");
+       return retval;
 }
 
-static void prv_msu_device_upload_delete(gpointer up)
+static void prv_get_xml_fragments(GUPnPDIDLLiteParser *parser,
+                                 GUPnPDIDLLiteObject *object,
+                                 gpointer user_data)
 {
-       msu_device_upload_t *upload = up;
+       GString *current_str;
+       GString *new_str;
+       gchar *frag1;
+       gchar *frag2;
+       GVariantIter viter;
+       const gchar *prop;
+       GVariant *value;
+       msu_prop_map_t *prop_map;
+       GUPnPDIDLLiteWriter *writer;
+       GUPnPDIDLLiteObject *scratch_object;
+       gboolean first = TRUE;
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_update_t *cb_task_data = &cb_data->ut.update;
+       msu_task_t *task = cb_data->task;
+       msu_task_update_t *task_data = &task->ut.update;
 
        MSU_LOG_DEBUG("Enter");
 
-       if (upload) {
-               if (upload->msg) {
-                       soup_session_cancel_message(upload->soup_session,
-                                                   upload->msg,
-                                                   SOUP_STATUS_CANCELLED);
-                       g_object_unref(upload->msg);
-               }
+       current_str = g_string_new("");
+       new_str = g_string_new("");
 
-               if (upload->soup_session)
-                       g_object_unref(upload->soup_session);
+       writer = gupnp_didl_lite_writer_new(NULL);
+       if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
+               scratch_object = GUPNP_DIDL_LITE_OBJECT(
+                       gupnp_didl_lite_writer_add_container(writer));
+       else
+               scratch_object = GUPNP_DIDL_LITE_OBJECT(
+                       gupnp_didl_lite_writer_add_item(writer));
 
-               if (upload->mapped_file)
-                       g_mapped_file_unref(upload->mapped_file);
+       (void) g_variant_iter_init(&viter, task_data->to_add_update);
 
-               g_free(upload);
-       }
+       while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
 
-       MSU_LOG_DEBUG("Exit");
-}
+               MSU_LOG_DEBUG("to_add_update = %s", prop);
 
-static msu_device_upload_t *prv_msu_device_upload_new(const gchar *file_path,
-                                                     const gchar *import_uri,
-                                                     const gchar *mime_type,
-                                                     GError **error)
-{
-       const char *body;
-       gsize body_length;
-       msu_device_upload_t *upload;
+               prop_map = g_hash_table_lookup(cb_task_data->map, prop);
 
-       MSU_LOG_DEBUG("Enter");
+               frag1 = prv_get_current_xml_fragment(object, prop_map->type);
+               frag2 = prv_get_new_xml_fragment(scratch_object, prop_map->type,
+                                                value);
 
-       upload = g_new0(msu_device_upload_t, 1);
+               if (!frag2) {
+                       MSU_LOG_DEBUG("Unable to set %s.  Skipping", prop);
 
-       upload->mapped_file = g_mapped_file_new(file_path, FALSE, NULL);
-       if (!upload->mapped_file) {
-               MSU_LOG_WARNING("Unable to map %s into memory", file_path);
+                       g_free(frag1);
+                       continue;
+               }
 
-               *error = g_error_new(MSU_ERROR, MSU_ERROR_IO,
-                                    "Unable to map %s into memory",
-                                    file_path);
-               goto on_error;
-       }
+               if (!first) {
+                       g_string_append(current_str, ",");
+                       g_string_append(new_str, ",");
+               } else {
+                       first = FALSE;
+               }
 
-       body = g_mapped_file_get_contents(upload->mapped_file);
-       body_length = g_mapped_file_get_length(upload->mapped_file);
+               if (frag1) {
+                       g_string_append(current_str, (const gchar *) frag1);
+                       g_free(frag1);
+               }
 
-       upload->soup_session = soup_session_async_new();
-       upload->msg = soup_message_new("POST", import_uri);
+               g_string_append(new_str, (const gchar *) frag2);
+               g_free(frag2);
+       }
 
-       if (!upload->msg) {
-               MSU_LOG_WARNING("Invalid URI %s", import_uri);
+       (void) g_variant_iter_init(&viter, task_data->to_delete);
 
-               *error = g_error_new(MSU_ERROR, MSU_ERROR_BAD_RESULT,
-                                    "Invalid URI %s", import_uri);
-               goto on_error;
-       }
+       while (g_variant_iter_next(&viter, "&s", &prop)) {
+               MSU_LOG_DEBUG("to_delete = %s", prop);
 
-       soup_message_headers_set_expectations(upload->msg->request_headers,
-                                             SOUP_EXPECTATION_CONTINUE);
+               prop_map = g_hash_table_lookup(cb_task_data->map, prop);
 
-       soup_message_set_request(upload->msg, mime_type, SOUP_MEMORY_STATIC,
-                                body, body_length);
+               frag1 = prv_get_current_xml_fragment(object, prop_map->type);
+               if (!frag1)
+                       continue;
 
-       MSU_LOG_DEBUG("Exit with Success");
+               if (!first)
+                       g_string_append(current_str, ",");
+               else
+                       first = FALSE;
 
-       return upload;
+               g_string_append(current_str, (const gchar *) frag1);
+               g_free(frag1);
+       }
 
-on_error:
+       cb_task_data->current_tag_value = g_string_free(current_str, FALSE);
+       MSU_LOG_DEBUG("current_tag_value = %s",
+                     cb_task_data->current_tag_value);
 
-       prv_msu_device_upload_delete(upload);
+       cb_task_data->new_tag_value = g_string_free(new_str, FALSE);
+       MSU_LOG_DEBUG("new_tag_value = %s", cb_task_data->new_tag_value);
 
-       MSU_LOG_WARNING("Exit with Fail");
+       g_object_unref(scratch_object);
+       g_object_unref(writer);
 
-       return NULL;
+       MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_create_object_cb(GUPnPServiceProxy *proxy,
-                                GUPnPServiceProxyAction *action,
-                                gpointer user_data)
+static void prv_update_object_browse_cb(GUPnPServiceProxy *proxy,
+                                       GUPnPServiceProxyAction *action,
+                                       gpointer user_data)
 {
-       gchar *result = NULL;
-       GUPnPDIDLLiteParser *parser = NULL;
        GError *upnp_error = NULL;
        msu_async_cb_data_t *cb_data = user_data;
-       msu_async_upload_t *cb_task_data = &cb_data->ut.upload;
-       gchar *import_uri = NULL;
-       gchar *object_id = NULL;
-       gboolean delete_needed = FALSE;
-       GVariant *out_params[2];
-       gchar *object_path;
-       msu_device_upload_t *upload;
-       gint *upload_id;
-       msu_device_upload_job_t *upload_job;
+       msu_async_update_t *cb_task_data = &cb_data->ut.update;
+       GUPnPDIDLLiteParser *parser = NULL;
+       gchar *result = NULL;
 
        MSU_LOG_DEBUG("Enter");
 
        if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
                                            &upnp_error,
-                                           "ObjectID", G_TYPE_STRING,
-                                           &object_id,
                                            "Result", G_TYPE_STRING,
-                                           &result,
-                                           NULL)) {
-               MSU_LOG_WARNING("Create Object operation failed: %s",
+                                           &result, NULL)) {
+               MSU_LOG_WARNING("Browse Object operation failed: %s",
                                upnp_error->message);
 
                cb_data->error = g_error_new(MSU_ERROR,
                                             MSU_ERROR_OPERATION_FAILED,
-                                            "Create Object operation "
-                                            " failed: %s",
+                                            "Browse operation failed: %s",
                                             upnp_error->message);
                goto on_error;
        }
 
-       delete_needed = TRUE;
-       parser = gupnp_didl_lite_parser_new();
-
-       g_signal_connect(parser, "object-available" ,
-                        G_CALLBACK(prv_extract_import_uri), &import_uri);
+       MSU_LOG_DEBUG("msu_device_update_ex_object result: %s", result);
 
-       MSU_LOG_DEBUG("Create Object Result: %s", result);
+       parser = gupnp_didl_lite_parser_new();
 
-       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
-               && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
+       g_signal_connect(parser, "object-available",
+                        G_CALLBACK(prv_get_xml_fragments),
+                        cb_data);
 
-               MSU_LOG_WARNING("Unable to parse results of CreateObject: %s",
-                               upnp_error->message);
+       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
+               if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
+                       MSU_LOG_WARNING("Property not defined for object");
 
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_OPERATION_FAILED,
-                                            "Unable to parse results of "
-                                            "CreateObject: %s",
-                                            upnp_error->message);
-               goto on_error;
-       }
+                       cb_data->error =
+                               g_error_new(MSU_ERROR,
+                                           MSU_ERROR_UNKNOWN_PROPERTY,
+                                           "Property not defined for object");
+               } else {
+                       MSU_LOG_WARNING("Unable to parse results of browse: %s",
+                                     upnp_error->message);
 
-       if (!import_uri) {
-               MSU_LOG_WARNING("Missing Import URI");
+                       cb_data->error =
+                               g_error_new(MSU_ERROR,
+                                           MSU_ERROR_OPERATION_FAILED,
+                                           "Unable to parse results of "
+                                           "browse: %s",
+                                           upnp_error->message);
+               }
 
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_OPERATION_FAILED,
-                                            "Missing Import URI");
                goto on_error;
        }
 
-       MSU_LOG_DEBUG("Import URI %s", import_uri);
-
-       upload = prv_msu_device_upload_new(cb_data->task->ut.upload.file_path,
-                                          import_uri, cb_task_data->mime_type,
-                                          &cb_data->error);
-       if (!upload)
-               goto on_error;
-
-       upload_job = g_new(msu_device_upload_job_t, 1);
-       upload_job->device = cb_task_data->device;
-       upload_job->upload_id = (gint) cb_task_data->device->upload_id;
-
-       soup_session_queue_message(upload->soup_session, upload->msg,
-                                  prv_post_finished, upload_job);
-       g_object_ref(upload->msg);
-
-       upload_id = g_new(gint, 1);
-       *upload_id = upload_job->upload_id;
-       g_hash_table_insert(cb_task_data->device->uploads, upload_id, upload);
-
-       object_path = msu_path_from_id(cb_task_data->root_path, object_id);
-
-       MSU_LOG_DEBUG("Upload ID %u", *upload_id);
-       MSU_LOG_DEBUG("Object ID %s", object_id);
-       MSU_LOG_DEBUG("Object Path %s", object_path);
-
-       out_params[0] = g_variant_new_uint32(*upload_id);
-       out_params[1] = g_variant_new_object_path(object_path);
-       cb_data->result = g_variant_ref_sink(g_variant_new_tuple(out_params,
-                                                                2));
-
-       ++cb_task_data->device->upload_id;
-       if (cb_task_data->device->upload_id > G_MAXINT)
-               cb_task_data->device->upload_id = 0;
+       cb_data->action = gupnp_service_proxy_begin_action(
+               cb_data->proxy, "UpdateObject",
+               prv_update_object_update_cb, cb_data,
+               "ObjectID", G_TYPE_STRING, cb_data->id,
+               "CurrentTagValue", G_TYPE_STRING,
+               cb_task_data->current_tag_value,
+               "NewTagValue", G_TYPE_STRING, cb_task_data->new_tag_value,
+               NULL);
 
-       g_free(object_path);
+       goto no_complete;
 
 on_error:
 
-       if (cb_data->error && delete_needed) {
-               MSU_LOG_WARNING("Upload failed deleting created object "
-                               "with id %s", object_id);
-
-               cb_data->action = gupnp_service_proxy_begin_action(
-                       cb_data->proxy, "DestroyObject", prv_upload_delete_cb,
-                       cb_data, "ObjectID", G_TYPE_STRING,
-                       object_id, NULL);
-       } else {
-               (void) g_idle_add(msu_async_complete_task, cb_data);
-               g_cancellable_disconnect(cb_data->cancellable,
-                                        cb_data->cancel_id);
-       }
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
 
-       g_free(object_id);
-       g_free(import_uri);
+no_complete:
 
        if (parser)
                g_object_unref(parser);
@@ -1936,43 +3564,36 @@ on_error:
        MSU_LOG_DEBUG("Exit");
 }
 
-void msu_device_upload(msu_device_t *device,  msu_task_t *task,
-                      const gchar *parent_id, msu_async_cb_data_t *cb_data,
-                      GCancellable *cancellable)
+void msu_device_update_object(msu_device_t *device, msu_client_t *client,
+                             msu_task_t *task,
+                             msu_async_cb_data_t *cb_data,
+                             const gchar *upnp_filter,
+                             GCancellable *cancellable)
 {
        msu_device_context_t *context;
-       gchar *didl;
-       msu_async_upload_t *cb_task_data;
 
        MSU_LOG_DEBUG("Enter");
-       MSU_LOG_DEBUG("Uploading file to %s", parent_id);
-
-       context = msu_device_get_context(device);
-       cb_task_data = &cb_data->ut.upload;
-
-       didl = prv_create_upload_didl(parent_id, task,
-                                     cb_task_data->object_class,
-                                     cb_task_data->mime_type);
 
-       MSU_LOG_DEBUG("DIDL: %s", didl);
+       context = msu_device_get_context(device, client);
 
        cb_data->action = gupnp_service_proxy_begin_action(
-               context->service_proxy, "CreateObject",
-               prv_create_object_cb, cb_data,
-               "ContainerID", G_TYPE_STRING, parent_id,
-               "Elements", G_TYPE_STRING, didl,
-               NULL);
+                               context->service_proxy, "Browse",
+                               prv_update_object_browse_cb, cb_data,
+                               "ObjectID", G_TYPE_STRING, cb_data->id,
+                               "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
+                               "Filter", G_TYPE_STRING, upnp_filter,
+                               "StartingIndex", G_TYPE_INT, 0,
+                               "RequestedCount", G_TYPE_INT, 0,
+                               "SortCriteria", G_TYPE_STRING,
+                               "", NULL);
 
        cb_data->proxy = context->service_proxy;
-       cb_task_data->device = device;
 
-       cb_data->cancel_id =
-               g_cancellable_connect(cancellable,
-                                     G_CALLBACK(msu_async_task_cancelled),
-                                     cb_data, NULL);
-       cb_data->cancellable = cancellable;
+       cb_data->cancel_id = g_cancellable_connect(cancellable,
+                                       G_CALLBACK(msu_async_task_cancelled),
+                                       cb_data, NULL);
 
-       g_free(didl);
+       cb_data->cancellable = cancellable;
 
        MSU_LOG_DEBUG("Exit");
 }
index c7b68e2..2a7f8cd 100644 (file)
@@ -26,6 +26,8 @@
 #include <libgupnp/gupnp-control-point.h>
 
 #include "async.h"
+#include "chain-task.h"
+#include "client.h"
 #include "props.h"
 
 typedef struct msu_device_context_t_ msu_device_context_t;
@@ -45,46 +47,76 @@ struct msu_device_t_ {
        GPtrArray *contexts;
        guint timeout_id;
        GHashTable *uploads;
+       GHashTable *upload_jobs;
        guint upload_id;
+       guint system_update_id;
+       GVariant *search_caps;
+       GVariant *sort_caps;
+       GVariant *sort_ext_caps;
+       GVariant *feature_list;
+       gboolean shutting_down;
 };
 
 void msu_device_append_new_context(msu_device_t *device,
                                   const gchar *ip_address,
                                   GUPnPDeviceProxy *proxy);
 void msu_device_delete(void *device);
-gboolean msu_device_new(GDBusConnection *connection,
-                       GUPnPDeviceProxy *proxy,
-                       const gchar *ip_address,
-                       const GDBusSubtreeVTable *vtable,
-                       void *user_data,
-                       guint counter,
-                       msu_device_t **device);
+msu_device_t *msu_device_new(GDBusConnection *connection,
+                            GUPnPDeviceProxy *proxy,
+                            const gchar *ip_address,
+                            const GDBusSubtreeVTable *vtable,
+                            void *user_data,
+                            GHashTable *filter_map,
+                            guint counter,
+                            msu_chain_task_t *chain);
+
 msu_device_t *msu_device_from_path(const gchar *path, GHashTable *device_list);
-msu_device_context_t *msu_device_get_context(msu_device_t *device);
-void msu_device_get_children(msu_device_t *device,  msu_task_t *task,
-                            msu_async_cb_data_t *cb_data,
+msu_device_context_t *msu_device_get_context(msu_device_t *device,
+                                            msu_client_t *client);
+void msu_device_get_children(msu_device_t *device, msu_client_t *client,
+                            msu_task_t *task, msu_async_cb_data_t *cb_data,
                             const gchar *upnp_filter, const gchar *sort_by,
                             GCancellable *cancellable);
-void msu_device_get_all_props(msu_device_t *device,  msu_task_t *task,
+void msu_device_get_all_props(msu_device_t *device, msu_client_t *client,
+                             msu_task_t *task,
                              msu_async_cb_data_t *cb_data,
                              gboolean root_object,
                              GCancellable *cancellable);
-void msu_device_get_prop(msu_device_t *device,  msu_task_t *task,
-                        msu_async_cb_data_t *cb_data,
+void msu_device_get_prop(msu_device_t *device, msu_client_t *client,
+                        msu_task_t *task, msu_async_cb_data_t *cb_data,
                         msu_prop_map_t *prop_map, gboolean root_object,
                         GCancellable *cancellable);
-void msu_device_search(msu_device_t *device,  msu_task_t *task,
-                      msu_async_cb_data_t *cb_data, const gchar *upnp_filter,
-                      const gchar *upnp_query, const gchar *sort_by,
-                      GCancellable *cancellable);
-void msu_device_get_resource(msu_device_t *device,  msu_task_t *task,
+void msu_device_search(msu_device_t *device, msu_client_t *client,
+                      msu_task_t *task, msu_async_cb_data_t *cb_data,
+                      const gchar *upnp_filter, const gchar *upnp_query,
+                      const gchar *sort_by, GCancellable *cancellable);
+void msu_device_get_resource(msu_device_t *device, msu_client_t *client,
+                            msu_task_t *task,
                             msu_async_cb_data_t *cb_data,
                             const gchar *upnp_filter,
                             GCancellable *cancellable);
 void msu_device_subscribe_to_contents_change(msu_device_t *device);
-void msu_device_upload(msu_device_t *device,  msu_task_t *task,
-                      const gchar *parent_id, msu_async_cb_data_t *cb_data,
-                      GCancellable *cancellable);
-
+void msu_device_upload(msu_device_t *device, msu_client_t *client,
+                      msu_task_t *task, const gchar *parent_id,
+                      msu_async_cb_data_t *cb_data, GCancellable *cancellable);
+gboolean msu_device_get_upload_status(msu_device_t *device,
+                                     msu_task_t *task, GError **error);
+gboolean msu_device_cancel_upload(msu_device_t *device, msu_task_t *task,
+                                 GError **error);
+void msu_device_get_upload_ids(msu_device_t *device, msu_task_t *task);
+void msu_device_delete_object(msu_device_t *device, msu_client_t *client,
+                             msu_task_t *task,
+                             msu_async_cb_data_t *cb_data,
+                             GCancellable *cancellable);
+void msu_device_create_container(msu_device_t *device, msu_client_t *client,
+                                msu_task_t *task,
+                                const gchar *parent_id,
+                                msu_async_cb_data_t *cb_data,
+                                GCancellable *cancellable);
+void msu_device_update_object(msu_device_t *device, msu_client_t *client,
+                             msu_task_t *task,
+                             msu_async_cb_data_t *cb_data,
+                             const gchar *upnp_filter,
+                             GCancellable *cancellable);
 
 #endif
index 1d97852..ead3784 100644 (file)
 #define MSU_INTERFACE_MEDIA_OBJECT "org.gnome.UPnP.MediaObject2"
 #define MSU_INTERFACE_MEDIA_ITEM "org.gnome.UPnP.MediaItem2"
 
-#define MSU_INTERFACE_PROP_PARENT "Parent"
-#define MSU_INTERFACE_PROP_TYPE "Type"
+/* Object Properties */
 #define MSU_INTERFACE_PROP_PATH "Path"
+#define MSU_INTERFACE_PROP_PARENT "Parent"
+#define MSU_INTERFACE_PROP_RESTRICTED "Restricted"
 #define MSU_INTERFACE_PROP_DISPLAY_NAME "DisplayName"
+#define MSU_INTERFACE_PROP_TYPE "Type"
+#define MSU_INTERFACE_PROP_CREATOR "Creator"
+#define MSU_INTERFACE_PROP_DLNA_MANAGED "DLNAManaged"
 
-#define MSU_INTERFACE_PROP_CHILD_COUNT "ChildCount"
-#define MSU_INTERFACE_PROP_SEARCHABLE "Searchable"
-
-#define MSU_INTERFACE_PROP_URLS "URLs"
-#define MSU_INTERFACE_PROP_URL "URL"
-#define MSU_INTERFACE_PROP_RESOURCES "Resources"
-#define MSU_INTERFACE_PROP_MIME_TYPE "MIMEType"
+/* Item Properties */
+#define MSU_INTERFACE_PROP_REFPATH "RefPath"
 #define MSU_INTERFACE_PROP_ARTIST "Artist"
+#define MSU_INTERFACE_PROP_ARTISTS "Artists"
 #define MSU_INTERFACE_PROP_ALBUM "Album"
 #define MSU_INTERFACE_PROP_DATE "Date"
 #define MSU_INTERFACE_PROP_GENRE "Genre"
-#define MSU_INTERFACE_PROP_DLNA_PROFILE "DLNAProfile"
 #define MSU_INTERFACE_PROP_TRACK_NUMBER "TrackNumber"
 #define MSU_INTERFACE_PROP_ALBUM_ART_URL "AlbumArtURL"
-#define MSU_INTERFACE_PROP_ICON_URL "IconURL"
-#define MSU_INTERFACE_PROP_REFPATH "RefPath"
-#define MSU_INTERFACE_PROP_RESTRICTED "Restricted"
+#define MSU_INTERFACE_PROP_RESOURCES "Resources"
 
-#define MSU_INTERFACE_PROP_SIZE "Size"
-#define MSU_INTERFACE_PROP_DURATION "Duration"
-#define MSU_INTERFACE_PROP_BITRATE "Bitrate"
-#define MSU_INTERFACE_PROP_SAMPLE_RATE "SampleRate"
-#define MSU_INTERFACE_PROP_BITS_PER_SAMPLE "BitsPerSample"
-#define MSU_INTERFACE_PROP_WIDTH "Width"
-#define MSU_INTERFACE_PROP_HEIGHT "Height"
-#define MSU_INTERFACE_PROP_COLOR_DEPTH "ColorDepth"
+/* Container Properties */
+#define MSU_INTERFACE_PROP_SEARCHABLE "Searchable"
+#define MSU_INTERFACE_PROP_CHILD_COUNT "ChildCount"
+#define MSU_INTERFACE_PROP_CREATE_CLASSES "CreateClasses"
 
+/* Device Properties */
 #define MSU_INTERFACE_PROP_LOCATION "Location"
 #define MSU_INTERFACE_PROP_UDN "UDN"
 #define MSU_INTERFACE_PROP_DEVICE_TYPE "DeviceType"
 #define MSU_INTERFACE_PROP_MODEL_URL "ModelURL"
 #define MSU_INTERFACE_PROP_SERIAL_NUMBER "SerialNumber"
 #define MSU_INTERFACE_PROP_PRESENTATION_URL "PresentationURL"
+#define MSU_INTERFACE_PROP_ICON_URL "IconURL"
+#define MSU_INTERFACE_PROP_SV_DLNA_CAPABILITIES "DLNACaps"
+#define MSU_INTERFACE_PROP_SV_SEARCH_CAPABILITIES "SearchCaps"
+#define MSU_INTERFACE_PROP_SV_SORT_CAPABILITIES "SortCaps"
+#define MSU_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES "SortExtCaps"
+#define MSU_INTERFACE_PROP_SV_FEATURE_LIST "FeatureList"
+
+/* Resources Properties */
+#define MSU_INTERFACE_PROP_MIME_TYPE "MIMEType"
+#define MSU_INTERFACE_PROP_DLNA_PROFILE "DLNAProfile"
+#define MSU_INTERFACE_PROP_SIZE "Size"
+#define MSU_INTERFACE_PROP_DURATION "Duration"
+#define MSU_INTERFACE_PROP_BITRATE "Bitrate"
+#define MSU_INTERFACE_PROP_SAMPLE_RATE "SampleRate"
+#define MSU_INTERFACE_PROP_BITS_PER_SAMPLE "BitsPerSample"
+#define MSU_INTERFACE_PROP_WIDTH "Width"
+#define MSU_INTERFACE_PROP_HEIGHT "Height"
+#define MSU_INTERFACE_PROP_COLOR_DEPTH "ColorDepth"
+#define MSU_INTERFACE_PROP_URLS "URLs"
+#define MSU_INTERFACE_PROP_URL "URL"
+
+/* Evented State Variable Properties */
+#define MSU_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID "SystemUpdateID"
+#define MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN "ServiceResetToken"
 
 #define MSU_INTERFACE_GET_VERSION "GetVersion"
 #define MSU_INTERFACE_GET_SERVERS "GetServers"
 #define MSU_INTERFACE_RELEASE "Release"
 #define MSU_INTERFACE_SET_PROTOCOL_INFO "SetProtocolInfo"
+#define MSU_INTERFACE_PREFER_LOCAL_ADDRESSES "PreferLocalAddresses"
 
 #define MSU_INTERFACE_FOUND_SERVER "FoundServer"
 #define MSU_INTERFACE_LOST_SERVER "LostServer"
 #define MSU_INTERFACE_LIST_CONTAINERS_EX "ListContainersEx"
 #define MSU_INTERFACE_SEARCH_OBJECTS "SearchObjects"
 #define MSU_INTERFACE_SEARCH_OBJECTS_EX "SearchObjectsEx"
-#define MSU_INTERFACE_UPLOAD "Upload"
+#define MSU_INTERFACE_UPDATE "Update"
 
 #define MSU_INTERFACE_GET_COMPATIBLE_RESOURCE "GetCompatibleResource"
 
 #define MSU_INTERFACE_PROPERTY_NAME "PropertyName"
 #define MSU_INTERFACE_PROPERTIES_VALUE "Properties"
 #define MSU_INTERFACE_VALUE "value"
+#define MSU_INTERFACE_CHILD_TYPES "ChildTypes"
 
 #define MSU_INTERFACE_VERSION "Version"
 #define MSU_INTERFACE_SERVERS "Servers"
 #define MSU_INTERFACE_PATH "Path"
 #define MSU_INTERFACE_QUERY "Query"
 #define MSU_INTERFACE_PROTOCOL_INFO "ProtocolInfo"
+#define MSU_INTERFACE_PREFER "Prefer"
 
 #define MSU_INTERFACE_OFFSET "Offset"
 #define MSU_INTERFACE_MAX "Max"
 #define MSU_INTERFACE_SORT_BY "SortBy"
 #define MSU_INTERFACE_TOTAL_ITEMS "TotalItems"
 
-#define MSU_INTERFACE_SYSTEM_UPDATE "SystemUpdate"
+#define MSU_INTERFACE_PROPERTIES_CHANGED "PropertiesChanged"
+#define MSU_INTERFACE_CHANGED_PROPERTIES "ChangedProperties"
+#define MSU_INTERFACE_INVALIDATED_PROPERTIES "InvalidatedProperties"
 #define MSU_INTERFACE_SYSTEM_UPDATE_ID "SystemUpdateId"
 #define MSU_INTERFACE_CONTAINER_UPDATE "ContainerUpdate"
 #define MSU_INTERFACE_CONTAINER_PATHS "ContainerPaths"
 
+#define MSU_INTERFACE_DELETE "Delete"
+
+#define MSU_INTERFACE_CREATE_CONTAINER "CreateContainer"
+#define MSU_INTERFACE_CREATE_CONTAINER_IN_ANY "CreateContainerInAnyContainer"
+
+#define MSU_INTERFACE_UPLOAD "Upload"
 #define MSU_INTERFACE_UPLOAD_TO_ANY "UploadToAnyContainer"
+#define MSU_INTERFACE_GET_UPLOAD_STATUS "GetUploadStatus"
+#define MSU_INTERFACE_GET_UPLOAD_IDS "GetUploadIDs"
+#define MSU_INTERFACE_CANCEL_UPLOAD "CancelUpload"
+#define MSU_INTERFACE_TOTAL "Total"
+#define MSU_INTERFACE_LENGTH "Length"
 #define MSU_INTERFACE_FILE_PATH "FilePath"
 #define MSU_INTERFACE_UPLOAD_ID "UploadId"
-
+#define MSU_INTERFACE_UPLOAD_IDS "UploadIDs"
+#define MSU_INTERFACE_UPLOAD_STATUS "UploadStatus"
+#define MSU_INTERFACE_UPLOAD_UPDATE "UploadUpdate"
+#define MSU_INTERFACE_TO_ADD_UPDATE "ToAddUpdate"
+#define MSU_INTERFACE_TO_DELETE "ToDelete"
 #endif
+
index db09283..963ab38 100644 (file)
 #include <syslog.h>
 #include <sys/signalfd.h>
 
+#include "client.h"
 #include "interface.h"
 #include "log.h"
 #include "settings.h"
 #include "task.h"
 #include "upnp.h"
 
-typedef struct msu_client_t_ msu_client_t;
-struct msu_client_t_ {
-       guint id;
-       gchar *protocol_info;
-};
-
 typedef struct msu_context_t_ msu_context_t;
 struct msu_context_t_ {
        bool error;
@@ -61,6 +56,8 @@ struct msu_context_t_ {
        msu_settings_context_t *settings;
 };
 
+static msu_context_t g_context;
+
 static const gchar g_msu_root_introspection[] =
        "<node>"
        "  <interface name='"MSU_INTERFACE_MANAGER"'>"
@@ -78,6 +75,10 @@ static const gchar g_msu_root_introspection[] =
        "      <arg type='s' name='"MSU_INTERFACE_PROTOCOL_INFO"'"
        "           direction='in'/>"
        "    </method>"
+       "    <method name='"MSU_INTERFACE_PREFER_LOCAL_ADDRESSES"'>"
+       "      <arg type='b' name='"MSU_INTERFACE_PREFER"'"
+       "           direction='in'/>"
+       "    </method>"
        "    <signal name='"MSU_INTERFACE_FOUND_SERVER"'>"
        "      <arg type='o' name='"MSU_INTERFACE_PATH"'/>"
        "    </signal>"
@@ -104,6 +105,11 @@ static const gchar g_msu_server_introspection[] =
        "      <arg type='a{sv}' name='"MSU_INTERFACE_PROPERTIES_VALUE"'"
        "           direction='out'/>"
        "    </method>"
+       "    <signal name='"MSU_INTERFACE_PROPERTIES_CHANGED"'>"
+       "      <arg type='s' name='"MSU_INTERFACE_INTERFACE_NAME"'/>"
+       "      <arg type='a{sv}' name='"MSU_INTERFACE_CHANGED_PROPERTIES"'/>"
+       "      <arg type='as' name='"MSU_INTERFACE_INVALIDATED_PROPERTIES"'/>"
+       "    </signal>"
        "  </interface>"
        "  <interface name='"MSU_INTERFACE_MEDIA_OBJECT"'>"
        "    <property type='o' name='"MSU_INTERFACE_PROP_PARENT"'"
@@ -114,8 +120,20 @@ static const gchar g_msu_server_introspection[] =
        "       access='read'/>"
        "    <property type='s' name='"MSU_INTERFACE_PROP_DISPLAY_NAME"'"
        "       access='read'/>"
+       "    <property type='s' name='"MSU_INTERFACE_PROP_CREATOR"'"
+       "       access='read'/>"
        "    <property type='b' name='"MSU_INTERFACE_PROP_RESTRICTED"'"
        "       access='read'/>"
+       "    <property type='a{sb}' name='"MSU_INTERFACE_PROP_DLNA_MANAGED"'"
+       "       access='read'/>"
+       "    <method name='"MSU_INTERFACE_DELETE"'>"
+       "    </method>"
+       "    <method name='"MSU_INTERFACE_UPDATE"'>"
+       "      <arg type='a{sv}' name='"MSU_INTERFACE_TO_ADD_UPDATE"'"
+       "           direction='in'/>"
+       "      <arg type='as' name='"MSU_INTERFACE_TO_DELETE"'"
+       "           direction='in'/>"
+       "    </method>"
        "  </interface>"
        "  <interface name='"MSU_INTERFACE_MEDIA_CONTAINER"'>"
        "    <method name='"MSU_INTERFACE_LIST_CHILDREN"'>"
@@ -222,10 +240,22 @@ static const gchar g_msu_server_introspection[] =
        "      <arg type='o' name='"MSU_INTERFACE_PATH"'"
        "           direction='out'/>"
        "    </method>"
+       "    <method name='"MSU_INTERFACE_CREATE_CONTAINER"'>"
+       "      <arg type='s' name='"MSU_INTERFACE_PROP_DISPLAY_NAME"'"
+       "           direction='in'/>"
+       "      <arg type='s' name='"MSU_INTERFACE_PROP_TYPE"'"
+       "           direction='in'/>"
+       "      <arg type='as' name='"MSU_INTERFACE_CHILD_TYPES"'"
+       "           direction='in'/>"
+       "      <arg type='o' name='"MSU_INTERFACE_PATH"'"
+       "           direction='out'/>"
+       "    </method>"
        "    <property type='u' name='"MSU_INTERFACE_PROP_CHILD_COUNT"'"
        "       access='read'/>"
        "    <property type='b' name='"MSU_INTERFACE_PROP_SEARCHABLE"'"
        "       access='read'/>"
+       "    <property type='a(sb)' name='"MSU_INTERFACE_PROP_CREATE_CLASSES"'"
+       "       access='read'/>"
        "  </interface>"
        "  <interface name='"MSU_INTERFACE_MEDIA_ITEM"'>"
        "    <method name='"MSU_INTERFACE_GET_COMPATIBLE_RESOURCE"'>"
@@ -242,6 +272,8 @@ static const gchar g_msu_server_introspection[] =
        "       access='read'/>"
        "    <property type='s' name='"MSU_INTERFACE_PROP_ARTIST"'"
        "       access='read'/>"
+       "    <property type='as' name='"MSU_INTERFACE_PROP_ARTISTS"'"
+       "       access='read'/>"
        "    <property type='s' name='"MSU_INTERFACE_PROP_ALBUM"'"
        "       access='read'/>"
        "    <property type='s' name='"MSU_INTERFACE_PROP_DATE"'"
@@ -286,6 +318,34 @@ static const gchar g_msu_server_introspection[] =
        "      <arg type='o' name='"MSU_INTERFACE_PATH"'"
        "           direction='out'/>"
        "    </method>"
+       "    <method name='"MSU_INTERFACE_GET_UPLOAD_STATUS"'>"
+       "      <arg type='u' name='"MSU_INTERFACE_UPLOAD_ID"'"
+       "           direction='in'/>"
+       "      <arg type='s' name='"MSU_INTERFACE_UPLOAD_STATUS"'"
+       "           direction='out'/>"
+       "      <arg type='t' name='"MSU_INTERFACE_LENGTH"'"
+       "           direction='out'/>"
+       "      <arg type='t' name='"MSU_INTERFACE_TOTAL"'"
+       "           direction='out'/>"
+       "    </method>"
+       "    <method name='"MSU_INTERFACE_GET_UPLOAD_IDS"'>"
+       "      <arg type='au' name='"MSU_INTERFACE_TOTAL"'"
+       "           direction='out'/>"
+       "    </method>"
+       "    <method name='"MSU_INTERFACE_CANCEL_UPLOAD"'>"
+       "      <arg type='u' name='"MSU_INTERFACE_UPLOAD_ID"'"
+       "           direction='in'/>"
+       "    </method>"
+       "    <method name='"MSU_INTERFACE_CREATE_CONTAINER_IN_ANY"'>"
+       "      <arg type='s' name='"MSU_INTERFACE_PROP_DISPLAY_NAME"'"
+       "           direction='in'/>"
+       "      <arg type='s' name='"MSU_INTERFACE_PROP_TYPE"'"
+       "           direction='in'/>"
+       "      <arg type='as' name='"MSU_INTERFACE_CHILD_TYPES"'"
+       "           direction='in'/>"
+       "      <arg type='o' name='"MSU_INTERFACE_PATH"'"
+       "           direction='out'/>"
+       "    </method>"
        "    <property type='s' name='"MSU_INTERFACE_PROP_LOCATION"'"
        "       access='read'/>"
        "    <property type='s' name='"MSU_INTERFACE_PROP_UDN"'"
@@ -312,12 +372,36 @@ static const gchar g_msu_server_introspection[] =
        "       access='read'/>"
        "    <property type='s' name='"MSU_INTERFACE_PROP_ICON_URL"'"
        "       access='read'/>"
-       "    <signal name='"MSU_INTERFACE_SYSTEM_UPDATE"'>"
-       "      <arg type='u' name='"MSU_INTERFACE_SYSTEM_UPDATE_ID"'/>"
-       "    </signal>"
+       "    <property type='a{sv}'name='"
+       MSU_INTERFACE_PROP_SV_DLNA_CAPABILITIES"'"
+       "       access='read'/>"
+       "    <property type='as' name='"
+       MSU_INTERFACE_PROP_SV_SEARCH_CAPABILITIES"'"
+       "       access='read'/>"
+       "    <property type='as' name='"
+       MSU_INTERFACE_PROP_SV_SORT_CAPABILITIES"'"
+       "       access='read'/>"
+       "    <property type='as' name='"
+       MSU_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES"'"
+       "       access='read'/>"
+       "    <property type='a(ssao)' name='"
+       MSU_INTERFACE_PROP_SV_FEATURE_LIST"'"
+       "       access='read'/>"
+       "    <property type='u' name='"
+       MSU_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID"'"
+       "       access='read'/>"
+       "    <property type='s' name='"
+       MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN"'"
+       "       access='read'/>"
        "    <signal name='"MSU_INTERFACE_CONTAINER_UPDATE"'>"
        "      <arg type='ao' name='"MSU_INTERFACE_CONTAINER_PATHS"'/>"
        "    </signal>"
+       "    <signal name='"MSU_INTERFACE_UPLOAD_UPDATE"'>"
+       "      <arg type='u' name='"MSU_INTERFACE_UPLOAD_ID"'/>"
+       "      <arg type='s' name='"MSU_INTERFACE_UPLOAD_STATUS"'/>"
+       "      <arg type='t' name='"MSU_INTERFACE_LENGTH"'/>"
+       "      <arg type='t' name='"MSU_INTERFACE_TOTAL"'/>"
+       "    </signal>"
        "  </interface>"
        "</node>";
 
@@ -328,25 +412,25 @@ static void prv_free_msu_task_cb(gpointer data, gpointer user_data)
        msu_task_delete(data);
 }
 
-static void prv_process_sync_task(msu_context_t *context, msu_task_t *task)
+static void prv_process_sync_task(msu_task_t *task)
 {
        const gchar *client_name;
        msu_client_t *client;
 
-       context->current_task = task;
+       g_context.current_task = task;
 
        switch (task->type) {
        case MSU_TASK_GET_VERSION:
                msu_task_complete_and_delete(task);
                break;
        case MSU_TASK_GET_SERVERS:
-               task->result = msu_upnp_get_server_ids(context->upnp);
+               task->result = msu_upnp_get_server_ids(g_context.upnp);
                msu_task_complete_and_delete(task);
                break;
        case MSU_TASK_SET_PROTOCOL_INFO:
                client_name =
                        g_dbus_method_invocation_get_sender(task->invocation);
-               client = g_hash_table_lookup(context->watchers, client_name);
+               client = g_hash_table_lookup(g_context.watchers, client_name);
                if (client) {
                        g_free(client->protocol_info);
                        if (task->ut.protocol_info.protocol_info[0]) {
@@ -359,24 +443,40 @@ static void prv_process_sync_task(msu_context_t *context, msu_task_t *task)
                }
                msu_task_complete_and_delete(task);
                break;
-
+       case MSU_TASK_SET_PREFER_LOCAL_ADDRESSES:
+               client_name =
+                       g_dbus_method_invocation_get_sender(task->invocation);
+               client = g_hash_table_lookup(g_context.watchers, client_name);
+               if (client) {
+                       client->prefer_local_addresses =
+                                       task->ut.prefer_local_addresses.prefer;
+               }
+               msu_task_complete_and_delete(task);
+               break;
+       case MSU_TASK_GET_UPLOAD_STATUS:
+               msu_upnp_get_upload_status(g_context.upnp, task);
+               break;
+       case MSU_TASK_GET_UPLOAD_IDS:
+               msu_upnp_get_upload_ids(g_context.upnp, task);
+               break;
+       case MSU_TASK_CANCEL_UPLOAD:
+               msu_upnp_cancel_upload(g_context.upnp, task);
+               break;
        default:
                break;
        }
 
-       context->current_task = NULL;
+       g_context.current_task = NULL;
 }
 
 static void prv_async_task_complete(msu_task_t *task, GVariant *result,
-                                   GError *error, void *user_data)
+                                   GError *error)
 {
-       msu_context_t *context = user_data;
-
        MSU_LOG_DEBUG("Enter");
 
-       g_object_unref(context->cancellable);
-       context->cancellable = NULL;
-       context->current_task = NULL;
+       g_object_unref(g_context.cancellable);
+       g_context.cancellable = NULL;
+       g_context.current_task = NULL;
 
        if (error) {
                msu_task_fail_and_delete(task, error);
@@ -386,64 +486,82 @@ static void prv_async_task_complete(msu_task_t *task, GVariant *result,
                msu_task_complete_and_delete(task);
        }
 
-       if (context->quitting)
-               g_main_loop_quit(context->main_loop);
-       else if (context->tasks->len > 0)
-               context->idle_id = g_idle_add(prv_process_task, context);
+       if (g_context.quitting)
+               g_main_loop_quit(g_context.main_loop);
+       else if (g_context.tasks->len > 0)
+               g_context.idle_id = g_idle_add(prv_process_task, NULL);
 
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_process_async_task(msu_context_t *context, msu_task_t *task)
+static void prv_process_async_task(msu_task_t *task)
 {
        const gchar *client_name;
        msu_client_t *client;
-       const gchar *protocol_info = NULL;
 
        MSU_LOG_DEBUG("Enter");
 
-       context->cancellable = g_cancellable_new();
-       context->current_task = task;
+       g_context.cancellable = g_cancellable_new();
+       g_context.current_task = task;
        client_name =
                g_dbus_method_invocation_get_sender(task->invocation);
-       client = g_hash_table_lookup(context->watchers, client_name);
-       if (client)
-               protocol_info = client->protocol_info;
+       client = g_hash_table_lookup(g_context.watchers, client_name);
 
        switch (task->type) {
        case MSU_TASK_GET_CHILDREN:
-               msu_upnp_get_children(context->upnp, task, protocol_info,
-                                     context->cancellable,
-                                     prv_async_task_complete, context);
+               msu_upnp_get_children(g_context.upnp, client, task,
+                                     g_context.cancellable,
+                                     prv_async_task_complete);
                break;
        case MSU_TASK_GET_PROP:
-               msu_upnp_get_prop(context->upnp, task, protocol_info,
-                                 context->cancellable,
-                                 prv_async_task_complete, context);
+               msu_upnp_get_prop(g_context.upnp, client, task,
+                                 g_context.cancellable,
+                                 prv_async_task_complete);
                break;
        case MSU_TASK_GET_ALL_PROPS:
-               msu_upnp_get_all_props(context->upnp, task, protocol_info,
-                                      context->cancellable,
-                                      prv_async_task_complete, context);
+               msu_upnp_get_all_props(g_context.upnp, client, task,
+                                      g_context.cancellable,
+                                      prv_async_task_complete);
                break;
        case MSU_TASK_SEARCH:
-               msu_upnp_search(context->upnp, task, protocol_info,
-                               context->cancellable,
-                               prv_async_task_complete, context);
+               msu_upnp_search(g_context.upnp, client, task,
+                               g_context.cancellable,
+                               prv_async_task_complete);
                break;
        case MSU_TASK_GET_RESOURCE:
-               msu_upnp_get_resource(context->upnp, task,
-                                     context->cancellable,
-                                     prv_async_task_complete, context);
+               msu_upnp_get_resource(g_context.upnp, client, task,
+                                     g_context.cancellable,
+                                     prv_async_task_complete);
                break;
        case MSU_TASK_UPLOAD_TO_ANY:
-               msu_upnp_upload_to_any(context->upnp, task,
-                                      context->cancellable,
-                                      prv_async_task_complete, context);
+               msu_upnp_upload_to_any(g_context.upnp, client, task,
+                                      g_context.cancellable,
+                                      prv_async_task_complete);
                break;
        case MSU_TASK_UPLOAD:
-               msu_upnp_upload(context->upnp, task, context->cancellable,
-                               prv_async_task_complete, context);
+               msu_upnp_upload(g_context.upnp, client, task,
+                               g_context.cancellable,
+                               prv_async_task_complete);
+               break;
+       case MSU_TASK_DELETE_OBJECT:
+               msu_upnp_delete_object(g_context.upnp, client, task,
+                                      g_context.cancellable,
+                                      prv_async_task_complete);
+               break;
+       case MSU_TASK_CREATE_CONTAINER:
+               msu_upnp_create_container(g_context.upnp, client, task,
+                                         g_context.cancellable,
+                                         prv_async_task_complete);
+               break;
+       case MSU_TASK_CREATE_CONTAINER_IN_ANY:
+               msu_upnp_create_container_in_any(g_context.upnp, client, task,
+                                                g_context.cancellable,
+                                                prv_async_task_complete);
+               break;
+       case MSU_TASK_UPDATE_OBJECT:
+               msu_upnp_update_object(g_context.upnp, client, task,
+                                      g_context.cancellable,
+                                      prv_async_task_complete);
                break;
        default:
                break;
@@ -454,22 +572,21 @@ static void prv_process_async_task(msu_context_t *context, msu_task_t *task)
 
 static gboolean prv_process_task(gpointer user_data)
 {
-       msu_context_t *context = user_data;
        msu_task_t *task;
        gboolean retval = FALSE;
 
-       if (context->tasks->len > 0) {
-               task = g_ptr_array_index(context->tasks, 0);
+       if (g_context.tasks->len > 0) {
+               task = g_ptr_array_index(g_context.tasks, 0);
                if (task->synchronous) {
-                       prv_process_sync_task(context, task);
+                       prv_process_sync_task(task);
                        retval = TRUE;
                } else {
-                       prv_process_async_task(context, task);
-                       context->idle_id = 0;
+                       prv_process_async_task(task);
+                       g_context.idle_id = 0;
                }
-               g_ptr_array_remove_index(context->tasks, 0);
+               g_ptr_array_remove_index(g_context.tasks, 0);
        } else {
-               context->idle_id = 0;
+               g_context.idle_id = 0;
        }
 
        return retval;
@@ -484,6 +601,15 @@ static void prv_msu_method_call(GDBusConnection *conn,
                                GDBusMethodInvocation *invocation,
                                gpointer user_data);
 
+static void prv_object_method_call(GDBusConnection *conn,
+                                  const gchar *sender,
+                                  const gchar *object,
+                                  const gchar *interface,
+                                  const gchar *method,
+                                  GVariant *parameters,
+                                  GDBusMethodInvocation *invocation,
+                                  gpointer user_data);
+
 static void prv_item_method_call(GDBusConnection *conn,
                                 const gchar *sender,
                                 const gchar *object,
@@ -526,6 +652,12 @@ static const GDBusInterfaceVTable g_msu_vtable = {
        NULL
 };
 
+static const GDBusInterfaceVTable g_object_vtable = {
+       prv_object_method_call,
+       NULL,
+       NULL
+};
+
 static const GDBusInterfaceVTable g_item_vtable = {
        prv_item_method_call,
        NULL,
@@ -552,91 +684,92 @@ static const GDBusInterfaceVTable g_device_vtable = {
 
 static const GDBusInterfaceVTable *g_server_vtables[MSU_INTERFACE_INFO_MAX] = {
        &g_props_vtable,
-       NULL,
+       &g_object_vtable,
        &g_ms_vtable,
        &g_item_vtable,
        &g_device_vtable
 };
 
-static void prv_msu_context_init(msu_context_t *context)
+static void prv_msu_context_init(void)
 {
-       memset(context, 0, sizeof(*context));
+       memset(&g_context, 0, sizeof(g_context));
 }
 
-static void prv_msu_context_free(msu_context_t *context)
+static void prv_msu_context_free(void)
 {
-       msu_upnp_delete(context->upnp);
+       msu_upnp_delete(g_context.upnp);
 
-       if (context->watchers)
-               g_hash_table_unref(context->watchers);
+       if (g_context.watchers)
+               g_hash_table_unref(g_context.watchers);
 
-       if (context->tasks) {
-               g_ptr_array_foreach(context->tasks, prv_free_msu_task_cb, NULL);
-               g_ptr_array_unref(context->tasks);
+       if (g_context.tasks) {
+               g_ptr_array_foreach(g_context.tasks, prv_free_msu_task_cb,
+                                   NULL);
+               g_ptr_array_unref(g_context.tasks);
        }
 
-       if (context->idle_id)
-               (void) g_source_remove(context->idle_id);
+       if (g_context.idle_id)
+               (void) g_source_remove(g_context.idle_id);
 
-       if (context->sig_id)
-               (void) g_source_remove(context->sig_id);
+       if (g_context.sig_id)
+               (void) g_source_remove(g_context.sig_id);
 
-       if (context->connection) {
-               if (context->msu_id)
+       if (g_context.connection) {
+               if (g_context.msu_id)
                        g_dbus_connection_unregister_object(
-                               context->connection,
-                               context->msu_id);
+                                                       g_context.connection,
+                                                       g_context.msu_id);
        }
 
-       if (context->owner_id)
-               g_bus_unown_name(context->owner_id);
+       if (g_context.owner_id)
+               g_bus_unown_name(g_context.owner_id);
 
-       if (context->connection)
-               g_object_unref(context->connection);
+       if (g_context.connection)
+               g_object_unref(g_context.connection);
 
-       if (context->main_loop)
-               g_main_loop_unref(context->main_loop);
+       if (g_context.main_loop)
+               g_main_loop_unref(g_context.main_loop);
 
-       if (context->server_node_info)
-               g_dbus_node_info_unref(context->server_node_info);
+       if (g_context.server_node_info)
+               g_dbus_node_info_unref(g_context.server_node_info);
 
-       if (context->root_node_info)
-               g_dbus_node_info_unref(context->root_node_info);
+       if (g_context.root_node_info)
+               g_dbus_node_info_unref(g_context.root_node_info);
 
-       if (context->settings)
-               msu_settings_delete(context->settings);
+       if (g_context.settings)
+               msu_settings_delete(g_context.settings);
 }
 
-static void prv_quit(msu_context_t *context)
+static void prv_quit(void)
 {
-       if (context->cancellable) {
-               g_cancellable_cancel(context->cancellable);
-               context->quitting = TRUE;
+       if (g_context.cancellable) {
+               g_cancellable_cancel(g_context.cancellable);
+               g_context.quitting = TRUE;
        } else {
-               g_main_loop_quit(context->main_loop);
+               g_main_loop_quit(g_context.main_loop);
        }
 }
 
-static void prv_remove_client(msu_context_t *context, const gchar *name)
+static void prv_remove_client(const gchar *name)
 {
        const gchar *client_name;
        msu_task_t *task;
        guint pos;
 
-       if (context->cancellable) {
+       if (g_context.cancellable) {
                client_name = g_dbus_method_invocation_get_sender(
-                                       context->current_task->invocation);
+                                       g_context.current_task->invocation);
                if (!strcmp(client_name, name)) {
                        MSU_LOG_DEBUG("Cancelling current task, type is %d",
-                                               context->current_task->type);
+                                     g_context.current_task->type);
 
-                       g_cancellable_cancel(context->cancellable);
+                       g_cancellable_cancel(g_context.cancellable);
                }
        }
 
        pos = 0;
-       while (pos < context->tasks->len) {
-               task = (msu_task_t *) g_ptr_array_index(context->tasks, pos);
+       while (pos < g_context.tasks->len) {
+               task = (msu_task_t *) g_ptr_array_index(g_context.tasks, pos);
 
                client_name = g_dbus_method_invocation_get_sender(
                                                        task->invocation);
@@ -648,15 +781,15 @@ static void prv_remove_client(msu_context_t *context, const gchar *name)
 
                MSU_LOG_DEBUG("Removing task type %d from array", task->type);
 
-               (void) g_ptr_array_remove_index(context->tasks, pos);
+               (void) g_ptr_array_remove_index(g_context.tasks, pos);
                msu_task_cancel_and_delete(task);
        }
 
-       (void) g_hash_table_remove(context->watchers, name);
+       (void) g_hash_table_remove(g_context.watchers, name);
 
-       if (g_hash_table_size(context->watchers) == 0)
-               if (!msu_settings_is_never_quit(context->settings))
-                       prv_quit(context);
+       if (g_hash_table_size(g_context.watchers) == 0)
+               if (!msu_settings_is_never_quit(g_context.settings))
+                       prv_quit();
 }
 
 static void prv_lost_client(GDBusConnection *connection, const gchar *name,
@@ -664,30 +797,31 @@ static void prv_lost_client(GDBusConnection *connection, const gchar *name,
 {
        MSU_LOG_DEBUG("Lost Client %s", name);
 
-       prv_remove_client(user_data, name);
+       prv_remove_client(name);
 }
 
-static void prv_add_task(msu_context_t *context, msu_task_t *task)
+static void prv_add_task(msu_task_t *task)
 {
        const gchar *client_name;
        msu_client_t *client;
 
        client_name = g_dbus_method_invocation_get_sender(task->invocation);
 
-       if (!g_hash_table_lookup(context->watchers, client_name)) {
+       if (!g_hash_table_lookup(g_context.watchers, client_name)) {
                client = g_new0(msu_client_t, 1);
+               client->prefer_local_addresses = TRUE;
                client->id = g_bus_watch_name(G_BUS_TYPE_SESSION, client_name,
                                              G_BUS_NAME_WATCHER_FLAGS_NONE,
-                                             NULL, prv_lost_client, context,
+                                             NULL, prv_lost_client, NULL,
                                              NULL);
-               g_hash_table_insert(context->watchers, g_strdup(client_name),
+               g_hash_table_insert(g_context.watchers, g_strdup(client_name),
                                    client);
        }
 
-       if (!context->cancellable && !context->idle_id)
-               context->idle_id = g_idle_add(prv_process_task, context);
+       if (!g_context.cancellable && !g_context.idle_id)
+               g_context.idle_id = g_idle_add(prv_process_task, NULL);
 
-       g_ptr_array_add(context->tasks, task);
+       g_ptr_array_add(g_context.tasks, task);
 }
 
 static void prv_msu_method_call(GDBusConnection *conn,
@@ -697,26 +831,52 @@ static void prv_msu_method_call(GDBusConnection *conn,
                                GDBusMethodInvocation *invocation,
                                gpointer user_data)
 {
-       msu_context_t *context = user_data;
        const gchar *client_name;
        msu_task_t *task;
 
        if (!strcmp(method, MSU_INTERFACE_RELEASE)) {
                client_name = g_dbus_method_invocation_get_sender(invocation);
-               prv_remove_client(context, client_name);
+               prv_remove_client(client_name);
                g_dbus_method_invocation_return_value(invocation, NULL);
        } else if (!strcmp(method, MSU_INTERFACE_GET_VERSION)) {
                task = msu_task_get_version_new(invocation);
-               prv_add_task(context, task);
+               prv_add_task(task);
        } else if (!strcmp(method, MSU_INTERFACE_GET_SERVERS)) {
                task = msu_task_get_servers_new(invocation);
-               prv_add_task(context, task);
+               prv_add_task(task);
        } else if (!strcmp(method, MSU_INTERFACE_SET_PROTOCOL_INFO)) {
                task = msu_task_set_protocol_info_new(invocation, parameters);
-               prv_add_task(context, task);
+               prv_add_task(task);
+       } else if (!strcmp(method, MSU_INTERFACE_PREFER_LOCAL_ADDRESSES)) {
+               task = msu_task_prefer_local_addresses_new(invocation,
+                                                          parameters);
+               prv_add_task(task);
        }
 }
 
+static void prv_object_method_call(GDBusConnection *conn,
+                                  const gchar *sender, const gchar *object,
+                                  const gchar *interface,
+                                  const gchar *method, GVariant *parameters,
+                                  GDBusMethodInvocation *invocation,
+                                  gpointer user_data)
+{
+       msu_task_t *task;
+
+       if (!strcmp(method, MSU_INTERFACE_DELETE)) {
+               task = msu_task_delete_new(invocation, object);
+       } else if (!strcmp(method, MSU_INTERFACE_UPDATE))
+               task = msu_task_update_new(invocation, object, parameters);
+       else
+               goto finished;
+
+       prv_add_task(task);
+
+finished:
+
+       return;
+}
+
 static void prv_item_method_call(GDBusConnection *conn,
                                 const gchar *sender, const gchar *object,
                                 const gchar *interface,
@@ -724,13 +884,12 @@ static void prv_item_method_call(GDBusConnection *conn,
                                 GDBusMethodInvocation *invocation,
                                 gpointer user_data)
 {
-       msu_context_t *context = user_data;
        msu_task_t *task;
 
        if (!strcmp(method, MSU_INTERFACE_GET_COMPATIBLE_RESOURCE)) {
                task = msu_task_get_resource_new(invocation, object,
                                                 parameters);
-               prv_add_task(context, task);
+               prv_add_task(task);
        }
 }
 
@@ -744,7 +903,6 @@ static void prv_con_method_call(GDBusConnection *conn,
                                GDBusMethodInvocation *invocation,
                                gpointer user_data)
 {
-       msu_context_t *context = user_data;
        msu_task_t *task;
 
        if (!strcmp(method, MSU_INTERFACE_LIST_CHILDREN))
@@ -773,10 +931,14 @@ static void prv_con_method_call(GDBusConnection *conn,
                                              parameters);
        else if (!strcmp(method, MSU_INTERFACE_UPLOAD))
                task = msu_task_upload_new(invocation, object, parameters);
+       else if (!strcmp(method, MSU_INTERFACE_CREATE_CONTAINER))
+               task = msu_task_create_container_new_generic(invocation,
+                                               MSU_TASK_CREATE_CONTAINER,
+                                               object, parameters);
        else
                goto finished;
 
-       prv_add_task(context, task);
+       prv_add_task(task);
 
 finished:
 
@@ -792,7 +954,6 @@ static void prv_props_method_call(GDBusConnection *conn,
                                  GDBusMethodInvocation *invocation,
                                  gpointer user_data)
 {
-       msu_context_t *context = user_data;
        msu_task_t *task;
 
        if (!strcmp(method, MSU_INTERFACE_GET_ALL))
@@ -802,7 +963,7 @@ static void prv_props_method_call(GDBusConnection *conn,
        else
                goto finished;
 
-       prv_add_task(context, task);
+       prv_add_task(task);
 
 finished:
 
@@ -816,21 +977,34 @@ static void prv_device_method_call(GDBusConnection *conn,
                                   GDBusMethodInvocation *invocation,
                                   gpointer user_data)
 {
-       msu_context_t *context = user_data;
        msu_task_t *task;
 
        if (!strcmp(method, MSU_INTERFACE_UPLOAD_TO_ANY)) {
                task = msu_task_upload_to_any_new(invocation, object,
                                                  parameters);
-               prv_add_task(context, task);
+               prv_add_task(task);
+       } else if (!strcmp(method, MSU_INTERFACE_CREATE_CONTAINER_IN_ANY)) {
+               task = msu_task_create_container_new_generic(invocation,
+                                       MSU_TASK_CREATE_CONTAINER_IN_ANY,
+                                       object, parameters);
+               prv_add_task(task);
+       } else if (!strcmp(method, MSU_INTERFACE_GET_UPLOAD_STATUS)) {
+               task = msu_task_get_upload_status_new(invocation, object,
+                                                     parameters);
+               prv_add_task(task);
+       } else if (!strcmp(method, MSU_INTERFACE_GET_UPLOAD_IDS)) {
+               task = msu_task_get_upload_ids_new(invocation, object);
+               prv_add_task(task);
+       } else if (!strcmp(method, MSU_INTERFACE_CANCEL_UPLOAD)) {
+               task = msu_task_cancel_upload_new(invocation, object,
+                                                 parameters);
+               prv_add_task(task);
        }
 }
 
 static void prv_found_media_server(const gchar *path, void *user_data)
 {
-       msu_context_t *context = user_data;
-
-       (void) g_dbus_connection_emit_signal(context->connection,
+       (void) g_dbus_connection_emit_signal(g_context.connection,
                                             NULL,
                                             MSU_OBJECT,
                                             MSU_INTERFACE_MANAGER,
@@ -841,9 +1015,7 @@ static void prv_found_media_server(const gchar *path, void *user_data)
 
 static void prv_lost_media_server(const gchar *path, void *user_data)
 {
-       msu_context_t *context = user_data;
-
-       (void) g_dbus_connection_emit_signal(context->connection,
+       (void) g_dbus_connection_emit_signal(g_context.connection,
                                             NULL,
                                             MSU_OBJECT,
                                             MSU_INTERFACE_MANAGER,
@@ -855,30 +1027,29 @@ static void prv_lost_media_server(const gchar *path, void *user_data)
 static void prv_bus_acquired(GDBusConnection *connection, const gchar *name,
                             gpointer user_data)
 {
-       msu_context_t *context = user_data;
        msu_interface_info_t *info;
        unsigned int i;
 
-       context->connection = connection;
+       g_context.connection = connection;
 
-       context->msu_id =
+       g_context.msu_id =
                g_dbus_connection_register_object(connection, MSU_OBJECT,
-                                                 context->root_node_info->
+                                                 g_context.root_node_info->
                                                  interfaces[0],
                                                  &g_msu_vtable,
                                                  user_data, NULL, NULL);
 
-       if (!context->msu_id) {
-               context->error = true;
-               g_main_loop_quit(context->main_loop);
+       if (!g_context.msu_id) {
+               g_context.error = true;
+               g_main_loop_quit(g_context.main_loop);
        } else {
                info = g_new(msu_interface_info_t, MSU_INTERFACE_INFO_MAX);
                for (i = 0; i < MSU_INTERFACE_INFO_MAX; ++i) {
                        info[i].interface =
-                               context->server_node_info->interfaces[i];
+                               g_context.server_node_info->interfaces[i];
                        info[i].vtable = g_server_vtables[i];
                }
-               context->upnp = msu_upnp_new(connection, info,
+               g_context.upnp = msu_upnp_new(connection, info,
                                            prv_found_media_server,
                                            prv_lost_media_server,
                                            user_data);
@@ -888,25 +1059,21 @@ static void prv_bus_acquired(GDBusConnection *connection, const gchar *name,
 static void prv_name_lost(GDBusConnection *connection, const gchar *name,
                          gpointer user_data)
 {
-       msu_context_t *context = user_data;
-
-       context->connection = NULL;
+       g_context.connection = NULL;
 
-       prv_quit(context);
+       prv_quit();
 }
 
 static gboolean prv_quit_handler(GIOChannel *source, GIOCondition condition,
                                 gpointer user_data)
 {
-       msu_context_t *context = user_data;
-
-       prv_quit(context);
-       context->sig_id = 0;
+       prv_quit();
+       g_context.sig_id = 0;
 
        return FALSE;
 }
 
-static bool prv_init_signal_handler(sigset_t mask, msu_context_t *context)
+static bool prv_init_signal_handler(sigset_t mask)
 {
        bool retval = false;
        int fd = -1;
@@ -927,9 +1094,9 @@ static bool prv_init_signal_handler(sigset_t mask, msu_context_t *context)
            G_IO_STATUS_NORMAL)
                goto on_error;
 
-       context->sig_id = g_io_add_watch(channel, G_IO_IN | G_IO_PRI,
+       g_context.sig_id = g_io_add_watch(channel, G_IO_IN | G_IO_PRI,
                                         prv_quit_handler,
-                                        context);
+                                        NULL);
 
        retval = true;
 
@@ -954,12 +1121,10 @@ static void prv_unregister_client(gpointer user_data)
 
 int main(int argc, char *argv[])
 {
-       msu_context_t context;
-
        sigset_t mask;
        int retval = 1;
 
-       prv_msu_context_init(&context);
+       prv_msu_context_init();
 
        sigemptyset(&mask);
        sigaddset(&mask, SIGTERM);
@@ -971,45 +1136,46 @@ int main(int argc, char *argv[])
        g_type_init();
 
        msu_log_init(argv[0]);
-       msu_settings_new(&context.settings);
+       msu_settings_new(&g_context.settings);
 
-       context.root_node_info =
+       g_context.root_node_info =
                g_dbus_node_info_new_for_xml(g_msu_root_introspection, NULL);
-       if (!context.root_node_info)
+       if (!g_context.root_node_info)
                goto on_error;
 
-       context.server_node_info =
+       g_context.server_node_info =
                g_dbus_node_info_new_for_xml(g_msu_server_introspection, NULL);
-       if (!context.server_node_info)
+       if (!g_context.server_node_info)
                goto on_error;
 
-       context.main_loop = g_main_loop_new(NULL, FALSE);
+       g_context.main_loop = g_main_loop_new(NULL, FALSE);
 
-       context.owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
+       g_context.owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
                                          MSU_SERVER_NAME,
                                          G_BUS_NAME_OWNER_FLAGS_NONE,
                                          prv_bus_acquired, NULL,
-                                         prv_name_lost, &context, NULL);
+                                         prv_name_lost, NULL, NULL);
 
-       context.tasks = g_ptr_array_new();
+       g_context.tasks = g_ptr_array_new();
 
-       context.watchers = g_hash_table_new_full(g_str_hash, g_str_equal,
+       g_context.watchers = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                 g_free, prv_unregister_client);
 
-       if (!prv_init_signal_handler(mask, &context))
+       if (!prv_init_signal_handler(mask))
                goto on_error;
 
-       g_main_loop_run(context.main_loop);
-       if (context.error)
+       g_main_loop_run(g_context.main_loop);
+       if (g_context.error)
                goto on_error;
 
        retval = 0;
 
 on_error:
 
-       prv_msu_context_free(&context);
+       prv_msu_context_free();
 
        msu_log_finalize();
 
        return retval;
 }
+
index daf6028..b7cecdc 100644 (file)
@@ -21,7 +21,9 @@
  */
 
 #include <string.h>
+#include <libgupnp-av/gupnp-didl-lite-contributor.h>
 
+#include "device.h"
 #include "interface.h"
 #include "log.h"
 #include "path.h"
@@ -88,180 +90,270 @@ static const gchar gMediaSpec2Image[] = "image";
 static msu_prop_map_t *prv_msu_prop_map_new(const gchar *prop_name,
                                            msu_upnp_prop_mask type,
                                            gboolean filter,
-                                           gboolean searchable)
+                                           gboolean searchable,
+                                           gboolean updateable)
 {
        msu_prop_map_t *retval = g_new(msu_prop_map_t, 1);
        retval->upnp_prop_name = prop_name;
        retval->type = type;
        retval->filter = filter;
        retval->searchable = searchable;
+       retval->updateable = updateable;
        return retval;
 }
 
-GHashTable *msu_prop_maps_new()
+void msu_prop_maps_new(GHashTable **property_map, GHashTable **filter_map)
 {
-       msu_prop_map_t *prop_map;
-       GHashTable *filter_map =
-               g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
-
-       prop_map = prv_msu_prop_map_new("@parentID", MSU_UPNP_MASK_PROP_PARENT,
-                                       FALSE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_PARENT, prop_map);
+       msu_prop_map_t *prop_t;
+       GHashTable *p_map;
+       GHashTable *f_map;
 
-       prop_map = prv_msu_prop_map_new("upnp:class", MSU_UPNP_MASK_PROP_TYPE,
-                                       FALSE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_TYPE, prop_map);
+       p_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+       f_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
 
-       prop_map = prv_msu_prop_map_new("@id", MSU_UPNP_MASK_PROP_PATH,
-                                       FALSE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_PATH, prop_map);
-
-       prop_map = prv_msu_prop_map_new("dc:title",
-                                       MSU_UPNP_MASK_PROP_DISPLAY_NAME,
-                                       FALSE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_DISPLAY_NAME,
-                           prop_map);
-
-       prop_map = prv_msu_prop_map_new("@childCount",
+       /* @childCount */
+       prop_t = prv_msu_prop_map_new("@childCount",
                                        MSU_UPNP_MASK_PROP_CHILD_COUNT,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_CHILD_COUNT,
-                           prop_map);
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_CHILD_COUNT, prop_t);
+       g_hash_table_insert(p_map, "@childCount",
+                           MSU_INTERFACE_PROP_CHILD_COUNT);
+
+       /* @id */
+       prop_t = prv_msu_prop_map_new("@id",
+                                       MSU_UPNP_MASK_PROP_PATH,
+                                       FALSE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_PATH, prop_t);
+       g_hash_table_insert(p_map, "@id", MSU_INTERFACE_PROP_PATH);
+
+       /* @parentID */
+       prop_t = prv_msu_prop_map_new("@parentID",
+                                       MSU_UPNP_MASK_PROP_PARENT,
+                                       FALSE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_PARENT, prop_t);
+       g_hash_table_insert(p_map, "@parentID", MSU_INTERFACE_PROP_PARENT);
+
+       /* @refID */
+       prop_t = prv_msu_prop_map_new("@refID",
+                                       MSU_UPNP_MASK_PROP_REFPATH,
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_REFPATH, prop_t);
+       g_hash_table_insert(p_map, "@refID", MSU_INTERFACE_PROP_REFPATH);
+
+       /* @restricted */
+       prop_t = prv_msu_prop_map_new("@restricted",
+                                       MSU_UPNP_MASK_PROP_RESTRICTED,
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_RESTRICTED, prop_t);
+       g_hash_table_insert(p_map, "@restricted",
+                           MSU_INTERFACE_PROP_RESTRICTED);
 
-       prop_map = prv_msu_prop_map_new("@searchable",
+       /* @searchable */
+       prop_t = prv_msu_prop_map_new("@searchable",
                                        MSU_UPNP_MASK_PROP_SEARCHABLE,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_SEARCHABLE, prop_map);
-
-       prop_map = prv_msu_prop_map_new("res", MSU_UPNP_MASK_PROP_URLS,
-                                       TRUE, FALSE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_URLS, prop_map);
-
-       prop_map = prv_msu_prop_map_new("res@protocolInfo",
-                                       MSU_UPNP_MASK_PROP_MIME_TYPE,
-                                       TRUE, FALSE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_MIME_TYPE, prop_map);
-
-       prop_map = prv_msu_prop_map_new("upnp:artist",
-                                       MSU_UPNP_MASK_PROP_ARTIST, TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_ARTIST, prop_map);
-
-       prop_map = prv_msu_prop_map_new("upnp:album", MSU_UPNP_MASK_PROP_ALBUM,
-                                      TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_ALBUM, prop_map);
-
-       prop_map = prv_msu_prop_map_new("dc:date", MSU_UPNP_MASK_PROP_DATE,
-                                      TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_DATE, prop_map);
-
-       prop_map = prv_msu_prop_map_new("upnp:genre", MSU_UPNP_MASK_PROP_GENRE,
-                                      TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_GENRE, prop_map);
-
-       prop_map = prv_msu_prop_map_new("res@protocolInfo",
-                                      MSU_UPNP_MASK_PROP_DLNA_PROFILE,
-                                      TRUE, FALSE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_DLNA_PROFILE,
-                           prop_map);
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_SEARCHABLE, prop_t);
+       g_hash_table_insert(p_map, "@searchable",
+                           MSU_INTERFACE_PROP_SEARCHABLE);
+
+       /* dc:creator */
+       prop_t = prv_msu_prop_map_new("dc:creator",
+                                       MSU_UPNP_MASK_PROP_CREATOR,
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_CREATOR, prop_t);
+       g_hash_table_insert(p_map, "dc:creator", MSU_INTERFACE_PROP_CREATOR);
+
+       /* dc:date */
+       prop_t = prv_msu_prop_map_new("dc:date",
+                                       MSU_UPNP_MASK_PROP_DATE,
+                                       TRUE, TRUE, TRUE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_DATE, prop_t);
+       g_hash_table_insert(p_map, "dc:date", MSU_INTERFACE_PROP_DATE);
+
+       /* dc:title */
+       prop_t = prv_msu_prop_map_new("dc:title",
+                                       MSU_UPNP_MASK_PROP_DISPLAY_NAME,
+                                       FALSE, TRUE, TRUE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_DISPLAY_NAME, prop_t);
+       g_hash_table_insert(p_map, "dc:title", MSU_INTERFACE_PROP_DISPLAY_NAME);
+
+       /* dlna:dlnaManaged */
+       prop_t = prv_msu_prop_map_new("dlna:dlnaManaged",
+                                       MSU_UPNP_MASK_PROP_DLNA_MANAGED,
+                                       TRUE, FALSE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_DLNA_MANAGED, prop_t);
+       g_hash_table_insert(p_map, "dlna:dlnaManaged",
+                           MSU_INTERFACE_PROP_DLNA_MANAGED);
+
+       /* res */
+       /* res - RES */
+       prop_t = prv_msu_prop_map_new("res",
+                                       MSU_UPNP_MASK_PROP_RESOURCES,
+                                       TRUE, FALSE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_RESOURCES, prop_t);
+
+       /* res - URL */
+       prop_t = prv_msu_prop_map_new("res",
+                                       MSU_UPNP_MASK_PROP_URL,
+                                       TRUE, FALSE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_URL, prop_t);
+
+       /* res - URLS */
+       prop_t = prv_msu_prop_map_new("res",
+                                       MSU_UPNP_MASK_PROP_URLS,
+                                       TRUE, FALSE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_URLS, prop_t);
+
+       /* res@bitrate */
+       prop_t = prv_msu_prop_map_new("res@bitrate",
+                                       MSU_UPNP_MASK_PROP_BITRATE,
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_BITRATE, prop_t);
+       g_hash_table_insert(p_map, "res@bitrate", MSU_INTERFACE_PROP_BITRATE);
 
-       prop_map = prv_msu_prop_map_new("upnp:originalTrackNumber",
-                                       MSU_UPNP_MASK_PROP_TRACK_NUMBER,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_TRACK_NUMBER,
-                           prop_map);
+       /* res@bitsPerSample */
+       prop_t = prv_msu_prop_map_new("res@bitsPerSample",
+                                       MSU_UPNP_MASK_PROP_BITS_PER_SAMPLE,
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_BITS_PER_SAMPLE, prop_t);
+       g_hash_table_insert(p_map, "res@bitsPerSample",
+                           MSU_INTERFACE_PROP_BITS_PER_SAMPLE);
 
-       prop_map = prv_msu_prop_map_new("res@size", MSU_UPNP_MASK_PROP_SIZE,
-                                      TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_SIZE, prop_map);
+       /* res@colorDepth */
+       prop_t = prv_msu_prop_map_new("res@colorDepth",
+                                       MSU_UPNP_MASK_PROP_COLOR_DEPTH,
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_COLOR_DEPTH, prop_t);
+       g_hash_table_insert(p_map, "res@colorDepth",
+                           MSU_INTERFACE_PROP_COLOR_DEPTH);
 
-       prop_map = prv_msu_prop_map_new("res@duration",
+       /* res@duration */
+       prop_t = prv_msu_prop_map_new("res@duration",
                                        MSU_UPNP_MASK_PROP_DURATION,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_DURATION, prop_map);
-
-       prop_map = prv_msu_prop_map_new("res@bitrate",
-                                       MSU_UPNP_MASK_PROP_BITRATE,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_BITRATE, prop_map);
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_DURATION, prop_t);
+       g_hash_table_insert(p_map, "res@duration",
+                           MSU_INTERFACE_PROP_DURATION);
+
+       /* res@protocolInfo */
+       /* res@protocolInfo - DLNA PROFILE*/
+       prop_t = prv_msu_prop_map_new("res@protocolInfo",
+                                      MSU_UPNP_MASK_PROP_DLNA_PROFILE,
+                                      TRUE, FALSE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_DLNA_PROFILE, prop_t);
 
-       prop_map = prv_msu_prop_map_new("res@sampleFrequency",
+       /* res@protocolInfo - MIME TYPES*/
+       prop_t = prv_msu_prop_map_new("res@protocolInfo",
+                                       MSU_UPNP_MASK_PROP_MIME_TYPE,
+                                       TRUE, FALSE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_MIME_TYPE, prop_t);
+
+       /* res@resolution */
+       /* res@resolution - HEIGH */
+       prop_t = prv_msu_prop_map_new("res@resolution",
+                                       MSU_UPNP_MASK_PROP_HEIGHT,
+                                       TRUE, FALSE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_HEIGHT, prop_t);
+
+       /* res@resolution - WIDTH */
+       prop_t = prv_msu_prop_map_new("res@resolution",
+                                       MSU_UPNP_MASK_PROP_WIDTH,
+                                       TRUE, FALSE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_WIDTH, prop_t);
+
+       /* res@sampleFrequency */
+       prop_t = prv_msu_prop_map_new("res@sampleFrequency",
                                        MSU_UPNP_MASK_PROP_SAMPLE_RATE,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_SAMPLE_RATE,
-                           prop_map);
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_SAMPLE_RATE, prop_t);
+       g_hash_table_insert(p_map, "res@sampleFrequency",
+                           MSU_INTERFACE_PROP_SAMPLE_RATE);
+
+       /* res@size */
+       prop_t = prv_msu_prop_map_new("res@size",
+                                       MSU_UPNP_MASK_PROP_SIZE,
+                                      TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_SIZE, prop_t);
+       g_hash_table_insert(p_map, "res@size", MSU_INTERFACE_PROP_SIZE);
+
+       /* upnp:album */
+       prop_t = prv_msu_prop_map_new("upnp:album",
+                                       MSU_UPNP_MASK_PROP_ALBUM,
+                                      TRUE, TRUE, TRUE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_ALBUM, prop_t);
+       g_hash_table_insert(p_map, "upnp:album", MSU_INTERFACE_PROP_ALBUM);
+
+       /* upnp:albumArtURI */
+       prop_t = prv_msu_prop_map_new("upnp:albumArtURI",
+                                       MSU_UPNP_MASK_PROP_ALBUM_ART_URL,
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_ALBUM_ART_URL, prop_t);
+       g_hash_table_insert(p_map, "upnp:albumArtURI",
+                           MSU_INTERFACE_PROP_ALBUM_ART_URL);
+
+       /* upnp:artist */
+       /* upnp:artist - ARTIST*/
+       prop_t = prv_msu_prop_map_new("upnp:artist",
+                                       MSU_UPNP_MASK_PROP_ARTIST,
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_ARTIST, prop_t);
+       g_hash_table_insert(p_map, "upnp:artist", MSU_INTERFACE_PROP_ARTIST);
+
+       /* upnp:artist - ARTISTS*/
+       prop_t = prv_msu_prop_map_new("upnp:artist",
+                                       MSU_UPNP_MASK_PROP_ARTISTS,
+                                       TRUE, FALSE, TRUE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_ARTISTS, prop_t);
+
+       /* upnp:class */
+       prop_t = prv_msu_prop_map_new("upnp:class",
+                                       MSU_UPNP_MASK_PROP_TYPE,
+                                       FALSE, TRUE, TRUE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_TYPE, prop_t);
+       g_hash_table_insert(p_map, "upnp:class", MSU_INTERFACE_PROP_TYPE);
+
+       /* upnp:createClass */
+       prop_t = prv_msu_prop_map_new("upnp:createClass",
+                                       MSU_UPNP_MASK_PROP_CREATE_CLASSES,
+                                       TRUE, FALSE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_CREATE_CLASSES, prop_t);
+
+       /* upnp:genre */
+       prop_t = prv_msu_prop_map_new("upnp:genre",
+                                       MSU_UPNP_MASK_PROP_GENRE,
+                                       TRUE, TRUE, FALSE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_GENRE, prop_t);
+       g_hash_table_insert(p_map, "upnp:genre", MSU_INTERFACE_PROP_GENRE);
+
+       /* upnp:originalTrackNumber */
+       prop_t = prv_msu_prop_map_new("upnp:originalTrackNumber",
+                                       MSU_UPNP_MASK_PROP_TRACK_NUMBER,
+                                       TRUE, TRUE, TRUE);
+       g_hash_table_insert(f_map, MSU_INTERFACE_PROP_TRACK_NUMBER, prop_t);
+       g_hash_table_insert(p_map, "upnp:originalTrackNumber",
+                           MSU_INTERFACE_PROP_TRACK_NUMBER);
 
-       prop_map = prv_msu_prop_map_new("res@bitsPerSample",
-                                       MSU_UPNP_MASK_PROP_BITS_PER_SAMPLE,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_BITS_PER_SAMPLE,
-                           prop_map);
-
-       prop_map = prv_msu_prop_map_new("res@resolution",
-                                       MSU_UPNP_MASK_PROP_WIDTH, TRUE, FALSE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_WIDTH, prop_map);
-
-       prop_map = prv_msu_prop_map_new("res@resolution",
-                                       MSU_UPNP_MASK_PROP_HEIGHT, TRUE, FALSE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_HEIGHT, prop_map);
-
-       prop_map = prv_msu_prop_map_new("res@colorDepth",
-                                       MSU_UPNP_MASK_PROP_COLOR_DEPTH,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_COLOR_DEPTH,
-                           prop_map);
+       *filter_map = f_map;
+       *property_map = p_map;
+}
 
-       prop_map = prv_msu_prop_map_new("upnp:albumArtURI",
-                                       MSU_UPNP_MASK_PROP_ALBUM_ART_URL,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_ALBUM_ART_URL,
-                           prop_map);
-
-       prop_map = prv_msu_prop_map_new("res", MSU_UPNP_MASK_PROP_RESOURCES,
-                                       TRUE, FALSE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_RESOURCES,
-                           prop_map);
-
-       prop_map = prv_msu_prop_map_new("res", MSU_UPNP_MASK_PROP_URL,
-                                       TRUE, FALSE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_URL, prop_map);
-
-       prop_map = prv_msu_prop_map_new("@refID", MSU_UPNP_MASK_PROP_REFPATH,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_REFPATH, prop_map);
-
-       prop_map = prv_msu_prop_map_new("@restricted",
-                                       MSU_UPNP_MASK_PROP_RESTRICTED,
-                                       TRUE, TRUE);
-       g_hash_table_insert(filter_map,
-                           (gpointer) MSU_INTERFACE_PROP_RESTRICTED, prop_map);
+static gchar *prv_compute_upnp_filter(GHashTable *upnp_props)
+{
+       gpointer key;
+       GString *str;
+       GHashTableIter iter;
 
-       return filter_map;
+       str = g_string_new("");
+       g_hash_table_iter_init(&iter, upnp_props);
+       if (g_hash_table_iter_next(&iter, &key, NULL)) {
+               g_string_append(str, (const gchar *) key);
+               while (g_hash_table_iter_next(&iter, &key, NULL)) {
+                       g_string_append(str, ",");
+                       g_string_append(str, (const gchar *) key);
+               }
+       }
+
+       return g_string_free(str, FALSE);
 }
 
 static guint32 prv_parse_filter_list(GHashTable *filter_map, GVariant *filter,
@@ -271,10 +363,7 @@ static guint32 prv_parse_filter_list(GHashTable *filter_map, GVariant *filter,
        const gchar *prop;
        msu_prop_map_t *prop_map;
        GHashTable *upnp_props;
-       GHashTableIter iter;
        guint32 mask = 0;
-       gpointer key;
-       GString *str;
 
        upnp_props = g_hash_table_new_full(g_str_hash, g_str_equal,
                                           NULL, NULL);
@@ -294,16 +383,7 @@ static guint32 prv_parse_filter_list(GHashTable *filter_map, GVariant *filter,
                                    (gpointer) prop_map->upnp_prop_name, NULL);
        }
 
-       str = g_string_new("");
-       g_hash_table_iter_init(&iter, upnp_props);
-       if (g_hash_table_iter_next(&iter, &key, NULL)) {
-               g_string_append(str, (const gchar *) key);
-               while (g_hash_table_iter_next(&iter, &key, NULL)) {
-                       g_string_append(str, ",");
-                       g_string_append(str, (const gchar *) key);
-               }
-       }
-       *upnp_filter = g_string_free(str, FALSE);
+       *upnp_filter = prv_compute_upnp_filter(upnp_props);
        g_hash_table_unref(upnp_props);
 
        return mask;
@@ -332,6 +412,73 @@ guint32 msu_props_parse_filter(GHashTable *filter_map, GVariant *filter,
        return mask;
 }
 
+gboolean msu_props_parse_update_filter(GHashTable *filter_map,
+                                      GVariant *to_add_update,
+                                      GVariant *to_delete, guint32 *mask,
+                                      gchar **upnp_filter)
+{
+       GVariantIter viter;
+       const gchar *prop;
+       GVariant *value;
+       msu_prop_map_t *prop_map;
+       GHashTable *upnp_props;
+       gboolean retval = FALSE;
+
+       *mask = 0;
+
+       upnp_props = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                          NULL, NULL);
+
+       (void) g_variant_iter_init(&viter, to_add_update);
+
+       while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
+               MSU_LOG_DEBUG("to_add_update = %s", prop);
+
+               prop_map = g_hash_table_lookup(filter_map, prop);
+
+               if ((!prop_map) || (!prop_map->updateable))
+                       goto on_error;
+
+               *mask |= prop_map->type;
+
+               if (!prop_map->filter)
+                       continue;
+
+               g_hash_table_insert(upnp_props,
+                                   (gpointer) prop_map->upnp_prop_name, NULL);
+       }
+
+       (void) g_variant_iter_init(&viter, to_delete);
+
+       while (g_variant_iter_next(&viter, "&s", &prop)) {
+               MSU_LOG_DEBUG("to_delete = %s", prop);
+
+               prop_map = g_hash_table_lookup(filter_map, prop);
+
+               if ((!prop_map) || (!prop_map->updateable) ||
+                   (*mask & prop_map->type) != 0)
+                       goto on_error;
+
+               *mask |= prop_map->type;
+
+               if (!prop_map->filter)
+                       continue;
+
+               g_hash_table_insert(upnp_props,
+                                   (gpointer) prop_map->upnp_prop_name, NULL);
+       }
+
+       *upnp_filter = prv_compute_upnp_filter(upnp_props);
+
+       retval = TRUE;
+
+on_error:
+
+       g_hash_table_unref(upnp_props);
+
+       return retval;
+}
+
 static void prv_add_string_prop(GVariantBuilder *vb, const gchar *key,
                                const gchar *value)
 {
@@ -378,6 +525,13 @@ static void prv_add_int_prop(GVariantBuilder *vb, const gchar *key,
                                      g_variant_new_int32(value));
 }
 
+static void prv_add_variant_prop(GVariantBuilder *vb, const gchar *key,
+                                GVariant *prop)
+{
+       if (prop)
+               g_variant_builder_add(vb, "{sv}", key, prop);
+}
+
 void msu_props_add_child_count(GVariantBuilder *item_vb, gint value)
 {
        prv_add_int_prop(item_vb, MSU_INTERFACE_PROP_CHILD_COUNT, value);
@@ -402,9 +556,66 @@ static void prv_add_int64_prop(GVariantBuilder *vb, const gchar *key,
        }
 }
 
-void msu_props_add_device(GUPnPDeviceInfo *proxy, GVariantBuilder *vb)
+static void prv_add_list_dlna_str(gpointer data, gpointer user_data)
+{
+       GVariantBuilder *vb = (GVariantBuilder *) user_data;
+       gchar *cap_str = (gchar *) data;
+       gchar *str;
+       int value = 0;
+
+       if (g_str_has_prefix(cap_str, "srs-rt-retention-period-")) {
+               str = cap_str + strlen("srs-rt-retention-period-");
+               cap_str = "srs-rt-retention-period";
+
+               if (*str) {
+                       if (!g_strcmp0(str, "infinity"))
+                               value = -1;
+                       else
+                               value = atoi(str);
+               }
+       }
+
+       prv_add_uint_prop(vb, cap_str, value);
+}
+
+static GVariant *prv_add_list_dlna_prop(GList* list)
+{
+       GVariantBuilder vb;
+
+       g_variant_builder_init(&vb, G_VARIANT_TYPE("a{sv}"));
+
+       g_list_foreach(list, prv_add_list_dlna_str, &vb);
+
+       return g_variant_builder_end(&vb);
+}
+
+static void prv_add_list_artists_str(gpointer data, gpointer user_data)
+{
+       GVariantBuilder *vb = (GVariantBuilder *) user_data;
+       GUPnPDIDLLiteContributor *contributor = data;
+       const char *str;
+
+       str = gupnp_didl_lite_contributor_get_name(contributor);
+       g_variant_builder_add(vb, "s", str);
+}
+
+static GVariant *prv_get_artists_prop(GList *list)
+{
+       GVariantBuilder vb;
+
+       g_variant_builder_init(&vb, G_VARIANT_TYPE("as"));
+       g_list_foreach(list, prv_add_list_artists_str, &vb);
+
+       return g_variant_builder_end(&vb);
+}
+
+void msu_props_add_device(GUPnPDeviceInfo *proxy,
+                         msu_device_t *device,
+                         GVariantBuilder *vb)
 {
        gchar *str;
+       GList *list;
+       GVariant *dlna_caps;
 
        prv_add_string_prop(vb, MSU_INTERFACE_PROP_LOCATION,
                            gupnp_device_info_get_location(proxy));
@@ -455,13 +666,46 @@ void msu_props_add_device(GUPnPDeviceInfo *proxy, GVariantBuilder *vb)
                                             NULL, NULL, NULL, NULL);
        prv_add_string_prop(vb, MSU_INTERFACE_PROP_ICON_URL, str);
        g_free(str);
+
+       list = gupnp_device_info_list_dlna_capabilities(proxy);
+       if (list != NULL){
+               dlna_caps = prv_add_list_dlna_prop(list);
+               g_variant_builder_add(vb, "{sv}",
+                                     MSU_INTERFACE_PROP_SV_DLNA_CAPABILITIES,
+                                     dlna_caps);
+               g_list_free_full(list, g_free);
+       }
+
+       if (device->search_caps != NULL)
+               g_variant_builder_add(vb, "{sv}",
+                                     MSU_INTERFACE_PROP_SV_SEARCH_CAPABILITIES,
+                                     device->search_caps);
+
+       if (device->sort_caps != NULL)
+               g_variant_builder_add(vb, "{sv}",
+                                     MSU_INTERFACE_PROP_SV_SORT_CAPABILITIES,
+                                     device->sort_caps);
+
+       if (device->sort_ext_caps != NULL)
+               g_variant_builder_add(vb, "{sv}",
+                               MSU_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES,
+                               device->sort_ext_caps);
+
+       if (device->feature_list != NULL)
+               g_variant_builder_add(vb, "{sv}",
+                                     MSU_INTERFACE_PROP_SV_FEATURE_LIST,
+                                     device->feature_list);
 }
 
-GVariant *msu_props_get_device_prop(GUPnPDeviceInfo *proxy, const gchar *prop)
+GVariant *msu_props_get_device_prop(GUPnPDeviceInfo *proxy,
+                                   msu_device_t *device,
+                                   const gchar *prop)
 {
+       GVariant *dlna_caps = NULL;
        GVariant *retval = NULL;
        const gchar *str = NULL;
        gchar *copy = NULL;
+       GList *list;
 
        if (!strcmp(MSU_INTERFACE_PROP_LOCATION, prop)) {
                str = gupnp_device_info_get_location(proxy);
@@ -501,17 +745,67 @@ GVariant *msu_props_get_device_prop(GUPnPDeviceInfo *proxy, const gchar *prop)
                                                      -1, -1, -1, FALSE,
                                                      NULL, NULL, NULL, NULL);
                str = copy;
-       }
+       } else if (!strcmp(MSU_INTERFACE_PROP_SV_DLNA_CAPABILITIES, prop)) {
+               list = gupnp_device_info_list_dlna_capabilities(proxy);
+               if (list != NULL){
+                       dlna_caps = prv_add_list_dlna_prop(list);
+                       g_list_free_full(list, g_free);
+                       retval = g_variant_ref_sink(dlna_caps);
+
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+                       copy = g_variant_print(dlna_caps, FALSE);
+                       MSU_LOG_DEBUG("Prop %s = %s", prop, copy);
+#endif
+               }
+       } else if (!strcmp(MSU_INTERFACE_PROP_SV_SEARCH_CAPABILITIES, prop)) {
+               if (device->search_caps != NULL){
+                       retval = g_variant_ref(device->search_caps);
 
-       if (str) {
-               MSU_LOG_DEBUG("Prop %s = %s", prop, str);
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+                       copy = g_variant_print(device->search_caps, FALSE);
+                       MSU_LOG_DEBUG("Prop %s = %s", prop, copy);
+#endif
+               }
+       } else if (!strcmp(MSU_INTERFACE_PROP_SV_SORT_CAPABILITIES, prop)) {
+               if (device->sort_caps != NULL){
+                       retval = g_variant_ref(device->sort_caps);
 
-               retval = g_variant_ref_sink(g_variant_new_string(str));
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+                       copy = g_variant_print(device->sort_caps, FALSE);
+                       MSU_LOG_DEBUG("Prop %s = %s", prop, copy);
+#endif
+               }
+       } else if (!strcmp(MSU_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES, prop)) {
+               if (device->sort_ext_caps != NULL){
+                       retval = g_variant_ref(device->sort_ext_caps);
+
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+                       copy = g_variant_print(device->sort_ext_caps, FALSE);
+                       MSU_LOG_DEBUG("Prop %s = %s", prop, copy);
+#endif
+               }
+       } else if (!strcmp(MSU_INTERFACE_PROP_SV_FEATURE_LIST, prop)) {
+               if (device->feature_list != NULL){
+                       retval = g_variant_ref(device->feature_list);
+
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+                       copy = g_variant_print(device->feature_list, FALSE);
+                       MSU_LOG_DEBUG("Prop %s = %s", prop, copy);
+#endif
+               }
        }
+
+       if (!retval){
+               if (str) {
+                       MSU_LOG_DEBUG("Prop %s = %s", prop, str);
+
+                       retval = g_variant_ref_sink(g_variant_new_string(str));
+               }
 #if MSU_LOG_LEVEL & MSU_LOG_LEVEL_WARNING
-       else
-               MSU_LOG_WARNING("Property %s not defined for device", prop);
+               else
+                       MSU_LOG_WARNING("Property %s not defined", prop);
 #endif
+       }
 
        g_free(copy);
 
@@ -653,6 +947,36 @@ static void prv_parse_resources(GVariantBuilder *item_vb,
        }
 }
 
+static GVariant *prv_compute_create_classes(GUPnPDIDLLiteContainer *container)
+{
+       GVariantBuilder create_classes_vb;
+       GList *create_classes;
+       GList *ptr;
+       GUPnPDIDLLiteCreateClass *create_class;
+       const char *content;
+       gboolean inc_derived;
+
+       g_variant_builder_init(&create_classes_vb, G_VARIANT_TYPE("a(sb)"));
+
+       create_classes = gupnp_didl_lite_container_get_create_classes_full(
+                                                                container);
+       ptr = create_classes;
+       while (ptr) {
+               create_class = ptr->data;
+               content = gupnp_didl_lite_create_class_get_content(
+                                                                create_class);
+               inc_derived = gupnp_didl_lite_create_class_get_include_derived(
+                                                                create_class);
+               g_variant_builder_add(&create_classes_vb,
+                                     "(sb)", content, inc_derived);
+               g_object_unref(ptr->data);
+               ptr = g_list_next(ptr);
+       }
+       g_list_free(create_classes);
+
+       return g_variant_builder_end(&create_classes_vb);
+}
+
 const gchar *msu_props_media_spec_to_upnp_class(const gchar *m2spec_class)
 {
        const gchar *retval = NULL;
@@ -761,6 +1085,31 @@ const gchar *msu_props_upnp_class_to_media_spec(const gchar *upnp_class)
        return retval;
 }
 
+static GVariant *prv_props_get_dlna_managed_dict(GUPnPOCMFlags flags)
+{
+       GVariantBuilder builder;
+       gboolean managed;
+
+       g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sb}"));
+
+       managed = (flags & GUPNP_OCM_FLAGS_UPLOAD);
+       g_variant_builder_add(&builder, "{sb}", "Upload", managed);
+
+       managed = (flags & GUPNP_OCM_FLAGS_CREATE_CONTAINER);
+       g_variant_builder_add(&builder, "{sb}", "CreateContainer", managed);
+
+       managed = (flags & GUPNP_OCM_FLAGS_DESTROYABLE);
+       g_variant_builder_add(&builder, "{sb}", "Delete", managed);
+
+       managed = (flags & GUPNP_OCM_FLAGS_UPLOAD_DESTROYABLE);
+       g_variant_builder_add(&builder, "{sb}", "UploadDelete", managed);
+
+       managed = (flags & GUPNP_OCM_FLAGS_CHANGE_METADATA);
+       g_variant_builder_add(&builder, "{sb}", "ChangeMeta", managed);
+
+       return g_variant_builder_end(&builder);
+}
+
 gboolean msu_props_add_object(GVariantBuilder *item_vb,
                              GUPnPDIDLLiteObject *object,
                              const char *root_path,
@@ -770,10 +1119,12 @@ gboolean msu_props_add_object(GVariantBuilder *item_vb,
        gchar *path = NULL;
        const char *id;
        const char *title;
+       const char *creator;
        const char *upnp_class;
        const char *media_spec_type;
        gboolean retval = FALSE;
        gboolean rest;
+       GUPnPOCMFlags flags;
 
        id = gupnp_didl_lite_object_get_id(object);
        if (!id)
@@ -786,6 +1137,7 @@ gboolean msu_props_add_object(GVariantBuilder *item_vb,
                goto on_error;
 
        title = gupnp_didl_lite_object_get_title(object);
+       creator = gupnp_didl_lite_object_get_creator(object);
        rest = gupnp_didl_lite_object_get_restricted(object);
        path = msu_path_from_id(root_path, id);
 
@@ -793,6 +1145,10 @@ gboolean msu_props_add_object(GVariantBuilder *item_vb,
                prv_add_string_prop(item_vb, MSU_INTERFACE_PROP_DISPLAY_NAME,
                                    title);
 
+       if (filter_mask & MSU_UPNP_MASK_PROP_CREATOR)
+               prv_add_string_prop(item_vb, MSU_INTERFACE_PROP_CREATOR,
+                                   creator);
+
        if (filter_mask & MSU_UPNP_MASK_PROP_PATH)
                prv_add_path_prop(item_vb, MSU_INTERFACE_PROP_PATH, path);
 
@@ -807,6 +1163,13 @@ gboolean msu_props_add_object(GVariantBuilder *item_vb,
        if (filter_mask & MSU_UPNP_MASK_PROP_RESTRICTED)
                prv_add_bool_prop(item_vb, MSU_INTERFACE_PROP_RESTRICTED, rest);
 
+       if (filter_mask & MSU_UPNP_MASK_PROP_DLNA_MANAGED) {
+               flags = gupnp_didl_lite_object_get_dlna_managed(object);
+               prv_add_variant_prop(item_vb,
+                                    MSU_INTERFACE_PROP_DLNA_MANAGED,
+                                    prv_props_get_dlna_managed_dict(flags));
+       }
+
        retval = TRUE;
 
 on_error:
@@ -840,6 +1203,11 @@ void msu_props_add_container(GVariantBuilder *item_vb,
                prv_add_bool_prop(item_vb, MSU_INTERFACE_PROP_SEARCHABLE,
                                  searchable);
        }
+
+       if (filter_mask & MSU_UPNP_MASK_PROP_CREATE_CLASSES)
+               prv_add_variant_prop(item_vb,
+                                    MSU_INTERFACE_PROP_CREATE_CLASSES,
+                                    prv_compute_create_classes(object));
 }
 
 static GVariant *prv_compute_resources(GUPnPDIDLLiteObject *object,
@@ -908,11 +1276,19 @@ void msu_props_add_item(GVariantBuilder *item_vb,
        GUPnPDIDLLiteResource *res;
        const char *str_val;
        char *path;
+       GList *list;
 
        if (filter_mask & MSU_UPNP_MASK_PROP_ARTIST)
                prv_add_string_prop(item_vb, MSU_INTERFACE_PROP_ARTIST,
                                    gupnp_didl_lite_object_get_artist(object));
 
+       if (filter_mask & MSU_UPNP_MASK_PROP_ARTISTS) {
+               list = gupnp_didl_lite_object_get_artists(object);
+               prv_add_variant_prop(item_vb, MSU_INTERFACE_PROP_ARTISTS,
+                                    prv_get_artists_prop(list));
+               g_list_free_full(list, g_object_unref);
+       }
+
        if (filter_mask & MSU_UPNP_MASK_PROP_ALBUM)
                prv_add_string_prop(item_vb, MSU_INTERFACE_PROP_ALBUM,
                                    gupnp_didl_lite_object_get_album(object));
@@ -1075,6 +1451,7 @@ GVariant *msu_props_get_object_prop(const gchar *prop, const gchar *root_path,
        const char *title;
        gboolean rest;
        GVariant *retval = NULL;
+       GUPnPOCMFlags dlna_managed;
 
        if (!strcmp(prop, MSU_INTERFACE_PROP_PARENT)) {
                id = gupnp_didl_lite_object_get_parent_id(object);
@@ -1122,12 +1499,27 @@ GVariant *msu_props_get_object_prop(const gchar *prop, const gchar *root_path,
                MSU_LOG_DEBUG("Prop %s = %s", prop, title);
 
                retval = g_variant_ref_sink(g_variant_new_string(title));
+       } else if (!strcmp(prop, MSU_INTERFACE_PROP_CREATOR)) {
+               title = gupnp_didl_lite_object_get_creator(object);
+               if (!title)
+                       goto on_error;
+
+               MSU_LOG_DEBUG("Prop %s = %s", prop, title);
+
+               retval = g_variant_ref_sink(g_variant_new_string(title));
        } else if (!strcmp(prop, MSU_INTERFACE_PROP_RESTRICTED)) {
                rest = gupnp_didl_lite_object_get_restricted(object);
 
                MSU_LOG_DEBUG("Prop %s = %d", prop, rest);
 
                retval = g_variant_ref_sink(g_variant_new_boolean(rest));
+       } else if (!strcmp(prop, MSU_INTERFACE_PROP_DLNA_MANAGED)) {
+               dlna_managed = gupnp_didl_lite_object_get_dlna_managed(object);
+
+               MSU_LOG_DEBUG("Prop %s = %0x", prop, dlna_managed);
+
+               retval = g_variant_ref_sink(
+                               prv_props_get_dlna_managed_dict(dlna_managed));
        }
 
 on_error:
@@ -1144,6 +1536,7 @@ GVariant *msu_props_get_item_prop(const gchar *prop, const gchar *root_path,
        gint track_number;
        GUPnPDIDLLiteResource *res;
        GVariant *retval = NULL;
+       GList *list;
 
        if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
                goto on_error;
@@ -1156,6 +1549,19 @@ GVariant *msu_props_get_item_prop(const gchar *prop, const gchar *root_path,
                MSU_LOG_DEBUG("Prop %s = %s", prop, str);
 
                retval = g_variant_ref_sink(g_variant_new_string(str));
+       } else if (!strcmp(prop, MSU_INTERFACE_PROP_ARTISTS)) {
+               list = gupnp_didl_lite_object_get_artists(object);
+               if (!list)
+                       goto on_error;
+
+               retval = g_variant_ref_sink(prv_get_artists_prop(list));
+               g_list_free_full(list, g_object_unref);
+
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+               path = g_variant_print(retval, FALSE);
+               MSU_LOG_DEBUG("Prop %s = %s", prop, path);
+               g_free(path);
+#endif
        } else if (!strcmp(prop, MSU_INTERFACE_PROP_ALBUM)) {
                str = gupnp_didl_lite_object_get_album(object);
                if (!str)
@@ -1233,7 +1639,9 @@ GVariant *msu_props_get_container_prop(const gchar *prop,
        gboolean searchable;
        GUPnPDIDLLiteContainer *container;
        GVariant *retval = NULL;
-
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       gchar *create_classes;
+#endif
        if (!GUPNP_IS_DIDL_LITE_CONTAINER(object))
                goto on_error;
 
@@ -1256,6 +1664,14 @@ GVariant *msu_props_get_container_prop(const gchar *prop,
 
                retval = g_variant_ref_sink(
                        g_variant_new_boolean(searchable));
+       } else if (!strcmp(prop, MSU_INTERFACE_PROP_CREATE_CLASSES)) {
+               retval = g_variant_ref_sink(
+                       prv_compute_create_classes(container));
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+               create_classes = g_variant_print(retval, FALSE);
+               MSU_LOG_DEBUG("Prop %s = %s", prop, create_classes);
+               g_free(create_classes);
+#endif
        }
 
 on_error:
index e998280..3e9e063 100644 (file)
@@ -24,6 +24,7 @@
 #define MSU_PROPS_H__
 
 #include <libgupnp-av/gupnp-av.h>
+#include "async.h"
 
 enum msu_upnp_prop_mask_ {
        MSU_UPNP_MASK_PROP_PARENT = 1,
@@ -52,7 +53,11 @@ enum msu_upnp_prop_mask_ {
        MSU_UPNP_MASK_PROP_RESOURCES = 1 << 23,
        MSU_UPNP_MASK_PROP_URL = 1 << 24,
        MSU_UPNP_MASK_PROP_REFPATH = 1 << 25,
-       MSU_UPNP_MASK_PROP_RESTRICTED = 1 << 26
+       MSU_UPNP_MASK_PROP_RESTRICTED = 1 << 26,
+       MSU_UPNP_MASK_PROP_DLNA_MANAGED = 1 << 27,
+       MSU_UPNP_MASK_PROP_CREATOR = 1 << 28,
+       MSU_UPNP_MASK_PROP_ARTISTS = 1 << 29,
+       MSU_UPNP_MASK_PROP_CREATE_CLASSES = 1 << 30
 };
 typedef enum msu_upnp_prop_mask_ msu_upnp_prop_mask;
 
@@ -62,19 +67,33 @@ struct msu_prop_map_t_ {
        msu_upnp_prop_mask type;
        gboolean filter;
        gboolean searchable;
+       gboolean updateable;
 };
 
-GHashTable *msu_prop_maps_new(void);
+void msu_prop_maps_new(GHashTable **property_map, GHashTable **filter_map);
+
 guint32 msu_props_parse_filter(GHashTable *filter_map, GVariant *filter,
                               gchar **upnp_filter);
-void msu_props_add_device(GUPnPDeviceInfo *proxy, GVariantBuilder *vb);
-GVariant *msu_props_get_device_prop(GUPnPDeviceInfo *proxy, const gchar *prop);
+
+gboolean msu_props_parse_update_filter(GHashTable *filter_map,
+                                      GVariant *to_add_update,
+                                      GVariant *to_delete, guint32 *mask,
+                                      gchar **upnp_filter);
+
+void msu_props_add_device(GUPnPDeviceInfo *proxy,
+                         msu_device_t *device,
+                         GVariantBuilder *vb);
+
+GVariant *msu_props_get_device_prop(GUPnPDeviceInfo *proxy,
+                                   msu_device_t *device,
+                                   const gchar *prop);
 
 gboolean msu_props_add_object(GVariantBuilder *item_vb,
                              GUPnPDIDLLiteObject *object,
                              const char *root_path,
                              const gchar *parent_path,
                              guint32 filter_mask);
+
 GVariant *msu_props_get_object_prop(const gchar *prop, const gchar *root_path,
                                    GUPnPDIDLLiteObject *object);
 
@@ -84,6 +103,7 @@ void msu_props_add_container(GVariantBuilder *item_vb,
                             gboolean *have_child_count);
 
 void msu_props_add_child_count(GVariantBuilder *item_vb, gint value);
+
 GVariant *msu_props_get_container_prop(const gchar *prop,
                                       GUPnPDIDLLiteObject *object);
 
@@ -91,17 +111,19 @@ void msu_props_add_resource(GVariantBuilder *item_vb,
                            GUPnPDIDLLiteObject *object,
                            guint32 filter_mask,
                            const gchar *protocol_info);
+
 void msu_props_add_item(GVariantBuilder *item_vb,
                        GUPnPDIDLLiteObject *object,
                        const gchar *root_path,
                        guint32 filter_mask,
                        const gchar *protocol_info);
+
 GVariant *msu_props_get_item_prop(const gchar *prop, const gchar *root_path,
                                  GUPnPDIDLLiteObject *object,
                                  const gchar *protocol_info);
 
 const gchar *msu_props_media_spec_to_upnp_class(const gchar *m2spec_class);
-const gchar *msu_props_upnp_class_to_media_spec(const gchar *upnp_class);
 
+const gchar *msu_props_upnp_class_to_media_spec(const gchar *upnp_class);
 
 #endif
index 1631d12..e0bd20d 100644 (file)
@@ -74,7 +74,7 @@ static void prv_msu_settings_get_keyfile_path(gchar **sys_path,
        if (sys_path != NULL) {
                *sys_path = NULL;
 
-               if (SYS_CONFIG_DIR && *SYS_CONFIG_DIR)
+               if (*SYS_CONFIG_DIR)
                        *sys_path = g_strdup_printf("%s/%s", SYS_CONFIG_DIR,
                                                    MSU_SETTINGS_KEYFILE_NAME);
        }
index 788f4d2..17e9e9a 100644 (file)
@@ -220,6 +220,20 @@ static msu_task_t *prv_upload_new_generic(msu_task_type_t type,
        return task;
 }
 
+msu_task_t *msu_task_prefer_local_addresses_new(
+                                       GDBusMethodInvocation *invocation,
+                                       GVariant *parameters)
+{
+       msu_task_t *task = g_new0(msu_task_t, 1);
+
+       task->type = MSU_TASK_SET_PREFER_LOCAL_ADDRESSES;
+       task->invocation = invocation;
+       task->synchronous = TRUE;
+       g_variant_get(parameters, "(b)",
+                     &task->ut.prefer_local_addresses.prefer);
+
+       return task;
+}
 
 msu_task_t *msu_task_upload_to_any_new(GDBusMethodInvocation *invocation,
                                       const gchar *path, GVariant *parameters)
@@ -235,6 +249,95 @@ msu_task_t *msu_task_upload_new(GDBusMethodInvocation *invocation,
                                      path, parameters);
 }
 
+msu_task_t *msu_task_get_upload_status_new(GDBusMethodInvocation *invocation,
+                                          const gchar *path,
+                                          GVariant *parameters)
+{
+       msu_task_t *task;
+
+       task = prv_m2spec_task_new(MSU_TASK_GET_UPLOAD_STATUS, invocation, path,
+                                  "(stt)");
+
+       g_variant_get(parameters, "(u)",
+                     &task->ut.upload_action.upload_id);
+       task->synchronous = TRUE;
+       task->multiple_retvals = TRUE;
+
+       return task;
+}
+
+msu_task_t *msu_task_get_upload_ids_new(GDBusMethodInvocation *invocation,
+                                       const gchar *path)
+{
+       msu_task_t *task;
+
+       task = prv_m2spec_task_new(MSU_TASK_GET_UPLOAD_IDS, invocation, path,
+                                  "(@au)");
+
+       task->synchronous = TRUE;
+
+       return task;
+}
+
+msu_task_t *msu_task_cancel_upload_new(GDBusMethodInvocation *invocation,
+                                      const gchar *path,
+                                      GVariant *parameters)
+{
+       msu_task_t *task;
+
+       task = prv_m2spec_task_new(MSU_TASK_CANCEL_UPLOAD, invocation, path,
+                                  NULL);
+
+       g_variant_get(parameters, "(u)",
+                     &task->ut.upload_action.upload_id);
+       task->synchronous = TRUE;
+
+       return task;
+}
+
+msu_task_t *msu_task_delete_new(GDBusMethodInvocation *invocation,
+                               const gchar *path)
+{
+       msu_task_t *task;
+
+       task = prv_m2spec_task_new(MSU_TASK_DELETE_OBJECT, invocation,
+                                  path, NULL);
+       return task;
+}
+
+msu_task_t *msu_task_create_container_new_generic(
+                                       GDBusMethodInvocation *invocation,
+                                       msu_task_type_t type,
+                                       const gchar *path,
+                                       GVariant *parameters)
+{
+       msu_task_t *task;
+
+       task = prv_m2spec_task_new(type, invocation, path, "(@o)");
+
+       g_variant_get(parameters, "(ss@as)",
+                                       &task->ut.create_container.display_name,
+                                       &task->ut.create_container.type,
+                                       &task->ut.create_container.child_types);
+
+       return task;
+}
+
+msu_task_t *msu_task_update_new(GDBusMethodInvocation *invocation,
+                               const gchar *path, GVariant *parameters)
+{
+       msu_task_t *task;
+
+       task = prv_m2spec_task_new(MSU_TASK_UPDATE_OBJECT, invocation, path,
+                                  NULL);
+
+       g_variant_get(parameters, "(@a{sv}@as)",
+                     &task->ut.update.to_add_update,
+                     &task->ut.update.to_delete);
+
+       return task;
+}
+
 static void prv_msu_task_delete(msu_task_t *task)
 {
        switch (task->type) {
@@ -270,6 +373,18 @@ static void prv_msu_task_delete(msu_task_t *task)
                g_free(task->ut.upload.display_name);
                g_free(task->ut.upload.file_path);
                break;
+       case MSU_TASK_CREATE_CONTAINER:
+       case MSU_TASK_CREATE_CONTAINER_IN_ANY:
+               g_free(task->ut.create_container.display_name);
+               g_free(task->ut.create_container.type);
+               g_variant_unref(task->ut.create_container.child_types);
+               break;
+       case MSU_TASK_UPDATE_OBJECT:
+               if (task->ut.update.to_add_update)
+                       g_variant_unref(task->ut.update.to_add_update);
+               if (task->ut.update.to_delete)
+                       g_variant_unref(task->ut.update.to_delete);
+               break;
        default:
                break;
        }
index d7c13df..4eb656d 100644 (file)
@@ -34,9 +34,17 @@ enum msu_task_type_t_ {
        MSU_TASK_GET_PROP,
        MSU_TASK_SEARCH,
        MSU_TASK_GET_RESOURCE,
+       MSU_TASK_SET_PREFER_LOCAL_ADDRESSES,
        MSU_TASK_SET_PROTOCOL_INFO,
        MSU_TASK_UPLOAD_TO_ANY,
-       MSU_TASK_UPLOAD
+       MSU_TASK_UPLOAD,
+       MSU_TASK_GET_UPLOAD_STATUS,
+       MSU_TASK_GET_UPLOAD_IDS,
+       MSU_TASK_CANCEL_UPLOAD,
+       MSU_TASK_DELETE_OBJECT,
+       MSU_TASK_CREATE_CONTAINER,
+       MSU_TASK_CREATE_CONTAINER_IN_ANY,
+       MSU_TASK_UPDATE_OBJECT
 };
 typedef enum msu_task_type_t_ msu_task_type_t;
 
@@ -78,6 +86,12 @@ struct msu_task_get_resource_t_ {
        GVariant *filter;
 };
 
+typedef struct msu_task_set_prefer_local_addresses_t_
+                                       msu_task_set_prefer_local_addresses_t;
+struct msu_task_set_prefer_local_addresses_t_ {
+       gboolean prefer;
+};
+
 typedef struct msu_task_set_protocol_info_t_ msu_task_set_protocol_info_t;
 struct msu_task_set_protocol_info_t_ {
        gchar *protocol_info;
@@ -89,6 +103,24 @@ struct msu_task_upload_t_ {
        gchar *file_path;
 };
 
+typedef struct msu_task_upload_action_t_ msu_task_upload_action_t;
+struct msu_task_upload_action_t_ {
+       guint upload_id;
+};
+
+typedef struct msu_task_create_container_t_ msu_task_create_container_t;
+struct msu_task_create_container_t_ {
+       gchar *display_name;
+       gchar *type;
+       GVariant *child_types;
+};
+
+typedef struct msu_task_update_t_ msu_task_update_t;
+struct msu_task_update_t_ {
+       GVariant *to_add_update;
+       GVariant *to_delete;
+};
+
 typedef struct msu_task_t_ msu_task_t;
 struct msu_task_t_ {
        msu_task_type_t type;
@@ -104,8 +136,12 @@ struct msu_task_t_ {
                msu_task_get_prop_t get_prop;
                msu_task_search_t search;
                msu_task_get_resource_t resource;
+               msu_task_set_prefer_local_addresses_t prefer_local_addresses;
                msu_task_set_protocol_info_t protocol_info;
                msu_task_upload_t upload;
+               msu_task_upload_action_t upload_action;
+               msu_task_create_container_t create_container;
+               msu_task_update_t update;
        } ut;
 };
 
@@ -130,10 +166,30 @@ msu_task_t *msu_task_get_resource_new(GDBusMethodInvocation *invocation,
                                      const gchar *path, GVariant *parameters);
 msu_task_t *msu_task_set_protocol_info_new(GDBusMethodInvocation *invocation,
                                           GVariant *parameters);
+msu_task_t *msu_task_prefer_local_addresses_new(
+                                       GDBusMethodInvocation *invocation,
+                                       GVariant *parameters);
 msu_task_t *msu_task_upload_to_any_new(GDBusMethodInvocation *invocation,
                                       const gchar *path, GVariant *parameters);
 msu_task_t *msu_task_upload_new(GDBusMethodInvocation *invocation,
                                const gchar *path, GVariant *parameters);
+msu_task_t *msu_task_get_upload_status_new(GDBusMethodInvocation *invocation,
+                                          const gchar *path,
+                                          GVariant *parameters);
+msu_task_t *msu_task_get_upload_ids_new(GDBusMethodInvocation *invocation,
+                                       const gchar *path);
+msu_task_t *msu_task_cancel_upload_new(GDBusMethodInvocation *invocation,
+                                      const gchar *path,
+                                      GVariant *parameters);
+msu_task_t *msu_task_delete_new(GDBusMethodInvocation *invocation,
+                               const gchar *path);
+msu_task_t *msu_task_create_container_new_generic(
+                                       GDBusMethodInvocation *invocation,
+                                       msu_task_type_t type,
+                                       const gchar *path,
+                                       GVariant *parameters);
+msu_task_t *msu_task_update_new(GDBusMethodInvocation *invocation,
+                                  const gchar *path, GVariant *parameters);
 void msu_task_complete_and_delete(msu_task_t *task);
 void msu_task_fail_and_delete(msu_task_t *task, GError *error);
 void msu_task_cancel_and_delete(msu_task_t *task);
index 3f2d38f..a93a303 100644 (file)
 
 #include <string.h>
 
+#include <libgssdp/gssdp-resource-browser.h>
 #include <libgupnp/gupnp-control-point.h>
 #include <libgupnp/gupnp-error.h>
 
 #include "async.h"
+#include "chain-task.h"
 #include "device.h"
 #include "error.h"
 #include "interface.h"
@@ -39,14 +41,25 @@ struct msu_upnp_t_ {
        GDBusConnection *connection;
        msu_interface_info_t *interface_info;
        GHashTable *filter_map;
+       GHashTable *property_map;
        msu_upnp_callback_t found_server;
        msu_upnp_callback_t lost_server;
        GUPnPContextManager *context_manager;
        void *user_data;
        GHashTable *server_udn_map;
+       GHashTable *server_uc_map;
        guint counter;
 };
 
+/* Private structure used in chain task */
+typedef struct prv_device_new_ct_t_ prv_device_new_ct_t;
+struct prv_device_new_ct_t_ {
+       msu_upnp_t *upnp;
+       const char *udn;
+       msu_device_t *device;
+       msu_chain_task_t *chain;
+};
+
 static gchar **prv_subtree_enumerate(GDBusConnection *connection,
                                     const gchar *sender,
                                     const gchar *object_path,
@@ -157,6 +170,9 @@ static const GDBusInterfaceVTable *prv_subtree_dispatch(
        if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, interface_name))
                retval = upnp->interface_info[
                        MSU_INTERFACE_INFO_CONTAINER].vtable;
+       else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT, interface_name))
+               retval = upnp->interface_info[
+                       MSU_INTERFACE_INFO_OBJECT].vtable;
        else if (!strcmp(MSU_INTERFACE_PROPERTIES, interface_name))
                retval = upnp->interface_info[
                        MSU_INTERFACE_INFO_PROPERTIES].vtable;
@@ -171,6 +187,34 @@ static const GDBusInterfaceVTable *prv_subtree_dispatch(
        return retval;
 }
 
+static void prv_device_chain_end(msu_chain_task_t *chain, gpointer data)
+{
+       msu_device_t *device;
+       gboolean canceled;
+       prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *) data;
+
+       device = msu_chain_task_get_device(chain);
+       canceled = msu_chain_task_is_canceled(chain);
+       if (canceled)
+               goto on_clear;
+
+       MSU_LOG_DEBUG("Notify new server available: %s", device->path);
+       g_hash_table_insert(priv_t->upnp->server_udn_map, g_strdup(priv_t->udn),
+                           device);
+       priv_t->upnp->found_server(device->path, priv_t->upnp->user_data);
+
+on_clear:
+
+       g_hash_table_remove(priv_t->upnp->server_uc_map, priv_t->udn);
+       msu_chain_task_delete(chain);
+       g_free(priv_t);
+
+       if (canceled)
+               msu_device_delete(device);
+
+       MSU_LOG_DEBUG_NL();
+}
+
 static void prv_server_available_cb(GUPnPControlPoint *cp,
                                    GUPnPDeviceProxy *proxy,
                                    gpointer user_data)
@@ -180,9 +224,9 @@ static void prv_server_available_cb(GUPnPControlPoint *cp,
        msu_device_t *device;
        const gchar *ip_address;
        msu_device_context_t *context;
+       msu_chain_task_t *chain;
        unsigned int i;
-
-       MSU_LOG_DEBUG("Enter");
+       prv_device_new_ct_t *priv_t;
 
        udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *) proxy);
        if (!udn)
@@ -197,16 +241,31 @@ static void prv_server_available_cb(GUPnPControlPoint *cp,
        device = g_hash_table_lookup(upnp->server_udn_map, udn);
 
        if (!device) {
+               priv_t = g_hash_table_lookup(upnp->server_uc_map, udn);
+
+               if (priv_t)
+                       device = priv_t->device;
+       }
+
+       if (!device) {
                MSU_LOG_DEBUG("Device not found. Adding");
+               MSU_LOG_DEBUG_NL();
 
-               if (msu_device_new(upnp->connection, proxy,
-                                  ip_address, &gSubtreeVtable, upnp,
-                                  upnp->counter, &device)) {
-                       upnp->counter++;
-                       g_hash_table_insert(upnp->server_udn_map, g_strdup(udn),
-                                           device);
-                       upnp->found_server(device->path, upnp->user_data);
-               }
+               priv_t = g_new0(prv_device_new_ct_t, 1);
+               chain = msu_chain_task_new(prv_device_chain_end, priv_t);
+               device = msu_device_new(upnp->connection, proxy, ip_address,
+                                       &gSubtreeVtable, upnp,
+                                       upnp->property_map, upnp->counter,
+                                       chain);
+
+               upnp->counter++;
+
+               priv_t->upnp = upnp;
+               priv_t->udn = udn;
+               priv_t->chain = chain;
+               priv_t->device = device;
+
+               g_hash_table_insert(upnp->server_uc_map, g_strdup(udn), priv_t);
        } else {
                MSU_LOG_DEBUG("Device Found");
 
@@ -221,13 +280,12 @@ static void prv_server_available_cb(GUPnPControlPoint *cp,
                        msu_device_append_new_context(device, ip_address,
                                                      proxy);
                }
+
+               MSU_LOG_DEBUG_NL();
        }
 
 on_error:
 
-       MSU_LOG_DEBUG("Exit");
-       MSU_LOG_DEBUG_NL();
-
        return;
 }
 
@@ -252,6 +310,8 @@ static void prv_server_unavailable_cb(GUPnPControlPoint *cp,
        unsigned int i;
        msu_device_context_t *context;
        gboolean subscribed;
+       gboolean under_construction = FALSE;
+       prv_device_new_ct_t *priv_t;
 
        MSU_LOG_DEBUG("Enter");
 
@@ -266,6 +326,16 @@ static void prv_server_unavailable_cb(GUPnPControlPoint *cp,
        MSU_LOG_DEBUG("IP Address %s", ip_address);
 
        device = g_hash_table_lookup(upnp->server_udn_map, udn);
+
+       if (!device) {
+               priv_t = g_hash_table_lookup(upnp->server_uc_map, udn);
+
+               if (priv_t) {
+                       device = priv_t->device;
+                       under_construction = TRUE;
+               }
+       }
+
        if (!device) {
                MSU_LOG_WARNING("Device not found. Ignoring");
                goto on_error;
@@ -282,9 +352,20 @@ static void prv_server_unavailable_cb(GUPnPControlPoint *cp,
 
                (void) g_ptr_array_remove_index(device->contexts, i);
                if (device->contexts->len == 0) {
-                       MSU_LOG_DEBUG("Last Context lost. Delete device");
-                       upnp->lost_server(device->path, upnp->user_data);
-                       g_hash_table_remove(upnp->server_udn_map, udn);
+                       if (!under_construction) {
+                               MSU_LOG_DEBUG("Last Context lost. " \
+                                              "Delete device");
+
+                               upnp->lost_server(device->path,
+                                                 upnp->user_data);
+                               g_hash_table_remove(upnp->server_udn_map, udn);
+                       } else {
+                               MSU_LOG_WARNING("Device under construction. "\
+                                                "Cancelling");
+
+                               msu_chain_task_cancel(priv_t->chain);
+                               prv_device_chain_end(priv_t->chain, priv_t);
+                       }
                } else if (subscribed && !device->timeout_id) {
 
                        MSU_LOG_DEBUG("Subscribe on new context");
@@ -342,7 +423,12 @@ msu_upnp_t *msu_upnp_new(GDBusConnection *connection,
        upnp->server_udn_map = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                     g_free,
                                                     msu_device_delete);
-       upnp->filter_map = msu_prop_maps_new();
+
+       upnp->server_uc_map = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                    g_free, NULL);
+
+       msu_prop_maps_new(&upnp->property_map, &upnp->filter_map);
+
        upnp->context_manager = gupnp_context_manager_create(0);
 
        g_signal_connect(upnp->context_manager, "context-available",
@@ -356,8 +442,10 @@ void msu_upnp_delete(msu_upnp_t *upnp)
 {
        if (upnp) {
                g_object_unref(upnp->context_manager);
+               g_hash_table_unref(upnp->property_map);
                g_hash_table_unref(upnp->filter_map);
                g_hash_table_unref(upnp->server_udn_map);
+               g_hash_table_unref(upnp->server_uc_map);
                g_free(upnp->interface_info);
                g_free(upnp);
        }
@@ -389,11 +477,10 @@ GVariant *msu_upnp_get_server_ids(msu_upnp_t *upnp)
        return retval;
 }
 
-void msu_upnp_get_children(msu_upnp_t *upnp, msu_task_t *task,
-                          const gchar *protocol_info,
+void msu_upnp_get_children(msu_upnp_t *upnp, msu_client_t *client,
+                          msu_task_t *task,
                           GCancellable *cancellable,
-                          msu_upnp_task_complete_t cb,
-                          void *user_data)
+                          msu_upnp_task_complete_t cb)
 {
        msu_async_cb_data_t *cb_data;
        msu_async_bas_t *cb_task_data;
@@ -407,7 +494,7 @@ void msu_upnp_get_children(msu_upnp_t *upnp, msu_task_t *task,
        MSU_LOG_DEBUG("Start: %u", task->ut.get_children.start);
        MSU_LOG_DEBUG("Count: %u", task->ut.get_children.count);
 
-       cb_data = msu_async_cb_data_new(task, cb, user_data);
+       cb_data = msu_async_cb_data_new(task, cb);
        cb_task_data = &cb_data->ut.bas;
 
        if (!msu_path_get_path_and_id(task->path, &cb_task_data->root_path,
@@ -449,9 +536,9 @@ void msu_upnp_get_children(msu_upnp_t *upnp, msu_task_t *task,
 
        MSU_LOG_DEBUG("Sort By %s", sort_by);
 
-       cb_task_data->protocol_info = protocol_info;
+       cb_task_data->protocol_info = client->protocol_info;
 
-       msu_device_get_children(device, task, cb_data,
+       msu_device_get_children(device, client, task, cb_data,
                                upnp_filter, sort_by, cancellable);
 
 on_error:
@@ -465,11 +552,10 @@ on_error:
        MSU_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
 }
 
-void msu_upnp_get_all_props(msu_upnp_t *upnp, msu_task_t *task,
-                           const gchar *protocol_info,
+void msu_upnp_get_all_props(msu_upnp_t *upnp, msu_client_t *client,
+                           msu_task_t *task,
                            GCancellable *cancellable,
-                           msu_upnp_task_complete_t cb,
-                           void *user_data)
+                           msu_upnp_task_complete_t cb)
 {
        gboolean root_object;
        msu_async_cb_data_t *cb_data;
@@ -481,7 +567,7 @@ void msu_upnp_get_all_props(msu_upnp_t *upnp, msu_task_t *task,
        MSU_LOG_DEBUG("Path: %s", task->path);
        MSU_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name);
 
-       cb_data = msu_async_cb_data_new(task, cb, user_data);
+       cb_data = msu_async_cb_data_new(task, cb);
        cb_task_data = &cb_data->ut.get_all;
 
        if (!msu_path_get_path_and_id(task->path, &cb_task_data->root_path,
@@ -508,9 +594,9 @@ void msu_upnp_get_all_props(msu_upnp_t *upnp, msu_task_t *task,
                goto on_error;
        }
 
-       cb_task_data->protocol_info = protocol_info;
+       cb_task_data->protocol_info = client->protocol_info;
 
-       msu_device_get_all_props(device, task, cb_data, root_object,
+       msu_device_get_all_props(device, client, task, cb_data, root_object,
                                 cancellable);
 
        MSU_LOG_DEBUG("Exit with SUCCESS");
@@ -524,11 +610,10 @@ on_error:
        MSU_LOG_DEBUG("Exit with FAIL");
 }
 
-void msu_upnp_get_prop(msu_upnp_t *upnp, msu_task_t *task,
-                      const gchar *protocol_info,
+void msu_upnp_get_prop(msu_upnp_t *upnp, msu_client_t *client,
+                      msu_task_t *task,
                       GCancellable *cancellable,
-                      msu_upnp_task_complete_t cb,
-                      void *user_data)
+                      msu_upnp_task_complete_t cb)
 {
        gboolean root_object;
        msu_async_cb_data_t *cb_data;
@@ -544,7 +629,7 @@ void msu_upnp_get_prop(msu_upnp_t *upnp, msu_task_t *task,
        MSU_LOG_DEBUG("Prop.%s", task->ut.get_prop.prop_name);
 
        task_data = &task->ut.get_prop;
-       cb_data = msu_async_cb_data_new(task, cb, user_data);
+       cb_data = msu_async_cb_data_new(task, cb);
        cb_task_data = &cb_data->ut.get_prop;
 
        if (!msu_path_get_path_and_id(task->path, &cb_task_data->root_path,
@@ -572,10 +657,10 @@ void msu_upnp_get_prop(msu_upnp_t *upnp, msu_task_t *task,
                goto on_error;
        }
 
-       cb_task_data->protocol_info = protocol_info;
+       cb_task_data->protocol_info = client->protocol_info;
        prop_map = g_hash_table_lookup(upnp->filter_map, task_data->prop_name);
 
-       msu_device_get_prop(device, task, cb_data, prop_map,
+       msu_device_get_prop(device, client, task, cb_data, prop_map,
                            root_object, cancellable);
 
        MSU_LOG_DEBUG("Exit with SUCCESS");
@@ -589,11 +674,10 @@ on_error:
        MSU_LOG_DEBUG("Exit with FAIL");
 }
 
-void msu_upnp_search(msu_upnp_t *upnp, msu_task_t *task,
-                    const gchar *protocol_info,
+void msu_upnp_search(msu_upnp_t *upnp, msu_client_t *client,
+                    msu_task_t *task,
                     GCancellable *cancellable,
-                    msu_upnp_task_complete_t cb,
-                    void *user_data)
+                    msu_upnp_task_complete_t cb)
 {
        gchar *upnp_filter = NULL;
        gchar *upnp_query = NULL;
@@ -609,7 +693,7 @@ void msu_upnp_search(msu_upnp_t *upnp, msu_task_t *task,
        MSU_LOG_DEBUG("Start: %u", task->ut.search.start);
        MSU_LOG_DEBUG("Count: %u", task->ut.search.count);
 
-       cb_data = msu_async_cb_data_new(task, cb, user_data);
+       cb_data = msu_async_cb_data_new(task, cb);
        cb_task_data = &cb_data->ut.bas;
 
        if (!msu_path_get_path_and_id(task->path, &cb_task_data->root_path,
@@ -663,9 +747,9 @@ void msu_upnp_search(msu_upnp_t *upnp, msu_task_t *task,
 
        MSU_LOG_DEBUG("Sort By %s", sort_by);
 
-       cb_task_data->protocol_info = protocol_info;
+       cb_task_data->protocol_info = client->protocol_info;
 
-       msu_device_search(device, task, cb_data, upnp_filter,
+       msu_device_search(device, client, task, cb_data, upnp_filter,
                          upnp_query, sort_by, cancellable);
 on_error:
 
@@ -679,10 +763,10 @@ on_error:
        MSU_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
 }
 
-void msu_upnp_get_resource(msu_upnp_t *upnp, msu_task_t *task,
+void msu_upnp_get_resource(msu_upnp_t *upnp, msu_client_t *client,
+                          msu_task_t *task,
                           GCancellable *cancellable,
-                          msu_upnp_task_complete_t cb,
-                          void *user_data)
+                          msu_upnp_task_complete_t cb)
 {
        msu_async_cb_data_t *cb_data;
        msu_async_get_all_t *cb_task_data;
@@ -694,7 +778,7 @@ void msu_upnp_get_resource(msu_upnp_t *upnp, msu_task_t *task,
 
        MSU_LOG_DEBUG("Protocol Info: %s ", task->ut.resource.protocol_info);
 
-       cb_data = msu_async_cb_data_new(task, cb, user_data);
+       cb_data = msu_async_cb_data_new(task, cb);
        cb_task_data = &cb_data->ut.get_all;
 
        if (!msu_path_get_path_and_id(task->path, &root_path, &cb_data->id,
@@ -723,7 +807,7 @@ void msu_upnp_get_resource(msu_upnp_t *upnp, msu_task_t *task,
 
        MSU_LOG_DEBUG("Filter Mask 0x%x", cb_task_data->filter_mask);
 
-       msu_device_get_resource(device, task, cb_data, upnp_filter,
+       msu_device_get_resource(device, client, task, cb_data, upnp_filter,
                                cancellable);
 
 on_error:
@@ -808,10 +892,10 @@ on_error:
        return FALSE;
 }
 
-void msu_upnp_upload_to_any(msu_upnp_t *upnp, msu_task_t *task,
+void msu_upnp_upload_to_any(msu_upnp_t *upnp, msu_client_t *client,
+                           msu_task_t *task,
                            GCancellable *cancellable,
-                           msu_upnp_task_complete_t cb,
-                           void *user_data)
+                           msu_upnp_task_complete_t cb)
 {
        msu_async_cb_data_t *cb_data;
        msu_async_upload_t *cb_task_data;
@@ -819,7 +903,7 @@ void msu_upnp_upload_to_any(msu_upnp_t *upnp, msu_task_t *task,
 
        MSU_LOG_DEBUG("Enter");
 
-       cb_data = msu_async_cb_data_new(task, cb, user_data);
+       cb_data = msu_async_cb_data_new(task, cb);
        cb_task_data = &cb_data->ut.upload;
 
        if (!msu_path_get_path_and_id(task->path, &cb_task_data->root_path,
@@ -861,8 +945,8 @@ void msu_upnp_upload_to_any(msu_upnp_t *upnp, msu_task_t *task,
        MSU_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type);
        MSU_LOG_DEBUG("Object class %s", cb_task_data->object_class);
 
-       msu_device_upload(device, task, "DLNA.ORG_AnyContainer", cb_data,
-                         cancellable);
+       msu_device_upload(device, client, task, "DLNA.ORG_AnyContainer",
+                         cb_data, cancellable);
 
 on_error:
 
@@ -872,10 +956,9 @@ on_error:
        MSU_LOG_DEBUG("Exit");
 }
 
-void msu_upnp_upload(msu_upnp_t *upnp, msu_task_t *task,
+void msu_upnp_upload(msu_upnp_t *upnp, msu_client_t *client, msu_task_t *task,
                     GCancellable *cancellable,
-                    msu_upnp_task_complete_t cb,
-                    void *user_data)
+                    msu_upnp_task_complete_t cb)
 {
        msu_async_cb_data_t *cb_data;
        msu_async_upload_t *cb_task_data;
@@ -883,7 +966,7 @@ void msu_upnp_upload(msu_upnp_t *upnp, msu_task_t *task,
 
        MSU_LOG_DEBUG("Enter");
 
-       cb_data = msu_async_cb_data_new(task, cb, user_data);
+       cb_data = msu_async_cb_data_new(task, cb);
        cb_task_data = &cb_data->ut.upload;
 
        if (!msu_path_get_path_and_id(task->path, &cb_task_data->root_path,
@@ -912,7 +995,265 @@ void msu_upnp_upload(msu_upnp_t *upnp, msu_task_t *task,
        MSU_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type);
        MSU_LOG_DEBUG("Object class %s", cb_task_data->object_class);
 
-       msu_device_upload(device, task, cb_data->id, cb_data, cancellable);
+       msu_device_upload(device, client, task, cb_data->id, cb_data,
+                         cancellable);
+
+on_error:
+
+       if (!cb_data->action)
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_upnp_get_upload_status(msu_upnp_t *upnp, msu_task_t *task)
+{
+       gchar *root_path = NULL;
+       gchar *id = NULL;
+       GError *error = NULL;
+       msu_device_t *device;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (!msu_path_get_path_and_id(task->path, &root_path, &id, &error)) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("Root Path %s Id %s", root_path, id);
+
+       device = msu_device_from_path(root_path, upnp->server_udn_map);
+       if (!device) {
+               MSU_LOG_WARNING("Cannot locate device for %s",
+                               root_path);
+
+               error = g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                   "Cannot locate device corresponding to"
+                                   " the specified path");
+               goto on_error;
+       }
+
+       if (strcmp(id, "0")) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               error = g_error_new(MSU_ERROR, MSU_ERROR_BAD_PATH,
+                                   "GetUploadStatus must be executed "
+                                   " on a root path");
+               goto on_error;
+       }
+
+       (void) msu_device_get_upload_status(device, task, &error);
+
+on_error:
+
+       if (error) {
+               msu_task_fail_and_delete(task, error);
+               g_error_free(error);
+       } else {
+               msu_task_complete_and_delete(task);
+       }
+
+       g_free(id);
+       g_free(root_path);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_upnp_get_upload_ids(msu_upnp_t *upnp, msu_task_t *task)
+{
+       gchar *root_path = NULL;
+       gchar *id = NULL;
+       GError *error = NULL;
+       msu_device_t *device;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (!msu_path_get_path_and_id(task->path, &root_path, &id, &error)) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("Root Path %s Id %s", root_path, id);
+
+       device = msu_device_from_path(root_path, upnp->server_udn_map);
+       if (!device) {
+               MSU_LOG_WARNING("Cannot locate device for %s",
+                               root_path);
+
+               error = g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                   "Cannot locate device corresponding to"
+                                   " the specified path");
+               goto on_error;
+       }
+
+       if (strcmp(id, "0")) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               error = g_error_new(MSU_ERROR, MSU_ERROR_BAD_PATH,
+                                   "GetUploadIDs must be executed "
+                                   " on a root path");
+               goto on_error;
+       }
+
+        msu_device_get_upload_ids(device, task);
+
+on_error:
+
+       if (error) {
+               msu_task_fail_and_delete(task, error);
+               g_error_free(error);
+       } else {
+               msu_task_complete_and_delete(task);
+       }
+
+       g_free(id);
+       g_free(root_path);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_upnp_cancel_upload(msu_upnp_t *upnp, msu_task_t *task)
+{
+       gchar *root_path = NULL;
+       gchar *id = NULL;
+       GError *error = NULL;
+       msu_device_t *device;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (!msu_path_get_path_and_id(task->path, &root_path, &id, &error)) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("Root Path %s Id %s", root_path, id);
+
+       device = msu_device_from_path(root_path, upnp->server_udn_map);
+       if (!device) {
+               MSU_LOG_WARNING("Cannot locate device for %s",
+                               root_path);
+
+               error = g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                   "Cannot locate device corresponding to"
+                                   " the specified path");
+               goto on_error;
+       }
+
+       if (strcmp(id, "0")) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               error = g_error_new(MSU_ERROR, MSU_ERROR_BAD_PATH,
+                                   "CancelUpload must be executed "
+                                   " on a root path");
+               goto on_error;
+       }
+
+       (void) msu_device_cancel_upload(device, task, &error);
+
+on_error:
+
+       if (error) {
+               msu_task_fail_and_delete(task, error);
+               g_error_free(error);
+       } else {
+               msu_task_complete_and_delete(task);
+       }
+
+       g_free(id);
+       g_free(root_path);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_upnp_delete_object(msu_upnp_t *upnp, msu_client_t *client,
+                           msu_task_t *task,
+                           GCancellable *cancellable,
+                           msu_upnp_task_complete_t cb)
+{
+       msu_async_cb_data_t *cb_data;
+       msu_device_t *device;
+       gchar *root_path = NULL;
+
+       MSU_LOG_DEBUG("Enter");
+
+       cb_data = msu_async_cb_data_new(task, cb);
+
+       if (!msu_path_get_path_and_id(task->path, &root_path,
+                                     &cb_data->id, &cb_data->error)) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("Root Path %s Id %s", root_path,
+                     cb_data->id);
+
+       device = msu_device_from_path(root_path, upnp->server_udn_map);
+       if (!device) {
+               MSU_LOG_WARNING("Cannot locate device for %s",
+                               root_path);
+
+               cb_data->error =
+                       g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                   "Cannot locate device corresponding to"
+                                   " the specified path");
+               goto on_error;
+       }
+
+       msu_device_delete_object(device, client, task, cb_data, cancellable);
+
+on_error:
+
+       if (root_path)
+               g_free(root_path);
+       if (!cb_data->action)
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_upnp_create_container(msu_upnp_t *upnp, msu_client_t *client,
+                              msu_task_t *task,
+                              GCancellable *cancellable,
+                              msu_upnp_task_complete_t cb)
+{
+       msu_async_cb_data_t *cb_data;
+       msu_async_create_container_t *cb_task_data;
+       msu_device_t *device;
+
+       MSU_LOG_DEBUG("Enter");
+
+       cb_data = msu_async_cb_data_new(task, cb);
+       cb_task_data = &cb_data->ut.create_container;
+
+       if (!msu_path_get_path_and_id(task->path, &cb_task_data->root_path,
+                                     &cb_data->id, &cb_data->error)) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("Root Path %s Id %s", cb_task_data->root_path,
+                     cb_data->id);
+
+       device = msu_device_from_path(cb_task_data->root_path,
+                                                       upnp->server_udn_map);
+       if (!device) {
+               MSU_LOG_WARNING("Cannot locate device for %s",
+                                               cb_task_data->root_path);
+
+               cb_data->error =
+                       g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                   "Cannot locate device corresponding to"
+                                   " the specified path");
+               goto on_error;
+       }
+
+       msu_device_create_container(device, client, task, cb_data->id,
+                                   cb_data, cancellable);
 
 on_error:
 
@@ -921,3 +1262,144 @@ on_error:
 
        MSU_LOG_DEBUG("Exit");
 }
+
+void msu_upnp_create_container_in_any(msu_upnp_t *upnp, msu_client_t *client,
+                                     msu_task_t *task,
+                                     GCancellable *cancellable,
+                                     msu_upnp_task_complete_t cb)
+{
+       msu_async_cb_data_t *cb_data;
+       msu_async_create_container_t *cb_task_data;
+       msu_device_t *device;
+
+       MSU_LOG_DEBUG("Enter");
+
+       cb_data = msu_async_cb_data_new(task, cb);
+       cb_task_data = &cb_data->ut.create_container;
+
+       if (!msu_path_get_path_and_id(task->path, &cb_task_data->root_path,
+                                     &cb_data->id, &cb_data->error)) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("Root Path %s Id %s", cb_task_data->root_path,
+                     cb_data->id);
+
+       if (strcmp(cb_data->id, "0")) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               cb_data->error =
+                       g_error_new(MSU_ERROR, MSU_ERROR_BAD_PATH,
+                                   "CreateContainerInAnyContainer must be "
+                                   "executed on a root path");
+               goto on_error;
+       }
+
+       device = msu_device_from_path(cb_task_data->root_path,
+                                                       upnp->server_udn_map);
+       if (!device) {
+               MSU_LOG_WARNING("Cannot locate device for %s",
+                                               cb_task_data->root_path);
+
+               cb_data->error =
+                       g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                   "Cannot locate device corresponding to"
+                                   " the specified path");
+               goto on_error;
+       }
+
+       msu_device_create_container(device, client, task,
+                                   "DLNA.ORG_AnyContainer",
+                                   cb_data, cancellable);
+
+on_error:
+
+       if (!cb_data->action)
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_upnp_update_object(msu_upnp_t *upnp, msu_client_t *client,
+                           msu_task_t *task,
+                           GCancellable *cancellable,
+                           msu_upnp_task_complete_t cb)
+{
+       msu_async_cb_data_t *cb_data;
+       msu_async_update_t *cb_task_data;
+       msu_device_t *device;
+       guint32 mask;
+       gchar *root_path = NULL;
+       gchar *upnp_filter = NULL;
+       msu_task_update_t *task_data;
+
+       MSU_LOG_DEBUG("Enter");
+
+       cb_data = msu_async_cb_data_new(task, cb);
+       cb_task_data = &cb_data->ut.update;
+       task_data = &task->ut.update;
+
+       if (!msu_path_get_path_and_id(task->path, &root_path,
+                                     &cb_data->id, &cb_data->error)) {
+               MSU_LOG_WARNING("Bad path %s", task->path);
+
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("Root Path = %s, Id = %s", root_path, cb_data->id);
+
+       device = msu_device_from_path(root_path, upnp->server_udn_map);
+       if (!device) {
+               MSU_LOG_WARNING("Cannot locate device for %s", root_path);
+
+               cb_data->error =
+                       g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                   "Cannot locate device corresponding to"
+                                   " the specified path");
+               goto on_error;
+       }
+
+       if (!msu_props_parse_update_filter(upnp->filter_map,
+                                          task_data->to_add_update,
+                                          task_data->to_delete,
+                                          &mask, &upnp_filter)) {
+               MSU_LOG_WARNING("Invalid Parameter");
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Invalid Parameter");
+               goto on_error;
+       }
+
+       cb_task_data->map = upnp->filter_map;
+
+       MSU_LOG_DEBUG("Filter = %s", upnp_filter);
+       MSU_LOG_DEBUG("Mask = 0x%x", mask);
+
+       if (mask == 0) {
+               MSU_LOG_WARNING("Empty Parameters");
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Empty Parameters");
+
+               goto on_error;
+       }
+
+       msu_device_update_object(device, client, task, cb_data, upnp_filter,
+                                cancellable);
+
+on_error:
+
+       if (root_path)
+               g_free(root_path);
+
+       g_free(upnp_filter);
+
+       if (!cb_data->action)
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+
+       MSU_LOG_DEBUG("Exit");
+}
index 3f34d62..7e8ae08 100644 (file)
@@ -23,6 +23,7 @@
 #ifndef MSU_UPNP_H__
 #define MSU_UPNP_H__
 
+#include "client.h"
 #include "task.h"
 
 typedef struct msu_upnp_t_ msu_upnp_t;
@@ -44,7 +45,7 @@ struct msu_interface_info_t_ {
 
 typedef void (*msu_upnp_callback_t)(const gchar *path, void *user_data);
 typedef void (*msu_upnp_task_complete_t)(msu_task_t *task, GVariant *result,
-                                        GError *error, void *user_data);
+                                        GError *error);
 
 msu_upnp_t *msu_upnp_new(GDBusConnection *connection,
                         msu_interface_info_t *interface_info,
@@ -53,38 +54,51 @@ msu_upnp_t *msu_upnp_new(GDBusConnection *connection,
                         void *user_data);
 void msu_upnp_delete(msu_upnp_t *upnp);
 GVariant *msu_upnp_get_server_ids(msu_upnp_t *upnp);
-void msu_upnp_get_children(msu_upnp_t *upnp, msu_task_t *task,
-                          const gchar *protocol_info,
+void msu_upnp_get_children(msu_upnp_t *upnp, msu_client_t *client,
+                          msu_task_t *task,
                           GCancellable *cancellable,
-                          msu_upnp_task_complete_t cb,
-                          void *user_data);
-void msu_upnp_get_all_props(msu_upnp_t *upnp, msu_task_t *task,
-                           const gchar *protocol_info,
+                          msu_upnp_task_complete_t cb);
+void msu_upnp_get_all_props(msu_upnp_t *upnp, msu_client_t *client,
+                           msu_task_t *task,
                            GCancellable *cancellable,
-                           msu_upnp_task_complete_t cb,
-                           void *user_data);
-void msu_upnp_get_prop(msu_upnp_t *upnp, msu_task_t *task,
-                      const gchar *protocol_info,
+                           msu_upnp_task_complete_t cb);
+void msu_upnp_get_prop(msu_upnp_t *upnp, msu_client_t *client,
+                      msu_task_t *task,
                       GCancellable *cancellable,
-                      msu_upnp_task_complete_t cb,
-                      void *user_data);
-void msu_upnp_search(msu_upnp_t *upnp, msu_task_t *task,
-                    const gchar *protocol_info,
+                      msu_upnp_task_complete_t cb);
+void msu_upnp_search(msu_upnp_t *upnp, msu_client_t *client,
+                    msu_task_t *task,
                     GCancellable *cancellable,
-                    msu_upnp_task_complete_t cb,
-                    void *user_data);
-void msu_upnp_get_resource(msu_upnp_t *upnp, msu_task_t *task,
+                    msu_upnp_task_complete_t cb);
+void msu_upnp_get_resource(msu_upnp_t *upnp, msu_client_t *client,
+                          msu_task_t *task,
                           GCancellable *cancellable,
-                          msu_upnp_task_complete_t cb,
-                          void *user_data);
-void msu_upnp_upload_to_any(msu_upnp_t *upnp, msu_task_t *task,
+                          msu_upnp_task_complete_t cb);
+void msu_upnp_upload_to_any(msu_upnp_t *upnp, msu_client_t *client,
+                           msu_task_t *task,
                            GCancellable *cancellable,
-                           msu_upnp_task_complete_t cb,
-                           void *user_data);
-void msu_upnp_upload(msu_upnp_t *upnp, msu_task_t *task,
+                           msu_upnp_task_complete_t cb);
+void msu_upnp_upload(msu_upnp_t *upnp, msu_client_t *client,
+                    msu_task_t *task,
                     GCancellable *cancellable,
-                    msu_upnp_task_complete_t cb,
-                    void *user_data);
-
-
+                    msu_upnp_task_complete_t cb);
+void msu_upnp_get_upload_status(msu_upnp_t *upnp, msu_task_t *task);
+void msu_upnp_get_upload_ids(msu_upnp_t *upnp, msu_task_t *task);
+void msu_upnp_cancel_upload(msu_upnp_t *upnp, msu_task_t *task);
+void msu_upnp_delete_object(msu_upnp_t *upnp, msu_client_t *client,
+                           msu_task_t *task,
+                           GCancellable *cancellable,
+                           msu_upnp_task_complete_t cb);
+void msu_upnp_create_container(msu_upnp_t *upnp, msu_client_t *client,
+                              msu_task_t *task,
+                              GCancellable *cancellable,
+                              msu_upnp_task_complete_t cb);
+void msu_upnp_create_container_in_any(msu_upnp_t *upnp, msu_client_t *client,
+                                     msu_task_t *task,
+                                     GCancellable *cancellable,
+                                     msu_upnp_task_complete_t cb);
+void msu_upnp_update_object(msu_upnp_t *upnp, msu_client_t *client,
+                           msu_task_t *task,
+                           GCancellable *cancellable,
+                           msu_upnp_task_complete_t cb);
 #endif
index 9993750..cd05e6d 100644 (file)
@@ -32,6 +32,10 @@ class MediaObject(object):
         self.__propsIF = dbus.Interface(bus.get_object(
                 'com.intel.media-service-upnp', path),
                                         'org.freedesktop.DBus.Properties')
+        self.__objIF = dbus.Interface(bus.get_object(
+        'com.intel.media-service-upnp', path),
+                                'org.gnome.UPnP.MediaObject2')
+
     def get_props(self, iface = ""):
         return self.__propsIF.GetAll(iface)
 
@@ -54,6 +58,12 @@ class MediaObject(object):
             i = i + 2
         print
 
+    def delete(self):
+        return self.__objIF.Delete()
+
+    def update(self, to_add_update, to_delete):
+        return self.__objIF.Update(to_add_update, to_delete)
+
 class Item(MediaObject):
     def __init__(self, path):
         MediaObject.__init__(self, path)
@@ -116,6 +126,10 @@ class Container(MediaObject):
         print "Transfer ID: " + str(tid)
         print "Path: " + path
 
+    def create_container(self, name, type, child_types):
+        path = self.__containerIF.CreateContainer(name, type, child_types)
+        print "New container path: " + path
+
 class Device(Container):
 
     def __init__(self, path):
@@ -130,6 +144,24 @@ class Device(Container):
         print "Transfer ID: " + str(tid)
         print "Path: " + path
 
+    def create_container_in_any(self, name, type, child_types):
+        path = self.__deviceIF.CreateContainerInAnyContainer(name, type,
+                                                                    child_types)
+        print "New container path: " + path
+
+    def get_upload_status(self, id):
+        (status, length, total) = self.__deviceIF.GetUploadStatus(id)
+        print "Status: " + status
+        print "Length: " + str(length)
+        print "Total: " + str(total)
+
+    def get_upload_ids(self):
+        upload_ids  = self.__deviceIF.GetUploadIDs()
+        print_properties(upload_ids)
+
+    def cancel_upload(self, id):
+        self.__deviceIF.CancelUpload(id)
+
 class UPNP(object):
 
     def __init__(self):
@@ -158,3 +190,5 @@ class UPNP(object):
     def set_protocol_info(self, protocol_info):
         self.__manager.SetProtocolInfo(protocol_info)
 
+    def prefer_local_addresses(self, prefer):
+        self.__manager.PreferLocalAddresses(prefer)