code:
From be0a01bdb83395d9f3a5ea09c1308a4f1a972cbd Mon Sep 17 00:00:00 2001
From: Matthew Garrett <mjg59@srcf.ucam.org>
Date: Tue, 23 Feb 2016 13:53:20 -0800
Subject: [PATCH] Don't allow unhandled POSTs to write to the filesystem by
 default

If there's no registered handler for a POST request, the default behaviour
is to write it to the filesystem. Several million deployed devices appear
to have this behaviour, making it possible to (at least) store arbitrary
data on them. Add a configure option that enables this behaviour, and change
the default to just drop POSTs that aren't directly handled.
---
 configure.ac                         | 4 ++++
 upnp/inc/upnpconfig.h.in             | 5 +++++
 upnp/src/genlib/net/http/webserver.c | 4 ++++
 3 files changed, 13 insertions(+)

diff --git a/configure.ac b/configure.ac
index dd88734..ea2bc09 100644
--- a/configure.ac
+++ b/configure.ac
@@ -482,6 +482,10 @@ if test "x$enable_scriptsupport" = xyes ; then
         AC_DEFINE(IXML_HAVE_SCRIPTSUPPORT, 1, [see upnpconfig.h])
 fi

+RT_BOOL_ARG_ENABLE([postwrite], [no], [write to the filesystem on otherwise unhandled POST requests])
+if test "x$enable_postwrite" = xyes ; then
+        AC_DEFINE(UPNP_ENABLE_POST_WRITE, 1, [see upnpconfig.h])
+fi

 RT_BOOL_ARG_ENABLE([samples], [yes], [compilation of upnp/sample/ code])

diff --git a/upnp/inc/upnpconfig.h.in b/upnp/inc/upnpconfig.h.in
index 46ddc6e..5df8c5a 100644
--- a/upnp/inc/upnpconfig.h.in
+++ b/upnp/inc/upnpconfig.h.in
@@ -135,5 +135,10 @@
  *  (i.e. configure --enable-open_ssl) */
 #undef UPNP_ENABLE_OPEN_SSL

+/** Defined to 1 if the library has been compiled to support filesystem writes on POST
+ *  (i.e. configure --enable-postwrite) */
+#undef UPNP_ENABLE_POST_WRITE
+
+
 #endif /* UPNP_CONFIG_H */

diff --git a/upnp/src/genlib/net/http/webserver.c b/upnp/src/genlib/net/http/webserver.c
index 8991c16..8b2ecf2 100644
--- a/upnp/src/genlib/net/http/webserver.c
+++ b/upnp/src/genlib/net/http/webserver.c
@@ -1369,9 +1369,13 @@ static int http_RecvPostMessage(
 		if (Fp == NULL)
 			return HTTP_INTERNAL_SERVER_ERROR;
 	} else {
+#ifdef UPNP_ENABLE_POST_WRITE
 		Fp = fopen(filename, "wb");
 		if (Fp == NULL)
 			return HTTP_UNAUTHORIZED;
+#else
+		return HTTP_NOT_FOUND;
+#endif
 	}
 	parser->position = POS_ENTITY;
 	do {


code:
Index: libupnp-1.6.6/m4/acx_pthread.m4
===================================================================
--- libupnp-1.6.6.orig/m4/acx_pthread.m4
+++ libupnp-1.6.6/m4/acx_pthread.m4
@@ -146,7 +146,7 @@ acx_pthread_flags="pthreads none -Kthrea
 # pthread-config: use pthread-config program (for GNU Pth library)

 case "${host_cpu}-${host_os}" in
-        *solaris*)
+        *solaris*|*linux*)

         # On Solaris (at least, for some versions), libc contains stubbed
         # (non-functional) versions of the pthreads routines, so link-based

repo_name: ayhanyalcinsoy/main


code:
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.pisilinux.org/projeler/pisi/pisi-spec.dtd">
<PISI>
    <Source>
        <Name>libupnp</Name>
        <Homepage>http://pupnp.sourceforge.net/</Homepage>
        <Packager>
            <Name>PisiLinux Community</Name>
            <Email>admins@pisilinux.org</Email>
        </Packager>
        <License>BSD</License>
        <IsA>library</IsA>
        Portable UPnP library
        <Description>The Universal Plug and Play (UPnP) SDK for Linux provides support for building UPnP-compliant control points, devices, and bridges on Linux.</Description>
        <Archive sha1sum="e07cb8f9f7016233f62519f7ea5c481c4a5f10b2" type="tarbz2">http://sourceforge.net/projects/pupnp/files/pupnp/libUPnP%201.6.20/libupnp-1.6.20.tar.bz2</Archive>
        <BuildDependencies>
            <Dependency>kernel-headers</Dependency>
        </BuildDependencies>
        <Patches>

            <Patch>CVE-2016-6255.patch</Patch>
        </Patches>
    </Source>

    <Package>
        <Name>libupnp</Name>
        <Files>
            <Path fileType="library">/usr/lib</Path>
            <Path fileType="doc">/usr/share/doc</Path>
        </Files>
    </Package>

    <Package>
        <Name>libupnp-devel</Name>
        Development files for libupnp
        <RuntimeDependencies>
            <Dependency release="current">libupnp</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="header">/usr/include</Path>
            <Path fileType="data">/usr/lib/pkgconfig</Path>
        </Files>
    </Package>

    <History>
        <Update release="3">
            <Date>2017-03-02</Date>
            <Version>1.6.20</Version>
            <Comment>Version Bump.</Comment>
            <Name>Ayhan Yalçınsoy</Name>
            <Email>ayhanyalcinsoy@pisilinux.org</Email>
        </Update>
        <Update release="2">
            <Date>2016-06-09</Date>
            <Version>1.6.19</Version>
            <Comment>Release Bump</Comment>
            <Name>Pisi Linux Community</Name>
            <Email>admin@pisilinux.org</Email>
        </Update>
        <Update release="1">
            <Date>2014-05-31</Date>
            <Version>1.6.19</Version>
            <Comment>First release</Comment>
            <Name>Alihan Öztürk</Name>
            <Email>alihan@pisilinux.org</Email>
        </Update>
    </History>
</PISI>


code:
<?xml version="1.0" ?>
<PISI>
    <Source>
        <Name>libupnp</Name>
        Taşınabilir UPnP kitaplığı
        Librairie UPnP.
        Portabels UPnP Bibliothek
    </Source>

    <Package>
        <Name>libupnp-devel</Name>
        libupnp için geliştirme dosyaları
    </Package>
</PISI>


code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Licensed under the GNU General Public License, version 3.
# See the file http://www.gnu.org/licenses/gpl.txt

from pisi.actionsapi import shelltools
from pisi.actionsapi import autotools
from pisi.actionsapi import pisitools
from pisi.actionsapi import get

def setup():
    shelltools.export("CFLAGS", "%s -fPIC -fno-strict-aliasing -fPIE -DPIE " % get.CFLAGS())
    shelltools.export("LDFLAGS", "%s -pie -Wl,-z,now" % get.LDFLAGS())

    autotools.autoreconf("-fi")

    autotools.configure("--disable-static \
                         --disable-dependency-tracking \
                         --disable-rpath \
                         --localstatedir=/var \
                         --enable-slpv1 \
                         --enable-slpv2-security")

    pisitools.dosed("libtool", " -shared ", " -Wl,-O1,--as-needed -shared ")

def build():
    autotools.make()

def install():
    autotools.install()

    pisitools.dohtml("doc/doc/html/*")
    pisitools.dodoc("AUTHORS", "FAQ", "ChangeLog", "NEWS", "README", "THANKS")


code:
#!/sbin/runscript
# Copyright © 2005  TUBITAK/UEKAE
# Licensed under the GNU General Public License, version 2.
# See the file http://www.gnu.org/copyleft/gpl.txt.
#
# Original work belongs Gentoo Linux

depend() {
	need net
}

#
# Does nothing if a route exists that supports multicast traffic.
# If no routes supporting multicast traffic exists, the function
# tries to add one.  A 0 is returned on success and a 1 on failure.
# One parameter must be passed in.  This variable determins verbosity.
# If parameter is non-zero debugging will appear.
#
multicast_route_set() {
	PING_OPTIONS_1='-c1 -w1'
	PING_OPTIONS_2='-c1 -i1'
	MULTICAST_ADDRESS='239.255.255.253'
	TMP_FILE=/tmp/route.check
	PING_ERROR_NO_ROUTE='unreachable'

	MSG_FAILED_TO_FIND='Failed to Detect Multicast Route'
	MSG_SUCCESS_ON_FIND='Multicast Route Enabled'
	MSG_ADDING_ROUTE='Attempting to Add Multicast Route ...'
	MSG_FAILED_TO_ADD=' FAILED - Route NOT Added.'
	MSG_SUCCES_ON_ADD=' SUCCESS - Route Added.'

	CMD_GET_INTERFACE="netstat -i | awk 'BEGIN{}(NR>2)&&(!/^lo*/){print \$1}'"
	CMD_ADD_ROUTE="route add -net 224.0.0.0 netmask 240.0.0.0"

	ping $PING_OPTIONS_1 $MULTICAST_ADDRESS 2> $TMP_FILE 1> /dev/null
	if [ $? = 2 ]; then
		ping $PING_OPTIONS_2 $MULTICAST_ADDRESS 2> $TMP_FILE 1> /dev/null
	fi

	grep $PING_ERROR_NO_ROUTE $TMP_FILE > /dev/null 2>&1
	err_unreachable_found=$?

	#If errors, add route. Otherwise, do nothing
	if [ -s $TMP_FILE ] && [ $err_unreachable_found = 0 ]; then

		if [ $1 != 0 ]; then
			echo $MSG_FAILED_TO_FIND
			echo $MSG_ADDING_ROUTE
		fi

		$CMD_ADD_ROUTE `eval $CMD_GET_INTERFACE` > /dev/null 2>&1
		retval=$?
		if [ $1 != 0 ]; then

			if [ $retval = 0 ]; then
				echo $MSG_SUCCES_ON_ADD
			else
				echo $MSG_FAILED_TO_ADD
			fi
		fi
	else
		if [ $1 != 0 ]; then
			echo -n $MSG_SUCCESS_ON_FIND
		fi
		retval=0
	fi

	rm -f $TMP_FILE # Clean up
	return $retval
}

checkconfig() {
	multicast_route_set 0
	if [ $? -ne 0 ]; then
		eerror "No route available for multicast traffic!"
		return 1
	fi
}

start() {
	checkconfig || return 1
	ebegin "Starting slpd"
	start-stop-daemon --start --quiet --background --exec /usr/sbin/slpd
	eend $?
}

stop() {
	ebegin "Stopping slpd"
	start-stop-daemon --stop --quiet --pidfile /run/slpd.pid
	eend $?
}


code:
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.pisilinux.org/projeler/pisi/pisi-spec.dtd">
<PISI>
    <Source>
        <Name>openslp</Name>
        <Homepage>http://www.openslp.org/</Homepage>
        <Packager>
            <Name>PisiLinux Community</Name>
            <Email>admins@pisilinux.org</Email>
        </Packager>
        <License>BSD</License>
        <IsA>service</IsA>
        An open-source implementation of Service Location Protocol
        <Description>Service Location Protocol is an IETF standards track protocol that provides a framework to allow networking applications to discover the existence, location, and configuration of networked services in enterprise networks.</Description>
        <Archive sha1sum="e4630bfb986cdffab6bb829b37e9340c9152d838" type="targz">mirrors://sourceforge/openslp/openslp-2.0.0.tar.gz</Archive>
        <BuildDependencies>
            <Dependency>zlib-devel</Dependency>
            <Dependency>openssl-devel</Dependency>
            <Dependency>doxygen</Dependency>
            <Dependency>docbook-xsl</Dependency>
        </BuildDependencies>
    </Source>

    <Package>
        <Name>openslp</Name>
        <RuntimeDependencies>
            <Dependency>openssl</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="config">/etc/slp.conf</Path>
            <Path fileType="executable">/usr/bin</Path>
            <Path fileType="library">/usr/lib</Path>
            <Path fileType="doc">/usr/share/doc</Path>
            <Path fileType="doc">/usr/share/doc/openslp/html/IntroductionToSLP</Path>
            <Path fileType="doc">/usr/share/doc/openslp/html/UsersGuide</Path>
            <Path fileType="doc">/usr/share/doc/openslp/html/faq*</Path>
            <Path fileType="data">/var/log</Path>
        </Files>
    </Package>

    <Package>
        <Name>openslp-server</Name>
        OpenSLP server daemon
        <Description>openslp-server contains ths OpenSLP server daemon to dynamically register services.</Description>
        <RuntimeDependencies>
            <Dependency>openssl</Dependency>
            <Dependency release="current">openslp</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="config">/etc/slp.reg</Path>
            <Path fileType="config">/etc/slp.spi</Path>
            <Path fileType="executable">/usr/sbin/slpd</Path>
        </Files>
    </Package>

    <Package>
        <Name>openslp-devel</Name>
        Development files for openslp
        <RuntimeDependencies>
            <Dependency release="current">openslp</Dependency>
            <Dependency>zlib-devel</Dependency>
            <Dependency>openssl-devel</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="header">/usr/include</Path>
            <Path fileType="doc">/usr/share/doc/openslp/html/ProgrammersGuide</Path>
        </Files>
    </Package>

    <History>
        <Update release="3">
            <Date>2017-03-06</Date>
            <Version>2.0.0</Version>
            <Comment>Release Bump</Comment>
            <Name>Ayhan Yalçınsoy</Name>
            <Email>ayhanyalcinsoy@pisilinux.org</Email>
        </Update>
        <Update release="2">
            <Date>2016-06-09</Date>
            <Version>2.0.0</Version>
            <Comment>Release Bump</Comment>
            <Name>Pisi Linux Community</Name>
            <Email>admin@pisilinux.org</Email>
        </Update>
        <Update release="1">
            <Date>2014-02-01</Date>
            <Version>2.0.0</Version>
            <Comment>First release</Comment>
            <Name>Alihan Öztürk</Name>
            <Email>alihan@pisilinux.org</Email>
        </Update>
    </History>
