diff --git a/contrib/src/pjproject/SHA512SUMS b/contrib/src/pjproject/SHA512SUMS
new file mode 100644
index 0000000000000000000000000000000000000000..8c708f76f2d7480df7d08d6021a1f8286a80bb8f
--- /dev/null
+++ b/contrib/src/pjproject/SHA512SUMS
@@ -0,0 +1 @@
+ead780282a1b3df8ca326992d30b83236c3fdb9dbafb134b33d3f59484ce7253ca8014f2d7d8b98968a696b75e8a29a545b3aa34a66acdf55a21942ca9b30370  pjproject-2.2.1.tar.bz2
diff --git a/contrib/src/pjproject/aconfigureupdate.patch b/contrib/src/pjproject/aconfigureupdate.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a010207430ea53cd0ac69ed866c49347b6e61b4d
--- /dev/null
+++ b/contrib/src/pjproject/aconfigureupdate.patch
@@ -0,0 +1,484 @@
+From 5ddeabda001689893f43e60a96436904f0597457 Mon Sep 17 00:00:00 2001
+From: Vittorio Giovara <vittorio.giovara@savoirfairelinux.com>
+Date: Mon, 9 Jun 2014 18:17:42 -0400
+Subject: [PATCH] update aconfigure
+
+---
+ aconfigure | 207 +++++++++++++++++++++++++++++++------------------------------
+ 1 file changed, 106 insertions(+), 101 deletions(-)
+
+diff --git a/aconfigure b/aconfigure
+index d4fc521..03f727f 100755
+--- a/aconfigure
++++ b/aconfigure
+@@ -1,11 +1,9 @@
+ #! /bin/sh
+ # Guess values for system-dependent variables and create Makefiles.
+-# Generated by GNU Autoconf 2.68 for pjproject 2.x.
++# Generated by GNU Autoconf 2.69 for pjproject 2.x.
+ #
+ #
+-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+-# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
+-# Foundation, Inc.
++# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+ #
+ #
+ # This configure script is free software; the Free Software Foundation
+@@ -134,6 +132,31 @@ export LANGUAGE
+ # CDPATH.
+ (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+ 
++# Use a proper internal environment variable to ensure we don't fall
++  # into an infinite loop, continuously re-executing ourselves.
++  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
++    _as_can_reexec=no; export _as_can_reexec;
++    # We cannot yet assume a decent shell, so we have to provide a
++# neutralization value for shells without unset; and this also
++# works around shells that cannot unset nonexistent variables.
++# Preserve -v and -x to the replacement shell.
++BASH_ENV=/dev/null
++ENV=/dev/null
++(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
++case $- in # ((((
++  *v*x* | *x*v* ) as_opts=-vx ;;
++  *v* ) as_opts=-v ;;
++  *x* ) as_opts=-x ;;
++  * ) as_opts= ;;
++esac
++exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
++# Admittedly, this is quite paranoid, since all the known shells bail
++# out after a failed `exec'.
++$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
++as_fn_exit 255
++  fi
++  # We don't want this to propagate to other subprocesses.
++          { _as_can_reexec=; unset _as_can_reexec;}
+ if test "x$CONFIG_SHELL" = x; then
+   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+   emulate sh
+@@ -167,7 +190,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+ else
+   exitcode=1; echo positional parameters were not saved.
+ fi
+-test x\$exitcode = x0 || exit 1"
++test x\$exitcode = x0 || exit 1
++test -x / || exit 1"
+   as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+   as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+   eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+@@ -212,21 +236,25 @@ IFS=$as_save_IFS
+ 
+ 
+       if test "x$CONFIG_SHELL" != x; then :
+-  # We cannot yet assume a decent shell, so we have to provide a
+-	# neutralization value for shells without unset; and this also
+-	# works around shells that cannot unset nonexistent variables.
+-	# Preserve -v and -x to the replacement shell.
+-	BASH_ENV=/dev/null
+-	ENV=/dev/null
+-	(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+-	export CONFIG_SHELL
+-	case $- in # ((((
+-	  *v*x* | *x*v* ) as_opts=-vx ;;
+-	  *v* ) as_opts=-v ;;
+-	  *x* ) as_opts=-x ;;
+-	  * ) as_opts= ;;
+-	esac
+-	exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}
++  export CONFIG_SHELL
++             # We cannot yet assume a decent shell, so we have to provide a
++# neutralization value for shells without unset; and this also
++# works around shells that cannot unset nonexistent variables.
++# Preserve -v and -x to the replacement shell.
++BASH_ENV=/dev/null
++ENV=/dev/null
++(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
++case $- in # ((((
++  *v*x* | *x*v* ) as_opts=-vx ;;
++  *v* ) as_opts=-v ;;
++  *x* ) as_opts=-x ;;
++  * ) as_opts= ;;
++esac
++exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
++# Admittedly, this is quite paranoid, since all the known shells bail
++# out after a failed `exec'.
++$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
++exit 255
+ fi
+ 
+     if test x$as_have_required = xno; then :
+@@ -328,6 +356,14 @@ $as_echo X"$as_dir" |
+ 
+ 
+ } # as_fn_mkdir_p
++
++# as_fn_executable_p FILE
++# -----------------------
++# Test if FILE is an executable regular file.
++as_fn_executable_p ()
++{
++  test -f "$1" && test -x "$1"
++} # as_fn_executable_p
+ # as_fn_append VAR VALUE
+ # ----------------------
+ # Append the text in VALUE to the end of the definition contained in VAR. Take
+@@ -449,6 +485,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
+   chmod +x "$as_me.lineno" ||
+     { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+ 
++  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
++  # already done that, so ensure we don't try to do so again and fall
++  # in an infinite loop.  This has already happened in practice.
++  _as_can_reexec=no; export _as_can_reexec
+   # Don't try to exec as it changes $[0], causing all sort of problems
+   # (the dirname of $[0] is not the place where we might find the
+   # original and so on.  Autoconf is especially sensitive to this).
+@@ -483,16 +523,16 @@ if (echo >conf$$.file) 2>/dev/null; then
+     # ... but there are two gotchas:
+     # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+     # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+-    # In both cases, we have to default to `cp -p'.
++    # In both cases, we have to default to `cp -pR'.
+     ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+-      as_ln_s='cp -p'
++      as_ln_s='cp -pR'
+   elif ln conf$$.file conf$$ 2>/dev/null; then
+     as_ln_s=ln
+   else
+-    as_ln_s='cp -p'
++    as_ln_s='cp -pR'
+   fi
+ else
+-  as_ln_s='cp -p'
++  as_ln_s='cp -pR'
+ fi
+ rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+ rmdir conf$$.dir 2>/dev/null
+@@ -504,28 +544,8 @@ else
+   as_mkdir_p=false
+ fi
+ 
+-if test -x / >/dev/null 2>&1; then
+-  as_test_x='test -x'
+-else
+-  if ls -dL / >/dev/null 2>&1; then
+-    as_ls_L_option=L
+-  else
+-    as_ls_L_option=
+-  fi
+-  as_test_x='
+-    eval sh -c '\''
+-      if test -d "$1"; then
+-	test -d "$1/.";
+-      else
+-	case $1 in #(
+-	-*)set "./$1";;
+-	esac;
+-	case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
+-	???[sx]*):;;*)false;;esac;fi
+-    '\'' sh
+-  '
+-fi
+-as_executable_p=$as_test_x
++as_test_x='test -x'
++as_executable_p=as_fn_executable_p
+ 
+ # Sed expression to map a string onto a valid CPP name.
+ as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+@@ -1252,8 +1272,6 @@ target=$target_alias
+ if test "x$host_alias" != x; then
+   if test "x$build_alias" = x; then
+     cross_compiling=maybe
+-    $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
+-    If a cross compiler is detected then cross compile mode will be used" >&2
+   elif test "x$build_alias" != "x$host_alias"; then
+     cross_compiling=yes
+   fi
+@@ -1567,9 +1585,9 @@ test -n "$ac_init_help" && exit $ac_status
+ if $ac_init_version; then
+   cat <<\_ACEOF
+ pjproject configure 2.x
+-generated by GNU Autoconf 2.68
++generated by GNU Autoconf 2.69
+ 
+-Copyright (C) 2010 Free Software Foundation, Inc.
++Copyright (C) 2012 Free Software Foundation, Inc.
+ This configure script is free software; the Free Software Foundation
+ gives unlimited permission to copy, distribute and modify it.
+ _ACEOF
+@@ -1683,7 +1701,7 @@ $as_echo "$ac_try_echo"; } >&5
+ 	 test ! -s conftest.err
+        } && test -s conftest$ac_exeext && {
+ 	 test "$cross_compiling" = yes ||
+-	 $as_test_x conftest$ac_exeext
++	 test -x conftest$ac_exeext
+        }; then :
+   ac_retval=0
+ else
+@@ -1970,7 +1988,7 @@ This file contains any messages produced by compilers while
+ running configure, to aid debugging if configure makes a mistake.
+ 
+ It was created by pjproject $as_me 2.x, which was
+-generated by GNU Autoconf 2.68.  Invocation command line was
++generated by GNU Autoconf 2.69.  Invocation command line was
+ 
+   $ $0 $@
+ 
+@@ -2495,7 +2513,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_CC="${ac_tool_prefix}gcc"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -2535,7 +2553,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_ac_ct_CC="gcc"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -2588,7 +2606,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_CC="${ac_tool_prefix}cc"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -2629,7 +2647,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+        continue
+@@ -2687,7 +2705,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -2731,7 +2749,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_ac_ct_CC="$ac_prog"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -3177,8 +3195,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ /* end confdefs.h.  */
+ #include <stdarg.h>
+ #include <stdio.h>
+-#include <sys/types.h>
+-#include <sys/stat.h>
++struct stat;
+ /* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+ struct buf { int x; };
+ FILE * (*rcsopen) (struct buf *, struct stat *, int);
+@@ -3291,7 +3308,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -3335,7 +3352,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_ac_ct_CXX="$ac_prog"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -3544,7 +3561,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -3584,7 +3601,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_ac_ct_RANLIB="ranlib"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -3638,7 +3655,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -3682,7 +3699,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_ac_ct_AR="$ac_prog"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -4464,7 +4481,7 @@ do
+     for ac_prog in grep ggrep; do
+     for ac_exec_ext in '' $ac_executable_extensions; do
+       ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+-      { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
++      as_fn_executable_p "$ac_path_GREP" || continue
+ # Check for GNU ac_path_GREP and select it if it is found.
+   # Check for GNU $ac_path_GREP
+ case `"$ac_path_GREP" --version 2>&1` in
+@@ -4530,7 +4547,7 @@ do
+     for ac_prog in egrep; do
+     for ac_exec_ext in '' $ac_executable_extensions; do
+       ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+-      { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
++      as_fn_executable_p "$ac_path_EGREP" || continue
+ # Check for GNU ac_path_EGREP and select it if it is found.
+   # Check for GNU $ac_path_EGREP
+ case `"$ac_path_EGREP" --version 2>&1` in
+@@ -6489,7 +6506,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_path_SDL_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -6535,7 +6552,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_path_SDL_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -6640,7 +6657,7 @@ do
+   IFS=$as_save_IFS
+   test -z "$as_dir" && as_dir=.
+     for ac_exec_ext in '' $ac_executable_extensions; do
+-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+     ac_cv_prog_PKG_CONFIG="$ac_prog"
+     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+     break 2
+@@ -8449,16 +8466,16 @@ if (echo >conf$$.file) 2>/dev/null; then
+     # ... but there are two gotchas:
+     # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+     # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+-    # In both cases, we have to default to `cp -p'.
++    # In both cases, we have to default to `cp -pR'.
+     ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+-      as_ln_s='cp -p'
++      as_ln_s='cp -pR'
+   elif ln conf$$.file conf$$ 2>/dev/null; then
+     as_ln_s=ln
+   else
+-    as_ln_s='cp -p'
++    as_ln_s='cp -pR'
+   fi
+ else
+-  as_ln_s='cp -p'
++  as_ln_s='cp -pR'
+ fi
+ rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+ rmdir conf$$.dir 2>/dev/null
+@@ -8518,28 +8535,16 @@ else
+   as_mkdir_p=false
+ fi
+ 
+-if test -x / >/dev/null 2>&1; then
+-  as_test_x='test -x'
+-else
+-  if ls -dL / >/dev/null 2>&1; then
+-    as_ls_L_option=L
+-  else
+-    as_ls_L_option=
+-  fi
+-  as_test_x='
+-    eval sh -c '\''
+-      if test -d "$1"; then
+-	test -d "$1/.";
+-      else
+-	case $1 in #(
+-	-*)set "./$1";;
+-	esac;
+-	case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
+-	???[sx]*):;;*)false;;esac;fi
+-    '\'' sh
+-  '
+-fi
+-as_executable_p=$as_test_x
++
++# as_fn_executable_p FILE
++# -----------------------
++# Test if FILE is an executable regular file.
++as_fn_executable_p ()
++{
++  test -f "$1" && test -x "$1"
++} # as_fn_executable_p
++as_test_x='test -x'
++as_executable_p=as_fn_executable_p
+ 
+ # Sed expression to map a string onto a valid CPP name.
+ as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+@@ -8561,7 +8566,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ # values after options handling.
+ ac_log="
+ This file was extended by pjproject $as_me 2.x, which was
+-generated by GNU Autoconf 2.68.  Invocation command line was
++generated by GNU Autoconf 2.69.  Invocation command line was
+ 
+   CONFIG_FILES    = $CONFIG_FILES
+   CONFIG_HEADERS  = $CONFIG_HEADERS
+@@ -8623,10 +8628,10 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ ac_cs_version="\\
+ pjproject config.status 2.x
+-configured by $0, generated by GNU Autoconf 2.68,
++configured by $0, generated by GNU Autoconf 2.69,
+   with options \\"\$ac_cs_config\\"
+ 
+-Copyright (C) 2010 Free Software Foundation, Inc.
++Copyright (C) 2012 Free Software Foundation, Inc.
+ This config.status script is free software; the Free Software Foundation
+ gives unlimited permission to copy, distribute and modify it."
+ 
+@@ -8714,7 +8719,7 @@ fi
+ _ACEOF
+ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ if \$ac_cs_recheck; then
+-  set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
++  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+   shift
+   \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+   CONFIG_SHELL='$SHELL'
+-- 
+1.8.3.2
+
diff --git a/contrib/src/pjproject/endianness.patch b/contrib/src/pjproject/endianness.patch
new file mode 100644
index 0000000000000000000000000000000000000000..84b9499448f65d588082fd4047906cb1295541dc
--- /dev/null
+++ b/contrib/src/pjproject/endianness.patch
@@ -0,0 +1,19 @@
+diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
+index 10f86fd..4ace1bc 100644
+--- a/pjlib/include/pj/config.h
++++ b/pjlib/include/pj/config.h
+@@ -245,7 +245,13 @@
+ #   define PJ_M_NAME		"armv4"
+ #   define PJ_HAS_PENTIUM	0
+ #   if !PJ_IS_LITTLE_ENDIAN && !PJ_IS_BIG_ENDIAN
+-#   	error Endianness must be declared for this processor
++#       if defined(__GNUC__)
++#           include <endian.h>
++#           define PJ_IS_LITTLE_ENDIAN  __BYTE_ORDER__ == __LITTLE_ENDIAN__
++#           define PJ_IS_BIG_ENDIAN     __BYTE_ORDER__ == __BIG_ENDIAN__
++#       else
++#           error Endianness must be declared for this processor
++#       endif
+ #   endif
+ 
+ #elif defined (PJ_M_POWERPC) || defined(__powerpc) || defined(__powerpc__) || \
diff --git a/contrib/src/pjproject/gnutls.patch b/contrib/src/pjproject/gnutls.patch
new file mode 100644
index 0000000000000000000000000000000000000000..7c07eaa08d66b42db8c4eb113a2c257209a80b59
--- /dev/null
+++ b/contrib/src/pjproject/gnutls.patch
@@ -0,0 +1,3253 @@
+From 73ffecc7f0224e64b0694ddc740f7795757a9fcf Mon Sep 17 00:00:00 2001
+From: Vittorio Giovara <vittorio.giovara@savoirfairelinux.com>
+Date: Mon, 9 Jun 2014 14:20:55 -0400
+Subject: [PATCH] ssl_sock: add gnutls backend
+
+This backend is mutually exclusive with the OpenSSL one, but completely
+compatible, and conformant to the PJSIP API. Also avoids any license issues
+when linking statically.
+
+The configure script is updated to select either OpenSSL or GnuTLS
+with --enable-ssl[='...'] and a new symbol (PJ_HAS_TLS_SOCK) is introduced
+to identify which backend is in use.
+
+Written by Vittorio Giovara <vittorio.giovara@savoirfairelinux.com> and
+Philippe Proulx <philippe.proulx@savoirfairelinux.com> on behalf of
+Savoir-Faire Linux.
+---
+ aconfigure                           |  183 ++-
+ aconfigure.ac                        |  100 +-
+ pjlib/build/Makefile                 |    2 +-
+ pjlib/include/pj/compat/os_auto.h.in |    3 +
+ pjlib/include/pj/config.h            |    4 +-
+ pjlib/src/pj/ssl_sock_common.c       |    5 +
+ pjlib/src/pj/ssl_sock_gtls.c         | 2781 ++++++++++++++++++++++++++++++++++
+ pjlib/src/pj/ssl_sock_ossl.c         |    6 +-
+ 8 files changed, 3022 insertions(+), 62 deletions(-)
+ create mode 100644 pjlib/src/pj/ssl_sock_gtls.c
+
+diff --git a/aconfigure b/aconfigure
+index 8fd8c64..d4fc521 100755
+--- a/aconfigure
++++ b/aconfigure
+@@ -617,6 +617,8 @@ ac_no_opencore_amrnb
+ libcrypto_present
+ libssl_present
+ openssl_h_present
++libgnutls_present
++gnutls_h_present
+ ac_no_ssl
+ ac_v4l2_ldflags
+ ac_v4l2_cflags
+@@ -1439,8 +1441,8 @@ Optional Features:
+                           package and samples location using IPPROOT and
+                           IPPSAMPLES env var or with --with-ipp and
+                           --with-ipp-samples options
+-  --disable-ssl           Exclude SSL support the build (default: autodetect)
+-
++  --enable-ssl=backend    Select 'gnutls' or 'openssl' (default) to provide
++                          SSL support (autodetect)
+   --disable-opencore-amr  Exclude OpenCORE AMR support from the build
+                           (default: autodetect)
+ 
+@@ -7363,33 +7365,159 @@ fi
+ 
+ # Check whether --enable-ssl was given.
+ if test "${enable_ssl+set}" = set; then :
+-  enableval=$enable_ssl;
+-		if test "$enable_ssl" = "no"; then
+-		 ac_no_ssl=1
+-		 { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if SSL support is disabled... yes" >&5
++  enableval=$enable_ssl;  if test "x$enableval" = "xgnutls"; then
++                    ssl_backend="gnutls"
++                else
++                    ssl_backend="openssl"
++                fi
++
++fi
++
++
++if test "x$enable_ssl" = "xno"; then
++    ac_no_ssl=1
++    { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if SSL support is disabled... yes" >&5
+ $as_echo "Checking if SSL support is disabled... yes" >&6; }
+-	        fi
++else
++    if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then
++        CFLAGS="$CFLAGS -I$with_ssl/include"
++        LDFLAGS="$LDFLAGS -L$with_ssl/lib"
++        { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using SSL prefix... $with_ssl" >&5
++$as_echo "Using SSL prefix... $with_ssl" >&6; }
++    fi
++    if test "x$ssl_backend" = "xgnutls"; then
++        for ac_prog in pkg-config "python pkgconfig.py"
++do
++  # Extract the first word of "$ac_prog", so it can be a program name with args.
++set dummy $ac_prog; ac_word=$2
++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
++$as_echo_n "checking for $ac_word... " >&6; }
++if ${ac_cv_prog_PKG_CONFIG+:} false; then :
++  $as_echo_n "(cached) " >&6
++else
++  if test -n "$PKG_CONFIG"; then
++  ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test.
++else
++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++    for ac_exec_ext in '' $ac_executable_extensions; do
++  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
++    ac_cv_prog_PKG_CONFIG="$ac_prog"
++    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
++    break 2
++  fi
++done
++  done
++IFS=$as_save_IFS
++
++fi
++fi
++PKG_CONFIG=$ac_cv_prog_PKG_CONFIG
++if test -n "$PKG_CONFIG"; then
++  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
++$as_echo "$PKG_CONFIG" >&6; }
++else
++  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
++$as_echo "no" >&6; }
++fi
++
+ 
++  test -n "$PKG_CONFIG" && break
++done
++test -n "$PKG_CONFIG" || PKG_CONFIG="none"
++
++        { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for GnuTLS installations.." >&5
++$as_echo "checking for GnuTLS installations.." >&6; }
++
++
++        ac_fn_c_check_header_mongrel "$LINENO" "gnutls/gnutls.h" "ac_cv_header_gnutls_gnutls_h" "$ac_includes_default"
++if test "x$ac_cv_header_gnutls_gnutls_h" = xyes; then :
++  gnutls_h_present=1
++fi
++
++
++
++        if test "$PKG_CONFIG" != "none"; then
++            if $PKG_CONFIG --exists gnutls; then
++                LIBS="$LIBS `$PKG_CONFIG --libs gnutls`"
++                libgnutls_present=1
++            else
++                { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gnutls_certificate_set_x509_system_trust in -lgnutls" >&5
++$as_echo_n "checking for gnutls_certificate_set_x509_system_trust in -lgnutls... " >&6; }
++if ${ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust+:} false; then :
++  $as_echo_n "(cached) " >&6
++else
++  ac_check_lib_save_LIBS=$LIBS
++LIBS="-lgnutls  $LIBS"
++cat confdefs.h - <<_ACEOF >conftest.$ac_ext
++/* end confdefs.h.  */
++
++/* Override any GCC internal prototype to avoid an error.
++   Use char because int might match the return type of a GCC
++   builtin and then its argument prototype would still apply.  */
++#ifdef __cplusplus
++extern "C"
++#endif
++char gnutls_certificate_set_x509_system_trust ();
++int
++main ()
++{
++return gnutls_certificate_set_x509_system_trust ();
++  ;
++  return 0;
++}
++_ACEOF
++if ac_fn_c_try_link "$LINENO"; then :
++  ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust=yes
+ else
++  ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust=no
++fi
++rm -f core conftest.err conftest.$ac_objext \
++    conftest$ac_exeext conftest.$ac_ext
++LIBS=$ac_check_lib_save_LIBS
++fi
++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" >&5
++$as_echo "$ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" >&6; }
++if test "x$ac_cv_lib_gnutls_gnutls_certificate_set_x509_system_trust" = xyes; then :
++  libgnutls_present=1 &&
++                              LIBS="$LIBS -lgnutls"
++fi
++
++            fi
++        else
++            { $as_echo "$as_me:${as_lineno-$LINENO}: result: *** Warning: neither pkg-config nor python is available, disabling gnutls. ***" >&5
++$as_echo "*** Warning: neither pkg-config nor python is available, disabling gnutls. ***" >&6; }
++        fi
++
++        if test "x$gnutls_h_present" = "x1" -a "x$libgnutls_present" = "x1"; then
++            { $as_echo "$as_me:${as_lineno-$LINENO}: result: GnuTLS library found, SSL support enabled" >&5
++$as_echo "GnuTLS library found, SSL support enabled" >&6; }
++            # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
++            #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
++            $as_echo "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h
+ 
+-		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for OpenSSL installations.." >&5
++            $as_echo "#define PJ_HAS_TLS_SOCK 1" >>confdefs.h
++
++        else
++            { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** No GnuTLS libraries found, disabling SSL support **" >&5
++$as_echo "** No GnuTLS libraries found, disabling SSL support **" >&6; }
++        fi
++    else
++        { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for OpenSSL installations.." >&5
+ $as_echo "checking for OpenSSL installations.." >&6; }
+-                if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then
+-                    CFLAGS="$CFLAGS -I$with_ssl/include"
+-                    LDFLAGS="$LDFLAGS -L$with_ssl/lib"
+-                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using SSL prefix... $with_ssl" >&5
+-$as_echo "Using SSL prefix... $with_ssl" >&6; }
+-                fi
+ 
+ 
+ 
+-		ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default"
++        ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default"
+ if test "x$ac_cv_header_openssl_ssl_h" = xyes; then :
+   openssl_h_present=1
+ fi
+ 
+ 
+-		{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ERR_load_BIO_strings in -lcrypto" >&5
++        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ERR_load_BIO_strings in -lcrypto" >&5
+ $as_echo_n "checking for ERR_load_BIO_strings in -lcrypto... " >&6; }
+ if ${ac_cv_lib_crypto_ERR_load_BIO_strings+:} false; then :
+   $as_echo_n "(cached) " >&6
+@@ -7429,7 +7557,7 @@ if test "x$ac_cv_lib_crypto_ERR_load_BIO_strings" = xyes; then :
+   libcrypto_present=1 && LIBS="$LIBS -lcrypto"
+ fi
+ 
+-		{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_library_init in -lssl" >&5
++        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_library_init in -lssl" >&5
+ $as_echo_n "checking for SSL_library_init in -lssl... " >&6; }
+ if ${ac_cv_lib_ssl_SSL_library_init+:} false; then :
+   $as_echo_n "(cached) " >&6
+@@ -7469,22 +7597,23 @@ if test "x$ac_cv_lib_ssl_SSL_library_init" = xyes; then :
+   libssl_present=1 && LIBS="$LIBS -lssl"
+ fi
+ 
+-		if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then
+-	        	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: OpenSSL library found, SSL support enabled" >&5
++        if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then
++            { $as_echo "$as_me:${as_lineno-$LINENO}: result: OpenSSL library found, SSL support enabled" >&5
+ $as_echo "OpenSSL library found, SSL support enabled" >&6; }
+-			# PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
+-			#AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
+-			$as_echo "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h
++            # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
++            #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
++            $as_echo "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h
+ 
+-		else
+-			{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ** OpenSSL libraries not found, disabling SSL support **" >&5
+-$as_echo "** OpenSSL libraries not found, disabling SSL support **" >&6; }
+-		fi
++            $as_echo "#define PJ_HAS_TLS_SOCK 0" >>confdefs.h
+ 
++        else
++            { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** No OpenSSL libraries found, disabling SSL support **" >&5
++$as_echo "** No OpenSSL libraries found, disabling SSL support **" >&6; }
++        fi
++    fi
+ fi
+ 
+ 
+-
+ # Check whether --with-opencore-amrnb was given.
+ if test "${with_opencore_amrnb+set}" = set; then :
+   withval=$with_opencore_amrnb; as_fn_error $? "This option is obsolete and replaced by --with-opencore-amr=DIR" "$LINENO" 5
+diff --git a/aconfigure.ac b/aconfigure.ac
+index cd71a7a..465285e 100644
+--- a/aconfigure.ac
++++ b/aconfigure.ac
+@@ -1346,38 +1346,76 @@ fi
+ 
+ dnl # Include SSL support
+ AC_SUBST(ac_no_ssl)
+-AC_ARG_ENABLE(ssl,
+-	      AC_HELP_STRING([--disable-ssl],
+-			     [Exclude SSL support the build (default: autodetect)])
+-	      ,
+-	      [
+-		if test "$enable_ssl" = "no"; then
+-		 [ac_no_ssl=1]
+-		 AC_MSG_RESULT([Checking if SSL support is disabled... yes])
+-	        fi
+-	      ],
+-	      [
+-		AC_MSG_RESULT([checking for OpenSSL installations..])
+-                if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then
+-                    CFLAGS="$CFLAGS -I$with_ssl/include"
+-                    LDFLAGS="$LDFLAGS -L$with_ssl/lib"
+-                    AC_MSG_RESULT([Using SSL prefix... $with_ssl])
++AC_ARG_ENABLE([ssl],
++              AC_HELP_STRING([--enable-ssl[=backend]],
++                             [Select 'gnutls' or 'openssl' (default) to provide SSL support (autodetect)]),
++              [ if test "x$enableval" = "xgnutls"; then
++                    [ssl_backend="gnutls"]
++                else
++                    [ssl_backend="openssl"]
+                 fi
+-		AC_SUBST(openssl_h_present)
+-		AC_SUBST(libssl_present)
+-		AC_SUBST(libcrypto_present)
+-		AC_CHECK_HEADER(openssl/ssl.h,[openssl_h_present=1])
+-		AC_CHECK_LIB(crypto,ERR_load_BIO_strings,[libcrypto_present=1 && LIBS="$LIBS -lcrypto"])
+-		AC_CHECK_LIB(ssl,SSL_library_init,[libssl_present=1 && LIBS="$LIBS -lssl"])
+-		if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then
+-	        	AC_MSG_RESULT([OpenSSL library found, SSL support enabled])
+-			# PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
+-			#AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
+-			AC_DEFINE(PJ_HAS_SSL_SOCK, 1)
+-		else
+-			AC_MSG_RESULT([** OpenSSL libraries not found, disabling SSL support **])
+-		fi
+-	      ])
++              ])
++
++if test "x$enable_ssl" = "xno"; then
++    [ac_no_ssl=1]
++    AC_MSG_RESULT([Checking if SSL support is disabled... yes])
++else
++    if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then
++        CFLAGS="$CFLAGS -I$with_ssl/include"
++        LDFLAGS="$LDFLAGS -L$with_ssl/lib"
++        AC_MSG_RESULT([Using SSL prefix... $with_ssl])
++    fi
++    if test "x$ssl_backend" = "xgnutls"; then
++        AC_CHECK_PROGS(PKG_CONFIG,
++                       pkg-config "python pkgconfig.py",
++                       none)
++        AC_MSG_RESULT([checking for GnuTLS installations..])
++        AC_SUBST(gnutls_h_present)
++        AC_SUBST(libgnutls_present)
++        AC_CHECK_HEADER(gnutls/gnutls.h, [gnutls_h_present=1])
++
++        if test "$PKG_CONFIG" != "none"; then
++            if $PKG_CONFIG --exists gnutls; then
++                LIBS="$LIBS `$PKG_CONFIG --libs gnutls`"
++                libgnutls_present=1
++            else
++                AC_CHECK_LIB(gnutls,
++                             gnutls_certificate_set_x509_system_trust,
++                             [libgnutls_present=1 &&
++                              LIBS="$LIBS -lgnutls"])
++            fi
++        else
++            AC_MSG_RESULT([*** Warning: neither pkg-config nor python is available, disabling gnutls. ***])
++        fi
++
++        if test "x$gnutls_h_present" = "x1" -a "x$libgnutls_present" = "x1"; then
++            AC_MSG_RESULT([GnuTLS library found, SSL support enabled])
++            # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
++            #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
++            AC_DEFINE(PJ_HAS_SSL_SOCK, 1)
++            AC_DEFINE(PJ_HAS_TLS_SOCK, 1)
++        else
++            AC_MSG_RESULT([** No GnuTLS libraries found, disabling SSL support **])
++        fi
++    else
++        AC_MSG_RESULT([checking for OpenSSL installations..])
++        AC_SUBST(openssl_h_present)
++        AC_SUBST(libssl_present)
++        AC_SUBST(libcrypto_present)
++        AC_CHECK_HEADER(openssl/ssl.h, [openssl_h_present=1])
++        AC_CHECK_LIB(crypto,ERR_load_BIO_strings, [libcrypto_present=1 && LIBS="$LIBS -lcrypto"])
++        AC_CHECK_LIB(ssl,SSL_library_init, [libssl_present=1 && LIBS="$LIBS -lssl"])
++        if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then
++            AC_MSG_RESULT([OpenSSL library found, SSL support enabled])
++            # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK
++            #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1)
++            AC_DEFINE(PJ_HAS_SSL_SOCK, 1)
++            AC_DEFINE(PJ_HAS_TLS_SOCK, 0)
++        else
++            AC_MSG_RESULT([** No OpenSSL libraries found, disabling SSL support **])
++        fi
++    fi
++fi
+ 
+ dnl # Obsolete option --with-opencore-amrnb
+ AC_ARG_WITH(opencore-amrnb,
+diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile
+index 1e64950..e650a31 100644
+--- a/pjlib/build/Makefile
++++ b/pjlib/build/Makefile
+@@ -35,7 +35,7 @@ export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
+ 	guid.o hash.o ip_helper_generic.o list.o lock.o log.o os_time_common.o \
+ 	os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \
+ 	rbtree.o sock_common.o sock_qos_common.o sock_qos_bsd.o \
+-	ssl_sock_common.o ssl_sock_ossl.o ssl_sock_dump.o \
++	ssl_sock_common.o ssl_sock_ossl.o ssl_sock_gtls.o ssl_sock_dump.o \
+ 	string.o timer.o types.o
+ export PJLIB_CFLAGS += $(_CFLAGS)
+ export PJLIB_CXXFLAGS += $(_CXXFLAGS)
+diff --git a/pjlib/include/pj/compat/os_auto.h.in b/pjlib/include/pj/compat/os_auto.h.in
+index 77980d3..6d6a625 100644
+--- a/pjlib/include/pj/compat/os_auto.h.in
++++ b/pjlib/include/pj/compat/os_auto.h.in
+@@ -206,6 +206,9 @@
+ #ifndef PJ_HAS_SSL_SOCK
+ #undef PJ_HAS_SSL_SOCK
+ #endif
++#ifndef PJ_HAS_TLS_SOCK
++#undef PJ_HAS_TLS_SOCK
++#endif
+ 
+ 
+ #endif	/* __PJ_COMPAT_OS_AUTO_H__ */
+diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
+index bfb4b69..b5fdb4a 100644
+--- a/pjlib/include/pj/config.h
++++ b/pjlib/include/pj/config.h
+@@ -848,13 +848,15 @@
+ 
+ /**
+  * Enable secure socket. For most platforms, this is implemented using
+- * OpenSSL, so this will require OpenSSL to be installed. For Symbian
++ * OpenSSL, so this will require OpenSSL or GnuTLS to be installed. For Symbian
+  * platform, this is implemented natively using CSecureSocket.
+  *
+  * Default: 0 (for now)
+  */
+ #ifndef PJ_HAS_SSL_SOCK
+ #  define PJ_HAS_SSL_SOCK	    0
++   // When set to 1 secure sockets will use the GnuTLS backend
++#  define PJ_HAS_TLS_SOCK	    0
+ #endif
+ 
+ 
+diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c
+index 67a8d63..602a1af 100644
+--- a/pjlib/src/pj/ssl_sock_common.c
++++ b/pjlib/src/pj/ssl_sock_common.c
+@@ -34,7 +34,12 @@ PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
+     param->async_cnt = 1;
+     param->concurrency = -1;
+     param->whole_data = PJ_TRUE;
++#if defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 1
++    // GnuTLS is allowed to send bigger chunks
++    param->send_buffer_size = 65536;
++#else
+     param->send_buffer_size = 8192;
++#endif
+ #if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0
+     param->read_buffer_size = 1500;
+ #endif
+diff --git a/pjlib/src/pj/ssl_sock_gtls.c b/pjlib/src/pj/ssl_sock_gtls.c
+new file mode 100644
+index 0000000..75b3723
+--- /dev/null
++++ b/pjlib/src/pj/ssl_sock_gtls.c
+@@ -0,0 +1,2781 @@
++/* $Id$ */
++/*
++ * Copyright (C) 2014 Savoir-Faire Linux. (http://www.savoirfairelinux.com)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <pj/ssl_sock.h>
++#include <pj/activesock.h>
++#include <pj/compat/socket.h>
++#include <pj/assert.h>
++#include <pj/errno.h>
++#include <pj/list.h>
++#include <pj/lock.h>
++#include <pj/log.h>
++#include <pj/math.h>
++#include <pj/os.h>
++#include <pj/pool.h>
++#include <pj/string.h>
++#include <pj/timer.h>
++#include <pj/file_io.h>
++
++
++/* Only build when PJ_HAS_SSL_SOCK and PJ_HAS_TLS_SOCK are enabled */
++#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \
++    defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK != 0
++
++#define THIS_FILE               "ssl_sock_gtls.c"
++
++/* Workaround for ticket #985 */
++#define DELAYED_CLOSE_TIMEOUT   200
++
++/* Maximum ciphers */
++#define MAX_CIPHERS             100
++
++/* Standard trust locations */
++#define TRUST_STORE_FILE1 "/etc/ssl/certs/ca-certificates.crt"
++#define TRUST_STORE_FILE2 "/etc/ssl/certs/ca-bundle.crt"
++
++/* Debugging output level for GnuTLS only */
++#define GNUTLS_LOG_LEVEL 0
++
++/* GnuTLS includes */
++#include <gnutls/gnutls.h>
++#include <gnutls/x509.h>
++#include <gnutls/abstract.h>
++
++#ifdef _MSC_VER
++#  pragma comment( lib, "gnutls")
++#endif
++
++
++/* TLS state enumeration. */
++enum tls_connection_state {
++    TLS_STATE_NULL,
++    TLS_STATE_HANDSHAKING,
++    TLS_STATE_ESTABLISHED
++};
++
++/* Internal timer types. */
++enum timer_id {
++    TIMER_NONE,
++    TIMER_HANDSHAKE_TIMEOUT,
++    TIMER_CLOSE
++};
++
++/* Structure of SSL socket read buffer. */
++typedef struct read_data_t {
++    void *data;
++    pj_size_t len;
++} read_data_t;
++
++/*
++ * Get the offset of pointer to read-buffer of SSL socket from read-buffer
++ * of active socket. Note that both SSL socket and active socket employ
++ * different but correlated read-buffers (as much as async_cnt for each),
++ * and to make it easier/faster to find corresponding SSL socket's read-buffer
++ * from known active socket's read-buffer, the pointer of corresponding
++ * SSL socket's read-buffer is stored right after the end of active socket's
++ * read-buffer.
++ */
++#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \
++                                        (read_data_t**) \
++                                        ((pj_int8_t *)(asock_rbuf) + \
++                                        ssock->param.read_buffer_size)
++
++/* Structure of SSL socket write data. */
++typedef struct write_data_t {
++    PJ_DECL_LIST_MEMBER(struct write_data_t);
++    pj_ioqueue_op_key_t  key;
++    pj_size_t            record_len;
++    pj_ioqueue_op_key_t *app_key;
++    pj_size_t            plain_data_len;
++    pj_size_t            data_len;
++    unsigned             flags;
++    union {
++        char             content[1];
++        const char      *ptr;
++    } data;
++} write_data_t;
++
++
++/* Structure of SSL socket write buffer (circular buffer). */
++typedef struct send_buf_t {
++    char                *buf;
++    pj_size_t            max_len;
++    char                *start;
++    pj_size_t            len;
++} send_buf_t;
++
++
++/* Circular buffer object */
++typedef struct circ_buf_t {
++    pj_size_t      cap;    /* maximum number of elements (must be power of 2) */
++    pj_size_t      readp;  /* index of oldest element */
++    pj_size_t      writep; /* index at which to write new element  */
++    pj_size_t      size;   /* number of elements */
++    pj_uint8_t    *buf;    /* data buffer */
++    pj_pool_t     *pool;   /* where new allocations will take place */
++} circ_buf_t;
++
++
++/* Secure socket structure definition. */
++struct pj_ssl_sock_t {
++    pj_pool_t            *pool;
++    pj_ssl_sock_t        *parent;
++    pj_ssl_sock_param     param;
++    pj_ssl_cert_t        *cert;
++
++    pj_ssl_cert_info      local_cert_info;
++    pj_ssl_cert_info      remote_cert_info;
++
++    pj_bool_t             is_server;
++    enum tls_connection_state connection_state;
++    pj_ioqueue_op_key_t   handshake_op_key;
++    pj_timer_entry        timer;
++    pj_status_t           verify_status;
++
++    int                   last_err;
++
++    pj_sock_t             sock;
++    pj_activesock_t      *asock;
++
++    pj_sockaddr           local_addr;
++    pj_sockaddr           rem_addr;
++    int                   addr_len;
++
++    pj_bool_t             read_started;
++    pj_size_t             read_size;
++    pj_uint32_t           read_flags;
++    void                **asock_rbuf;
++    read_data_t          *ssock_rbuf;
++
++    write_data_t          write_pending;       /* list of pending writes */
++    write_data_t          write_pending_empty; /* cache for write_pending */
++    pj_bool_t             flushing_write_pend; /* flag of flushing is ongoing */
++    send_buf_t            send_buf;
++    write_data_t          send_pending; /* list of pending write to network */
++
++    gnutls_session_t      session;
++    gnutls_certificate_credentials_t xcred;
++
++    circ_buf_t            circ_buf_input;
++    pj_lock_t            *circ_buf_input_mutex;
++
++    circ_buf_t            circ_buf_output;
++    pj_lock_t            *circ_buf_output_mutex;
++
++    int                   tls_init_count; /* library initialization counter */
++};
++
++
++/* Certificate/credential structure definition. */
++struct pj_ssl_cert_t {
++    pj_str_t CA_file;
++    pj_str_t cert_file;
++    pj_str_t privkey_file;
++    pj_str_t privkey_pass;
++};
++
++/* GnuTLS available ciphers */
++static unsigned tls_available_ciphers;
++
++/* Array of id/names for available ciphers */
++static struct tls_ciphers_t {
++    pj_ssl_cipher id;
++    const char *name;
++} tls_ciphers[MAX_CIPHERS];
++
++/* Last error reported somehow */
++static int tls_last_error;
++
++
++/*
++ *******************************************************************
++ * Circular buffer functions.
++ *******************************************************************
++ */
++
++static pj_status_t circ_init(pj_pool_factory *factory,
++                             circ_buf_t *cb, pj_size_t cap)
++{
++    cb->cap    = cap;
++    cb->readp  = 0;
++    cb->writep = 0;
++    cb->size   = 0;
++
++    /* Initial pool holding the buffer elements */
++    cb->pool = pj_pool_create(factory, "tls-circ%p", cap, cap, NULL);
++    if (!cb->pool)
++        return PJ_ENOMEM;
++
++    /* Allocate circular buffer */
++    cb->buf = pj_pool_alloc(cb->pool, cap);
++    if (!cb->buf) {
++        pj_pool_release(cb->pool);
++        return PJ_ENOMEM;
++    }
++
++    return PJ_SUCCESS;
++}
++
++static void circ_deinit(circ_buf_t *cb)
++{
++    if (cb->pool) {
++        pj_pool_release(cb->pool);
++        cb->pool = NULL;
++    }
++}
++
++static pj_bool_t circ_empty(const circ_buf_t *cb)
++{
++    return cb->size == 0;
++}
++
++static pj_size_t circ_size(const circ_buf_t *cb)
++{
++    return cb->size;
++}
++
++static pj_size_t circ_avail(const circ_buf_t *cb)
++{
++    return cb->cap - cb->size;
++}
++
++static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len)
++{
++    pj_size_t size_after = cb->cap - cb->readp;
++    pj_size_t tbc = PJ_MIN(size_after, len);
++    pj_size_t rem = len - tbc;
++
++    pj_memcpy(dst, cb->buf + cb->readp, tbc);
++    pj_memcpy(dst + tbc, cb->buf, rem);
++
++    cb->readp += len;
++    cb->readp &= (cb->cap - 1);
++
++    cb->size -= len;
++}
++
++static pj_status_t circ_write(circ_buf_t *cb,
++                              const pj_uint8_t *src, pj_size_t len)
++{
++    /* Overflow condition: resize */
++    if (len > circ_avail(cb)) {
++        /* Minimum required capacity */
++        pj_size_t min_cap = len + cb->size;
++
++        /* Next 32-bit power of two */
++        min_cap--;
++        min_cap |= min_cap >> 1;
++        min_cap |= min_cap >> 2;
++        min_cap |= min_cap >> 4;
++        min_cap |= min_cap >> 8;
++        min_cap |= min_cap >> 16;
++        min_cap++;
++
++        /* Create a new pool to hold a bigger buffer, using the same factory */
++        pj_pool_t *pool = pj_pool_create(cb->pool->factory, "tls-circ%p",
++                                         min_cap, min_cap, NULL);
++        if (!pool)
++            return PJ_ENOMEM;
++
++        /* Allocate our new buffer */
++        pj_uint8_t *buf = pj_pool_alloc(pool, min_cap);
++        if (!buf) {
++            pj_pool_release(pool);
++            return PJ_ENOMEM;
++        }
++
++        /* Save old size, which we shall restore after the next read */
++        pj_size_t old_size = cb->size;
++
++        /* Copy old data into beginning of new buffer */
++        circ_read(cb, buf, cb->size);
++
++        /* Restore old size now */
++        cb->size = old_size;
++
++        /* Release the previous pool */
++        pj_pool_release(cb->pool);
++
++        /* Update circular buffer members */
++        cb->pool = pool;
++        cb->buf = buf;
++        cb->readp = 0;
++        cb->writep = cb->size;
++        cb->cap = min_cap;
++    }
++
++    pj_size_t size_after = cb->cap - cb->writep;
++    pj_size_t tbc = PJ_MIN(size_after, len);
++    pj_size_t rem = len - tbc;
++
++    pj_memcpy(cb->buf + cb->writep, src, tbc);
++    pj_memcpy(cb->buf, src + tbc, rem);
++
++    cb->writep += len;
++    cb->writep &= (cb->cap - 1);
++
++    cb->size += len;
++
++    return PJ_SUCCESS;
++}
++
++
++/*
++ *******************************************************************
++ * Static/internal functions.
++ *******************************************************************
++ */
++
++/* Convert from GnuTLS error to pj_status_t. */
++static pj_status_t tls_status_from_err(pj_ssl_sock_t *ssock, int err)
++{
++    pj_status_t status;
++
++    switch (err) {
++    case GNUTLS_E_SUCCESS:
++        status = PJ_SUCCESS;
++        break;
++    case GNUTLS_E_MEMORY_ERROR:
++        status = PJ_ENOMEM;
++        break;
++    case GNUTLS_E_LARGE_PACKET:
++        status = PJ_ETOOBIG;
++        break;
++    case GNUTLS_E_NO_CERTIFICATE_FOUND:
++        status = PJ_ENOTFOUND;
++        break;
++    case GNUTLS_E_SESSION_EOF:
++        status = PJ_EEOF;
++        break;
++    case GNUTLS_E_HANDSHAKE_TOO_LARGE:
++        status = PJ_ETOOBIG;
++        break;
++    case GNUTLS_E_EXPIRED:
++        status = PJ_EGONE;
++        break;
++    case GNUTLS_E_TIMEDOUT:
++        status = PJ_ETIMEDOUT;
++        break;
++    case GNUTLS_E_PREMATURE_TERMINATION:
++        status = PJ_ECANCELLED;
++        break;
++    case GNUTLS_E_INTERNAL_ERROR:
++    case GNUTLS_E_UNIMPLEMENTED_FEATURE:
++        status = PJ_EBUG;
++        break;
++    case GNUTLS_E_AGAIN:
++    case GNUTLS_E_INTERRUPTED:
++    case GNUTLS_E_REHANDSHAKE:
++        status = PJ_EPENDING;
++        break;
++    case GNUTLS_E_TOO_MANY_EMPTY_PACKETS:
++    case GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS:
++    case GNUTLS_E_RECORD_LIMIT_REACHED:
++        status = PJ_ETOOMANY;
++        break;
++    case GNUTLS_E_UNSUPPORTED_VERSION_PACKET:
++    case GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM:
++    case GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE:
++    case GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE:
++    case GNUTLS_E_X509_UNSUPPORTED_EXTENSION:
++    case GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION:
++        status = PJ_ENOTSUP;
++        break;
++    case GNUTLS_E_INVALID_SESSION:
++    case GNUTLS_E_INVALID_REQUEST:
++    case GNUTLS_E_INVALID_PASSWORD:
++    case GNUTLS_E_ILLEGAL_PARAMETER:
++    case GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION:
++    case GNUTLS_E_UNEXPECTED_PACKET:
++    case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
++    case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET:
++    case GNUTLS_E_UNWANTED_ALGORITHM:
++    case GNUTLS_E_USER_ERROR:
++        status = PJ_EINVAL;
++        break;
++    default:
++        status = PJ_EUNKNOWN;
++        break;
++    }
++
++    /* Not thread safe */
++    tls_last_error = err;
++    if (ssock)
++        ssock->last_err = err;
++    return status;
++}
++
++
++/* Get error string from GnuTLS using tls_last_error */
++static pj_str_t tls_strerror(pj_status_t status,
++                             char *buf, pj_size_t bufsize)
++{
++    pj_str_t errstr;
++    const char *tmp = gnutls_strerror(tls_last_error);
++
++#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
++    if (tmp) {
++        pj_ansi_strncpy(buf, tmp, bufsize);
++        errstr = pj_str(buf);
++        return errstr;
++    }
++#endif /* PJ_HAS_ERROR_STRING */
++
++    errstr.ptr = buf;
++    errstr.slen = pj_ansi_snprintf(buf, bufsize, "GnuTLS error %d: %s",
++                                   tls_last_error, tmp);
++    if (errstr.slen < 1 || errstr.slen >= (int) bufsize)
++        errstr.slen = bufsize - 1;
++
++    return errstr;
++}
++
++
++/* GnuTLS way of reporting internal operations. */
++static void tls_print_logs(int level, const char* msg)
++{
++    PJ_LOG(3, (THIS_FILE, "GnuTLS [%d]: %s", level, msg));
++}
++
++
++/* Initialize GnuTLS. */
++static pj_status_t tls_init(void)
++{
++    /* Register error subsystem */
++    pj_status_t status = pj_register_strerror(PJ_ERRNO_START_USER +
++                                              PJ_ERRNO_SPACE_SIZE * 6,
++                                              PJ_ERRNO_SPACE_SIZE,
++                                              &tls_strerror);
++    pj_assert(status == PJ_SUCCESS);
++
++    /* Init GnuTLS library */
++    int ret = gnutls_global_init();
++    if (ret < 0)
++        return tls_status_from_err(NULL, ret);
++
++    gnutls_global_set_log_level(GNUTLS_LOG_LEVEL);
++    gnutls_global_set_log_function(tls_print_logs);
++
++    /* Init available ciphers */
++    if (!tls_available_ciphers) {
++        unsigned int i;
++
++        for (i = 0; ; i++) {
++            unsigned char id[2];
++            const char *suite = gnutls_cipher_suite_info(i, (unsigned char *)id,
++                                                         NULL, NULL, NULL, NULL);
++            tls_ciphers[i].id = 0;
++            /* usually the array size is bigger than the number of available
++             * ciphers anyway, so by checking here we can exit the loop as soon
++             * as either all ciphers have been added or the array is full */
++            if (suite && i < PJ_ARRAY_SIZE(tls_ciphers)) {
++                tls_ciphers[i].id = (pj_ssl_cipher)
++                    (pj_uint32_t) ((id[0] << 8) | id[1]);
++                tls_ciphers[i].name = suite;
++            } else
++                break;
++        }
++
++        tls_available_ciphers = i;
++    }
++
++    return PJ_SUCCESS;
++}
++
++
++/* Shutdown GnuTLS */
++static void tls_deinit(void)
++{
++    gnutls_global_deinit();
++}
++
++
++/* Callback invoked every time a certificate has to be validated. */
++static int tls_cert_verify_cb(gnutls_session_t session)
++{
++    pj_ssl_sock_t *ssock;
++    unsigned int status;
++    int ret;
++
++    /* Get SSL socket instance */
++    ssock = (pj_ssl_sock_t *)gnutls_session_get_ptr(session);
++    pj_assert(ssock);
++
++    /* Support only x509 format */
++    ret = gnutls_certificate_type_get(session) != GNUTLS_CRT_X509;
++    if (ret < 0) {
++        ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
++        return GNUTLS_E_CERTIFICATE_ERROR;
++    }
++
++    /* Store verification status */
++    ret = gnutls_certificate_verify_peers2(session, &status);
++    if (ret < 0) {
++        ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
++        return GNUTLS_E_CERTIFICATE_ERROR;
++    }
++    if (status & GNUTLS_CERT_INVALID) {
++        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
++            ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND;
++        else if (status & GNUTLS_CERT_EXPIRED ||
++                 status & GNUTLS_CERT_NOT_ACTIVATED)
++            ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD;
++        else if (status & GNUTLS_CERT_SIGNER_NOT_CA ||
++                 status & GNUTLS_CERT_INSECURE_ALGORITHM)
++            ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED;
++        else if (status & GNUTLS_CERT_UNEXPECTED_OWNER ||
++                 status & GNUTLS_CERT_MISMATCH)
++            ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH;
++        else if (status & GNUTLS_CERT_REVOKED)
++            ssock->verify_status |= PJ_SSL_CERT_EREVOKED;
++        else
++            ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
++
++        return GNUTLS_E_CERTIFICATE_ERROR;
++    }
++
++    /* When verification is not requested just return ok here, however
++     * applications can still get the verification status. */
++    if (ssock->param.verify_peer) {
++        gnutls_x509_crt_t cert;
++        unsigned int cert_list_size;
++        const gnutls_datum_t *cert_list;
++        int ret;
++
++        ret = gnutls_x509_crt_init(&cert);
++        if (ret < 0)
++            goto out;
++
++        cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
++        if (cert_list == NULL) {
++            ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
++            goto out;
++        }
++
++        /* TODO: verify whole chain perhaps? */
++        ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
++        if (ret < 0)
++            ret = gnutls_x509_crt_import(cert, &cert_list[0],
++                                         GNUTLS_X509_FMT_PEM);
++        if (ret < 0) {
++            ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
++            goto out;
++        }
++        ret = gnutls_x509_crt_check_hostname(cert, ssock->param.server_name.ptr);
++        if (ret < 0)
++            goto out;
++
++        gnutls_x509_crt_deinit(cert);
++
++        /* notify GnuTLS to continue handshake normally */
++        return GNUTLS_E_SUCCESS;
++
++out:
++        tls_last_error = ret;
++        ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
++        return GNUTLS_E_CERTIFICATE_ERROR;
++    }
++
++    return GNUTLS_E_SUCCESS;
++}
++
++
++/* gnutls_handshake() and gnutls_record_send() will call this function to
++ * send/write (encrypted) data */
++static ssize_t tls_data_push(gnutls_transport_ptr_t ptr,
++                             const void *data, size_t len)
++{
++    pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr;
++
++    pj_lock_acquire(ssock->circ_buf_output_mutex);
++    if (circ_write(&ssock->circ_buf_output, data, len) != PJ_SUCCESS) {
++        pj_lock_release(ssock->circ_buf_output_mutex);
++
++        errno = ENOMEM;
++        return -1;
++    }
++
++    pj_lock_release(ssock->circ_buf_output_mutex);
++
++    return len;
++}
++
++
++/* gnutls_handshake() and gnutls_record_recv() will call this function to
++ * receive/read (encrypted) data */
++static ssize_t tls_data_pull(gnutls_transport_ptr_t ptr,
++                             void *data, pj_size_t len)
++{
++    pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr;
++
++    pj_lock_acquire(ssock->circ_buf_input_mutex);
++
++    if (circ_empty(&ssock->circ_buf_input)) {
++        pj_lock_release(ssock->circ_buf_input_mutex);
++
++        /* Data buffers not yet filled */
++        errno = EAGAIN;
++        return -1;
++    }
++
++    pj_size_t circ_buf_size = circ_size(&ssock->circ_buf_input);
++    pj_size_t read_size = PJ_MIN(circ_buf_size, len);
++
++    circ_read(&ssock->circ_buf_input, data, read_size);
++
++    pj_lock_release(ssock->circ_buf_input_mutex);
++
++    return read_size;
++}
++
++
++/* Append a string to the priority string, only once. */
++static pj_status_t tls_str_append_once(pj_str_t *dst, pj_str_t *src)
++{
++    if (pj_strstr(dst, src) == NULL) {
++        /* Check buffer size */
++        if (dst->slen + src->slen + 3 > 1024)
++            return PJ_ETOOMANY;
++
++        pj_strcat2(dst, ":+");
++        pj_strcat(dst, src);
++    }
++    return PJ_SUCCESS;
++}
++
++
++/* Generate priority string with user preference order. */
++static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock)
++{
++    char buf[1024];
++    pj_str_t cipher_list;
++    pj_str_t compression = pj_str("COMP-NULL");
++    pj_str_t server = pj_str(":%SERVER_PRECEDENCE");
++    int i, j, ret;
++    const char *priority;
++    const char *err;
++
++    pj_strset(&cipher_list, buf, 0);
++
++    /* For each level, enable only the requested protocol */
++    switch (ssock->param.proto) {
++    case PJ_SSL_SOCK_PROTO_DEFAULT:
++    case PJ_SSL_SOCK_PROTO_TLS1:
++        // set lowest compatibility mode, ask for TLS client hello
++        if (ssock->param.ciphers_num == 0)
++            priority = "NORMAL:-VERS-SSL3.0:-VERS-TLS1.1:-VERS-TLS1.2:%LATEST_RECORD_VERSION";
++        else
++            priority = "NONE:+VERS-TLS1.0:%LATEST_RECORD_VERSION";
++        break;
++    case PJ_SSL_SOCK_PROTO_SSL3:
++        if (ssock->param.ciphers_num == 0)
++            priority = "NORMAL:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2";
++        else
++            priority = "NONE:+VERS-SSL3.0";
++        break;
++    case PJ_SSL_SOCK_PROTO_SSL23:
++        /* GnuTLS does not support any SSLv2 suite, let's just enable SSLv3
++         * with maximum compatibility */
++        if (ssock->param.ciphers_num == 0)
++            priority = "NORMAL:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:%COMPAT";
++        else
++            priority = "NONE:+VERS-SSL3.0:%COMPAT";
++        break;
++    default:
++        return PJ_ENOTSUP;
++    }
++
++    pj_strcat2(&cipher_list, priority);
++    for (i = 0; i < ssock->param.ciphers_num; i++) {
++        for (j = 0; ; j++) {
++            pj_ssl_cipher c;
++            const char *suite;
++            unsigned char id[2];
++            gnutls_protocol_t proto;
++            gnutls_kx_algorithm_t kx;
++            gnutls_mac_algorithm_t mac;
++            gnutls_cipher_algorithm_t algo;
++
++            suite = gnutls_cipher_suite_info(j, (unsigned char *)id,
++                                             &kx, &algo, &mac, &proto);
++            if (!suite)
++                break;
++
++            c = (pj_ssl_cipher) (pj_uint32_t) ((id[0] << 8) | id[1]);
++            if (ssock->param.ciphers[i] == c) {
++                char temp[256];
++                pj_str_t cipher_entry;
++
++                /* Protocol version */
++                pj_strset(&cipher_entry, temp, 0);
++                pj_strcat2(&cipher_entry, "VERS-");
++                pj_strcat2(&cipher_entry, gnutls_protocol_get_name(proto));
++                ret = tls_str_append_once(&cipher_list, &cipher_entry);
++                if (ret != PJ_SUCCESS)
++                    return ret;
++
++                /* Cipher */
++                pj_strset(&cipher_entry, temp, 0);
++                pj_strcat2(&cipher_entry, gnutls_cipher_get_name(algo));
++                ret = tls_str_append_once(&cipher_list, &cipher_entry);
++                if (ret != PJ_SUCCESS)
++                    return ret;
++
++                /* Mac */
++                pj_strset(&cipher_entry, temp, 0);
++                pj_strcat2(&cipher_entry, gnutls_mac_get_name(mac));
++                ret = tls_str_append_once(&cipher_list, &cipher_entry);
++                if (ret != PJ_SUCCESS)
++                    return ret;
++
++                /* Key exchange */
++                pj_strset(&cipher_entry, temp, 0);
++                pj_strcat2(&cipher_entry, gnutls_kx_get_name(kx));
++                ret = tls_str_append_once(&cipher_list, &cipher_entry);
++                if (ret != PJ_SUCCESS)
++                    return ret;
++
++                /* Compression is always disabled */
++                /* Signature is level-default */
++                break;
++            }
++        }
++    }
++
++    /* Disable compression, it's a TLS-only extension after all */
++    tls_str_append_once(&cipher_list, &compression);
++
++    /* Server will be the one deciding which crypto to use */
++    if (ssock->is_server) {
++        if (cipher_list.slen + server.slen + 1 > sizeof(buf))
++            return PJ_ETOOMANY;
++        else
++            pj_strcat(&cipher_list, &server);
++    }
++
++    /* End the string and print it */
++    cipher_list.ptr[cipher_list.slen] = '\0';
++    PJ_LOG(5, (ssock->pool->obj_name, "Priority string: %s", cipher_list.ptr));
++
++    /* Set our priority string */
++    ret = gnutls_priority_set_direct(ssock->session,
++                                        cipher_list.ptr, &err);
++    if (ret < 0) {
++        tls_last_error = GNUTLS_E_INVALID_REQUEST;
++        return PJ_EINVAL;
++    }
++
++    return PJ_SUCCESS;
++}
++
++
++/* Load root CA file or load the installed ones. */
++static pj_status_t tls_trust_set(pj_ssl_sock_t *ssock)
++{
++    int ntrusts = 0;
++    int err;
++
++    err = gnutls_certificate_set_x509_system_trust(ssock->xcred);
++    if (err > 0)
++        ntrusts += err;
++    err = gnutls_certificate_set_x509_trust_file(ssock->xcred,
++                                                 TRUST_STORE_FILE1,
++                                                 GNUTLS_X509_FMT_PEM);
++    if (err > 0)
++        ntrusts += err;
++
++    err = gnutls_certificate_set_x509_trust_file(ssock->xcred,
++                                                 TRUST_STORE_FILE2,
++                                                 GNUTLS_X509_FMT_PEM);
++    if (err > 0)
++        ntrusts += err;
++
++    if (ntrusts > 0)
++        return PJ_SUCCESS;
++    else if (!ntrusts)
++        return PJ_ENOTFOUND;
++    else
++        return PJ_EINVAL;
++}
++
++
++/* Create and initialize new GnuTLS context and instance */
++static pj_status_t tls_open(pj_ssl_sock_t *ssock)
++{
++    pj_ssl_cert_t *cert;
++    pj_status_t status;
++    int ret;
++
++    pj_assert(ssock);
++
++    cert = ssock->cert;
++
++    /* Even if reopening is harmless, having one instance only simplifies
++     * deallocating it later on */
++    if (!ssock->tls_init_count) {
++        ssock->tls_init_count++;
++        ret = tls_init();
++        if (ret < 0)
++            return ret;
++    } else
++        return PJ_SUCCESS;
++
++    /* Start this socket session */
++    ret = gnutls_init(&ssock->session, ssock->is_server ? GNUTLS_SERVER
++                                                        : GNUTLS_CLIENT);
++    if (ret < 0)
++        goto out;
++
++    /* Set the ssock object to be retrieved by transport (send/recv) and by
++     * user data from this session */
++    gnutls_transport_set_ptr(ssock->session,
++                             (gnutls_transport_ptr_t) (uintptr_t) ssock);
++    gnutls_session_set_ptr(ssock->session,
++                           (gnutls_transport_ptr_t) (uintptr_t) ssock);
++
++    /* Initialize input circular buffer */
++    status = circ_init(ssock->pool->factory, &ssock->circ_buf_input, 512);
++    if (status != PJ_SUCCESS)
++        return status;
++
++    /* Initialize output circular buffer */
++    status = circ_init(ssock->pool->factory, &ssock->circ_buf_output, 512);
++    if (status != PJ_SUCCESS)
++        return status;
++
++    /* Set the callback that allows GnuTLS to PUSH and PULL data
++     * TO and FROM the transport layer */
++    gnutls_transport_set_push_function(ssock->session, tls_data_push);
++    gnutls_transport_set_pull_function(ssock->session, tls_data_pull);
++
++    /* Determine which cipher suite to support */
++    status = tls_priorities_set(ssock);
++    if (status != PJ_SUCCESS)
++        return status;
++
++    /* Allocate credentials for handshaking and transmission */
++    ret = gnutls_certificate_allocate_credentials(&ssock->xcred);
++    if (ret < 0)
++        goto out;
++    gnutls_certificate_set_verify_function(ssock->xcred, tls_cert_verify_cb);
++
++    /* Load system trust file(s) */
++    status = tls_trust_set(ssock);
++    if (status != PJ_SUCCESS)
++        return status;
++
++    /* Load user-provided CA, certificate and key if available */
++    if (cert) {
++        /* Load CA if one is specified. */
++        if (cert->CA_file.slen) {
++            ret = gnutls_certificate_set_x509_trust_file(ssock->xcred,
++                                                         cert->CA_file.ptr,
++                                                         GNUTLS_X509_FMT_PEM);
++            if (ret < 0)
++                ret = gnutls_certificate_set_x509_trust_file(ssock->xcred,
++                                                             cert->CA_file.ptr,
++                                                             GNUTLS_X509_FMT_DER);
++            if (ret < 0)
++                goto out;
++        }
++
++        /* Load certificate, key and pass if one is specified */
++        if (cert->cert_file.slen) {
++            const char *prikey_file = cert->privkey_file.slen
++                                    ? cert->privkey_file.ptr
++                                    : NULL;
++            const char *prikey_pass = (cert->privkey_file.slen &&
++                                       cert->privkey_pass.slen)
++                                    ? cert->privkey_pass.ptr
++                                    : NULL;
++            ret = gnutls_certificate_set_x509_key_file2(ssock->xcred,
++                                                        cert->cert_file.ptr,
++                                                        prikey_file,
++                                                        GNUTLS_X509_FMT_PEM,
++                                                        prikey_pass,
++                                                        0);
++            if (ret != GNUTLS_E_SUCCESS)
++                ret = gnutls_certificate_set_x509_key_file2(ssock->xcred,
++                                                            cert->cert_file.ptr,
++                                                            prikey_file,
++                                                            GNUTLS_X509_FMT_DER,
++                                                            prikey_pass,
++                                                            0);
++            if (ret < 0)
++                goto out;
++        }
++    }
++
++    /* Require client certificate if asked */
++    if (ssock->is_server && ssock->param.require_client_cert)
++        gnutls_certificate_server_set_request(ssock->session,
++                                              GNUTLS_CERT_REQUIRE);
++
++    /* Finally set credentials for this session */
++    ret = gnutls_credentials_set(ssock->session,
++                                 GNUTLS_CRD_CERTIFICATE, ssock->xcred);
++    if (ret < 0)
++        goto out;
++
++    ret = GNUTLS_E_SUCCESS;
++out:
++    return tls_status_from_err(ssock, ret);
++}
++
++
++/* Destroy GnuTLS credentials and session. */
++static void tls_close(pj_ssl_sock_t *ssock)
++{
++    if (ssock->session) {
++        gnutls_bye(ssock->session, GNUTLS_SHUT_RDWR);
++        gnutls_deinit(ssock->session);
++        ssock->session = NULL;
++    }
++
++    if (ssock->xcred) {
++        gnutls_certificate_free_credentials(ssock->xcred);
++        ssock->xcred = NULL;
++    }
++
++    /* Free GnuTLS library */
++    if (ssock->tls_init_count) {
++        ssock->tls_init_count--;
++        tls_deinit();
++    }
++
++    /* Destroy circular buffers */
++    circ_deinit(&ssock->circ_buf_input);
++    circ_deinit(&ssock->circ_buf_output);
++}
++
++
++/* Reset socket state. */
++static void tls_sock_reset(pj_ssl_sock_t *ssock)
++{
++    ssock->connection_state = TLS_STATE_NULL;
++
++    tls_close(ssock);
++
++    if (ssock->asock) {
++        pj_activesock_close(ssock->asock);
++        ssock->asock = NULL;
++        ssock->sock = PJ_INVALID_SOCKET;
++    }
++    if (ssock->sock != PJ_INVALID_SOCKET) {
++        pj_sock_close(ssock->sock);
++        ssock->sock = PJ_INVALID_SOCKET;
++    }
++
++    ssock->last_err = tls_last_error = GNUTLS_E_SUCCESS;
++}
++
++
++/* Get Common Name field string from a general name string */
++static void tls_cert_get_cn(const pj_str_t *gen_name, pj_str_t *cn)
++{
++    pj_str_t CN_sign = {"CN=", 3};
++    char *p, *q;
++
++    pj_bzero(cn, sizeof(cn));
++
++    p = pj_strstr(gen_name, &CN_sign);
++    if (!p)
++        return;
++
++    p += 3; /* shift pointer to value part */
++    pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr));
++    q = pj_strchr(cn, ',');
++    if (q)
++        cn->slen = q - p;
++}
++
++
++/* Get certificate info; in case the certificate info is already populated,
++ * this function will check if the contents need updating by inspecting the
++ * issuer and the serial number. */
++static void tls_cert_get_info(pj_pool_t *pool, pj_ssl_cert_info *ci,
++                              gnutls_x509_crt_t cert)
++{
++    pj_bool_t update_needed;
++    char buf[512] = { 0 };
++    size_t bufsize = sizeof(buf);
++    pj_uint8_t serial_no[64] = { 0 }; /* should be >= sizeof(ci->serial_no) */
++    size_t serialsize = sizeof(serial_no);
++    size_t len = sizeof(buf);
++    int i, ret, seq = 0;
++    pj_ssl_cert_name_type type;
++
++    pj_assert(pool && ci && cert);
++
++    /* Get issuer */
++    gnutls_x509_crt_get_issuer_dn(cert, buf, &bufsize);
++
++    /* Get serial no */
++    gnutls_x509_crt_get_serial(cert, serial_no, &serialsize);
++
++    /* Check if the contents need to be updated */
++    update_needed = pj_strcmp2(&ci->issuer.info, buf) ||
++                    pj_memcmp(ci->serial_no, serial_no, serialsize);
++    if (!update_needed)
++        return;
++
++    /* Update cert info */
++
++    pj_bzero(ci, sizeof(pj_ssl_cert_info));
++
++    /* Version */
++    ci->version = gnutls_x509_crt_get_version(cert);
++
++    /* Issuer */
++    pj_strdup2(pool, &ci->issuer.info, buf);
++    tls_cert_get_cn(&ci->issuer.info, &ci->issuer.cn);
++
++    /* Serial number */
++    pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no));
++
++    /* Subject */
++    bufsize = sizeof(buf);
++    gnutls_x509_crt_get_dn(cert, buf, &bufsize);
++    pj_strdup2(pool, &ci->subject.info, buf);
++    tls_cert_get_cn(&ci->subject.info, &ci->subject.cn);
++
++    /* Validity */
++    ci->validity.end.sec = gnutls_x509_crt_get_expiration_time(cert);
++    ci->validity.start.sec = gnutls_x509_crt_get_activation_time(cert);
++    ci->validity.gmt = 0;
++
++    /* Subject Alternative Name extension */
++    if (ci->version >= 3) {
++        char out[256] = { 0 };
++        /* Get the number of all alternate names so that we can allocate
++         * the correct number of bytes in subj_alt_name */
++        while (gnutls_x509_crt_get_subject_alt_name(cert, seq, out, &len,
++                                                    NULL) != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
++            seq++;
++
++        ci->subj_alt_name.entry = pj_pool_calloc(pool, seq,
++                                                 sizeof(*ci->subj_alt_name.entry));
++        if (!ci->subj_alt_name.entry) {
++            tls_last_error = GNUTLS_E_MEMORY_ERROR;
++            return;
++        }
++
++        /* Now populate the alternative names */
++        for (i = 0; i < seq; i++) {
++            len = sizeof(out) - 1;
++            ret = gnutls_x509_crt_get_subject_alt_name(cert, i, out, &len, NULL);
++            switch (ret) {
++            case GNUTLS_SAN_IPADDRESS:
++                type = PJ_SSL_CERT_NAME_IP;
++                pj_inet_ntop2(len == sizeof(pj_in6_addr) ? pj_AF_INET6()
++                                                         : pj_AF_INET(),
++                              out, buf, sizeof(buf));
++                break;
++            case GNUTLS_SAN_URI:
++                type = PJ_SSL_CERT_NAME_URI;
++                break;
++            case GNUTLS_SAN_RFC822NAME:
++                type = PJ_SSL_CERT_NAME_RFC822;
++                break;
++            case GNUTLS_SAN_DNSNAME:
++                type = PJ_SSL_CERT_NAME_DNS;
++                break;
++            default:
++                type = PJ_SSL_CERT_NAME_UNKNOWN;
++                break;
++            }
++
++            if (len && type != PJ_SSL_CERT_NAME_UNKNOWN) {
++                ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type;
++                pj_strdup2(pool,
++                           &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
++                           type == PJ_SSL_CERT_NAME_IP ? buf : out);
++                ci->subj_alt_name.cnt++;
++            }
++        }
++        /* TODO: if no DNS alt. names were found, we could check against
++         * the commonName as per RFC3280. */
++    }
++}
++
++
++/* Update local & remote certificates info. This function should be
++ * called after handshake or renegotiation successfully completed. */
++static void tls_cert_update(pj_ssl_sock_t *ssock)
++{
++    gnutls_x509_crt_t cert = NULL;
++    const gnutls_datum_t *us;
++    const gnutls_datum_t *certs;
++    unsigned int certslen = 0;
++    int ret = GNUTLS_CERT_INVALID;
++
++    pj_assert(ssock->connection_state == TLS_STATE_ESTABLISHED);
++
++    /* Get active local certificate */
++    us = gnutls_certificate_get_ours(ssock->session);
++    if (!us)
++        goto us_out;
++
++    ret = gnutls_x509_crt_init(&cert);
++    if (ret < 0)
++        goto us_out;
++    ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_DER);
++    if (ret < 0)
++        ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_PEM);
++    if (ret < 0)
++        goto us_out;
++
++    tls_cert_get_info(ssock->pool, &ssock->local_cert_info, cert);
++
++us_out:
++    tls_last_error = ret;
++    if (cert)
++        gnutls_x509_crt_deinit(cert);
++    else
++        pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info));
++
++    cert = NULL;
++
++    /* Get active remote certificate */
++    certs = gnutls_certificate_get_peers(ssock->session, &certslen);
++    if (certs == NULL || certslen == 0)
++        goto peer_out;
++
++    ret = gnutls_x509_crt_init(&cert);
++    if (ret < 0)
++        goto peer_out;
++
++    ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_PEM);
++    if (ret < 0)
++        ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER);
++    if (ret < 0)
++        goto peer_out;
++
++    tls_cert_get_info(ssock->pool, &ssock->remote_cert_info, cert);
++
++peer_out:
++    tls_last_error = ret;
++    if (cert)
++        gnutls_x509_crt_deinit(cert);
++    else
++        pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
++}
++
++
++/* When handshake completed:
++ * - notify application
++ * - if handshake failed, reset SSL state
++ * - return PJ_FALSE when SSL socket instance is destroyed by application. */
++static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock,
++                                       pj_status_t status)
++{
++    pj_bool_t ret = PJ_TRUE;
++
++    /* Cancel handshake timer */
++    if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) {
++        pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
++        ssock->timer.id = TIMER_NONE;
++    }
++
++    /* Update certificates info on successful handshake */
++    if (status == PJ_SUCCESS)
++        tls_cert_update(ssock);
++
++    /* Accepting */
++    if (ssock->is_server) {
++        if (status != PJ_SUCCESS) {
++            /* Handshake failed in accepting, destroy our self silently. */
++
++            char errmsg[PJ_ERR_MSG_SIZE];
++            char buf[PJ_INET6_ADDRSTRLEN + 10];
++
++            pj_strerror(status, errmsg, sizeof(errmsg));
++            PJ_LOG(3, (ssock->pool->obj_name,
++                       "Handshake failed in accepting %s: %s",
++                       pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3),
++                       errmsg));
++
++            /* Workaround for ticket #985 */
++#if (defined(PJ_WIN32) && PJ_WIN32 != 0) || (defined(PJ_WIN64) && PJ_WIN64 != 0)
++            if (ssock->param.timer_heap) {
++                pj_time_val interval = {0, DELAYED_CLOSE_TIMEOUT};
++
++                tls_sock_reset(ssock);
++
++                ssock->timer.id = TIMER_CLOSE;
++                pj_time_val_normalize(&interval);
++                if (pj_timer_heap_schedule(ssock->param.timer_heap,
++                                           &ssock->timer, &interval) != 0)
++                {
++                    ssock->timer.id = TIMER_NONE;
++                    pj_ssl_sock_close(ssock);
++                }
++            } else
++#endif /* PJ_WIN32 */
++            {
++                pj_ssl_sock_close(ssock);
++            }
++
++            return PJ_FALSE;
++        }
++        /* Notify application the newly accepted SSL socket */
++        if (ssock->param.cb.on_accept_complete)
++            ret = (*ssock->param.cb.on_accept_complete)
++                      (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr,
++                       pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr));
++
++    } else { /* Connecting */
++        /* On failure, reset SSL socket state first, as app may try to
++         * reconnect in the callback. */
++        if (status != PJ_SUCCESS) {
++            /* Server disconnected us, possibly due to negotiation failure */
++            tls_sock_reset(ssock);
++        }
++        if (ssock->param.cb.on_connect_complete) {
++
++            ret = (*ssock->param.cb.on_connect_complete)(ssock, status);
++        }
++    }
++
++    return ret;
++}
++
++static write_data_t *alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len)
++{
++    send_buf_t *send_buf = &ssock->send_buf;
++    pj_size_t avail_len, skipped_len = 0;
++    char *reg1, *reg2;
++    pj_size_t reg1_len, reg2_len;
++    write_data_t *p;
++
++    /* Check buffer availability */
++    avail_len = send_buf->max_len - send_buf->len;
++    if (avail_len < len)
++        return NULL;
++
++    /* If buffer empty, reset start pointer and return it */
++    if (send_buf->len == 0) {
++        send_buf->start = send_buf->buf;
++        send_buf->len   = len;
++        p = (write_data_t*)send_buf->start;
++        goto init_send_data;
++    }
++
++    /* Free space may be wrapped/splitted into two regions, so let's
++     * analyze them if any region can hold the write data. */
++    reg1 = send_buf->start + send_buf->len;
++    if (reg1 >= send_buf->buf + send_buf->max_len)
++        reg1 -= send_buf->max_len;
++        reg1_len = send_buf->max_len - send_buf->len;
++    if (reg1 + reg1_len > send_buf->buf + send_buf->max_len) {
++        reg1_len = send_buf->buf + send_buf->max_len - reg1;
++        reg2 = send_buf->buf;
++        reg2_len = send_buf->start - send_buf->buf;
++    } else {
++        reg2 = NULL;
++        reg2_len = 0;
++    }
++
++    /* More buffer availability check, note that the write data must be in
++     * a contigue buffer. */
++    avail_len = PJ_MAX(reg1_len, reg2_len);
++    if (avail_len < len)
++    return NULL;
++
++    /* Get the data slot */
++    if (reg1_len >= len) {
++        p = (write_data_t*)reg1;
++    } else {
++        p = (write_data_t*)reg2;
++        skipped_len = reg1_len;
++    }
++
++    /* Update buffer length */
++    send_buf->len += len + skipped_len;
++
++init_send_data:
++    /* Init the new send data */
++    pj_bzero(p, sizeof(*p));
++    pj_list_init(p);
++    pj_list_push_back(&ssock->send_pending, p);
++
++    return p;
++}
++
++static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata)
++{
++    send_buf_t *buf = &ssock->send_buf;
++    write_data_t *spl = &ssock->send_pending;
++
++    pj_assert(!pj_list_empty(&ssock->send_pending));
++
++    /* Free slot from the buffer */
++    if (spl->next == wdata && spl->prev == wdata) {
++    /* This is the only data, reset the buffer */
++    buf->start = buf->buf;
++    buf->len = 0;
++    } else if (spl->next == wdata) {
++    /* This is the first data, shift start pointer of the buffer and
++     * adjust the buffer length.
++     */
++    buf->start = (char*)wdata->next;
++    if (wdata->next > wdata) {
++        buf->len -= ((char*)wdata->next - buf->start);
++    } else {
++        /* Overlapped */
++        pj_size_t right_len, left_len;
++        right_len = buf->buf + buf->max_len - (char*)wdata;
++        left_len  = (char*)wdata->next - buf->buf;
++        buf->len -= (right_len + left_len);
++    }
++    } else if (spl->prev == wdata) {
++    /* This is the last data, just adjust the buffer length */
++    if (wdata->prev < wdata) {
++        pj_size_t jump_len;
++        jump_len = (char*)wdata -
++               ((char*)wdata->prev + wdata->prev->record_len);
++        buf->len -= (wdata->record_len + jump_len);
++    } else {
++        /* Overlapped */
++        pj_size_t right_len, left_len;
++        right_len = buf->buf + buf->max_len -
++            ((char*)wdata->prev + wdata->prev->record_len);
++        left_len  = (char*)wdata + wdata->record_len - buf->buf;
++        buf->len -= (right_len + left_len);
++    }
++    }
++    /* For data in the middle buffer, just do nothing on the buffer. The slot
++     * will be freed later when freeing the first/last data. */
++
++    /* Remove the data from send pending list */
++    pj_list_erase(wdata);
++}
++
++#if 0
++/* Just for testing send buffer alloc/free */
++#include <pj/rand.h>
++pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool)
++{
++    enum { MAX_CHUNK_NUM = 20 };
++    unsigned chunk_size, chunk_cnt, i;
++    write_data_t *wdata[MAX_CHUNK_NUM] = {0};
++    pj_time_val now;
++    pj_ssl_sock_t *ssock = NULL;
++    pj_ssl_sock_param param;
++    pj_status_t status;
++
++    pj_gettimeofday(&now);
++    pj_srand((unsigned)now.sec);
++
++    pj_ssl_sock_param_default(&param);
++    status = pj_ssl_sock_create(pool, &param, &ssock);
++    if (status != PJ_SUCCESS) {
++        return status;
++    }
++
++    if (ssock->send_buf.max_len == 0) {
++        ssock->send_buf.buf = (char *)
++                              pj_pool_alloc(ssock->pool,
++                                            ssock->param.send_buffer_size);
++        ssock->send_buf.max_len = ssock->param.send_buffer_size;
++        ssock->send_buf.start = ssock->send_buf.buf;
++        ssock->send_buf.len = 0;
++    }
++
++    chunk_size = ssock->param.send_buffer_size / MAX_CHUNK_NUM / 2;
++    chunk_cnt = 0;
++    for (i = 0; i < MAX_CHUNK_NUM; i++) {
++        wdata[i] = alloc_send_data(ssock, pj_rand() % chunk_size + 321);
++        if (wdata[i])
++            chunk_cnt++;
++        else
++            break;
++    }
++
++    while (chunk_cnt) {
++        i = pj_rand() % MAX_CHUNK_NUM;
++        if (wdata[i]) {
++            free_send_data(ssock, wdata[i]);
++            wdata[i] = NULL;
++            chunk_cnt--;
++        }
++    }
++
++    if (ssock->send_buf.len != 0)
++        status = PJ_EBUG;
++
++    pj_ssl_sock_close(ssock);
++    return status;
++}
++#endif
++
++/* Flush write circular buffer to network socket. */
++static pj_status_t flush_circ_buf_output(pj_ssl_sock_t *ssock,
++                                         pj_ioqueue_op_key_t *send_key,
++                                         pj_size_t orig_len, unsigned flags)
++{
++    pj_ssize_t len;
++    write_data_t *wdata;
++    pj_size_t needed_len;
++    pj_status_t status;
++
++    pj_lock_acquire(ssock->circ_buf_output_mutex);
++
++    /* Check if there is data in the circular buffer, flush it if any */
++    if (circ_empty(&ssock->circ_buf_output)) {
++        pj_lock_release(ssock->circ_buf_output_mutex);
++
++        return PJ_SUCCESS;
++    }
++
++    len = circ_size(&ssock->circ_buf_output);
++
++    /* Calculate buffer size needed, and align it to 8 */
++    needed_len = len + sizeof(write_data_t);
++    needed_len = ((needed_len + 7) >> 3) << 3;
++
++    /* Allocate buffer for send data */
++    wdata = alloc_send_data(ssock, needed_len);
++    if (wdata == NULL) {
++        pj_lock_release(ssock->circ_buf_output_mutex);
++        return PJ_ENOMEM;
++    }
++
++    /* Copy the data and set its properties into the send data */
++    pj_ioqueue_op_key_init(&wdata->key, sizeof(pj_ioqueue_op_key_t));
++    wdata->key.user_data = wdata;
++    wdata->app_key = send_key;
++    wdata->record_len = needed_len;
++    wdata->data_len = len;
++    wdata->plain_data_len = orig_len;
++    wdata->flags = flags;
++    circ_read(&ssock->circ_buf_output, (pj_uint8_t *)&wdata->data, len);
++
++    /* Ticket #1573: Don't hold mutex while calling PJLIB socket send(). */
++    pj_lock_release(ssock->circ_buf_output_mutex);
++
++    /* Send it */
++    if (ssock->param.sock_type == pj_SOCK_STREAM()) {
++        status = pj_activesock_send(ssock->asock, &wdata->key,
++                                    wdata->data.content, &len,
++                                    flags);
++    } else {
++        status = pj_activesock_sendto(ssock->asock, &wdata->key,
++                                      wdata->data.content, &len,
++                                      flags,
++                                      (pj_sockaddr_t*)&ssock->rem_addr,
++                                      ssock->addr_len);
++    }
++
++    if (status != PJ_EPENDING) {
++        /* When the sending is not pending, remove the wdata from send
++         * pending list. */
++        pj_lock_acquire(ssock->circ_buf_output_mutex);
++        free_send_data(ssock, wdata);
++        pj_lock_release(ssock->circ_buf_output_mutex);
++    }
++
++    return status;
++}
++
++static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te)
++{
++    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data;
++    int timer_id = te->id;
++
++    te->id = TIMER_NONE;
++
++    PJ_UNUSED_ARG(th);
++
++    switch (timer_id) {
++    case TIMER_HANDSHAKE_TIMEOUT:
++        PJ_LOG(1, (ssock->pool->obj_name, "TLS timeout after %d.%ds",
++                   ssock->param.timeout.sec, ssock->param.timeout.msec));
++
++        on_handshake_complete(ssock, PJ_ETIMEDOUT);
++        break;
++    case TIMER_CLOSE:
++        pj_ssl_sock_close(ssock);
++        break;
++    default:
++        pj_assert(!"Unknown timer");
++        break;
++    }
++}
++
++
++/* Try to perform an asynchronous handshake */
++static pj_status_t tls_try_handshake(pj_ssl_sock_t *ssock)
++{
++    int ret;
++    pj_status_t status;
++
++    /* Perform SSL handshake */
++    ret = gnutls_handshake(ssock->session);
++
++    status = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0);
++    if (status != PJ_SUCCESS)
++        return status;
++
++    if (ret == GNUTLS_E_SUCCESS) {
++        /* System are GO */
++        ssock->connection_state = TLS_STATE_ESTABLISHED;
++        status = PJ_SUCCESS;
++    } else if (!gnutls_error_is_fatal(ret)) {
++        /* Non fatal error, retry later (busy or again) */
++        status = PJ_EPENDING;
++    } else {
++        /* Fatal error invalidates session, no fallback */
++        status = PJ_EINVAL;
++    }
++
++    tls_last_error = ret;
++
++    return status;
++}
++
++
++/*
++ *******************************************************************
++ * Active socket callbacks.
++ *******************************************************************
++ */
++
++/* PJ_TRUE asks the socket to read more data, PJ_FALSE takes it off the queue */
++static pj_bool_t asock_on_data_read(pj_activesock_t *asock, void *data,
++                                    pj_size_t size, pj_status_t status,
++                                    pj_size_t *remainder)
++{
++    pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)
++                           pj_activesock_get_user_data(asock);
++
++    pj_size_t app_remainder = 0;
++
++    if (data && size > 0) {
++        /* Push data into input circular buffer (for GnuTLS) */
++        pj_lock_acquire(ssock->circ_buf_input_mutex);
++        circ_write(&ssock->circ_buf_input, data, size);
++        pj_lock_release(ssock->circ_buf_input_mutex);
++    }
++
++    /* Check if SSL handshake hasn't finished yet */
++    if (ssock->connection_state == TLS_STATE_HANDSHAKING) {
++        pj_bool_t ret = PJ_TRUE;
++
++        if (status == PJ_SUCCESS)
++            status = tls_try_handshake(ssock);
++
++        /* Not pending is either success or failed */
++        if (status != PJ_EPENDING)
++            ret = on_handshake_complete(ssock, status);
++
++        return ret;
++    }
++
++    /* See if there is any decrypted data for the application */
++    if (ssock->read_started) {
++        do {
++            /* Get read data structure at the end of the data */
++            read_data_t *app_read_data = *(OFFSET_OF_READ_DATA_PTR(ssock, data));
++            int app_data_size = (int)(ssock->read_size - app_read_data->len);
++
++            /* Decrypt received data using GnuTLS (will read our input
++             * circular buffer) */
++            int decrypted_size = gnutls_record_recv(ssock->session,
++                                                    app_read_data->data +
++                                                    app_read_data->len,
++                                                    app_data_size);
++
++            if (decrypted_size > 0 || status != PJ_SUCCESS) {
++                if (ssock->param.cb.on_data_read) {
++                    pj_bool_t ret;
++                    app_remainder = 0;
++
++                    if (decrypted_size > 0)
++                        app_read_data->len += decrypted_size;
++
++                    ret = (*ssock->param.cb.on_data_read)(ssock,
++                                                          app_read_data->data,
++                                                          app_read_data->len,
++                                                          status,
++                                                          &app_remainder);
++
++                    if (!ret) {
++                        /* We've been destroyed */
++                        return PJ_FALSE;
++                    }
++
++                    /* Application may have left some data to be consumed
++                     * later as remainder */
++                    app_read_data->len = app_remainder;
++                }
++
++                /* Active socket signalled connection closed/error, this has
++                 * been signalled to the application along with any remaining
++                 * buffer. So, let's just reset SSL socket now.  */
++                if (status != PJ_SUCCESS) {
++                    tls_sock_reset(ssock);
++                    return PJ_FALSE;
++                }
++            } else if (decrypted_size == 0) {
++                /* Nothing more to read */
++
++                return PJ_TRUE;
++            } else if (decrypted_size == GNUTLS_E_AGAIN ||
++                       decrypted_size == GNUTLS_E_INTERRUPTED) {
++                return PJ_TRUE;
++            } else if (decrypted_size == GNUTLS_E_REHANDSHAKE) {
++                /* Seems like we are renegotiating */
++                pj_status_t try_handshake_status = tls_try_handshake(ssock);
++
++                /* Not pending is either success or failed */
++                if (try_handshake_status != PJ_EPENDING) {
++                    if (!on_handshake_complete(ssock, try_handshake_status)) {
++                        return PJ_FALSE;
++                    }
++                }
++
++                if (try_handshake_status != PJ_SUCCESS &&
++                    try_handshake_status != PJ_EPENDING) {
++                    return PJ_FALSE;
++                }
++            } else if (!gnutls_error_is_fatal(decrypted_size)) {
++                /* non-fatal error, let's just continue */
++            } else {
++                return PJ_FALSE;
++            }
++        } while (PJ_TRUE);
++    }
++
++    return PJ_TRUE;
++}
++
++
++/* Callback every time new data is available from the active socket */
++static pj_bool_t asock_on_data_sent(pj_activesock_t *asock,
++                                    pj_ioqueue_op_key_t *send_key,
++                                    pj_ssize_t sent)
++{
++    pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)pj_activesock_get_user_data(asock);
++
++    PJ_UNUSED_ARG(send_key);
++    PJ_UNUSED_ARG(sent);
++
++    if (ssock->connection_state == TLS_STATE_HANDSHAKING) {
++        /* Initial handshaking */
++        pj_status_t status = tls_try_handshake(ssock);
++
++        /* Not pending is either success or failed */
++        if (status != PJ_EPENDING)
++            return on_handshake_complete(ssock, status);
++
++    } else if (send_key != &ssock->handshake_op_key) {
++        /* Some data has been sent, notify application */
++        write_data_t *wdata = (write_data_t*)send_key->user_data;
++        if (ssock->param.cb.on_data_sent) {
++            pj_bool_t ret;
++            pj_ssize_t sent_len;
++
++            sent_len = sent > 0 ? wdata->plain_data_len : sent;
++
++            ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key,
++                                                  sent_len);
++            if (!ret) {
++                /* We've been destroyed */
++                return PJ_FALSE;
++            }
++        }
++
++        /* Update write buffer state */
++        pj_lock_acquire(ssock->circ_buf_output_mutex);
++        free_send_data(ssock, wdata);
++        pj_lock_release(ssock->circ_buf_output_mutex);
++    } else {
++        /* SSL re-negotiation is on-progress, just do nothing */
++        /* FIXME: check if this is valid for GnuTLS too */
++    }
++
++    return PJ_TRUE;
++}
++
++
++/* Callback every time a new connection has been accepted (server) */
++static pj_bool_t asock_on_accept_complete(pj_activesock_t *asock,
++                                          pj_sock_t newsock,
++                                          const pj_sockaddr_t *src_addr,
++                                          int src_addr_len)
++{
++    pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t *)
++                                  pj_activesock_get_user_data(asock);
++
++    pj_ssl_sock_t *ssock;
++    pj_activesock_cb asock_cb;
++    pj_activesock_cfg asock_cfg;
++    unsigned int i;
++    pj_status_t status;
++
++    PJ_UNUSED_ARG(src_addr_len);
++
++    /* Create new SSL socket instance */
++    status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->param,
++                                &ssock);
++    if (status != PJ_SUCCESS)
++        goto on_return;
++
++    /* Update new SSL socket attributes */
++    ssock->sock = newsock;
++    ssock->parent = ssock_parent;
++    ssock->is_server = PJ_TRUE;
++    if (ssock_parent->cert) {
++        status = pj_ssl_sock_set_certificate(ssock, ssock->pool,
++                                             ssock_parent->cert);
++        if (status != PJ_SUCCESS)
++            goto on_return;
++    }
++
++    /* Apply QoS, if specified */
++    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
++                                &ssock->param.qos_params, 1,
++                                ssock->pool->obj_name, NULL);
++    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
++        goto on_return;
++
++    /* Update local address */
++    ssock->addr_len = src_addr_len;
++    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
++                                 &ssock->addr_len);
++    if (status != PJ_SUCCESS) {
++        /* This fails on few envs, e.g: win IOCP, just tolerate this and
++         * use parent local address instead.
++         */
++        pj_sockaddr_cp(&ssock->local_addr, &ssock_parent->local_addr);
++    }
++
++    /* Set remote address */
++    pj_sockaddr_cp(&ssock->rem_addr, src_addr);
++
++    /* Create SSL context */
++    status = tls_open(ssock);
++    if (status != PJ_SUCCESS)
++        goto on_return;
++
++    /* Prepare read buffer */
++    ssock->asock_rbuf = (void **)pj_pool_calloc(ssock->pool,
++                                                ssock->param.async_cnt,
++                                                sizeof(void*));
++    if (!ssock->asock_rbuf)
++        return PJ_ENOMEM;
++
++    for (i = 0; i < ssock->param.async_cnt; ++i) {
++        ssock->asock_rbuf[i] = (void *)pj_pool_alloc(
++                                            ssock->pool,
++                                            ssock->param.read_buffer_size +
++                                            sizeof(read_data_t*));
++        if (!ssock->asock_rbuf[i])
++            return PJ_ENOMEM;
++    }
++
++    /* Create active socket */
++    pj_activesock_cfg_default(&asock_cfg);
++    asock_cfg.async_cnt = ssock->param.async_cnt;
++    asock_cfg.concurrency = ssock->param.concurrency;
++    asock_cfg.whole_data = PJ_TRUE;
++
++    pj_bzero(&asock_cb, sizeof(asock_cb));
++    asock_cb.on_data_read = asock_on_data_read;
++    asock_cb.on_data_sent = asock_on_data_sent;
++
++    status = pj_activesock_create(ssock->pool,
++                                  ssock->sock,
++                                  ssock->param.sock_type,
++                                  &asock_cfg,
++                                  ssock->param.ioqueue,
++                                  &asock_cb,
++                                  ssock,
++                                  &ssock->asock);
++
++    if (status != PJ_SUCCESS)
++        goto on_return;
++
++    /* Start reading */
++    status = pj_activesock_start_read2(ssock->asock, ssock->pool,
++                                       (unsigned)ssock->param.read_buffer_size,
++                                       ssock->asock_rbuf,
++                                       PJ_IOQUEUE_ALWAYS_ASYNC);
++    if (status != PJ_SUCCESS)
++        goto on_return;
++
++    /* Prepare write/send state */
++    pj_assert(ssock->send_buf.max_len == 0);
++    ssock->send_buf.buf = (char *)pj_pool_alloc(ssock->pool,
++                                                ssock->param.send_buffer_size);
++    if (!ssock->send_buf.buf)
++        return PJ_ENOMEM;
++
++    ssock->send_buf.max_len = ssock->param.send_buffer_size;
++    ssock->send_buf.start = ssock->send_buf.buf;
++    ssock->send_buf.len = 0;
++
++    /* Start handshake timer */
++    if (ssock->param.timer_heap &&
++        (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0)) {
++        pj_assert(ssock->timer.id == TIMER_NONE);
++        ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
++        status = pj_timer_heap_schedule(ssock->param.timer_heap,
++                                        &ssock->timer,
++                                        &ssock->param.timeout);
++        if (status != PJ_SUCCESS)
++            ssock->timer.id = TIMER_NONE;
++    }
++
++    /* Start SSL handshake */
++    ssock->connection_state = TLS_STATE_HANDSHAKING;
++
++    status = tls_try_handshake(ssock);
++
++on_return:
++    if (ssock && status != PJ_EPENDING)
++        on_handshake_complete(ssock, status);
++
++    /* Must return PJ_TRUE whatever happened, as active socket must
++     * continue listening.
++     */
++    return PJ_TRUE;
++}
++
++
++/* Callback every time a new connection has been completed (client) */
++static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock,
++                                            pj_status_t status)
++{
++    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
++                           pj_activesock_get_user_data(asock);
++
++    unsigned int i;
++    int ret;
++
++    if (status != PJ_SUCCESS)
++        goto on_return;
++
++    /* Update local address */
++    ssock->addr_len = sizeof(pj_sockaddr);
++    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
++                                 &ssock->addr_len);
++    if (status != PJ_SUCCESS)
++        goto on_return;
++
++    /* Create SSL context */
++    status = tls_open(ssock);
++    if (status != PJ_SUCCESS)
++        goto on_return;
++
++    /* Prepare read buffer */
++    ssock->asock_rbuf = (void **)pj_pool_calloc(ssock->pool,
++                                                ssock->param.async_cnt,
++                                                sizeof(void *));
++    if (!ssock->asock_rbuf)
++        return PJ_ENOMEM;
++
++    for (i = 0; i < ssock->param.async_cnt; ++i) {
++        ssock->asock_rbuf[i] = (void *)pj_pool_alloc(
++                                            ssock->pool,
++                                            ssock->param.read_buffer_size +
++                                            sizeof(read_data_t *));
++        if (!ssock->asock_rbuf[i])
++            return PJ_ENOMEM;
++    }
++
++    /* Start read */
++    status = pj_activesock_start_read2(ssock->asock, ssock->pool,
++                                       (unsigned) ssock->param.read_buffer_size,
++                                       ssock->asock_rbuf,
++                                       PJ_IOQUEUE_ALWAYS_ASYNC);
++    if (status != PJ_SUCCESS)
++        goto on_return;
++
++    /* Prepare write/send state */
++    pj_assert(ssock->send_buf.max_len == 0);
++    ssock->send_buf.buf = (char *)pj_pool_alloc(ssock->pool,
++                                                ssock->param.send_buffer_size);
++    if (!ssock->send_buf.buf)
++        return PJ_ENOMEM;
++
++    ssock->send_buf.max_len = ssock->param.send_buffer_size;
++    ssock->send_buf.start = ssock->send_buf.buf;
++    ssock->send_buf.len = 0;
++
++    /* Set server name to connect */
++    if (ssock->param.server_name.slen) {
++        /* Server name is null terminated already */
++        ret = gnutls_server_name_set(ssock->session, GNUTLS_NAME_DNS,
++                                     ssock->param.server_name.ptr,
++                                     ssock->param.server_name.slen);
++        if (ret < 0) {
++            PJ_LOG(3, (ssock->pool->obj_name,
++                       "gnutls_server_name_set() failed: %s",
++                       gnutls_strerror(ret)));
++        }
++    }
++
++    /* Start handshake */
++    ssock->connection_state = TLS_STATE_HANDSHAKING;
++
++    status = tls_try_handshake(ssock);
++    if (status != PJ_EPENDING)
++        goto on_return;
++
++    return PJ_TRUE;
++
++on_return:
++    return on_handshake_complete(ssock, status);
++}
++
++static void tls_ciphers_fill(void)
++{
++     if (!tls_available_ciphers) {
++         tls_init();
++         tls_deinit();
++     }
++}
++
++/*
++ *******************************************************************
++ * API
++ *******************************************************************
++ */
++
++/* Load credentials from files. */
++PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
++                                                const pj_str_t *CA_file,
++                                                const pj_str_t *cert_file,
++                                                const pj_str_t *privkey_file,
++                                                const pj_str_t *privkey_pass,
++                                                pj_ssl_cert_t **p_cert)
++{
++    pj_ssl_cert_t *cert;
++
++    PJ_ASSERT_RETURN(pool && CA_file && cert_file && privkey_file, PJ_EINVAL);
++
++    cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
++    pj_strdup_with_null(pool, &cert->CA_file, CA_file);
++    pj_strdup_with_null(pool, &cert->cert_file, cert_file);
++    pj_strdup_with_null(pool, &cert->privkey_file, privkey_file);
++    pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass);
++
++    *p_cert = cert;
++
++    return PJ_SUCCESS;
++}
++
++
++/* Store credentials. */
++PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate(pj_ssl_sock_t *ssock,
++                                                 pj_pool_t *pool,
++                                                 const pj_ssl_cert_t *cert)
++{
++    pj_ssl_cert_t *cert_;
++
++    PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL);
++
++    cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
++    pj_memcpy(cert_, cert, sizeof(cert));
++    pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file);
++    pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file);
++    pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file);
++    pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass);
++
++    ssock->cert = cert_;
++
++    return PJ_SUCCESS;
++}
++
++
++/* Get available ciphers. */
++PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[],
++                                                 unsigned *cipher_num)
++{
++    unsigned int i;
++
++    PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL);
++
++    tls_ciphers_fill();
++
++    if (!tls_available_ciphers) {
++        *cipher_num = 0;
++        return PJ_ENOTFOUND;
++    }
++
++    *cipher_num = PJ_MIN(*cipher_num, tls_available_ciphers);
++
++    for (i = 0; i < *cipher_num; ++i)
++        ciphers[i] = tls_ciphers[i].id;
++
++    return PJ_SUCCESS;
++}
++
++
++/* Get cipher name string. */
++PJ_DEF(const char *)pj_ssl_cipher_name(pj_ssl_cipher cipher)
++{
++    unsigned int i;
++
++    tls_ciphers_fill();
++
++    for (i = 0; i < tls_available_ciphers; ++i) {
++        if (cipher == tls_ciphers[i].id)
++            return tls_ciphers[i].name;
++    }
++
++    return NULL;
++}
++
++
++/* Get cipher identifier. */
++PJ_DEF(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name)
++{
++    unsigned int i;
++
++    tls_ciphers_fill();
++
++    for (i = 0; i < tls_available_ciphers; ++i) {
++        if (!pj_ansi_stricmp(tls_ciphers[i].name, cipher_name))
++            return tls_ciphers[i].id;
++    }
++
++    return PJ_TLS_UNKNOWN_CIPHER;
++}
++
++
++/* Check if the specified cipher is supported by the TLS backend. */
++PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher)
++{
++    unsigned int i;
++
++    tls_ciphers_fill();
++
++    for (i = 0; i < tls_available_ciphers; ++i) {
++        if (cipher == tls_ciphers[i].id)
++            return PJ_TRUE;
++    }
++
++    return PJ_FALSE;
++}
++
++/* Create SSL socket instance. */
++PJ_DEF(pj_status_t) pj_ssl_sock_create(pj_pool_t *pool,
++                                       const pj_ssl_sock_param *param,
++                                       pj_ssl_sock_t **p_ssock)
++{
++    pj_ssl_sock_t *ssock;
++    pj_status_t status;
++
++    PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
++    PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP);
++
++    pool = pj_pool_create(pool->factory, "tls%p", 512, 512, NULL);
++
++    /* Create secure socket */
++    ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t);
++    ssock->pool = pool;
++    ssock->sock = PJ_INVALID_SOCKET;
++    ssock->connection_state = TLS_STATE_NULL;
++    pj_list_init(&ssock->write_pending);
++    pj_list_init(&ssock->write_pending_empty);
++    pj_list_init(&ssock->send_pending);
++    pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer);
++    pj_ioqueue_op_key_init(&ssock->handshake_op_key,
++                           sizeof(pj_ioqueue_op_key_t));
++
++    /* Create secure socket mutex */
++    status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
++                                            &ssock->circ_buf_output_mutex);
++    if (status != PJ_SUCCESS)
++        return status;
++
++    /* Create input circular buffer mutex */
++    status = pj_lock_create_simple_mutex(pool, pool->obj_name,
++                                         &ssock->circ_buf_input_mutex);
++    if (status != PJ_SUCCESS)
++        return status;
++
++    /* Create output circular buffer mutex */
++    status = pj_lock_create_simple_mutex(pool, pool->obj_name,
++                                         &ssock->circ_buf_output_mutex);
++    if (status != PJ_SUCCESS)
++        return status;
++
++    /* Init secure socket param */
++    ssock->param = *param;
++    ssock->param.read_buffer_size = ((ssock->param.read_buffer_size + 7) >> 3) << 3;
++
++    if (param->ciphers_num > 0) {
++        unsigned int i;
++        ssock->param.ciphers = (pj_ssl_cipher *)
++                               pj_pool_calloc(pool, param->ciphers_num,
++                                              sizeof(pj_ssl_cipher));
++        if (!ssock->param.ciphers)
++            return PJ_ENOMEM;
++
++        for (i = 0; i < param->ciphers_num; ++i)
++            ssock->param.ciphers[i] = param->ciphers[i];
++    }
++
++    /* Server name must be null-terminated */
++    pj_strdup_with_null(pool, &ssock->param.server_name, &param->server_name);
++
++    /* Finally */
++    *p_ssock = ssock;
++
++    return PJ_SUCCESS;
++}
++
++
++/*
++ * Close the secure socket. This will unregister the socket from the
++ * ioqueue and ultimately close the socket.
++ */
++PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
++{
++    pj_pool_t *pool;
++
++    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
++
++    if (!ssock->pool)
++        return PJ_SUCCESS;
++
++    if (ssock->timer.id != TIMER_NONE) {
++        pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
++        ssock->timer.id = TIMER_NONE;
++    }
++
++    tls_sock_reset(ssock);
++
++    pj_lock_destroy(ssock->circ_buf_output_mutex);
++    pj_lock_destroy(ssock->circ_buf_input_mutex);
++
++    pool = ssock->pool;
++    ssock->pool = NULL;
++    if (pool)
++        pj_pool_release(pool);
++
++    return PJ_SUCCESS;
++}
++
++
++/* Associate arbitrary data with the secure socket. */
++PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock,
++                                              void *user_data)
++{
++    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
++
++    ssock->param.user_data = user_data;
++    return PJ_SUCCESS;
++}
++
++
++/* Retrieve the user data previously associated with this secure socket. */
++PJ_DEF(void *)pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
++{
++    PJ_ASSERT_RETURN(ssock, NULL);
++
++    return ssock->param.user_data;
++}
++
++
++/* Retrieve the local address and port used by specified SSL socket. */
++PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
++                                          pj_ssl_sock_info *info)
++{
++    pj_bzero(info, sizeof(*info));
++
++    /* Established flag */
++    info->established = (ssock->connection_state == TLS_STATE_ESTABLISHED);
++
++    /* Protocol */
++    info->proto = ssock->param.proto;
++
++    /* Local address */
++    pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
++
++    if (info->established) {
++        int i;
++        gnutls_cipher_algorithm_t lookup;
++        gnutls_cipher_algorithm_t cipher;
++
++        /* Current cipher */
++        cipher = gnutls_cipher_get(ssock->session);
++        for (i = 0; ; i++) {
++            unsigned char id[2];
++            const char *suite = gnutls_cipher_suite_info(i, (unsigned char *)id,
++                                                         NULL, &lookup, NULL,
++                                                         NULL);
++            if (suite) {
++                if (lookup == cipher) {
++                    info->cipher = (pj_uint32_t) ((id[0] << 8) | id[1]);
++                    break;
++                }
++            } else
++                break;
++        }
++
++        /* Remote address */
++        pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr);
++
++        /* Certificates info */
++        info->local_cert_info = &ssock->local_cert_info;
++        info->remote_cert_info = &ssock->remote_cert_info;
++
++        /* Verification status */
++        info->verify_status = ssock->verify_status;
++    }
++
++    /* Last known GnuTLS error code */
++    info->last_native_err = ssock->last_err;
++
++    return PJ_SUCCESS;
++}
++
++
++/* Starts read operation on this secure socket. */
++PJ_DEF(pj_status_t) pj_ssl_sock_start_read(pj_ssl_sock_t *ssock,
++                                           pj_pool_t *pool,
++                                           unsigned buff_size,
++                                           pj_uint32_t flags)
++{
++    void **readbuf;
++    unsigned int i;
++
++    PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
++    PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED,
++                     PJ_EINVALIDOP);
++
++    readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt,
++                                      sizeof(void *));
++    if (!readbuf)
++        return PJ_ENOMEM;
++
++    for (i = 0; i < ssock->param.async_cnt; ++i) {
++        readbuf[i] = pj_pool_alloc(pool, buff_size);
++        if (!readbuf[i])
++            return PJ_ENOMEM;
++    }
++
++    return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags);
++}
++
++
++/*
++ * Same as #pj_ssl_sock_start_read(), except that the application
++ * supplies the buffers for the read operation so that the acive socket
++ * does not have to allocate the buffers.
++ */
++PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
++                                             pj_pool_t *pool,
++                                             unsigned buff_size,
++                                             void *readbuf[],
++                                             pj_uint32_t flags)
++{
++    unsigned int i;
++
++    PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL);
++    PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED,
++                     PJ_EINVALIDOP);
++
++    /* Create SSL socket read buffer */
++    ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool,
++                                                     ssock->param.async_cnt,
++                                                     sizeof(read_data_t));
++    if (!ssock->ssock_rbuf)
++        return PJ_ENOMEM;
++
++    /* Store SSL socket read buffer pointer in the activesock read buffer */
++    for (i = 0; i < ssock->param.async_cnt; ++i) {
++        read_data_t **p_ssock_rbuf =
++                        OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]);
++
++        ssock->ssock_rbuf[i].data = readbuf[i];
++        ssock->ssock_rbuf[i].len = 0;
++
++        *p_ssock_rbuf = &ssock->ssock_rbuf[i];
++    }
++
++    ssock->read_size = buff_size;
++    ssock->read_started = PJ_TRUE;
++    ssock->read_flags = flags;
++
++    return PJ_SUCCESS;
++}
++
++
++/*
++ * Same as pj_ssl_sock_start_read(), except that this function is used
++ * only for datagram sockets, and it will trigger \a on_data_recvfrom()
++ * callback instead.
++ */
++PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
++                                                pj_pool_t *pool,
++                                                unsigned buff_size,
++                                                pj_uint32_t flags)
++{
++    PJ_UNUSED_ARG(ssock);
++    PJ_UNUSED_ARG(pool);
++    PJ_UNUSED_ARG(buff_size);
++    PJ_UNUSED_ARG(flags);
++
++    return PJ_ENOTSUP;
++}
++
++
++/*
++ * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom()
++ * operation takes the buffer from the argument rather than creating
++ * new ones.
++ */
++PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
++                                                 pj_pool_t *pool,
++                                                 unsigned buff_size,
++                                                 void *readbuf[],
++                                                 pj_uint32_t flags)
++{
++    PJ_UNUSED_ARG(ssock);
++    PJ_UNUSED_ARG(pool);
++    PJ_UNUSED_ARG(buff_size);
++    PJ_UNUSED_ARG(readbuf);
++    PJ_UNUSED_ARG(flags);
++
++    return PJ_ENOTSUP;
++}
++
++
++/*
++ * Write the plain data to GnuTLS, it will be encrypted by gnutls_record_send()
++ * and sent via tls_data_push. Note that re-negotitation may be on progress, so
++ * sending data should be delayed until re-negotiation is completed.
++ */
++static pj_status_t tls_write(pj_ssl_sock_t *ssock,
++                             pj_ioqueue_op_key_t *send_key,
++                             const void *data, pj_ssize_t size, unsigned flags)
++{
++    pj_status_t status;
++    int nwritten;
++    pj_ssize_t total_written = 0;
++
++    /* Ask GnuTLS to encrypt our plaintext now. GnuTLS will use the push
++     * callback to actually write the encrypted bytes into our output circular
++     * buffer. GnuTLS may refuse to "send" everything at once, but since we are
++     * not really sending now, we will just call it again now until it succeeds
++     * (or fails in a fatal way). */
++    while (total_written < size) {
++        /* Try encrypting using GnuTLS */
++        nwritten = gnutls_record_send(ssock->session, data + total_written,
++                                      size);
++
++        if (nwritten > 0) {
++            /* Good, some data was encrypted and written */
++            total_written += nwritten;
++        } else {
++            /* Normally we would have to retry record_send but our internal
++             * state has not changed, so we have to ask for more data first.
++             * We will just try again later, although this should never happen.
++             */
++            return tls_status_from_err(ssock, nwritten);
++        }
++    }
++
++    /* All encrypted data is written to the output circular buffer;
++     * now send it on the socket (or notify problem). */
++    if (total_written == size)
++        status = flush_circ_buf_output(ssock, send_key, size, flags);
++    else
++        status = PJ_ENOMEM;
++
++    return status;
++}
++
++
++/* Flush delayed data sending in the write pending list. */
++static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock)
++{
++    /* Check for another ongoing flush */
++    if (ssock->flushing_write_pend) {
++        return PJ_EBUSY;
++    }
++
++    pj_lock_acquire(ssock->circ_buf_output_mutex);
++
++    /* Again, check for another ongoing flush */
++    if (ssock->flushing_write_pend) {
++        pj_lock_release(ssock->circ_buf_output_mutex);
++        return PJ_EBUSY;
++    }
++
++    /* Set ongoing flush flag */
++    ssock->flushing_write_pend = PJ_TRUE;
++
++    while (!pj_list_empty(&ssock->write_pending)) {
++        write_data_t *wp;
++        pj_status_t status;
++
++        wp = ssock->write_pending.next;
++
++        /* Ticket #1573: Don't hold mutex while calling socket send. */
++        pj_lock_release(ssock->circ_buf_output_mutex);
++
++        status = tls_write(ssock, &wp->key, wp->data.ptr,
++                           wp->plain_data_len, wp->flags);
++        if (status != PJ_SUCCESS) {
++            /* Reset ongoing flush flag first. */
++            ssock->flushing_write_pend = PJ_FALSE;
++            return status;
++        }
++
++        pj_lock_acquire(ssock->circ_buf_output_mutex);
++        pj_list_erase(wp);
++        pj_list_push_back(&ssock->write_pending_empty, wp);
++    }
++
++    /* Reset ongoing flush flag */
++    ssock->flushing_write_pend = PJ_FALSE;
++
++    pj_lock_release(ssock->circ_buf_output_mutex);
++
++    return PJ_SUCCESS;
++}
++
++
++/* Sending is delayed, push back the sending data into pending list. */
++static pj_status_t delay_send(pj_ssl_sock_t *ssock,
++                              pj_ioqueue_op_key_t *send_key,
++                              const void *data, pj_ssize_t size,
++                              unsigned flags)
++{
++    write_data_t *wp;
++
++    pj_lock_acquire(ssock->circ_buf_output_mutex);
++
++    /* Init write pending instance */
++    if (!pj_list_empty(&ssock->write_pending_empty)) {
++        wp = ssock->write_pending_empty.next;
++        pj_list_erase(wp);
++    } else {
++        wp = PJ_POOL_ZALLOC_T(ssock->pool, write_data_t);
++    }
++
++    wp->app_key = send_key;
++    wp->plain_data_len = size;
++    wp->data.ptr = data;
++    wp->flags = flags;
++
++    pj_list_push_back(&ssock->write_pending, wp);
++
++    pj_lock_release(ssock->circ_buf_output_mutex);
++
++    /* Must return PJ_EPENDING */
++    return PJ_EPENDING;
++}
++
++
++/**
++ * Send data using the socket.
++ */
++PJ_DEF(pj_status_t) pj_ssl_sock_send(pj_ssl_sock_t *ssock,
++                                     pj_ioqueue_op_key_t *send_key,
++                                     const void *data, pj_ssize_t *size,
++                                     unsigned flags)
++{
++    pj_status_t status;
++
++    PJ_ASSERT_RETURN(ssock && data && size && (*size > 0), PJ_EINVAL);
++    PJ_ASSERT_RETURN(ssock->connection_state==TLS_STATE_ESTABLISHED,
++                     PJ_EINVALIDOP);
++
++    /* Flush delayed send first. Sending data might be delayed when
++     * re-negotiation is on-progress. */
++    status = flush_delayed_send(ssock);
++    if (status == PJ_EBUSY) {
++        /* Re-negotiation or flushing is on progress, delay sending */
++        status = delay_send(ssock, send_key, data, *size, flags);
++        goto on_return;
++    } else if (status != PJ_SUCCESS) {
++        goto on_return;
++    }
++
++    /* Write data to SSL */
++    status = tls_write(ssock, send_key, data, *size, flags);
++    if (status == PJ_EBUSY) {
++        /* Re-negotiation is on progress, delay sending */
++        status = delay_send(ssock, send_key, data, *size, flags);
++    }
++
++on_return:
++    return status;
++}
++
++
++/**
++ * Send datagram using the socket.
++ */
++PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
++                                        pj_ioqueue_op_key_t *send_key,
++                                        const void *data, pj_ssize_t *size,
++                                        unsigned flags,
++                                        const pj_sockaddr_t *addr, int addr_len)
++{
++    PJ_UNUSED_ARG(ssock);
++    PJ_UNUSED_ARG(send_key);
++    PJ_UNUSED_ARG(data);
++    PJ_UNUSED_ARG(size);
++    PJ_UNUSED_ARG(flags);
++    PJ_UNUSED_ARG(addr);
++    PJ_UNUSED_ARG(addr_len);
++
++    return PJ_ENOTSUP;
++}
++
++
++/**
++ * Starts asynchronous socket accept() operations on this secure socket.
++ */
++PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
++                                              pj_pool_t *pool,
++                                              const pj_sockaddr_t *localaddr,
++                                              int addr_len)
++{
++    pj_activesock_cb asock_cb;
++    pj_activesock_cfg asock_cfg;
++    pj_status_t status;
++
++    PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL);
++
++    /* Create socket */
++    status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0,
++                            &ssock->sock);
++    if (status != PJ_SUCCESS)
++        goto on_error;
++
++    /* Apply SO_REUSEADDR */
++    if (ssock->param.reuse_addr) {
++        int enabled = 1;
++        status = pj_sock_setsockopt(ssock->sock, pj_SOL_SOCKET(),
++                                    pj_SO_REUSEADDR(),
++                                    &enabled, sizeof(enabled));
++        if (status != PJ_SUCCESS) {
++            PJ_PERROR(4,(ssock->pool->obj_name, status,
++                         "Warning: error applying SO_REUSEADDR"));
++        }
++    }
++
++    /* Apply QoS, if specified */
++    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
++                                &ssock->param.qos_params, 2,
++                                ssock->pool->obj_name, NULL);
++    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
++        goto on_error;
++
++    /* Bind socket */
++    status = pj_sock_bind(ssock->sock, localaddr, addr_len);
++    if (status != PJ_SUCCESS)
++        goto on_error;
++
++    /* Start listening to the address */
++    status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN);
++    if (status != PJ_SUCCESS)
++        goto on_error;
++
++    /* Create active socket */
++    pj_activesock_cfg_default(&asock_cfg);
++    asock_cfg.async_cnt = ssock->param.async_cnt;
++    asock_cfg.concurrency = ssock->param.concurrency;
++    asock_cfg.whole_data = PJ_TRUE;
++
++    pj_bzero(&asock_cb, sizeof(asock_cb));
++    asock_cb.on_accept_complete = asock_on_accept_complete;
++
++    status = pj_activesock_create(pool,
++                                  ssock->sock,
++                                  ssock->param.sock_type,
++                                  &asock_cfg,
++                                  ssock->param.ioqueue,
++                                  &asock_cb,
++                                  ssock,
++                                  &ssock->asock);
++
++    if (status != PJ_SUCCESS)
++        goto on_error;
++
++    /* Start accepting */
++    status = pj_activesock_start_accept(ssock->asock, pool);
++    if (status != PJ_SUCCESS)
++        goto on_error;
++
++    /* Update local address */
++    ssock->addr_len = addr_len;
++    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
++                                 &ssock->addr_len);
++    if (status != PJ_SUCCESS)
++        pj_sockaddr_cp(&ssock->local_addr, localaddr);
++
++    ssock->is_server = PJ_TRUE;
++
++    return PJ_SUCCESS;
++
++on_error:
++    tls_sock_reset(ssock);
++    return status;
++}
++
++
++/**
++ * Starts asynchronous socket connect() operation.
++ */
++PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock,
++                                               pj_pool_t *pool,
++                                               const pj_sockaddr_t *localaddr,
++                                               const pj_sockaddr_t *remaddr,
++                                               int addr_len)
++{
++    pj_activesock_cb asock_cb;
++    pj_activesock_cfg asock_cfg;
++    pj_status_t status;
++
++    PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
++                     PJ_EINVAL);
++
++    /* Create socket */
++    status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0,
++                            &ssock->sock);
++    if (status != PJ_SUCCESS)
++        goto on_error;
++
++    /* Apply QoS, if specified */
++    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
++                                &ssock->param.qos_params, 2,
++                                ssock->pool->obj_name, NULL);
++    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
++        goto on_error;
++
++    /* Bind socket */
++    status = pj_sock_bind(ssock->sock, localaddr, addr_len);
++    if (status != PJ_SUCCESS)
++        goto on_error;
++
++    /* Create active socket */
++    pj_activesock_cfg_default(&asock_cfg);
++    asock_cfg.async_cnt = ssock->param.async_cnt;
++    asock_cfg.concurrency = ssock->param.concurrency;
++    asock_cfg.whole_data = PJ_TRUE;
++
++    pj_bzero(&asock_cb, sizeof(asock_cb));
++    asock_cb.on_connect_complete = asock_on_connect_complete;
++    asock_cb.on_data_read = asock_on_data_read;
++    asock_cb.on_data_sent = asock_on_data_sent;
++
++    status = pj_activesock_create(pool,
++                                  ssock->sock,
++                                  ssock->param.sock_type,
++                                  &asock_cfg,
++                                  ssock->param.ioqueue,
++                                  &asock_cb,
++                                  ssock,
++                                  &ssock->asock);
++
++    if (status != PJ_SUCCESS)
++        goto on_error;
++
++    /* Save remote address */
++    pj_sockaddr_cp(&ssock->rem_addr, remaddr);
++
++    /* Start timer */
++    if (ssock->param.timer_heap &&
++        (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0))
++    {
++        pj_assert(ssock->timer.id == TIMER_NONE);
++        ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
++        status = pj_timer_heap_schedule(ssock->param.timer_heap,
++                                        &ssock->timer,
++                                        &ssock->param.timeout);
++        if (status != PJ_SUCCESS)
++            ssock->timer.id = TIMER_NONE;
++    }
++
++    status = pj_activesock_start_connect(ssock->asock, pool, remaddr,
++                                         addr_len);
++
++    if (status == PJ_SUCCESS)
++        asock_on_connect_complete(ssock->asock, PJ_SUCCESS);
++    else if (status != PJ_EPENDING)
++        goto on_error;
++
++    /* Update local address */
++    ssock->addr_len = addr_len;
++    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
++                                 &ssock->addr_len);
++    /* Note that we may not get an IP address here. This can
++     * happen for example on Windows, where getsockname()
++     * would return 0.0.0.0 if socket has just started the
++     * async connect. In this case, just leave the local
++     * address with 0.0.0.0 for now; it will be updated
++     * once the socket is established.
++     */
++
++    /* Update socket state */
++    ssock->is_server = PJ_FALSE;
++
++    return PJ_EPENDING;
++
++on_error:
++    tls_sock_reset(ssock);
++    return status;
++}
++
++
++PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock)
++{
++    int status;
++
++    /* Nothing established yet */
++    PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED,
++                     PJ_EINVALIDOP);
++
++    /* Cannot renegotiate; we're a client */
++    /* FIXME: in fact maybe that's not true */
++    PJ_ASSERT_RETURN(!ssock->is_server, PJ_EINVALIDOP);
++
++    /* First call gnutls_rehandshake() to see if this is even possible */
++    status = gnutls_rehandshake(ssock->session);
++
++    if (status == GNUTLS_E_SUCCESS) {
++        /* Rehandshake is possible, so try a GnuTLS handshake now. The eventual
++         * gnutls_record_recv() calls could return a few specific values during
++         * this state:
++         *
++         *   - GNUTLS_E_REHANDSHAKE: rehandshake message processing
++         *   - GNUTLS_E_WARNING_ALERT_RECEIVED: client does not wish to
++         *                                      renegotiate
++         */
++        ssock->connection_state = TLS_STATE_HANDSHAKING;
++        status = tls_try_handshake(ssock);
++
++        return status;
++    } else {
++        return tls_status_from_err(ssock, status);
++    }
++}
++
++#endif /* PJ_HAS_SSL_SOCK */
+diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
+index 7129f21..ed57d56 100644
+--- a/pjlib/src/pj/ssl_sock_ossl.c
++++ b/pjlib/src/pj/ssl_sock_ossl.c
+@@ -31,8 +31,10 @@
+ #include <pj/timer.h>
+ 
+ 
+-/* Only build when PJ_HAS_SSL_SOCK is enabled */
+-#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0
++/* Only build when PJ_HAS_SSL_SOCK is enabled and when PJ_HAS_TLS_SOCK is
++ * disabled (meaning GnuTLS is off) */
++#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \
++    defined(PJ_HAS_TLS_SOCK) && PJ_HAS_TLS_SOCK == 0
+ 
+ #define THIS_FILE		"ssl_sock_ossl.c"
+ 
+-- 
+1.8.3.2
+
diff --git a/contrib/src/pjproject/ipv6.patch b/contrib/src/pjproject/ipv6.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8b42fbbda1bd8ab36f61182e0d4cf6d096b26418
--- /dev/null
+++ b/contrib/src/pjproject/ipv6.patch
@@ -0,0 +1,11 @@
+--- a/pjlib/include/pj/config.h
++++ b/pjlib/include/pj/config.h
+@@ -549,7 +549,7 @@
+  * Default: 0 (disabled, for now)
+  */
+ #ifndef PJ_HAS_IPV6
+-#  define PJ_HAS_IPV6		    0
++#  define PJ_HAS_IPV6		    1
+ #endif
+ 
+  /**
diff --git a/contrib/src/pjproject/notestsapps.patch b/contrib/src/pjproject/notestsapps.patch
new file mode 100644
index 0000000000000000000000000000000000000000..662f1dbf1630e69c90863bc98a9843f71a09769f
--- /dev/null
+++ b/contrib/src/pjproject/notestsapps.patch
@@ -0,0 +1,97 @@
+diff --git a/Makefile b/Makefile
+index 33a4e6b..a486eb7 100644
+--- a/Makefile
++++ b/Makefile
+@@ -4,7 +4,7 @@ include build/host-$(HOST_NAME).mak
+ include version.mak
+ 
+ LIB_DIRS = pjlib/build pjlib-util/build pjnath/build third_party/build pjmedia/build pjsip/build
+-DIRS = $(LIB_DIRS) pjsip-apps/build $(EXTRA_DIRS)
++DIRS = $(LIB_DIRS) $(EXTRA_DIRS)
+ 
+ ifdef MINSIZE
+ MAKE_FLAGS := MINSIZE=1
+diff --git a/pjlib-util/build/Makefile b/pjlib-util/build/Makefile
+index cb601cb..862a78a 100644
+--- a/pjlib-util/build/Makefile
++++ b/pjlib-util/build/Makefile
+@@ -54,7 +54,6 @@ export UTIL_TEST_OBJS += xml.o encryption.o stun.o resolver_test.o test.o \
+ export UTIL_TEST_CFLAGS += $(_CFLAGS)
+ export UTIL_TEST_CXXFLAGS += $(_CXXFLAGS)
+ export UTIL_TEST_LDFLAGS += $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
+-export UTIL_TEST_EXE:=pjlib-util-test-$(TARGET_NAME)$(HOST_EXE)
+ 
+ 	
+ export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT 
+diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile
+index 1e64950..a75fa65 100644
+--- a/pjlib/build/Makefile
++++ b/pjlib/build/Makefile
+@@ -56,7 +56,6 @@ export TEST_OBJS += activesock.o atomic.o echo_clt.o errno.o exception.o \
+ export TEST_CFLAGS += $(_CFLAGS)
+ export TEST_CXXFLAGS += $(_CXXFLAGS)
+ export TEST_LDFLAGS += $(PJLIB_LDLIB) $(_LDFLAGS)
+-export TEST_EXE := pjlib-test-$(TARGET_NAME)$(HOST_EXE)
+ 
+ 
+ export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT 
+diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile
+index 8012cb7..2ca283a 100644
+--- a/pjmedia/build/Makefile
++++ b/pjmedia/build/Makefile
+@@ -165,7 +165,6 @@ export PJMEDIA_TEST_LDFLAGS += $(PJMEDIA_CODEC_LDLIB) \
+ 			       $(PJLIB_UTIL_LDLIB) \
+ 			       $(PJNATH_LDLIB) \
+ 			       $(_LDFLAGS)
+-export PJMEDIA_TEST_EXE:=pjmedia-test-$(TARGET_NAME)$(HOST_EXE)
+ 
+ 	
+ export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT 
+diff --git a/pjnath/build/Makefile b/pjnath/build/Makefile
+index 1bc08b5..109f79b 100644
+--- a/pjnath/build/Makefile
++++ b/pjnath/build/Makefile
+@@ -54,7 +54,6 @@ export PJNATH_TEST_OBJS += ice_test.o stun.o sess_auth.o server.o concur_test.o
+ export PJNATH_TEST_CFLAGS += $(_CFLAGS)
+ export PJNATH_TEST_CXXFLAGS += $(_CXXFLAGS)
+ export PJNATH_TEST_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
+-export PJNATH_TEST_EXE:=pjnath-test-$(TARGET_NAME)$(HOST_EXE)
+ 
+ 	
+ ###############################################################################
+@@ -65,7 +64,6 @@ export PJTURN_CLIENT_OBJS += client_main.o
+ export PJTURN_CLIENT_CFLAGS += $(_CFLAGS)
+ export PJTURN_CLIENT_CXXFLAGS += $(_CXXFLAGS)
+ export PJTURN_CLIENT_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
+-export PJTURN_CLIENT_EXE:=pjturn-client-$(TARGET_NAME)$(HOST_EXE)
+ 
+ ###############################################################################
+ # Defines for building TURN server application
+@@ -76,7 +74,6 @@ export PJTURN_SRV_OBJS += allocation.o auth.o listener_udp.o \
+ export PJTURN_SRV_CFLAGS += $(_CFLAGS)
+ export PJTURN_SRV_CXXFLAGS += $(_CXXFLAGS)
+ export PJTURN_SRV_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
+-export PJTURN_SRV_EXE:=pjturn-srv-$(TARGET_NAME)$(HOST_EXE)
+ 
+ 	
+ 	
+diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile
+index d2a5c2a..7e2ec60 100644
+--- a/pjsip/build/Makefile
++++ b/pjsip/build/Makefile
+@@ -165,7 +165,6 @@ export PJSUA2_TEST_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
+ export PJSUA2_TEST_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS)
+ export PJSUA2_TEST_CXXFLAGS = $(PJSUA2_LIB_CFLAGS) 
+ export PJSUA2_TEST_LDFLAGS += $(PJ_LDXXFLAGS) $(PJ_LDXXLIBS) $(LDFLAGS)
+-export PJSUA2_TEST_EXE := pjsua2-test-$(TARGET_NAME)$(HOST_EXE)
+ 
+ export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT 
+ 
+@@ -195,7 +194,6 @@ export TEST_LDFLAGS += $(PJSIP_LDLIB) \
+ 		       $(PJLIB_UTIL_LDLIB) \
+ 		       $(PJNATH_LDLIB) \
+ 		       $(_LDFLAGS)
+-export TEST_EXE := pjsip-test-$(TARGET_NAME)$(HOST_EXE)
+ 
+ 	
+ export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT 
diff --git a/contrib/src/pjproject/rules.mak b/contrib/src/pjproject/rules.mak
new file mode 100644
index 0000000000000000000000000000000000000000..0dc96862a1faee9aa3f054daf5c86abb9a39697c
--- /dev/null
+++ b/contrib/src/pjproject/rules.mak
@@ -0,0 +1,57 @@
+# PJPROJECT
+PJPROJECT_VERSION := 2.2.1
+PJPROJECT_URL := http://www.pjsip.org/release/$(PJPROJECT_VERSION)/pjproject-$(PJPROJECT_VERSION).tar.bz2
+
+PJPROJECT_OPTIONS := --disable-oss          \
+                     --disable-sound        \
+                     --disable-video        \
+                     --enable-ext-sound     \
+                     --disable-speex-aec    \
+                     --disable-g711-codec   \
+                     --disable-l16-codec    \
+                     --disable-gsm-codec    \
+                     --disable-g722-codec   \
+                     --disable-g7221-codec  \
+                     --disable-speex-codec  \
+                     --disable-ilbc-codec   \
+                     --disable-opencore-amr \
+                     --disable-sdl          \
+                     --disable-ffmpeg       \
+                     --disable-v4l2         \
+                     --enable-ssl=gnutls
+
+ifdef HAVE_ANDROID
+PJPROJECT_OPTIONS += --with-ssl=$(PREFIX)
+endif
+
+PKGS += pjproject
+# nominally 2.2.0 is enough, but it has to be patched for gnutls
+#ifeq ($(call need_pkg,'libpjproject >= 2.2.0'),)
+#PKGS_FOUND += pjproject
+#endif
+
+DEPS_pjproject += gnutls
+ifndef HAVE_MACOSX
+DEPS_pjproject += uuid
+endif
+
+$(TARBALLS)/pjproject-$(PJPROJECT_VERSION).tar.bz2:
+	$(call download,$(PJPROJECT_URL))
+
+.sum-pjproject: pjproject-$(PJPROJECT_VERSION).tar.bz2
+
+pjproject: pjproject-$(PJPROJECT_VERSION).tar.bz2 .sum-pjproject
+	$(UNPACK)
+	$(APPLY) $(SRC)/pjproject/aconfigureupdate.patch
+	$(APPLY) $(SRC)/pjproject/endianness.patch
+	$(APPLY) $(SRC)/pjproject/unknowncipher.patch
+	$(APPLY) $(SRC)/pjproject/gnutls.patch
+	$(APPLY) $(SRC)/pjproject/notestsapps.patch
+	$(APPLY) $(SRC)/pjproject/ipv6.patch
+	$(UPDATE_AUTOCONFIG)
+	$(MOVE)
+
+.pjproject: pjproject
+	cd $< && $(HOSTVARS) ./aconfigure $(HOSTCONF) $(PJPROJECT_OPTIONS)
+	cd $< && $(MAKE) && $(MAKE) install
+	touch $@
diff --git a/contrib/src/pjproject/unknowncipher.patch b/contrib/src/pjproject/unknowncipher.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b9e86ec753dabf0b6ce51adc59b6fc4bc40038a2
--- /dev/null
+++ b/contrib/src/pjproject/unknowncipher.patch
@@ -0,0 +1,12 @@
+--- a/pjlib/include/pj/ssl_sock.h	2013-04-26 02:01:43.000000000 -0400
++++ b/pjlib/include/pj/ssl_sock.h	2014-06-16 18:31:58.464991714 -0400
+@@ -243,6 +243,9 @@
+  */
+ typedef enum pj_ssl_cipher {
+ 
++    /* Unsupported cipher */
++    PJ_TLS_UNKNOWN_CIPHER                       = -1,
++
+     /* NULL */
+     PJ_TLS_NULL_WITH_NULL_NULL               	= 0x00000000,
+