</PISI>


code:
<?xml version="1.0" ?>
<PISI>
    <Source>
        <Name>openslp</Name>
        Servis Yer Protokolünün (Service Location Protocol - SLP) bir açık kaynak uyarlaması
        <Description xml:lang="tr">SLP (Service Location Protocol – Hizmet Yer Protokolu), ağlara bağlı hizmetlerin bulunabilmesi için gereken altyapıyı sağlar.</Description>
        <Description xml:lang="fr">Le Service Location Protocol (Protocole de Localisation de Service) est un protocole standard de recherche de l'IETF fournissant un framework (cadre de développement) pour permettre aux application résau de découvrir l'existence, la localisation et la configuration de services réseaux au sein des réseaux d'entreprises.</Description>
        <Description xml:lang="es">Service Location Protocol es un protocolo según estándar IETF, que provee un framework que permite a aplicaciones descubrir la existencia, locación y configuración de servicios de red en redes empresariales.</Description>
    </Source>

    <Package>
        <Name>openslp-devel</Name>
        openslp için geliştirme dosyaları
    </Package>

    <Package>
        <Name>openslp-server</Name>
        OpenSLP hizmet sunucusu
        <Description xml:lang="tr">openslp-server, dinamik hizmet kaydı için gerekli OpenSLP sunucusunu içerir.</Description>
    </Package>
</PISI>


code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Licensed under the GNU General Public License, version 3.
# See the file http://www.gnu.org/licenses/gpl.txt

from pisi.actionsapi import autotools
from pisi.actionsapi import pisitools
from pisi.actionsapi import shelltools
from pisi.actionsapi import perlmodules
from pisi.actionsapi import get

def setup():
    shelltools.export("AUTOPOINT", "/bin/true")
#    autotools.autoreconf("-vfi")
    autotools.configure("--disable-silent-rules \
                         --disable-static \
                         --disable-rpath \
                         --enable-perl \
                         --enable-ruby \
                         --enable-lua \
                         --enable-tcl \
                         --enable-python \
                         --with-rrd-default-font=/usr/share/fonts/dejavu/DejaVuSansMono.ttf \
                         --with-perl-options='installdirs=vendor destdir=%(DESTDIR)s' \
                         --with-ruby-options='sitedir=%(DESTDIR)s/usr/lib/ruby' \
                         " % {"DESTDIR": get.installDIR()})

    pisitools.dosed("Makefile", "^RRDDOCDIR.*$", "RRDDOCDIR=${datadir}/doc/${PACKAGE}")
    pisitools.dosed("doc/Makefile", "^RRDDOCDIR.*$", "RRDDOCDIR=${datadir}/doc/${PACKAGE}")
    pisitools.dosed("bindings/Makefile", "^RRDDOCDIR.*$", "RRDDOCDIR=${datadir}/doc/${PACKAGE}")
    pisitools.dosed("examples/Makefile", "examplesdir = .*$", "examplesdir = $(datadir)/doc/${PACKAGE}/examples")

    pisitools.dosed("libtool", " -shared ", " -Wl,-O1,--as-needed -shared ")

def build():
    autotools.make()

def install():
    autotools.rawInstall("DESTDIR=%s includedir=/usr/include" % get.installDIR())

    # remove unnecessary files
    perlmodules.removePacklist()
    perlmodules.removePodfiles()


code:
diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -148,7 +148,7 @@
 AC_PROG_CPP
 AC_PROG_CC
 AM_PROG_CC_C_O
-AC_PROG_LIBTOOL
+LT_INIT

 dnl Try to detect/use GNU features
 CFLAGS="$CFLAGS -D_GNU_SOURCE"
@@ -204,9 +204,6 @@
 AC_CHECK_FUNC(acos, , AC_CHECK_LIB(m, acos))

-dnl add pic flag in any case this makes sure all our code is relocatable
-eval `./libtool --config | grep pic_flag=`
-CFLAGS="$CFLAGS $pic_flag"

 dnl Checks for library functions.


code:
Install dir:
    /usr/$(get_libdir)/rrdtool/ifOctets.tcl
    /usr/$(get_libdir)/rrdtool/pkgIndex.tcl

Due to the following change, pkglib_{DATA,SCRIPTS} is invalid:
    http://git.savannah.gnu.org/cgit/automake.git/commit/?id=9ca632642b006ac6b0fc4ce0ae5b34023faa8cbf

---
 bindings/tcl/Makefile.am |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/bindings/tcl/Makefile.am b/bindings/tcl/Makefile.am
index c0e8b0f..b7205e7 100644
--- a/bindings/tcl/Makefile.am
+++ b/bindings/tcl/Makefile.am
@@ -27,8 +27,9 @@ tclpkgdir = @TCL_PACKAGE_DIR@
 tclpkg_DATA = pkgIndex.tcl
 tclpkg_SCRIPTS = ifOctets.tcl
 else
-pkglib_DATA = pkgIndex.tcl
-pkglib_SCRIPTS = ifOctets.tcl
+tclpkgdir = $(libdir)/@PACKAGE@
+tclpkg_DATA = pkgIndex.tcl
+tclpkg_SCRIPTS = ifOctets.tcl
 endif

 # Automake doen't like `tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX)' as


code:
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.pisilinux.org/projeler/pisi/pisi-spec.dtd">
<PISI>
    <Source>
        <Name>rrdtool</Name>
        <Homepage>http://oss.oetiker.ch/rrdtool/</Homepage>
        <Packager>
            <Name>PisiLinux Community</Name>
            <Email>admins@pisilinux.org</Email>
        </Packager>
        <License>GPLv2</License>
        <IsA>app:console</IsA>
        <IsA>library</IsA>
        <Summary>A system to store and display time-series data</Summary>
        <Description>RRD is the acronym for Round Robin Database. RRD is a system to store and display time-series data (i.e. network bandwidth, machine/room temperature, server load average).</Description>
        <Archive sha1sum="15a2009e2ee67eb41a2598d1a0f1f7cf1a651c78" type="targz">http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.7.0.tar.gz</Archive>
        <BuildDependencies>
            <Dependency>lua-devel</Dependency>
            <Dependency>tcl-devel</Dependency>
            <Dependency>ruby-devel</Dependency>
            <Dependency>cairo-devel</Dependency>
            <Dependency>pango-devel</Dependency>
            <Dependency>libart_lgpl-devel</Dependency>
            <Dependency>dejavu-fonts</Dependency>
            <Dependency>perl</Dependency>
            <Dependency>glib2-devel</Dependency>
            <Dependency>python-devel</Dependency>
            <Dependency>libxml2-devel</Dependency>
            <Dependency>python-setuptools</Dependency>
            <Dependency>tcp-wrappers-devel</Dependency>
            <Dependency>groff</Dependency>
        </BuildDependencies>
        <Patches>
            <!--Patch level="1">rrdtool-1.4.7-ruby-2-fix.patch</Patch-->
<!--             <Patch level="1">0001_rrdtool-1.4.7-configure.ac.patch</Patch> -->
<!--             <Patch level="1">rrdtool-1.4.5-automake-1.11.2.patch</Patch> -->
        </Patches>
    </Source>

    <Package>
        <Name>rrdtool</Name>
        <RuntimeDependencies>
            <Dependency>lua</Dependency>
            <Dependency>tcl</Dependency>
            <Dependency>ruby</Dependency>
            <Dependency>cairo</Dependency>
            <Dependency>pango</Dependency>
            <Dependency>libpng</Dependency>
            <Dependency>libart_lgpl</Dependency>
            <Dependency>dejavu-fonts</Dependency>
            <Dependency>perl</Dependency>
            <Dependency>glib2</Dependency>
            <Dependency>python</Dependency>
            <Dependency>libxml2</Dependency>
            <Dependency>tcp-wrappers</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="executable">/usr/bin</Path>
            <Path fileType="library">/usr/lib</Path>
            <Path fileType="man">/usr/share/man</Path>
            <Path fileType="doc">/usr/share/doc</Path>
            <Path fileType="data">/usr/share/rrdtool</Path>
            <Path fileType="localedata">/usr/share/locale</Path>
        </Files>
    </Package>

    <Package>
        <Name>rrdtool-devel</Name>
        <RuntimeDependencies>
            <Dependency release="current">rrdtool</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="header">/usr/include</Path>
            <Path fileType="data">/usr/lib/pkgconfig</Path>
        </Files>
    </Package>

    <History>
        <Update release="4">
            <Date>2018-01-31</Date>
            <Version>1.7.0</Version>
            <Comment>Version Bump</Comment>
            <Name>Pisi Linux Community</Name>
            <Email>admin@pisilinux.org</Email>
        </Update>
        <Update release="3">
            <Date>2017-03-01</Date>
            <Version>1.6.0</Version>
            <Comment>Version Bump</Comment>
            <Name>Pisi Linux Community</Name>
            <Email>admin@pisilinux.org</Email>
        </Update>
        <Update release="2">
            <Date>2016-06-15</Date>
            <Version>1.5.3</Version>
            <Comment>Release Bump</Comment>
            <Name>Pisi Linux Community</Name>
            <Email>admin@pisilinux.org</Email>
        </Update>
        <Update release="1">
            <Date>2015-07-30</Date>
            <Version>1.5.3</Version>
            <Comment>First release</Comment>
            <Name>Ertuğrul Erata</Name>
            <Email>ertugrulerata@gmail.com</Email>
        </Update>
    </History>
</PISI>


code:
<?xml version="1.0" ?>
<PISI>
    <Source>
        <Name>rrdtool</Name>
        <Summary xml:lang="tr">Zaman serisi verilerini saklamak ve göstermek için bir araç</Summary>
        <Description xml:lang="tr">RRD, Round Robin Database için kullanılan bir kısaltmadır. RRD, zaman serisi verilerini (ör. ağ bantgenişliği, makine/oda sıcaklığı, ortalama sunucu yükü) saklamak ve göstermek için kullanılan bir sistemdir.</Description>
    </Source>
</PISI>


code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Licensed under the GNU General Public License, version 3.
# See the file http://www.gnu.org/licenses/gpl.txt

from pisi.actionsapi import autotools
from pisi.actionsapi import pisitools
from pisi.actionsapi import get

def build():
    autotools.make('CC="%s" CFLAGS="%s" SKIPDIRS="patches"' % (get.CC(),get.CFLAGS()))

def install():
    pisitools.doexe("traceroute/traceroute", "/bin/")
    pisitools.dosym("/bin/traceroute", "/bin/tracert")

    pisitools.doman("traceroute/traceroute.8")
    pisitools.dodoc("ChangeLog", "COPYING", "CREDITS", "README", "TODO")


code:
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.pisilinux.org/projeler/pisi/pisi-spec.dtd">
<PISI>
    <Source>
        <Name>traceroute</Name>
        <Homepage>http://traceroute.sourceforge.net/</Homepage>
        <Packager>
            <Name>PisiLinux Community</Name>
            <Email>admins@pisilinux.org</Email>
        </Packager>
        <License>GPLv2</License>
        <IsA>app:console</IsA>
        <Summary>Utility to trace the route of IP packets</Summary>
        <Description>The traceroute utility displays the route used by IP packets on their way to a specified network (or Internet) host. Traceroute is used as a network debugging tool. If you're having network connectivity problems, traceroute will show you where along the route the trouble is.</Description>
        <Archive sha1sum="bc5c6c8022187511be5665b3818d919be5987dcc" type="targz">https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz</Archive>
    </Source>

    <Package>
        <Name>traceroute</Name>
        <Files>
            <Path fileType="executable">/bin</Path>
            <Path fileType="doc">/usr/share/doc</Path>
            <Path fileType="man">/usr/share/man</Path>
        </Files>
    </Package>

    <History>
        <Update release="4">
            <Date>2017-01-07</Date>
            <Version>2.1.0</Version>
            <Comment>First Release.</Comment>
            <Name>Stefan Gronewold(groni)</Name>
            <Email>groni@pisilinux.org</Email>
        </Update>
    </History>
</PISI>


code:
<?xml version="1.0" ?>
<PISI>
    <Source>
        <Name>traceroute</Name>
        <Summary xml:lang="tr">IP paketlerinin izlediği yolları takip etme aracı</Summary>
        <Description xml:lang="tr">traceroute IP paketlerinin ağda veya internette izlediği yolu görüntüler. Ağda bağlantı probleminiz varsa, traceroute problemin yol üzerinde tam olarak nereden kaynaklandığını gösterir.</Description>
    </Source>
</PISI>


code:
<PISI>
    <Name>network.chat</Name>
</PISI>


code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Licensed under the GNU General Public License, version 3.
# See the file http://www.gnu.org/licenses/gpl.txt

from pisi.actionsapi import autotools
from pisi.actionsapi import pisitools

def setup():
    autotools.autoreconf("-fi")
    autotools.configure("--disable-static")

def build():
    autotools.make()

def install():
    autotools.install()

    pisitools.dodoc("AUTHORS", "ChangeLog", "COPYING", "NEWS", "README")


code:
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.pisilinux.org/projeler/pisi/pisi-spec.dtd">
<PISI>
    <Source>
        <Name>libotr</Name>
        <Homepage>http://www.cypherpunks.ca/otr/</Homepage>
        <Packager>
            <Name>PisiLinux Community</Name>
            <Email>admins@pisilinux.org</Email>
        </Packager>
        <License>GPLv2</License>
        <IsA>library</IsA>
        Portable OTR (Off The Record) messaging library
        <Description>OTR messaging allows you to have private conversations over instant messaging.</Description>
        <Archive sha1sum="3894b82a6c307ad011681ad342d69b18344933ae" type="targz">https://otr.cypherpunks.ca/libotr-4.1.1.tar.gz</Archive>
        <BuildDependencies>
            <Dependency>libgcrypt-devel</Dependency>
        </BuildDependencies>
    </Source>

    <Package>
        <Name>libotr</Name>
        <RuntimeDependencies>
            <Dependency>libgcrypt</Dependency>
            <Dependency>libgpg-error</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="executable">/usr/bin</Path>
            <Path fileType="library">/usr/lib</Path>
            <Path fileType="doc">/usr/share/doc</Path>
            <Path fileType="man">/usr/share/man</Path>
        </Files>
    </Package>

    <Package>
        <Name>libotr-devel</Name>
        Development files for libotr
        <RuntimeDependencies>
            <Dependency release="current">libotr</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="header">/usr/include</Path>
            <Path fileType="data">/usr/lib/pkgconfig</Path>
            <Path fileType="data">/usr/share/aclocal</Path>
            <Path fileType="doc">/usr/share/doc/libotr/html</Path>
        </Files>
    </Package>

    <History>
        <Update release="3">
            <Date>2017-04-18</Date>
            <Version>4.1.1</Version>
            <Comment>Rebuild with new toolchain</Comment>
            <Name>Pisi Linux Community</Name>
            <Email>admin@pisilinux.org</Email>
        </Update>
        <Update release="2">
            <Date>2016-06-09</Date>
            <Version>4.1.0</Version>
            <Comment>Release Bump</Comment>
            <Name>Pisi Linux Community</Name>
            <Email>admin@pisilinux.org</Email>
        </Update>
        <Update release="1">
            <Date>2015-11-22</Date>
            <Version>4.1.0</Version>
            <Comment>First release</Comment>
            <Name>Alihan Öztürk</Name>
            <Email>alihan@pisilinux.org</Email>
        </Update>
    </History>
</PISI>


code:
<?xml version="1.0" ?>
<PISI>
    <Source>
        <Name>libotr</Name>
        Taşınabilir OTR (Off The Record/Kayıt dışı) mesajlaşma kitaplığı
        <Description xml:lang="tr">OTR mesajlaşma, hızlı mesajlaşma araçları ile gizli sohbetler yapmanızı sağlar.</Description>
    </Source>

    <Package>
        <Name>libotr-devel</Name>
        libotr için geliştirme dosyaları
    </Package>
</PISI>


code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Licensed under the GNU General Public License, version 2
# See the file http://www.gnu.org/copyleft/gpl.txt

from pisi.actionsapi import autotools
from pisi.actionsapi import pisitools

def setup():
    autotools.configure("--disable-static \
                         --prefix=/usr")

    pisitools.dosed("libtool"," -shared ", " -Wl,--as-needed -shared ")

def build():
    autotools.make()

def install():
    autotools.install()

    pisitools.dodoc("README", "AUTHORS", "ChangeLog", "NEWS")


code:
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.pisilinux.org/projeler/pisi/pisi-spec.dtd">
<PISI>
    <Source>
        <Name>loudmouth</Name>
        <Homepage>http://www.loudmouth-project.org</Homepage>
        <Packager>
            <Name>PisiLinux Community</Name>
            <Email>admins@pisilinux.org</Email>
        </Packager>
        <License>LGPLv2.1</License>
        <IsA>library</IsA>
        Lightweight C Jabber library
        <Description>Loudmouth is a lightweight and easy-to-use C library for programming with the XMPP protocol. It's designed to be easy to get started with and yet extensible to let you do anything the XMPP protocol allows.</Description>
        <Archive sha1sum="a69ef9a57e0ae993340921e73ada27979850db04" type="tarbz2">https://mcabber.com/files/loudmouth/loudmouth-1.5.3.tar.bz2</Archive>
        <BuildDependencies>
            <Dependency>intltool</Dependency>
            <Dependency>glib2-devel</Dependency>
            <Dependency>mit-kerberos</Dependency>
            <Dependency>gnutls-devel</Dependency>
        </BuildDependencies>
    </Source>

    <Package>
        <Name>loudmouth</Name>
        <RuntimeDependencies>
            <Dependency>glib2</Dependency>
            <Dependency>gnutls</Dependency>
            <Dependency>mit-kerberos</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="library">/usr/lib/libloudmouth*</Path>
            <Path fileType="doc">/usr/share/doc</Path>
            <Path fileType="doc">/usr/share/gtk-doc</Path>
        </Files>
    </Package>

    <Package>
        <Name>loudmouth-devel</Name>
        Development files for loudmouth
        <RuntimeDependencies>
            <Dependency>glib2-devel</Dependency>
            <Dependency release="current">loudmouth</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="header">/usr/include</Path>
            <Path fileType="data">/usr/lib/pkgconfig</Path>
        </Files>
    </Package>

    <History>
        <Update release="1">
            <Date>2017-05-19</Date>
            <Version>1.5.3</Version>
            <Comment>First release</Comment>
            <Name>Alihan Öztürk</Name>
            <Email>alihan@pisilinux.org</Email>
        </Update>
    </History>
</PISI>


code:
<?xml version="1.0" ?>
<PISI>
    <Source>
        <Name>loudmouth</Name>
        Hafif bir C jabber kitaplığı
        <Description xml:lang="tr">Loudmouth XMPP protokolüyle programlama imkanı sunan hafif ve kullanımı kolay bir C kitaplığı. Programlamada kolay bir başlangıç sunmak ve XMPP protokolü el verdiği ölçüde esnek bir yapıda kalmak için tasarlanmıştır.</Description>
    </Source>

    <Package>
        <Name>loudmouth-devel</Name>
        loudmouth için geliştirme dosyaları
    </Package>
</PISI>


code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Licensed under the GNU General Public License, version 3.
# See the file http://www.gnu.org/licenses/gpl.txt

from pisi.actionsapi import autotools
from pisi.actionsapi import pisitools
from pisi.actionsapi import shelltools
from pisi.actionsapi import perlmodules
from pisi.actionsapi import get

def setup():
    #shelltools.export("CFLAGS","%s -fstack-protector-all" % get.CFLAGS())
    #shelltools.export("CXXFLAGS", "%s -fstack-protector-all" % get.CXXFLAGS())
    #shelltools.export("LDFLAGS", "%s -pie" % get.LDFLAGS())
    #autotools.autoreconf("-fi")
    autotools.configure("--prefix=/usr \
                         --enable-dbus \
                         --enable-cyrus-sasl \
                         --enable-gnutls=yes \
                         --enable-nss=yes \
                         --enable-nm \
                         --enable-cap \
                         --disable-mono \
                         --disable-schemas-install \
                         --disable-meanwhile \
                         --disable-vv \
                         --enable-nm \
                         --enable-tcl \
                         --enable-tk \
                         --with-python=/usr/bin/python2.7 \
                         --x-includes=/usr/include/X11 \
                         --with-gnutls-includes=/usr/include/gnutls \
                         --with-gnutls-libs=/usr/lib \
                         --with-system-ssl-certs=/etc/ssl/certs")

    #pisitools.dosed("libtool", " -shared ", " -Wl,-O1,--as-needed -shared ")

def build():
    autotools.make()

def install():
    autotools.rawInstall("DESTDIR=%s" % get.installDIR())

    pisitools.dosym("../icons/hicolor/48x48/apps/pidgin.png", "/usr/share/pixmaps/pidgin.png")

    pisitools.dodoc("AUTHORS", "COPYING", "HACKING", "NEWS", "README", "ChangeLog")

    perlmodules.removePodfiles()


code:
pidgin-2.7.2.tar.bz2
pidgin-2.7.3.tar.bz2
/pidgin-2.7.4.tar.bz2
/pidgin-2.7.5.tar.bz2
/pidgin-2.7.7.tar.bz2
/pidgin-2.7.9.tar.bz2
/pidgin-2.7.10.tar.bz2
/pidgin-2.7.11.tar.bz2
/pidgin-2.8.0.tar.bz2
/pidgin-2.9.0.tar.bz2
/pidgin-2.10.0.tar.bz2
/pidgin-2.10.1.tar.bz2
/pidgin-2.10.2.tar.bz2
/pidgin-2.10.4.tar.bz2
/pidgin-2.10.5.tar.bz2
/pidgin-2.10.6.tar.bz2
/pidgin-2.10.7.tar.bz2


code:
diff -urN pidgin-2.5.2.orig/pidgin/gtksound.c pidgin-2.5.2/pidgin/gtksound.c
--- pidgin-2.5.2.orig/pidgin/gtksound.c	2008-07-13 22:05:38.000000000 -0400
+++ pidgin-2.5.2/pidgin/gtksound.c	2008-11-22 13:36:54.000000000 -0500
@@ -298,6 +298,9 @@
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/mute", FALSE);
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/command", "");
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/sound/method", "automatic");
+#ifndef USE_GSTREAMER
+	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/rhel4migrated", FALSE);
+#endif
 	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/sound/volume", 50);

 #ifdef USE_GSTREAMER
@@ -433,6 +436,19 @@
 		return;
 	}

+#ifndef USE_GSTREAMER
+	/* RHEL4 migration code */
+	if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/sound/rhel4migrated")) {
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/rhel4migrated", TRUE);
+		if(!strcmp(method, "automatic") ||
+		   !strcmp(method, "esd") ||
+		   !strcmp(method, "arts")) {
+			purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/method", "custom");
+			purple_prefs_set_path(PIDGIN_PREFS_ROOT "/sound/command", "aplay %s");
+		}
+	}
+#endif
+
 #ifndef _WIN32
 	if (!strcmp(method, "custom")) {
 		const char *sound_cmd;


code:
--- pidgin-2.10.0/libpurple/protocols/gg/gg.c.orig	2011-08-18 10:51:03.000000000 +0200
+++ pidgin-2.10.0/libpurple/protocols/gg/gg.c	2011-08-21 09:54:41.000000000 +0200
@@ -533,6 +533,8 @@
 		purple_request_fields_get_string(fields, "firstname"));
 	form->nickname = g_strdup(
 		purple_request_fields_get_string(fields, "nickname"));
+	form->uin = g_strdup(
+		purple_request_fields_get_string(fields, "uin"));
 	form->city = g_strdup(
 		purple_request_fields_get_string(fields, "city"));
 	form->birthyear = g_strdup(
@@ -586,6 +588,11 @@
 	purple_request_field_string_set_masked(field, FALSE);
 	purple_request_field_group_add_field(group, field);

+	field = purple_request_field_string_new("uin",
+			_("UIN"), NULL, FALSE);
+	purple_request_field_string_set_masked(field, FALSE);
+	purple_request_field_group_add_field(group, field);
+
 	field = purple_request_field_string_new("city",
 			_("City"), NULL, FALSE);
 	purple_request_field_string_set_masked(field, FALSE);


code:
--- libpurple/protocols/jabber/Makefile.am.orig	2010-02-16 10:34:06.000000000 +0100
+++ libpurple/protocols/jabber/Makefile.am	2010-02-18 10:31:46.828001077 +0100
@@ -80,6 +80,7 @@

 AM_CFLAGS = $(st)

+libjabber_la_LDFLAGS = -module -avoid-version
 libxmpp_la_LDFLAGS = -module -avoid-version

 if USE_CYRUS_SASL
--- libpurple/protocols/oscar/Makefile.am.orig	2010-02-16 10:34:06.000000000 +0100
+++ libpurple/protocols/oscar/Makefile.am	2010-02-18 10:31:46.828001077 +0100
@@ -50,6 +50,7 @@

 libaim_la_LDFLAGS = -module -avoid-version
 libicq_la_LDFLAGS = -module -avoid-version
+liboscar_la_LDFLAGS = -module -avoid-version
 if STATIC_OSCAR

 st = -DPURPLE_STATIC_PRPL


code:
diff -p -up pidgin-2.7.0/configure.ac~ pidgin-2.7.0/configure.ac
--- pidgin-2.7.0/configure.ac~	2010-05-13 22:24:35.000000000 +0200
+++ pidgin-2.7.0/configure.ac	2010-05-13 22:28:11.000000000 +0200
@@ -1522,7 +1522,7 @@ dnl # Check for Mono support
 dnl #######################################################################
 AC_ARG_ENABLE(mono, [AC_HELP_STRING([--enable-mono], [compile with Mono runtime support (experimental)])], , enable_mono=no)
 if test x"$enable_mono" = x"yes" ; then
-	PKG_CHECK_MODULES(MONO, mono, [
+	PKG_CHECK_MODULES(MONO, mono glib-2.0 gthread-2.0, [
 		AC_SUBST(MONO_CFLAGS)
 		AC_SUBST(MONO_LIBS)
 		enable_mono=yes
diff -p -up pidgin-2.7.0/libpurple/plugins/mono/loader/status-glue.c~ pidgin-2.7.0/libpurple/plugins/mono/loader/status-glue.c
--- pidgin-2.7.0/libpurple/plugins/mono/loader/status-glue.c~	2010-05-12 06:36:11.000000000 +0200
+++ pidgin-2.7.0/libpurple/plugins/mono/loader/status-glue.c	2010-05-13 22:33:45.000000000 +0200
@@ -1,3 +1,4 @@
+#include <glib.h>
 #include "status.h"
 #include "mono-helper.h"
 #include "mono-glue.h"


code:
diff -p -up pidgin-2.7.0/pidgin/pixmaps/emotes/default/24/theme.smiley pidgin-2.7.0/pidgin/pixmaps/emotes/default/24/theme
--- pidgin-2.7.0/pidgin/pixmaps/emotes/default/24/theme.smiley	2010-05-12 06:37:31.000000000 +0200
+++ pidgin-2.7.0/pidgin/pixmaps/emotes/default/24/theme	2010-05-13 21:15:22.000000000 +0200
@@ -6,9 +6,9 @@ Author=Hylke Bons

 # Default smileys
 [default]
-happy.png           :)      :-)
+happy.png           :)      :-)     =)
 excited.png         :-D     :-d     :D      :d
-sad.png             :-(     :(
+sad.png             :-(     :(      =(
 wink.png            ;-)     ;)
 tongue.png          :P      :p      :-P     :-p
 shocked.png         =-O     =-o
@@ -16,7 +16,7 @@ kiss.png            :-*
 glasses-cool.png    8-)
 embarrassed.png     :-[
 crying.png          :'(     :'-(
-thinking.png        :-/     :-\\
+thinking.png        :-/     :-\      :\
 angel.png           O:-)    o:-)
 shut-mouth.png      :-X
 moneymouth.png      :-$


code:
--- pidgin-2.8.0/libpurple/protocols/gg/gg.c.orig 2011-06-08 03:10:11.000000000 +0200
+++ pidgin-2.8.0/libpurple/protocols/gg/gg.c 2011-06-08 03:13:44.000000000 +0200
@@ -1816,6 +1816,11 @@
		case GG_EVENT_PUBDIR50_SEARCH_REPLY:
			ggp_pubdir_reply_handler(gc, ev->event.pubdir50);
			break;
+ 		case GG_EVENT_DISCONNECT:
+			purple_connection_error_reason (gc,
+					PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+					_("Server Disconnect."));
+			break;
		case GG_EVENT_TYPING_NOTIFICATION:
			ggp_typing_notification_handler(gc, ev->event.typing_notification.uin,
				ev->event.typing_notification.length);


code:
--- pidgin-2.8.0/libpurple/network.c.reread-resolvconf	2011-06-09 16:30:04.764927571 +0200
+++ pidgin-2.8.0/libpurple/network.c	2011-06-09 16:31:29.356249181 +0200
@@ -874,8 +874,6 @@
 #else
 		case NM_STATE_CONNECTED:
 #endif
-			/* Call res_init in case DNS servers have changed */
-			res_init();
 			/* update STUN IP in case we it changed (theoretically we could
 			   have gone from IPv4 to IPv6, f.ex. or we were previously
 			   offline */


code:
--- pidgin.desktop.in	2009-08-18 23:48:30.163325146 +0300
+++ pidgin.desktop2	2009-08-18 23:47:51.648352181 +0300
@@ -1,5 +1,5 @@
 [Desktop Entry]
-_Name=Pidgin Internet Messenger
+_Name=Pidgin
 _GenericName=Internet Messenger
 _Comment=Send instant messages over multiple protocols
 Exec=pidgin


code:
diff -uprb pidgin-2.10.12.orig/libpurple/purple-remote pidgin-2.10.12/libpurple/purple-remote
--- pidgin-2.10.12.orig/libpurple/purple-remote	2016-01-01 01:19:41.000000000 +0200
+++ pidgin-2.10.12/libpurple/purple-remote	2016-01-02 06:23:14.082831498 +0200
@@ -3,13 +3,20 @@
 import codecs
 import dbus
 import re
-import urllib
 import sys
+try:
+    from urllib.parse import unquote
+except ImportError:
+    from urllib import unquote

 import xml.dom.minidom

-sys.stdin = codecs.getwriter('utf-8')(sys.stdin);
-sys.stdout = codecs.getwriter('utf-8')(sys.stdout);
+if sys.version_info[0] >= 3:
+    sys.stdin = codecs.getwriter('utf-8')(sys.stdin.buffer)
+    sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer)
+else:
+    sys.stdin = codecs.getwriter('utf-8')(sys.stdin)
+    sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

 xml.dom.minidom.Element.all   = xml.dom.minidom.Element.getElementsByTagName

@@ -41,7 +48,7 @@ class CheckedAttribute:
         return result

 def show_help(requested=False):
-    print """This program uses D-Bus to communicate with purple.
+    print("""This program uses D-Bus to communicate with purple.

 Usage:

@@ -72,7 +79,7 @@ Examples of commands:

     PurpleAccountsFindConnected?name=&protocol=prpl-jabber
     PurpleAccountsFindConnected(,prpl-jabber)
-""" % sys.argv[0]
+""" % sys.argv[0])
     if (requested):
         sys.exit(0)
     else:
@@ -120,7 +127,7 @@ def execute(uri):
     if paramstring is not None:
         for param in paramstring.split("&"):
             key, value = extendlist(param.split("=",1), 2, "")
-            params[key] = urllib.unquote(value)
+            params[key] = unquote(value)

     accountname = params.get("account", "")

@@ -233,12 +240,11 @@ if len(sys.argv) == 1:
 elif (sys.argv[1] == "--help" or sys.argv[1] == "-h"):
     show_help(True)
 elif (obj == None):
-    print "No existing libpurple instance detected."
+    print("No existing libpurple instance detected.")
     sys.exit(1);

 for arg in sys.argv[1:]:
     output = execute(arg)

     if (output != None):
-        print output
-
+        print(output)


code:
# HG changeset patch
# User Youness Alaoui <kakaroto@kakaroto.homelinux.net>
# Date 1404159462 14400
# Node ID 2b41ba1fde8a80a4f2d715874a0b02449d4b66ad
# Parent  f02f7d1fb4d545c6d7353745094e1afcf0428ca9
Fix send-video enum typo

Refs #16315

diff --git a/libpurple/media/enum-types.c b/libpurple/media/enum-types.c
--- a/libpurple/media/enum-types.c
+++ b/libpurple/media/enum-types.c
@@ -175,7 +175,7 @@
 			{ PURPLE_MEDIA_RECV_VIDEO,
 				"PURPLE_MEDIA_RECV_VIDEO", "recv-video" },
 			{ PURPLE_MEDIA_SEND_VIDEO,
-				"PURPLE_MEDIA_SEND_VIDEO", "send-audio" },
+				"PURPLE_MEDIA_SEND_VIDEO", "send-video" },
 			{ PURPLE_MEDIA_AUDIO,
 				"PURPLE_MEDIA_AUDIO", "audio" },
 			{ PURPLE_MEDIA_VIDEO,


code:
# HG changeset patch
# User David Woodhouse <David.Woodhouse@intel.com>
# Date 1425675783 0
# Node ID 6b4576edf2a694ab55d0d06d3643c44601a75b15
# Parent  714ba418d0aa5ba0cc4cc3b9db37296cd2bbf041
Add out-of-band DTMF support and dialpad to use it

This is a backport of e4c122196b08 from the trunk. It adds the UI and
farstream backend support for sending DTMF.

Fixes #15575

diff --git a/libpurple/media.c b/libpurple/media.c
--- a/libpurple/media.c
+++ b/libpurple/media.c
@@ -1439,3 +1439,46 @@
 }
 #endif /* USE_GSTREAMER */

+gboolean
+purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
+		gchar dtmf, guint8 volume, guint16 duration)
+{
+#ifdef USE_VV
+	PurpleAccount *account = NULL;
+	PurpleConnection *gc = NULL;
+	PurplePlugin *prpl = NULL;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurpleMediaBackendIface *backend_iface = NULL;
+
+	if (media)
+	{
+		account = purple_media_get_account(media);
+		backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(media->priv->backend);
+	}
+	if (account)
+		gc = purple_account_get_connection(account);
+	if (gc)
+		prpl = purple_connection_get_prpl(gc);
+	if (prpl)
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (dtmf == 'a')
+		dtmf = 'A';
+	else if (dtmf == 'b')
+		dtmf = 'B';
+	else if (dtmf == 'c')
+		dtmf = 'C';
+	else if (dtmf == 'd')
+		dtmf = 'D';
+
+	g_return_val_if_fail(strchr("0123456789ABCD#*", dtmf), FALSE);
+
+	if (backend_iface && backend_iface->send_dtmf
+		&& backend_iface->send_dtmf(media->priv->backend,
+				session_id, dtmf, volume, duration))
+	{
+		return TRUE;
+	}
+#endif
+	return FALSE;
+}
diff --git a/libpurple/media.h b/libpurple/media.h
--- a/libpurple/media.h
+++ b/libpurple/media.h
@@ -437,6 +437,21 @@
  */
 void purple_media_remove_output_windows(PurpleMedia *media);

+/**
+ * Sends a DTMF signal out-of-band.
+ *
+ * @param media The media instance to send a DTMF signal to.
+ * @param sess_id The session id of the session to send the DTMF signal on.
+ * @param dtmf The character representing the DTMF in the range [0-9#*A-D].
+ * @param volume The power level expressed in dBm0 after dropping the sign
+ *      in the range of 0 to 63.  A larger value represents a lower volume.
+ * @param duration The duration of the tone in milliseconds.
+ *
+ * @since 2.11
+ */
+gboolean purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
+		gchar dtmf, guint8 volume, guint16 duration);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libpurple/media/backend-fs2.c b/libpurple/media/backend-fs2.c
--- a/libpurple/media/backend-fs2.c
+++ b/libpurple/media/backend-fs2.c
@@ -94,6 +94,9 @@
 static void purple_media_backend_fs2_set_params(PurpleMediaBackend *self,
 		guint num_params, GParameter *params);
 static const gchar **purple_media_backend_fs2_get_available_params(void);
+static gboolean purple_media_backend_fs2_send_dtmf(
+		PurpleMediaBackend *self, const gchar *sess_id,
+		gchar dtmf, guint8 volume, guint16 duration);

 static void free_stream(PurpleMediaBackendFs2Stream *stream);
 static void free_session(PurpleMediaBackendFs2Session *session);
@@ -499,6 +502,7 @@
 	iface->set_send_codec = purple_media_backend_fs2_set_send_codec;
 	iface->set_params = purple_media_backend_fs2_set_params;
 	iface->get_available_params = purple_media_backend_fs2_get_available_params;
+	iface->send_dtmf = purple_media_backend_fs2_send_dtmf;
 }

 static FsMediaType
@@ -2436,6 +2440,65 @@

 	return supported_params;
 }
+static gboolean
+send_dtmf_callback(gpointer userdata)
+{
+	FsSession *session = userdata;
+
+	fs_session_stop_telephony_event(session);
+
+	return FALSE;
+}
+static gboolean
+purple_media_backend_fs2_send_dtmf(PurpleMediaBackend *self,
+		const gchar *sess_id, gchar dtmf, guint8 volume,
+		guint16 duration)
+{
+	PurpleMediaBackendFs2Session *session;
+	FsDTMFEvent event;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE);
+
+	session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id);
+	if (session == NULL)
+		return FALSE;
+
+	/* Convert DTMF char into FsDTMFEvent enum */
+	switch(dtmf) {
+		case '0': event = FS_DTMF_EVENT_0; break;
+		case '1': event = FS_DTMF_EVENT_1; break;
+		case '2': event = FS_DTMF_EVENT_2; break;
+		case '3': event = FS_DTMF_EVENT_3; break;
+		case '4': event = FS_DTMF_EVENT_4; break;
+		case '5': event = FS_DTMF_EVENT_5; break;
+		case '6': event = FS_DTMF_EVENT_6; break;
+		case '7': event = FS_DTMF_EVENT_7; break;
+		case '8': event = FS_DTMF_EVENT_8; break;
+		case '9': event = FS_DTMF_EVENT_9; break;
+		case '*': event = FS_DTMF_EVENT_STAR; break;
+		case '#': event = FS_DTMF_EVENT_POUND; break;
+		case 'A': event = FS_DTMF_EVENT_A; break;
+		case 'B': event = FS_DTMF_EVENT_B; break;
+		case 'C': event = FS_DTMF_EVENT_C; break;
+		case 'D': event = FS_DTMF_EVENT_D; break;
+		default:
+			return FALSE;
+	}
+
+	if (!fs_session_start_telephony_event(session->session,
+			event, volume)) {
+		return FALSE;
+	}
+
+	if (duration <= 50) {
+		fs_session_stop_telephony_event(session->session);
+	} else {
+		purple_timeout_add(duration, send_dtmf_callback,
+				session->session);
+	}
+
+	return TRUE;
+}
 #else
 GType
 purple_media_backend_fs2_get_type(void)
diff --git a/libpurple/media/backend-iface.h b/libpurple/media/backend-iface.h
--- a/libpurple/media/backend-iface.h
+++ b/libpurple/media/backend-iface.h
@@ -71,6 +71,9 @@
 	void (*set_params) (PurpleMediaBackend *self,
 		guint num_params, GParameter *params);
 	const gchar **(*get_available_params) (void);
+	gboolean (*send_dtmf) (PurpleMediaBackend *self,
+		const gchar *sess_id, gchar dtmf, guint8 volume,
+		guint16 duration);
 };

 /**
diff --git a/pidgin/gtkmedia.c b/pidgin/gtkmedia.c
--- a/pidgin/gtkmedia.c
+++ b/pidgin/gtkmedia.c
@@ -41,6 +41,7 @@
 #ifdef _WIN32
 #include <gdk/gdkwin32.h>
 #endif
+#include <gdk/gdkkeysyms.h>

 #include <gst/interfaces/xoverlay.h>

@@ -759,6 +760,136 @@
 }

 static void
+phone_dtmf_pressed_cb(GtkButton *button, gpointer user_data)
+{
+	PidginMedia *gtkmedia = user_data;
+	gint num;
+	gchar *sid;
+
+	num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "dtmf-digit"));
+	sid = g_object_get_data(G_OBJECT(button), "session-id");
+
+	purple_media_send_dtmf(gtkmedia->priv->media, sid, num, 25, 50);
+}
+
+static inline GtkWidget *
+phone_create_button(const gchar *text_hi, const gchar *text_lo)
+{
+	GtkWidget *button;
+	GtkWidget *label_hi;
+	GtkWidget *label_lo;
+	GtkWidget *grid;
+	const gchar *text_hi_local;
+
+	if (text_hi)
+		text_hi_local = _(text_hi);
+	else
+		text_hi_local = "";
+
+	grid = gtk_vbox_new(TRUE, 0);
+
+	button = gtk_button_new();
+	label_hi = gtk_label_new(text_hi_local);
+	gtk_misc_set_alignment(GTK_MISC(label_hi), 0.5, 0.5);
+	gtk_box_pack_end(GTK_BOX(grid), label_hi, FALSE, TRUE, 0);
+	label_lo = gtk_label_new(text_lo);
+	gtk_misc_set_alignment(GTK_MISC(label_lo), 0.5, 0.5);
+	gtk_label_set_use_markup(GTK_LABEL(label_lo), TRUE);
+	gtk_box_pack_end(GTK_BOX(grid), label_lo, FALSE, TRUE, 0);
+	gtk_container_add(GTK_CONTAINER(button), grid);
+
+	return button;
+}
+
+static struct phone_label {
+	gchar *subtext;
+	gchar *text;
+	gchar chr;
+} phone_labels[] = {
+	{"1", NULL, '1'},
+	/* Translators note: These are the letters on the keys of a numeric
+	   keypad; translate according to §7.2.4 of
+	 http://www.etsi.org/deliver/etsi_es/202100_202199/202130/01.01.01_60/es_20213 */
+	 /* Letters on the '2' key of a numeric keypad */
+	{"2", N_("ABC"), '2'},
+	 /* Letters on the '3' key of a numeric keypad */
+	{"3", N_("DEF"), '3'},
+	 /* Letters on the '4' key of a numeric keypad */
+	{"4", N_("GHI"), '4'},
+	 /* Letters on the '5' key of a numeric keypad */
+	{"5", N_("JKL"), '5'},
+	 /* Letters on the '6' key of a numeric keypad */
+	{"6", N_("MNO"), '6'},
+	 /* Letters on the '7' key of a numeric keypad */
+	{"7", N_("PQRS"), '7'},
+	 /* Letters on the '8' key of a numeric keypad */
+	{"8", N_("TUV"), '8'},
+	 /* Letters on the '9' key of a numeric keypad */
+	{"9", N_("WXYZ"), '9'},
+	{"*", NULL, '*'},
+	{"0", NULL, '0'},
+	{"#", NULL, '#'},
+	{NULL, NULL, 0}
+};
+
+static gboolean
+pidgin_media_dtmf_key_press_event_cb(GtkWidget *widget,
+		GdkEvent *event, gpointer user_data)
+{
+	PidginMedia *gtkmedia = user_data;
+	GdkEventKey *key = (GdkEventKey *) event;
+
+	if (event->type != GDK_KEY_PRESS) {
+		return FALSE;
+	}
+
+	if ((key->keyval >= GDK_KEY_0 && key->keyval <= GDK_KEY_9) ||
+		key->keyval == GDK_KEY_asterisk ||
+		key->keyval == GDK_KEY_numbersign) {
+		gchar *sid = g_object_get_data(G_OBJECT(widget), "session-id");
+
+		purple_media_send_dtmf(gtkmedia->priv->media, sid, key->keyval, 25, 50);
+	}
+
+	return FALSE;
+}
+
+static GtkWidget *
+pidgin_media_add_dtmf_widget(PidginMedia *gtkmedia,
+		PurpleMediaSessionType type, const gchar *_sid)
+{
+	GtkWidget *grid = gtk_table_new(4, 3, TRUE);
+	GtkWidget *button;
+	gint index = 0;
+	GtkWindow *win = &gtkmedia->parent;
+
+	/* Add buttons */
+	for (index = 0; phone_labels[index].subtext != NULL; index++) {
+		button = phone_create_button(phone_labels[index].text,
+				phone_labels[index].subtext);
+		g_signal_connect(button, "pressed",
+				G_CALLBACK(phone_dtmf_pressed_cb), gtkmedia);
+		g_object_set_data(G_OBJECT(button), "dtmf-digit",
+				GINT_TO_POINTER(phone_labels[index].chr));
+		g_object_set_data_full(G_OBJECT(button), "session-id",
+				g_strdup(_sid), g_free);
+		gtk_table_attach(GTK_TABLE(grid), button, index % 3,
+				index % 3 + 1, index / 3, index / 3 + 1,
+				GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND,
+				2, 2);
+	}
+
+	g_signal_connect(G_OBJECT(win), "key-press-event",
+		G_CALLBACK(pidgin_media_dtmf_key_press_event_cb), gtkmedia);
+	g_object_set_data_full(G_OBJECT(win), "session-id",
+		g_strdup(_sid), g_free);
+
+	gtk_widget_show_all(grid);
+
+	return grid;
+}
+
+static void
 pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *sid)
 {
 	GtkWidget *send_widget = NULL, *recv_widget = NULL, *button_widget = NULL;
@@ -888,7 +1019,11 @@

 		gtk_box_pack_end(GTK_BOX(recv_widget),
 				pidgin_media_add_audio_widget(gtkmedia,
-				PURPLE_MEDIA_SEND_AUDIO, NULL), FALSE, FALSE, 0);
+				PURPLE_MEDIA_SEND_AUDIO, sid), FALSE, FALSE, 0);
+
+		gtk_box_pack_end(GTK_BOX(recv_widget),
+				pidgin_media_add_dtmf_widget(gtkmedia,
+				PURPLE_MEDIA_SEND_AUDIO, sid), FALSE, FALSE, 0);
 	}

 	if (type & PURPLE_MEDIA_AUDIO &&


code:
# HG changeset patch
# User Youness Alaoui <kakaroto@kakaroto.homelinux.net>
# Date 1405979621 14400
# Node ID 4fe1034f3dce1c5cd3c929ab8c58db8e27655beb
# Parent  d729a9b2126594df3e38647e926ac7c0a7db807b
Add application media type and APIs

Fixes #16315

Index: pidgin-2.10.11/configure.ac
===================================================================
--- pidgin-2.10.11.orig/configure.ac
+++ pidgin-2.10.11/configure.ac
@@ -916,6 +916,20 @@ fi
 AM_CONDITIONAL(USE_VV, test "x$enable_vv" != "xno")

 dnl #######################################################################
+dnl # Check for Raw data streams support in Farstream
+dnl #######################################################################
+if test "x$enable_vv" != "xno" -a "x$with_gstreamer" == "x1.0"; then
+	AC_MSG_CHECKING(for raw data support in Farstream)
+	PKG_CHECK_MODULES(GSTAPP, [gstreamer-app-1.0], [
+		AC_DEFINE(USE_GSTAPP, 1, [Use GStreamer Video Overlay support])
+		AC_SUBST(GSTAPP_CFLAGS)
+		AC_SUBST(GSTAPP_LIBS)
+		AC_DEFINE(HAVE_MEDIA_APPLICATION, 1, [Define if we have support for application media type.])
+		AC_MSG_RESULT(yes)
+		], [AC_MSG_RESULT(no)])
+fi
+
+dnl #######################################################################
 dnl # Check for Internationalized Domain Name support
 dnl #######################################################################

Index: pidgin-2.10.11/libpurple/Makefile.am
===================================================================
--- pidgin-2.10.11.orig/libpurple/Makefile.am
+++ pidgin-2.10.11/libpurple/Makefile.am
@@ -314,6 +314,7 @@ libpurple_la_LIBADD = \
 	$(FARSTREAM_LIBS) \
 	$(GSTREAMER_LIBS) \
 	$(GSTVIDEO_LIBS) \
+	$(GSTAPP_LIBS) \
 	$(GSTINTERFACES_LIBS) \
 	$(IDN_LIBS) \
 	ciphers/libpurple-ciphers.la \
@@ -331,6 +332,7 @@ AM_CPPFLAGS = \
 	$(FARSTREAM_CFLAGS) \
 	$(GSTREAMER_CFLAGS) \
 	$(GSTVIDEO_CFLAGS) \
+	$(GSTAPP_CFLAGS) \
 	$(GSTINTERFACES_CFLAGS) \
 	$(IDN_CFLAGS) \
 	$(NETWORKMANAGER_CFLAGS) \
Index: pidgin-2.10.11/libpurple/media-gst.h
===================================================================
--- pidgin-2.10.11.orig/libpurple/media-gst.h
+++ pidgin-2.10.11/libpurple/media-gst.h
@@ -71,6 +71,7 @@ typedef enum {

 	PURPLE_MEDIA_ELEMENT_SRC = 1 << 9,		/** can be set as an active src */
 	PURPLE_MEDIA_ELEMENT_SINK = 1 << 10,		/** can be set as an active sink */
+	PURPLE_MEDIA_ELEMENT_APPLICATION = 1 << 11,	/** supports application data */
 } PurpleMediaElementType;

 #ifdef __cplusplus
Index: pidgin-2.10.11/libpurple/media/backend-fs2.c
===================================================================
--- pidgin-2.10.11.orig/libpurple/media/backend-fs2.c
+++ pidgin-2.10.11/libpurple/media/backend-fs2.c
@@ -543,6 +543,10 @@ session_type_to_fs_media_type(PurpleMedi
 		return FS_MEDIA_TYPE_AUDIO;
 	else if (type & PURPLE_MEDIA_VIDEO)
 		return FS_MEDIA_TYPE_VIDEO;
+#ifdef HAVE_MEDIA_APPLICATION
+	else if (type & PURPLE_MEDIA_APPLICATION)
+		return FS_MEDIA_TYPE_APPLICATION;
+#endif
 	else
 		return 0;
 }
@@ -551,7 +555,7 @@ static FsStreamDirection
 session_type_to_fs_stream_direction(PurpleMediaSessionType type)
 {
 	if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO ||
-			(type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO)
+		(type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO)
 		return FS_DIRECTION_BOTH;
 	else if ((type & PURPLE_MEDIA_SEND_AUDIO) ||
 			(type & PURPLE_MEDIA_SEND_VIDEO))
@@ -559,6 +563,14 @@ session_type_to_fs_stream_direction(Purp
 	else if ((type & PURPLE_MEDIA_RECV_AUDIO) ||
 			(type & PURPLE_MEDIA_RECV_VIDEO))
 		return FS_DIRECTION_RECV;
+#ifdef HAVE_MEDIA_APPLICATION
+	else if ((type & PURPLE_MEDIA_APPLICATION) == PURPLE_MEDIA_APPLICATION)
+		return FS_DIRECTION_BOTH;
+	else if (type & PURPLE_MEDIA_SEND_APPLICATION)
+		return FS_DIRECTION_SEND;
+	else if (type & PURPLE_MEDIA_RECV_APPLICATION)
+		return FS_DIRECTION_RECV;
+#endif
 	else
 		return FS_DIRECTION_NONE;
 }
@@ -577,6 +589,13 @@ session_type_from_fs(FsMediaType type, F
 			result |= PURPLE_MEDIA_SEND_VIDEO;
 		if (direction & FS_DIRECTION_RECV)
 			result |= PURPLE_MEDIA_RECV_VIDEO;
+#ifdef HAVE_MEDIA_APPLICATION
+	} else if (type == FS_MEDIA_TYPE_APPLICATION) {
+		if (direction & FS_DIRECTION_SEND)
+			result |= PURPLE_MEDIA_SEND_APPLICATION;
+		if (direction & FS_DIRECTION_RECV)
+			result |= PURPLE_MEDIA_RECV_APPLICATION;
+#endif
 	}
 	return result;
 }
@@ -1333,7 +1352,8 @@ gst_handle_message_error(GstBus *bus, Gs
 				& PURPLE_MEDIA_AUDIO)
 			purple_media_error(priv->media,
 					_("Error with your microphone"));
-		else
+		else if (purple_media_get_session_type(priv->media,
+                        sessions->data) & PURPLE_MEDIA_VIDEO)
 			purple_media_error(priv->media,
 					_("Error with your webcam"));

@@ -1756,6 +1776,21 @@ create_session(PurpleMediaBackendFs2 *se
 	session->session = fs_conference_new_session(priv->conference,
 			session_type_to_fs_media_type(type), &err);

+#ifdef HAVE_MEDIA_APPLICATION
+	if (type == PURPLE_MEDIA_APPLICATION) {
+		GstCaps *caps;
+		GObject *rtpsession = NULL;
+
+		caps = gst_caps_new_empty_simple ("application/octet-stream");
+		fs_session_set_allowed_caps (session->session, caps, caps, NULL);
+		gst_caps_unref (caps);
+		g_object_get (session->session, "internal-session", &rtpsession, NULL);
+		if (rtpsession) {
+			g_object_set (rtpsession, "probation", 0, NULL);
+			g_object_unref (rtpsession);
+		}
+	}
+#endif
 	if (err != NULL) {
 		purple_media_error(priv->media,
 				_("Error creating session: %s"),
@@ -1970,6 +2005,21 @@ src_pad_added_cb(FsStream *fsstream, Gst
 			gst_bin_add(GST_BIN(priv->confbin), sink);
 			gst_element_set_state(sink, GST_STATE_PLAYING);
 			stream->fakesink = sink;
+#ifdef HAVE_MEDIA_APPLICATION
+		} else if (codec->media_type == FS_MEDIA_TYPE_APPLICATION) {
+#if GST_CHECK_VERSION(1,0,0)
+			stream->src = gst_element_factory_make("funnel", NULL);
+#else
+			stream->src = gst_element_factory_make("fsfunnel", NULL);
+#endif
+			sink = purple_media_manager_get_element(
+					purple_media_get_manager(priv->media),
+					PURPLE_MEDIA_RECV_APPLICATION, priv->media,
+					stream->session->id,
+					stream->participant);
+			gst_bin_add(GST_BIN(priv->confbin), sink);
+			gst_element_set_state(sink, GST_STATE_PLAYING);
+#endif
 		}
 		stream->tee = gst_element_factory_make("tee", NULL);
 		gst_bin_add_many(GST_BIN(priv->confbin),
@@ -2332,6 +2382,9 @@ purple_media_backend_fs2_codecs_ready(Pu
 			return FALSE;

 		if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
+#ifdef HAVE_MEDIA_APPLICATION
+				PURPLE_MEDIA_SEND_APPLICATION |
+#endif
 				PURPLE_MEDIA_SEND_VIDEO)) {
 #ifdef HAVE_FARSIGHT
 			g_object_get(session->session,
@@ -2355,6 +2408,9 @@ purple_media_backend_fs2_codecs_ready(Pu
 			PurpleMediaBackendFs2Session *session = values->data;

 			if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
+#ifdef HAVE_MEDIA_APPLICATION
+					PURPLE_MEDIA_SEND_APPLICATION |
+#endif
 					PURPLE_MEDIA_SEND_VIDEO)) {
 #ifdef HAVE_FARSIGHT
 				g_object_get(session->session,
Index: pidgin-2.10.11/libpurple/media/codec.c
===================================================================
--- pidgin-2.10.11.orig/libpurple/media/codec.c
+++ pidgin-2.10.11/libpurple/media/codec.c
@@ -188,7 +188,7 @@ purple_media_codec_class_init(PurpleMedi
 	g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE,
 			g_param_spec_flags("media-type",
 			"Media Type",
-			"Whether this is an audio of video codec.",
+			"Whether this is an audio, video or application codec.",
 			PURPLE_TYPE_MEDIA_SESSION_TYPE,
 			PURPLE_MEDIA_NONE,
 			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
@@ -402,6 +402,8 @@ purple_media_codec_to_string(const Purpl
 		media_type_str = "audio";
 	else if (priv->media_type & PURPLE_MEDIA_VIDEO)
 		media_type_str = "video";
+	else if (priv->media_type & PURPLE_MEDIA_APPLICATION)
+		media_type_str = "application";

 	g_string_printf(string, "%d: %s %s clock:%d channels:%d", priv->id,
 			media_type_str, priv->encoding_name,
Index: pidgin-2.10.11/libpurple/media/enum-types.c
===================================================================
--- pidgin-2.10.11.orig/libpurple/media/enum-types.c
+++ pidgin-2.10.11/libpurple/media/enum-types.c
@@ -176,10 +176,16 @@ purple_media_session_type_get_type()
 				"PURPLE_MEDIA_RECV_VIDEO", "recv-video" },
 			{ PURPLE_MEDIA_SEND_VIDEO,
 				"PURPLE_MEDIA_SEND_VIDEO", "send-video" },
+			{ PURPLE_MEDIA_RECV_APPLICATION,
+				"PURPLE_MEDIA_RECV_APPLICATION", "recv-application" },
+			{ PURPLE_MEDIA_SEND_APPLICATION,
+				"PURPLE_MEDIA_SEND_APPLICATION", "send-application" },
 			{ PURPLE_MEDIA_AUDIO,
 				"PURPLE_MEDIA_AUDIO", "audio" },
 			{ PURPLE_MEDIA_VIDEO,
 				"PURPLE_MEDIA_VIDEO", "video" },
+			{ PURPLE_MEDIA_APPLICATION,
+				"PURPLE_MEDIA_APPLICATION", "application" },
 			{ 0, NULL, NULL }
 		};
 		type = g_flags_register_static(
Index: pidgin-2.10.11/libpurple/media/enum-types.h
===================================================================
--- pidgin-2.10.11.orig/libpurple/media/enum-types.h
+++ pidgin-2.10.11/libpurple/media/enum-types.h
@@ -92,8 +92,12 @@ typedef enum {
 	PURPLE_MEDIA_SEND_AUDIO = 1 << 1,
 	PURPLE_MEDIA_RECV_VIDEO = 1 << 2,
 	PURPLE_MEDIA_SEND_VIDEO = 1 << 3,
+	PURPLE_MEDIA_RECV_APPLICATION = 1 << 4,
+	PURPLE_MEDIA_SEND_APPLICATION = 1 << 5,
 	PURPLE_MEDIA_AUDIO = PURPLE_MEDIA_RECV_AUDIO | PURPLE_MEDIA_SEND_AUDIO,
-	PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO
+	PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO,
+	PURPLE_MEDIA_APPLICATION = PURPLE_MEDIA_RECV_APPLICATION |
+                                   PURPLE_MEDIA_SEND_APPLICATION
 } PurpleMediaSessionType;

 /** Media state-changed types */
Index: pidgin-2.10.11/libpurple/mediamanager.c
===================================================================
--- pidgin-2.10.11.orig/libpurple/mediamanager.c
+++ pidgin-2.10.11/libpurple/mediamanager.c
@@ -44,6 +44,9 @@
 #else
 #include <farstream/fs-element-added-notifier.h>
 #endif
+#ifdef HAVE_MEDIA_APPLICATION
+#include <gst/app/app.h>
+#endif

 #if GST_CHECK_VERSION(1,0,0)
 #include <gst/video/videooverlay.h>
@@ -97,14 +100,45 @@ struct _PurpleMediaManagerPrivate
 	PurpleMediaElementInfo *video_sink;
 	PurpleMediaElementInfo *audio_src;
 	PurpleMediaElementInfo *audio_sink;
+
+#ifdef HAVE_MEDIA_APPLICATION
+	/* Application data streams */
+	GList *appdata_info; /* holds PurpleMediaAppDataInfo */
+	GMutex appdata_mutex;
+#endif
 };

+#ifdef HAVE_MEDIA_APPLICATION
+typedef struct {
+	PurpleMedia *media;
+	GWeakRef media_ref;
+	gchar *session_id;
+	gchar *participant;
+	PurpleMediaAppDataCallbacks callbacks;
+	gpointer user_data;
+	GDestroyNotify notify;
+	GstAppSrc *appsrc;
+	GstAppSink *appsink;
+	gint num_samples;
+	GstSample *current_sample;
+	guint sample_offset;
+	gboolean writable;
+	gboolean connected;
+	guint writable_timer_id;
+	guint readable_timer_id;
+	GCond readable_cond;
+} PurpleMediaAppDataInfo;
+#endif
+
 #define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate))
 #define PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfoPrivate))

 static void purple_media_manager_class_init (PurpleMediaManagerClass *klass);
 static void purple_media_manager_init (PurpleMediaManager *media);
 static void purple_media_manager_finalize (GObject *object);
+#ifdef HAVE_MEDIA_APPLICATION
+static void free_appdata_info_locked (PurpleMediaAppDataInfo *info);
+#endif

 static GObjectClass *parent_class = NULL;

@@ -190,8 +224,10 @@ purple_media_manager_init (PurpleMediaMa
 	media->priv->medias = NULL;
 	media->priv->private_medias = NULL;
 	media->priv->next_output_window_id = 1;
-#ifdef USE_VV
 	media->priv->backend_type = PURPLE_TYPE_MEDIA_BACKEND_FS2;
+#ifdef HAVE_MEDIA_APPLICATION
+	media->priv->appdata_info = NULL;
+	g_mutex_init (&media->priv->appdata_mutex);
 #endif

 	purple_prefs_add_none("/purple/media");
@@ -220,6 +256,13 @@ purple_media_manager_finalize (GObject *
 	}
 	if (priv->video_caps)
 		gst_caps_unref(priv->video_caps);
+#ifdef HAVE_MEDIA_APPLICATION
+	if (priv->appdata_info)
+		g_list_free_full (priv->appdata_info,
+			(GDestroyNotify) free_appdata_info_locked);
+	g_mutex_clear (&priv->appdata_mutex);
+#endif
+
 	parent_class->finalize(media);
 }
 #endif
@@ -440,8 +483,23 @@ purple_media_manager_remove_media(Purple
 		medias = &manager->priv->private_medias;
 	}

-	if (list)
+	if (list) {
 		*medias = g_list_delete_link(*medias, list);
+
+#ifdef HAVE_MEDIA_APPLICATION
+		g_mutex_lock (&manager->priv->appdata_mutex);
+		for (list = manager->priv->appdata_info; list; list = list->next) {
+			PurpleMediaAppDataInfo *info = list->data;
+
+			if (info->media == media) {
+				manager->priv->appdata_info = g_list_delete_link (
+					manager->priv->appdata_info, list);
+				free_appdata_info_locked (info);
+			}
+		}
+		g_mutex_unlock (&manager->priv->appdata_mutex);
+#endif
+	}
 #endif
 }

@@ -493,6 +551,92 @@ purple_media_manager_get_private_media_b
 	return get_media_by_account (manager, account, TRUE);
 }

+#ifdef HAVE_MEDIA_APPLICATION
+static void
+free_appdata_info_locked (PurpleMediaAppDataInfo *info)
+{
+	if (info->notify)
+		info->notify (info->user_data);
+
+	/* Make sure no other thread is using the structure */
+	g_free (info->session_id);
+	g_free (info->participant);
+
+	if (info->readable_timer_id) {
+		purple_timeout_remove (info->readable_timer_id);
+		info->readable_timer_id = 0;
+	}
+
+	if (info->writable_timer_id) {
+		purple_timeout_remove (info->writable_timer_id);
+		info->writable_timer_id = 0;
+	}
+
+	if (info->current_sample)
+		gst_sample_unref (info->current_sample);
+	info->current_sample = NULL;
+
+	/* Unblock any reading thread before destroying the GCond */
+	g_cond_broadcast (&info->readable_cond);
+
+	g_cond_clear (&info->readable_cond);
+
+	g_slice_free (PurpleMediaAppDataInfo, info);
+}
+
+/*
+ * Get an app data info struct associated with a session and lock the mutex
+ * We don't want to return an info struct and unlock then it gets destroyed
+ * so we need to return it with the lock still taken
+ */
+static PurpleMediaAppDataInfo *
+get_app_data_info_and_lock (PurpleMediaManager *manager,
+	PurpleMedia *media, const gchar *session_id, const gchar *participant)
+{
+	GList *i;
+
+	g_mutex_lock (&manager->priv->appdata_mutex);
+	for (i = manager->priv->appdata_info; i; i = i->next) {
+		PurpleMediaAppDataInfo *info = i->data;
+
+		if (info->media == media &&
+			g_strcmp0 (info->session_id, session_id) == 0 &&
+			(participant == NULL ||
+				g_strcmp0 (info->participant, participant) == 0)) {
+			return info;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Get an app data info struct associated with a session and lock the mutex
+ * if it doesn't exist, we create it.
+ */
+static PurpleMediaAppDataInfo *
+ensure_app_data_info_and_lock (PurpleMediaManager *manager, PurpleMedia *media,
+	const gchar *session_id, const gchar *participant)
+{
+	PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager, media,
+		session_id, participant);
+
+	if (info == NULL) {
+		info = g_slice_new0 (PurpleMediaAppDataInfo);
+		info->media = media;
+		g_weak_ref_init (&info->media_ref, media);
+		info->session_id = g_strdup (session_id);
+		info->participant = g_strdup (participant);
+		g_cond_init (&info->readable_cond);
+		manager->priv->appdata_info = g_list_prepend (
+			manager->priv->appdata_info, info);
+	}
+
+	return info;
+}
+#endif
+
+
 #ifdef USE_VV
 static void
 request_pad_unlinked_cb(GstPad *pad, GstPad *peer, gpointer user_data)
@@ -577,6 +721,351 @@ purple_media_manager_get_video_caps(Purp
 #endif
 }

+#ifdef HAVE_MEDIA_APPLICATION
+/*
+ * Calls the appdata writable callback from the main thread.
+ * This needs to grab the appdata lock and make sure it didn't get destroyed
+ * before calling the callback.
+ */
+static gboolean
+appsrc_writable (gpointer user_data)
+{
+	PurpleMediaManager *manager = purple_media_manager_get ();
+	PurpleMediaAppDataInfo *info = user_data;
+	void (*writable_cb) (PurpleMediaManager *manager, PurpleMedia *media,
+		const gchar *session_id, const gchar *participant, gboolean writable,
+		gpointer user_data);
+	PurpleMedia *media;
+	gchar *session_id;
+	gchar *participant;
+	gboolean writable;
+	gpointer cb_data;
+	guint *timer_id_ptr = &info->writable_timer_id;
+	guint timer_id = *timer_id_ptr;
+
+
+	g_mutex_lock (&manager->priv->appdata_mutex);
+	if (timer_id == 0 || timer_id != *timer_id_ptr) {
+		/* In case info was freed while we were waiting for the mutex to unlock
+		 * we still have a pointer to the timer_id which should still be
+		 * accessible since it's in the Glib slice allocator. It gets set to 0
+		 * just after the timeout is canceled which happens also before the
+		 * AppDataInfo is freed, so even if that memory slice gets reused, the
+		 * timer_id would be different from its previous value (unless
+		 * extremely unlucky). So checking if the value for the timer_id changed
+		 * should be enough to prevent any kind of race condition in which the
+		 * media/AppDataInfo gets destroyed in one thread while the timeout was
+		 * triggered and is waiting on the mutex to get unlocked in this thread
+		 */
+		g_mutex_unlock (&manager->priv->appdata_mutex);
+		return FALSE;
+	}
+	writable_cb = info->callbacks.writable;
+	media = g_weak_ref_get (&info->media_ref);
+	session_id = g_strdup (info->session_id);
+	participant = g_strdup (info->participant);
+	writable = info->writable && info->connected;
+	cb_data = info->user_data;
+
+    info->writable_timer_id = 0;
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+
+
+	if (writable_cb && media)
+		writable_cb (manager, media, session_id, participant, writable,
+			cb_data);
+
+	g_object_unref (media);
+	g_free (session_id);
+	g_free (participant);
+
+	return FALSE;
+}
+
+/*
+ * Schedule a writable callback to be called from the main thread.
+ * We need to do this because need-data/enough-data signals from appsrc
+ * will come from the streaming thread and we need to create
+ * a source that we attach to the main context but we can't use
+ * g_main_context_invoke since we need to be able to cancel the source if the
+ * media gets destroyed.
+ * We use a timeout source instead of idle source, so the callback gets a higher
+ * priority
+ */
+static void
+call_appsrc_writable_locked (PurpleMediaAppDataInfo *info)
+{
+	/* We already have a writable callback scheduled, don't create another one */
+	if (info->writable_timer_id || info->callbacks.writable == NULL)
+		return;
+
+	info->writable_timer_id = purple_timeout_add (0, appsrc_writable, info);
+}
+
+static void
+appsrc_need_data (GstAppSrc *appsrc, guint length, gpointer user_data)
+{
+	PurpleMediaAppDataInfo *info = user_data;
+	PurpleMediaManager *manager = purple_media_manager_get ();
+
+	g_mutex_lock (&manager->priv->appdata_mutex);
+	if (!info->writable) {
+		info->writable = TRUE;
+		/* Only signal writable if we also established a connection */
+		if (info->connected)
+			call_appsrc_writable_locked (info);
+	}
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+}
+
+static void
+appsrc_enough_data (GstAppSrc *appsrc, gpointer user_data)
+{
+	PurpleMediaAppDataInfo *info = user_data;
+	PurpleMediaManager *manager = purple_media_manager_get ();
+
+	g_mutex_lock (&manager->priv->appdata_mutex);
+	if (info->writable) {
+		info->writable = FALSE;
+		call_appsrc_writable_locked (info);
+	}
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+}
+
+static gboolean
+appsrc_seek_data (GstAppSrc *appsrc, guint64 offset, gpointer user_data)
+{
+	return FALSE;
+}
+
+static void
+appsrc_destroyed (PurpleMediaAppDataInfo *info)
+{
+	PurpleMediaManager *manager = purple_media_manager_get ();
+
+	g_mutex_lock (&manager->priv->appdata_mutex);
+	info->appsrc = NULL;
+	if (info->writable) {
+		info->writable = FALSE;
+		call_appsrc_writable_locked (info);
+	}
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+}
+
+static void
+media_established_cb (PurpleMedia *media,const gchar *session_id,
+	const gchar *participant, PurpleMediaCandidate *local_candidate,
+	PurpleMediaCandidate *remote_candidate, PurpleMediaAppDataInfo *info)
+{
+	PurpleMediaManager *manager = purple_media_manager_get ();
+
+	g_mutex_lock (&manager->priv->appdata_mutex);
+	info->connected = TRUE;
+	/* We established the connection, if we were writable, then we need to
+	 * signal it now */
+	if (info->writable)
+		call_appsrc_writable_locked (info);
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+}
+
+static GstElement *
+create_send_appsrc(PurpleMedia *media,
+		const gchar *session_id, const gchar *participant)
+{
+	PurpleMediaManager *manager = purple_media_manager_get ();
+	PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager,
+		media, session_id, participant);
+	GstElement *appsrc = (GstElement *)info->appsrc;
+
+	if (appsrc == NULL) {
+		GstAppSrcCallbacks callbacks = {appsrc_need_data, appsrc_enough_data,
+										appsrc_seek_data, {NULL}};
+		GstCaps *caps = gst_caps_new_empty_simple ("application/octet-stream");
+
+		appsrc = gst_element_factory_make("appsrc", NULL);
+
+		info->appsrc = (GstAppSrc *)appsrc;
+
+		gst_app_src_set_caps (info->appsrc, caps);
+		gst_app_src_set_callbacks (info->appsrc,
+			&callbacks, info, (GDestroyNotify) appsrc_destroyed);
+		g_signal_connect (media, "candidate-pair-established",
+			(GCallback) media_established_cb, info);
+		gst_caps_unref (caps);
+	}
+
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+	return appsrc;
+}
+
+static void
+appsink_eos (GstAppSink *appsink, gpointer user_data)
+{
+}
+
+static GstFlowReturn
+appsink_new_preroll (GstAppSink *appsink, gpointer user_data)
+{
+	return GST_FLOW_OK;
+}
+
+static gboolean
+appsink_readable (gpointer user_data)
+{
+	PurpleMediaManager *manager = purple_media_manager_get ();
+	PurpleMediaAppDataInfo *info = user_data;
+	void (*readable_cb) (PurpleMediaManager *manager, PurpleMedia *media,
+		const gchar *session_id, const gchar *participant, gpointer user_data);
+	PurpleMedia *media;
+	gchar *session_id;
+	gchar *participant;
+	gpointer cb_data;
+	guint *timer_id_ptr = &info->readable_timer_id;
+	guint timer_id = *timer_id_ptr;
+
+	g_mutex_lock (&manager->priv->appdata_mutex);
+	if (timer_id == 0 || timer_id != *timer_id_ptr) {
+		/* Avoided a race condition (see writable callback) */
+		g_mutex_unlock (&manager->priv->appdata_mutex);
+		return FALSE;
+	}
+	/* We need to signal readable until there are no more samples */
+	while (info->callbacks.readable &&
+		(info->num_samples > 0 || info->current_sample != NULL)) {
+		readable_cb = info->callbacks.readable;
+		media = g_weak_ref_get (&info->media_ref);
+		session_id = g_strdup (info->session_id);
+		participant = g_strdup (info->participant);
+		cb_data = info->user_data;
+		g_mutex_unlock (&manager->priv->appdata_mutex);
+
+		if (readable_cb)
+			readable_cb (manager, media, session_id, participant, cb_data);
+
+		g_mutex_lock (&manager->priv->appdata_mutex);
+		g_object_unref (media);
+		g_free (session_id);
+		g_free (participant);
+		if (timer_id == 0 || timer_id != *timer_id_ptr) {
+			/* We got cancelled */
+			g_mutex_unlock (&manager->priv->appdata_mutex);
+			return FALSE;
+		}
+	}
+    info->readable_timer_id = 0;
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+	return FALSE;
+}
+
+static void
+call_appsink_readable_locked (PurpleMediaAppDataInfo *info)
+{
+	/* We must signal that a new sample has arrived to release blocking reads */
+	g_cond_broadcast (&info->readable_cond);
+
+	/* We already have a writable callback scheduled, don't create another one */
+	if (info->readable_timer_id || info->callbacks.readable == NULL)
+		return;
+
+	info->readable_timer_id = purple_timeout_add (0, appsink_readable, info);
+}
+
+static GstFlowReturn
+appsink_new_sample (GstAppSink *appsink, gpointer user_data)
+{
+	PurpleMediaManager *manager = purple_media_manager_get ();
+	PurpleMediaAppDataInfo *info = user_data;
+
+	g_mutex_lock (&manager->priv->appdata_mutex);
+	info->num_samples++;
+	call_appsink_readable_locked (info);
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+
+	return GST_FLOW_OK;
+}
+
+static void
+appsink_destroyed (PurpleMediaAppDataInfo *info)
+{
+	PurpleMediaManager *manager = purple_media_manager_get ();
+
+	g_mutex_lock (&manager->priv->appdata_mutex);
+	info->appsink = NULL;
+	info->num_samples = 0;
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+}
+
+static GstElement *
+create_recv_appsink(PurpleMedia *media,
+		const gchar *session_id, const gchar *participant)
+{
+	PurpleMediaManager *manager = purple_media_manager_get ();
+	PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager,
+		media, session_id, participant);
+	GstElement *appsink = (GstElement *)info->appsink;
+
+	if (appsink == NULL) {
+		GstAppSinkCallbacks callbacks = {appsink_eos, appsink_new_preroll,
+										 appsink_new_sample, {NULL}};
+		GstCaps *caps = gst_caps_new_empty_simple ("application/octet-stream");
+
+		appsink = gst_element_factory_make("appsink", NULL);
+
+		info->appsink = (GstAppSink *)appsink;
+
+		gst_app_sink_set_caps (info->appsink, caps);
+		gst_app_sink_set_callbacks (info->appsink,
+			&callbacks, info, (GDestroyNotify) appsink_destroyed);
+		gst_caps_unref (caps);
+
+	}
+
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+	return appsink;
+}
+#endif
+
+static PurpleMediaElementInfo *
+get_send_application_element_info ()
+{
+	static PurpleMediaElementInfo *info = NULL;
+
+#ifdef HAVE_MEDIA_APPLICATION
+	if (info == NULL) {
+		info = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
+			"id", "pidginappsrc",
+			"name", "Pidgin Application Source",
+			"type", PURPLE_MEDIA_ELEMENT_APPLICATION
+					| PURPLE_MEDIA_ELEMENT_SRC
+					| PURPLE_MEDIA_ELEMENT_ONE_SRC,
+			"create-cb", create_send_appsrc, NULL);
+	}
+#endif
+
+	return info;
+}
+
+
+static PurpleMediaElementInfo *
+get_recv_application_element_info ()
+{
+	static PurpleMediaElementInfo *info = NULL;
+
+#ifdef HAVE_MEDIA_APPLICATION
+	if (info == NULL) {
+		info = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
+			"id", "pidginappsink",
+			"name", "Pidgin Application Sink",
+			"type", PURPLE_MEDIA_ELEMENT_APPLICATION
+					| PURPLE_MEDIA_ELEMENT_SINK
+					| PURPLE_MEDIA_ELEMENT_ONE_SINK,
+			"create-cb", create_recv_appsink, NULL);
+	}
+#endif
+
+	return info;
+}
+
 GstElement *
 purple_media_manager_get_element(PurpleMediaManager *manager,
 		PurpleMediaSessionType type, PurpleMedia *media,
@@ -595,6 +1084,10 @@ purple_media_manager_get_element(PurpleM
 		info = manager->priv->video_src;
 	else if (type & PURPLE_MEDIA_RECV_VIDEO)
 		info = manager->priv->video_sink;
+	else if (type & PURPLE_MEDIA_SEND_APPLICATION)
+		info = get_send_application_element_info ();
+	else if (type & PURPLE_MEDIA_RECV_APPLICATION)
+		info = get_recv_application_element_info ();

 	if (info == NULL)
 		return NULL;
@@ -834,11 +1327,16 @@ purple_media_manager_get_active_element(
 			return manager->priv->audio_src;
 		else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
 			return manager->priv->video_src;
+		else if (type & PURPLE_MEDIA_ELEMENT_APPLICATION)
+			return get_send_application_element_info ();
 	} else if (type & PURPLE_MEDIA_ELEMENT_SINK) {
 		if (type & PURPLE_MEDIA_ELEMENT_AUDIO)
 			return manager->priv->audio_sink;
 		else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
 			return manager->priv->video_sink;
+		else if (type & PURPLE_MEDIA_ELEMENT_APPLICATION)
+			return get_recv_application_element_info ();
+
 	}
 #endif

@@ -1138,6 +1636,174 @@ purple_media_manager_get_backend_type(Pu
 #endif
 }

+void
+purple_media_manager_set_application_data_callbacks(PurpleMediaManager *manager,
+		PurpleMedia *media, const gchar *session_id,
+		const gchar *participant, PurpleMediaAppDataCallbacks *callbacks,
+		gpointer user_data, GDestroyNotify notify)
+{
+#ifdef HAVE_MEDIA_APPLICATION
+	PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager,
+		media, session_id, participant);
+
+	if (info->notify)
+		info->notify (info->user_data);
+
+	if (info->readable_timer_id) {
+		purple_timeout_remove (info->readable_timer_id);
+		info->readable_timer_id = 0;
+	}
+
+	if (info->writable_timer_id) {
+		purple_timeout_remove (info->writable_timer_id);
+		info->writable_timer_id = 0;
+	}
+
+	if (callbacks) {
+		info->callbacks = *callbacks;
+	} else {
+		info->callbacks.writable = NULL;
+		info->callbacks.readable = NULL;
+	}
+	info->user_data = user_data;
+	info->notify = notify;
+
+	call_appsrc_writable_locked (info);
+	if (info->num_samples > 0 || info->current_sample != NULL)
+		call_appsink_readable_locked (info);
+
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+#endif
+}
+
+gint
+purple_media_manager_send_application_data (
+	PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
+	const gchar *participant, gpointer buffer, guint size, gboolean blocking)
+{
+#ifdef HAVE_MEDIA_APPLICATION
+	PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager,
+		media, session_id, participant);
+
+	if (info && info->appsrc && info->connected) {
+		GstBuffer *gstbuffer = gst_buffer_new_wrapped (g_memdup (buffer, size),
+			size);
+		GstAppSrc *appsrc = gst_object_ref (info->appsrc);
+
+		g_mutex_unlock (&manager->priv->appdata_mutex);
+		if (gst_app_src_push_buffer (appsrc, gstbuffer) == GST_FLOW_OK) {
+			if (blocking) {
+				GstPad *srcpad;
+
+				srcpad = gst_element_get_static_pad (GST_ELEMENT (appsrc),
+					"src");
+				if (srcpad) {
+					gst_pad_peer_query (srcpad, gst_query_new_drain ());
+					gst_object_unref (srcpad);
+				}
+			}
+			gst_object_unref (appsrc);
+			return size;
+		} else {
+			gst_object_unref (appsrc);
+			return -1;
+		}
+	}
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+	return -1;
+#else
+	return -1;
+#endif
+}
+
+gint
+purple_media_manager_receive_application_data (
+	PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
+	const gchar *participant, gpointer buffer, guint max_size,
+	gboolean blocking)
+{
+#ifdef HAVE_MEDIA_APPLICATION
+	PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager,
+		media, session_id, participant);
+	guint bytes_read = 0;
+
+	if (info) {
+		/* If we are in a blocking read, we need to loop until max_size data
+		 * is read into the buffer, if we're not, then we need to read as much
+		 * data as possible
+		 */
+		do {
+			if (!info->current_sample && info->appsink && info->num_samples > 0) {
+				info->current_sample = gst_app_sink_pull_sample (info->appsink);
+				info->sample_offset = 0;
+				if (info->current_sample)
+					info->num_samples--;
+			}
+
+			if (info->current_sample) {
+				GstBuffer *gstbuffer = gst_sample_get_buffer (
+					info->current_sample);
+
+				if (gstbuffer) {
+					GstMapInfo mapinfo;
+					guint bytes_to_copy;
+
+					gst_buffer_map (gstbuffer, &mapinfo, GST_MAP_READ);
+					/* We must copy only the data remaining in the buffer without
+					 * overflowing the buffer */
+					bytes_to_copy = max_size - bytes_read;
+					if (bytes_to_copy > mapinfo.size - info->sample_offset)
+						bytes_to_copy = mapinfo.size - info->sample_offset;
+					memcpy ((guint8 *)buffer + bytes_read,
+						mapinfo.data + info->sample_offset,	bytes_to_copy);
+
+					gst_buffer_unmap (gstbuffer, &mapinfo);
+					info->sample_offset += bytes_to_copy;
+					bytes_read += bytes_to_copy;
+					if (info->sample_offset == mapinfo.size) {
+						gst_sample_unref (info->current_sample);
+						info->current_sample = NULL;
+						info->sample_offset = 0;
+					}
+				} else {
+					/* In case there's no buffer in the sample (should never
+					 * happen), we need to at least unref it */
+					gst_sample_unref (info->current_sample);
+					info->current_sample = NULL;
+					info->sample_offset = 0;
+				}
+			}
+
+			/* If blocking, wait until there's an available sample */
+			while (bytes_read < max_size && blocking &&
+				info->current_sample == NULL && info->num_samples == 0) {
+				g_cond_wait (&info->readable_cond, &manager->priv->appdata_mutex);
+
+				/* We've been signaled, we need to unlock and regrab the info
+				 * struct to make sure nothing changed */
+				g_mutex_unlock (&manager->priv->appdata_mutex);
+				info = get_app_data_info_and_lock (manager,
+					media, session_id, participant);
+				if (info == NULL || info->appsink == NULL) {
+					/* The session was destroyed while we were waiting, we
+					 * should return here */
+					g_mutex_unlock (&manager->priv->appdata_mutex);
+					return bytes_read;
+				}
+			}
+		} while (bytes_read < max_size &&
+			(blocking || info->num_samples > 0));
+
+		g_mutex_unlock (&manager->priv->appdata_mutex);
+		return bytes_read;
+	}
+	g_mutex_unlock (&manager->priv->appdata_mutex);
+	return -1;
+#else
+	return -1;
+#endif
+}
+
 #ifdef USE_GSTREAMER

 /*
@@ -1185,6 +1851,8 @@ purple_media_element_type_get_type()
 				"PURPLE_MEDIA_ELEMENT_SRC", "src" },
 			{ PURPLE_MEDIA_ELEMENT_SINK,
 				"PURPLE_MEDIA_ELEMENT_SINK", "sink" },
+			{ PURPLE_MEDIA_ELEMENT_APPLICATION,
+				"PURPLE_MEDIA_ELEMENT_APPLICATION", "application" },
 			{ 0, NULL, NULL }
 		};
 		type = g_flags_register_static(
@@ -1410,5 +2078,6 @@ purple_media_element_info_call_create(Pu
 	return NULL;
 }

+
 #endif /* USE_GSTREAMER */

Index: pidgin-2.10.11/libpurple/mediamanager.h
===================================================================
--- pidgin-2.10.11.orig/libpurple/mediamanager.h
+++ pidgin-2.10.11/libpurple/mediamanager.h
@@ -38,6 +38,30 @@ typedef struct _PurpleMediaManagerClass
 #include "account.h"
 #include "media.h"

+/**
+ * PurpleMediaAppDataCallbacks:
+ * @readable: Called when the stream has received data and is readable.
+ * @writable: Called when the stream has become writable or has stopped being
+ * writable.
+ *
+ * A set of callbacks that can be installed on an Application data session with
+ * purple_media_manager_set_application_data_callbacks()
+ *
+ * Once installed the @readable callback will get called as long as data is
+ * available to read, so the data must be read completely.
+ * The @writable callback will only be called when the writable state of the
+ * stream changes. The @writable argument defines whether the stream has
+ * become writable or stopped being writable.
+ *
+ */
+typedef struct {
+	void      (*readable)    (PurpleMediaManager *manager, PurpleMedia *media,
+		const gchar *session_id, const gchar *participant, gpointer user_data);
+	void      (*writable)    (PurpleMediaManager *manager, PurpleMedia *media,
+		const gchar *session_id, const gchar *participant, gboolean writable,
+		gpointer user_data);
+} PurpleMediaAppDataCallbacks;
+
 G_BEGIN_DECLS

 #define PURPLE_TYPE_MEDIA_MANAGER            (purple_media_manager_get_type())
@@ -285,6 +309,71 @@ void purple_media_manager_set_backend_ty
  */
 GType purple_media_manager_get_backend_type(PurpleMediaManager *manager);

+/**
+ * purple_media_manager_set_application_data_callbacks:
+ * @manager: The manager to register the callbacks with.
+ * @media: The media instance to register the callbacks with.
+ * @session_id: The session to register the callbacks with.
+ * @participant: The participant to register the callbacks with.
+ * @callbacks: The callbacks to be set on the session.
+ * @user_data: a user_data argument for the callbacks.
+ * @notify: a destroy notify function.
+ *
+ * Set callbacks on a session to be called when the stream becomes writable
+ * or readable for media sessions of type #PURPLE_MEDIA_APPLICATION
+ */
+void purple_media_manager_set_application_data_callbacks(
+	PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
+	const gchar *participant, PurpleMediaAppDataCallbacks *callbacks,
+	gpointer user_data, GDestroyNotify notify);
+
+/**
+ * purple_media_manager_send_application_data:
+ * @manager: The manager to send data with.
+ * @media: The media instance to which the session belongs.
+ * @session_id: The session to send data to.
+ * @participant: The participant to send data to.
+ * @buffer: The buffer of data to send.
+ * @size: The size of @buffer
+ * @blocking: Whether to block until the data was send or not.
+ *
+ * Sends a buffer of data to a #PURPLE_MEDIA_APPLICATION session.
+ * If @blocking is set, unless an error occured, the function will not return
+ * until the data has been flushed into the network.
+ * If the stream is not writable, the data will be queued. It is the
+ * responsability of the user to stop sending data when the stream isn't
+ * writable anymore. It is also the responsability of the user to only start
+ * sending data after the stream has been configured correctly (encryption
+ * parameters for example).
+ *
+ * Returns: Number of bytes sent or -1 in case of error.
+ */
+gint purple_media_manager_send_application_data (
+	PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
+	const gchar *participant, gpointer buffer, guint size, gboolean blocking);
+
+/**
+ * purple_media_manager_receive_application_data:
+ * @manager: The manager to receive data with.
+ * @media: The media instance to which the session belongs.
+ * @session_id: The session to receive data from.
+ * @participant: The participant to receive data from.
+ * @buffer: The buffer to receive data into.
+ * @max_size: The max_size of @buffer
+ * @blocking: Whether to block until the buffer is entirely filled or return
+ * with currently available data.
+ *
+ * Receive a buffer of data from a #PURPLE_MEDIA_APPLICATION session.
+ * If @blocking is set, unless an error occured, the function will not return
+ * until @max_size bytes are read.
+ *
+ * Returns: Number of bytes received or -1 in case of error.
+ */
+gint purple_media_manager_receive_application_data (
+	PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
+	const gchar *participant, gpointer buffer, guint max_size,
+	gboolean blocking);
+
 /*}@*/

 #ifdef __cplusplus


code:
--- a/finch/gntsound.c
+++ b/finch/gntsound.c
@@ -449,6 +449,7 @@ bus_call (GstBus *bus, GstMessage *msg,
 	case GST_MESSAGE_EOS:
 		gst_element_set_state(play, GST_STATE_NULL);
 		gst_object_unref(GST_OBJECT(play));
+		return FALSE;
 		break;
 	case GST_MESSAGE_WARNING:
 		gst_message_parse_warning(msg, &err, NULL);
--- a/libpurple/mediamanager.c
+++ b/libpurple/mediamanager.c
@@ -288,6 +288,7 @@ pipeline_bus_call(GstBus *bus, GstMessag
 	switch(GST_MESSAGE_TYPE(msg)) {
 		case GST_MESSAGE_EOS:
 			purple_debug_info("mediamanager", "End of Stream\n");
+			return FALSE;
 			break;
 		case GST_MESSAGE_ERROR: {
 			gchar *debug = NULL;
@@ -305,6 +306,7 @@
 						"Debug details: %s\n", debug);
 				g_free (debug);
 			}
+			return FALSE;
 			break;
 		}
 		default:
--- a/pidgin/gtksound.c
+++ b/pidgin/gtksound.c
@@ -394,6 +394,7 @@ bus_call (GstBus     *bus,
 	case GST_MESSAGE_EOS:
 		gst_element_set_state(play, GST_STATE_NULL);
 		gst_object_unref(GST_OBJECT(play));
+		return FALSE;
 		break;
 	case GST_MESSAGE_WARNING:
 		gst_message_parse_warning(msg, &err, NULL);


code:
# HG changeset patch
# User Youness Alaoui <kakaroto@kakaroto.homelinux.net>
# Date 1404874798 14400
# Node ID b52be4fef1ded825e262095480915f2c675a5694
# Parent  2b41ba1fde8a80a4f2d715874a0b02449d4b66ad
Fix gstreamer elements references
In backend-fs2, the create_src will unref the src after it's done with
it, if we simply return the created source, it will segfault.
The issue never happened before because every source so far also has
the UNIQUE flag, which causes it to go in a different branch of the
code which does ref the element and add it to the bin.

Refs #16315

diff --git a/libpurple/mediamanager.c b/libpurple/mediamanager.c
--- a/libpurple/mediamanager.c
+++ b/libpurple/mediamanager.c
@@ -443,7 +443,11 @@

 		if (src) {
 			GstElement *capsfilter = gst_bin_get_by_name(GST_BIN(src), "prpl_video_caps");
-			g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
+			if (capsfilter) {
+				g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
+				gst_object_unref (capsfilter);
+			}
+			gst_object_unref (src);
 		}

 		g_free(id);
@@ -550,6 +554,11 @@
 	} else {
 		ret = purple_media_element_info_call_create(info,
 				media, session_id, participant);
+		if (element_type & PURPLE_MEDIA_ELEMENT_SRC) {
+			gst_object_ref(ret);
+			gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(manager)),
+				ret);
+		}
 	}

 	if (ret == NULL)


code:
# HG changeset patch
# User Jakub Adam <jakub.adam@ktknet.cz>
# Date 1407847148 -7200
# Node ID 7767aaeade6404396204794f9bc75d9a2cb723f0
# Parent  8e4fa54f166211ffd6cd869cca611e8d64ea1fdf
media: make "init-media" signal handler optional

Change the logic so that the PurpleMedia instance isn't disposed when
"init-media" has no connected handlers.

We want the media object freed only when some signal callback function
explicitly returns FALSE, indicating an error during the initialization.

This is mostly useful for the imminent addition of private media streams.

diff --git a/libpurple/mediamanager.c b/libpurple/mediamanager.c
--- a/libpurple/mediamanager.c
+++ b/libpurple/mediamanager.c
@@ -334,7 +334,7 @@
 {
 #ifdef USE_VV
 	PurpleMedia *media;
-	gboolean signal_ret;
+	guint signal_id;

 	media = PURPLE_MEDIA(g_object_new(purple_media_get_type(),
 			     "manager", manager,
@@ -343,12 +343,17 @@
 			     "initiator", initiator,
 			     NULL));

-	g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0,
-			media, account, remote_user, &signal_ret);
+	signal_id = purple_media_manager_signals[INIT_MEDIA];

-	if (signal_ret == FALSE) {
-		g_object_unref(media);
-		return NULL;
+	if (g_signal_has_handler_pending(manager, signal_id, 0, FALSE)) {
+		gboolean signal_ret;
+
+		g_signal_emit(manager, signal_id, 0, media, account, remote_user,
+				&signal_ret);
+		if (signal_ret == FALSE) {
+			g_object_unref(media);
+			return NULL;
+		}
 	}

 	manager->priv->medias = g_list_append(manager->priv->medias, media);